* modules/*: sanitization of the modules description strings.
[vlc.git] / modules / video_output / wingdi.c
1 /*****************************************************************************
2  * wingdi.c : Win32 / WinCE GDI video output plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2002 VideoLAN
5  * $Id: wingdi.c,v 1.6 2003/03/30 18:14:38 gbazin Exp $
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>                                      /* malloc(), free() */
28 #include <string.h>
29
30 #include <vlc/vlc.h>
31 #include <vlc/vout.h>
32
33 #define WIN32_LEAN_AND_MEAN
34 #include <windows.h>
35
36 #define MAX_DIRECTBUFFERS 10
37
38 /*****************************************************************************
39  * Local prototypes
40  *****************************************************************************/
41 static int  OpenVideo  ( vlc_object_t * );
42 static void CloseVideo ( vlc_object_t * );
43
44 static int  Init      ( vout_thread_t * );
45 static void End       ( vout_thread_t * );
46 static int  Manage    ( vout_thread_t * );
47 static void Render    ( vout_thread_t *, picture_t * );
48 static void Display   ( vout_thread_t *, picture_t * );
49 static void SetPalette( vout_thread_t *, uint16_t *, uint16_t *, uint16_t * );
50
51 static void EventThread        ( vlc_object_t * );
52 static long FAR PASCAL WndProc ( HWND, UINT, WPARAM, LPARAM );
53 static void InitBuffers        ( vout_thread_t * );
54
55 /*****************************************************************************
56  * Private structure
57  *****************************************************************************/
58 struct vout_sys_t
59 {
60     /* The event thread */
61     vlc_object_t * p_event;
62
63     /* Our video output window */
64     HWND window;
65     int  i_depth;
66
67     /* Our offscreen bitmap and its framebuffer */
68     HDC        off_dc;
69     HBITMAP    off_bitmap;
70     uint8_t *  p_buffer;
71
72     BITMAPINFO bitmapinfo;
73     RGBQUAD    red;
74     RGBQUAD    green;
75     RGBQUAD    blue;
76 };
77
78 /*****************************************************************************
79  * Module descriptor
80  *****************************************************************************/
81 vlc_module_begin();
82     set_description( _("Windows GDI video output") );
83     set_capability( "video output", 10 );
84     set_callbacks( OpenVideo, CloseVideo );
85 vlc_module_end();
86
87 /*****************************************************************************
88  * OpenVideo: activate GDI video thread output method
89  *****************************************************************************/
90 static int OpenVideo ( vlc_object_t *p_this )
91 {
92     vout_thread_t * p_vout = (vout_thread_t *)p_this;
93     vlc_value_t val;
94
95     p_vout->p_sys = malloc( sizeof(vout_sys_t) );
96     if( !p_vout->p_sys )
97     {
98         return VLC_ENOMEM;
99     }
100
101     p_vout->p_sys->p_event = vlc_object_create( p_vout, VLC_OBJECT_GENERIC );
102     if( !p_vout->p_sys->p_event )
103     {
104         free( p_vout->p_sys );
105         return VLC_ENOMEM;
106     }
107
108     var_Create( p_vout->p_sys->p_event, "p_vout", VLC_VAR_ADDRESS );
109     val.p_address = (void *)p_vout;
110     var_Set( p_vout->p_sys->p_event, "p_vout", val );
111
112     p_vout->pf_init = Init;
113     p_vout->pf_end = End;
114     p_vout->pf_manage = Manage;
115     p_vout->pf_render = Render;
116     p_vout->pf_display = Display;
117
118     return VLC_SUCCESS;
119 }
120
121 /*****************************************************************************
122  * CloseVideo: deactivate the GDI video output
123  *****************************************************************************/
124 static void CloseVideo ( vlc_object_t *p_this )
125 {
126     vout_thread_t * p_vout = (vout_thread_t *)p_this;
127
128     var_Destroy( p_vout->p_sys->p_event, "p_vout" );
129     vlc_object_destroy( p_vout->p_sys->p_event );
130     free( p_vout->p_sys );
131 }
132
133 /*****************************************************************************
134  * Init: initialize video thread output method
135  *****************************************************************************/
136 static int Init( vout_thread_t *p_vout )
137 {
138     int i_index;
139     picture_t *p_pic;
140
141     if( vlc_thread_create( p_vout->p_sys->p_event, "GDI Event Thread",
142                            EventThread, 0, 1 ) )
143     {
144         msg_Err( p_vout, "cannot spawn EventThread" );
145         return VLC_ETHREAD;
146     }
147
148     I_OUTPUTPICTURES = 0;
149
150     /* Initialize the output structure */
151     switch( p_vout->p_sys->i_depth )
152     {
153         case 8:
154             p_vout->output.i_chroma = VLC_FOURCC('R','G','B','2');
155             p_vout->output.pf_setpalette = SetPalette;
156             break;
157         case 24:
158             p_vout->output.i_chroma = VLC_FOURCC('R','V','3','2');
159             p_vout->output.i_rmask  = 0x00ff0000;
160             p_vout->output.i_gmask  = 0x0000ff00;
161             p_vout->output.i_bmask  = 0x000000ff;
162             break;
163         default:
164         case 16:
165             p_vout->output.i_chroma = VLC_FOURCC('R','V','1','5');
166             p_vout->output.i_rmask  = 0x7c00;
167             p_vout->output.i_gmask  = 0x03e0;
168             p_vout->output.i_bmask  = 0x001f;
169             break;
170     }
171
172     p_vout->output.i_width  = p_vout->render.i_width;
173     p_vout->output.i_height = p_vout->render.i_height;
174     p_vout->output.i_aspect = p_vout->render.i_aspect;
175
176     /* Try to initialize MAX_DIRECTBUFFERS direct buffers */
177     while( I_OUTPUTPICTURES < MAX_DIRECTBUFFERS )
178     {
179         p_pic = NULL;
180
181         /* Find an empty picture slot */
182         for( i_index = 0 ; i_index < VOUT_MAX_PICTURES ; i_index++ )
183         {
184             if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
185             {
186                 p_pic = p_vout->p_picture + i_index;
187                 break;
188             }
189         }
190
191         /* Allocate the picture */
192         if( p_pic == NULL )
193         {
194             break;
195         }
196
197         vout_AllocatePicture( p_vout, p_pic, p_vout->output.i_width,
198                               p_vout->output.i_height,
199                               p_vout->output.i_chroma );
200
201         if( p_pic->i_planes == 0 )
202         {
203             break;
204         }
205
206         p_pic->i_status = DESTROYED_PICTURE;
207         p_pic->i_type   = DIRECT_PICTURE;
208
209         PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
210
211         I_OUTPUTPICTURES++;
212     }
213
214     return VLC_SUCCESS;
215 }
216
217 /*****************************************************************************
218  * End: terminate video thread output method
219  *****************************************************************************/
220 static void End( vout_thread_t *p_vout )
221 {
222     int i_index;
223
224     /* Free the fake output buffers we allocated */
225     for( i_index = I_OUTPUTPICTURES ; i_index ; )
226     {
227         i_index--;
228         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
229     }
230
231     p_vout->p_sys->p_event->b_die = VLC_TRUE;
232     PostMessage( p_vout->p_sys->window, WM_NULL, 0, 0 );
233     vlc_thread_join( p_vout->p_sys->p_event );
234 }
235
236 /*****************************************************************************
237  * Manage: handle events
238  *****************************************************************************
239  * This function should be called regularly by video output thread. It manages
240  * console events. It returns a non null value on error.
241  *****************************************************************************/
242 static int Manage( vout_thread_t *p_vout )
243 {
244     return VLC_SUCCESS;
245 }
246
247 /*****************************************************************************
248  * Render: render previously calculated output
249  *****************************************************************************/
250 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
251 {
252     /* No need to do anything, the fake direct buffers stay as they are */
253 }
254
255 /*****************************************************************************
256  * Display: displays previously rendered output
257  *****************************************************************************/
258 static void Display( vout_thread_t *p_vout, picture_t *p_pic )
259 {
260     /* No need to do anything, the fake direct buffers stay as they are */
261     HDC hdc;
262     int i_src_bytes, i_dest_bytes;
263
264     hdc = GetDC( p_vout->p_sys->window );
265     SelectObject( p_vout->p_sys->off_dc, p_vout->p_sys->off_bitmap );
266
267     /* Stupid GDI is upside-down */
268     i_src_bytes = p_pic->p->i_lines * p_pic->p->i_pitch;
269     i_dest_bytes = 0;
270
271     while( i_src_bytes )
272     {
273         i_src_bytes -= p_pic->p->i_pitch;
274
275         p_vout->p_vlc->pf_memcpy( p_vout->p_sys->p_buffer + i_dest_bytes,
276                                   p_pic->p->p_pixels + i_src_bytes,
277                                   p_pic->p->i_visible_pitch );
278
279         i_dest_bytes += p_pic->p->i_pitch;
280     }
281
282     BitBlt( hdc, 0, 0, p_vout->output.i_width, p_vout->output.i_height,
283             p_vout->p_sys->off_dc, 0, 0, SRCCOPY );
284
285     ReleaseDC( p_vout->p_sys->window, hdc );
286 }
287
288 /*****************************************************************************
289  * SetPalette: sets an 8 bpp palette
290  *****************************************************************************/
291 static void SetPalette( vout_thread_t *p_vout,
292                         uint16_t *red, uint16_t *green, uint16_t *blue )
293 {
294     msg_Err( p_vout, "FIXME: SetPalette unimplemented" );
295 }
296
297 /*****************************************************************************
298  * EventThread: Event handling thread
299  *****************************************************************************/
300 static void EventThread ( vlc_object_t *p_event )
301 {
302     vout_thread_t * p_vout;
303     vlc_value_t     val;
304
305     HINSTANCE  instance;
306     WNDCLASS   wc;
307     MSG        msg;
308
309 #ifdef UNDER_CE
310     wchar_t *psz_class = L"VOUT";
311     wchar_t *psz_title = L"Video Output";
312 #else
313     char *psz_class = "VOUT";
314     char *psz_title = "Video Output";
315 #endif
316
317     var_Get( p_event, "p_vout", &val );
318     p_vout = (vout_thread_t *)val.p_address;
319
320     instance = GetModuleHandle( NULL );
321
322     /* Register window class */
323     memset( &wc, 0, sizeof(wc) );
324     wc.style          = CS_HREDRAW | CS_VREDRAW;
325     wc.lpfnWndProc    = (WNDPROC)WndProc;
326     wc.cbClsExtra     = 0;
327     wc.cbWndExtra     = 0;
328     wc.hInstance      = instance;
329     wc.hIcon          = 0;
330     wc.hCursor        = 0;
331     wc.hbrBackground  = (HBRUSH)GetStockObject( BLACK_BRUSH );
332     wc.lpszMenuName   = 0;
333     wc.lpszClassName  = psz_class;
334
335     RegisterClass( &wc );
336
337     /* Create output window */
338     p_vout->p_sys->window =
339              CreateWindow( psz_class, psz_title,
340                            WS_VISIBLE | WS_SIZEBOX | WS_CAPTION,
341                            CW_USEDEFAULT, CW_USEDEFAULT,
342                            p_vout->render.i_width,
343                            p_vout->render.i_height + 10,
344                            NULL, NULL, instance, (LPVOID)p_vout );
345
346     /* Initialize offscreen buffer */
347     InitBuffers( p_vout );
348
349     /* Tell the video output we're ready to receive data */
350     vlc_thread_ready( p_event );
351
352     /* Display our window */
353     ShowWindow( p_vout->p_sys->window, SW_SHOWNORMAL );
354     UpdateWindow( p_vout->p_sys->window );
355
356     while( !p_event->b_die
357              && GetMessage( &msg, p_vout->p_sys->window, 0, 0 ) )
358     {
359         if( p_event->b_die )
360         {
361             break;
362         }
363
364         switch( msg.message )
365         {
366         case WM_KEYDOWN:
367             switch( msg.wParam )
368             {
369             case VK_ESCAPE:
370                 p_event->p_vlc->b_die = VLC_TRUE;
371                 break;
372             }
373             TranslateMessage( &msg );
374             break;
375
376         case WM_CHAR:
377             switch( msg.wParam )
378             {
379             case 'q':
380             case 'Q':
381                 p_event->p_vlc->b_die = VLC_TRUE;
382                 break;
383             }
384             break;
385
386         default:
387             TranslateMessage( &msg );
388             DispatchMessage( &msg );
389             break;
390         }
391     }
392
393     DestroyWindow( p_vout->p_sys->window );
394
395     DeleteDC( p_vout->p_sys->off_dc );
396     DeleteObject( p_vout->p_sys->off_bitmap );
397 }
398
399 /*****************************************************************************
400  * Message handler for the main window
401  *****************************************************************************/
402 static long FAR PASCAL WndProc ( HWND hWnd, UINT message,
403                                  WPARAM wParam, LPARAM lParam )
404 {
405     /* Caution: this only works */
406     vout_thread_t *p_vout;
407
408     if( message == WM_CREATE )
409     {
410         /* Store p_vout for future use */
411         p_vout = (vout_thread_t *)((CREATESTRUCT *)lParam)->lpCreateParams;
412         SetWindowLong( hWnd, GWL_USERDATA, (LONG)p_vout );
413     }
414     else
415     {
416         p_vout = (vout_thread_t *)GetWindowLong( hWnd, GWL_USERDATA );
417     }
418
419     switch( message )
420     {
421         case WM_LBUTTONDOWN:
422             break;
423         case WM_MOUSEMOVE:
424             break;
425         case WM_LBUTTONUP:
426             break;
427
428         case WM_CREATE:
429             break;
430
431         case WM_DESTROY:
432             PostQuitMessage( 0 );
433             break;
434
435         default:
436             return DefWindowProc( hWnd, message, wParam, lParam );
437    }
438
439    return 0;
440 }
441
442 /*****************************************************************************
443  * InitBuffers: initialize an offscreen bitmap for direct buffer operations.
444  *****************************************************************************/
445 static void InitBuffers( vout_thread_t *p_vout )
446 {
447     BITMAPINFOHEADER * p_header = &p_vout->p_sys->bitmapinfo.bmiHeader;
448     BITMAPINFO *       p_info = &p_vout->p_sys->bitmapinfo;
449     int   i_pixels = p_vout->render.i_height * p_vout->render.i_width;
450     HDC   window_dc;
451
452     window_dc = GetDC( p_vout->p_sys->window );
453
454     /* Get screen properties */
455     p_vout->p_sys->i_depth = GetDeviceCaps( window_dc, PLANES )
456                               * GetDeviceCaps( window_dc, BITSPIXEL );
457     msg_Dbg( p_vout, "GDI depth is %i", p_vout->p_sys->i_depth );
458
459     /* Initialize offscreen bitmap */
460     memset( p_info, 0, sizeof( BITMAPINFO ) + 3 * sizeof( RGBQUAD ) );
461
462     p_header->biSize = sizeof( BITMAPINFOHEADER );
463     p_header->biPlanes = 1;
464     switch( p_vout->p_sys->i_depth )
465     {
466         case 8:
467             p_header->biBitCount = 8;
468             p_header->biSizeImage = i_pixels;
469             p_header->biCompression = BI_RGB;
470             /* FIXME: we need a palette here */
471             break;
472         case 24:
473         case 32:
474             p_header->biBitCount = 32;
475             p_header->biSizeImage = i_pixels * 4;
476             p_header->biCompression = BI_BITFIELDS;
477             ((DWORD*)p_info->bmiColors)[0] = 0x00ff0000;
478             ((DWORD*)p_info->bmiColors)[1] = 0x0000ff00;
479             ((DWORD*)p_info->bmiColors)[2] = 0x000000ff;
480             break;
481         case 16:
482         default:
483             p_header->biBitCount = 16;
484             p_header->biSizeImage = i_pixels * 2;
485             p_header->biCompression = BI_BITFIELDS;
486             ((DWORD*)p_info->bmiColors)[0] = 0x00007c00;
487             ((DWORD*)p_info->bmiColors)[1] = 0x000003e0;
488             ((DWORD*)p_info->bmiColors)[2] = 0x0000001f;
489             break;
490     }
491     p_header->biWidth = p_vout->render.i_width;
492     p_header->biHeight = p_vout->render.i_height;
493     p_header->biClrImportant = 0;
494     p_header->biClrUsed = 0;
495     p_header->biXPelsPerMeter = 0;
496     p_header->biYPelsPerMeter = 0;
497
498     p_vout->p_sys->off_bitmap =
499           CreateDIBSection( window_dc, (BITMAPINFO *)p_header, DIB_RGB_COLORS,
500                             (void**)&p_vout->p_sys->p_buffer, NULL, 0 );
501
502     p_vout->p_sys->off_dc = CreateCompatibleDC( window_dc );
503
504     SelectObject( p_vout->p_sys->off_dc, p_vout->p_sys->off_bitmap );
505     ReleaseDC( 0, window_dc );
506 }
507