]> git.sesse.net Git - vlc/blob - modules/video_output/wingdi.c
* modules/video_output/directx/events.c:
[vlc] / 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.8 2003/12/04 14:48:24 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','2','4');
159             p_vout->output.i_rmask  = 0x00ff0000;
160             p_vout->output.i_gmask  = 0x0000ff00;
161             p_vout->output.i_bmask  = 0x000000ff;
162             break;
163         case 32:
164             p_vout->output.i_chroma = VLC_FOURCC('R','V','3','2');
165             p_vout->output.i_rmask  = 0x00ff0000;
166             p_vout->output.i_gmask  = 0x0000ff00;
167             p_vout->output.i_bmask  = 0x000000ff;
168             break;
169         default:
170         case 16:
171             p_vout->output.i_chroma = VLC_FOURCC('R','V','1','5');
172             p_vout->output.i_rmask  = 0x7c00;
173             p_vout->output.i_gmask  = 0x03e0;
174             p_vout->output.i_bmask  = 0x001f;
175             break;
176     }
177
178     p_vout->output.i_width  = p_vout->render.i_width;
179     p_vout->output.i_height = p_vout->render.i_height;
180     p_vout->output.i_aspect = p_vout->render.i_aspect;
181
182     /* Try to initialize MAX_DIRECTBUFFERS direct buffers */
183     while( I_OUTPUTPICTURES < MAX_DIRECTBUFFERS )
184     {
185         p_pic = NULL;
186
187         /* Find an empty picture slot */
188         for( i_index = 0 ; i_index < VOUT_MAX_PICTURES ; i_index++ )
189         {
190             if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
191             {
192                 p_pic = p_vout->p_picture + i_index;
193                 break;
194             }
195         }
196
197         /* Allocate the picture */
198         if( p_pic == NULL )
199         {
200             break;
201         }
202
203         vout_AllocatePicture( p_vout, p_pic, p_vout->output.i_chroma,
204                               p_vout->output.i_width, p_vout->output.i_height,
205                               p_vout->output.i_aspect );
206
207         if( p_pic->i_planes == 0 )
208         {
209             break;
210         }
211
212         p_pic->i_status = DESTROYED_PICTURE;
213         p_pic->i_type   = DIRECT_PICTURE;
214
215         PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
216
217         I_OUTPUTPICTURES++;
218     }
219
220     return VLC_SUCCESS;
221 }
222
223 /*****************************************************************************
224  * End: terminate video thread output method
225  *****************************************************************************/
226 static void End( vout_thread_t *p_vout )
227 {
228     int i_index;
229
230     /* Free the fake output buffers we allocated */
231     for( i_index = I_OUTPUTPICTURES ; i_index ; )
232     {
233         i_index--;
234         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
235     }
236
237     p_vout->p_sys->p_event->b_die = VLC_TRUE;
238     PostMessage( p_vout->p_sys->window, WM_NULL, 0, 0 );
239     vlc_thread_join( p_vout->p_sys->p_event );
240 }
241
242 /*****************************************************************************
243  * Manage: handle events
244  *****************************************************************************
245  * This function should be called regularly by video output thread. It manages
246  * console events. It returns a non null value on error.
247  *****************************************************************************/
248 static int Manage( vout_thread_t *p_vout )
249 {
250     return VLC_SUCCESS;
251 }
252
253 /*****************************************************************************
254  * Render: render previously calculated output
255  *****************************************************************************/
256 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
257 {
258     /* No need to do anything, the fake direct buffers stay as they are */
259 }
260
261 /*****************************************************************************
262  * Display: displays previously rendered output
263  *****************************************************************************/
264 static void Display( vout_thread_t *p_vout, picture_t *p_pic )
265 {
266     /* No need to do anything, the fake direct buffers stay as they are */
267     HDC hdc;
268     int i_src_bytes, i_dest_bytes;
269
270     hdc = GetDC( p_vout->p_sys->window );
271     SelectObject( p_vout->p_sys->off_dc, p_vout->p_sys->off_bitmap );
272
273     /* Stupid GDI is upside-down */
274     i_src_bytes = p_pic->p->i_lines * p_pic->p->i_pitch;
275     i_dest_bytes = 0;
276
277     while( i_src_bytes )
278     {
279         i_src_bytes -= p_pic->p->i_pitch;
280
281         p_vout->p_vlc->pf_memcpy( p_vout->p_sys->p_buffer + i_dest_bytes,
282                                   p_pic->p->p_pixels + i_src_bytes,
283                                   p_pic->p->i_visible_pitch );
284
285         i_dest_bytes += p_pic->p->i_pitch;
286     }
287
288     BitBlt( hdc, 0, 0, p_vout->output.i_width, p_vout->output.i_height,
289             p_vout->p_sys->off_dc, 0, 0, SRCCOPY );
290
291     ReleaseDC( p_vout->p_sys->window, hdc );
292 }
293
294 /*****************************************************************************
295  * SetPalette: sets an 8 bpp palette
296  *****************************************************************************/
297 static void SetPalette( vout_thread_t *p_vout,
298                         uint16_t *red, uint16_t *green, uint16_t *blue )
299 {
300     msg_Err( p_vout, "FIXME: SetPalette unimplemented" );
301 }
302
303 /*****************************************************************************
304  * EventThread: Event handling thread
305  *****************************************************************************/
306 static void EventThread ( vlc_object_t *p_event )
307 {
308     vout_thread_t * p_vout;
309     vlc_value_t     val;
310
311     HINSTANCE  instance;
312     WNDCLASS   wc;
313     MSG        msg;
314
315 #ifdef UNDER_CE
316     wchar_t *psz_class = L"VOUT";
317     wchar_t *psz_title = L"Video Output";
318 #else
319     char *psz_class = "VOUT";
320     char *psz_title = "Video Output";
321 #endif
322
323     var_Get( p_event, "p_vout", &val );
324     p_vout = (vout_thread_t *)val.p_address;
325
326     instance = GetModuleHandle( NULL );
327
328     /* Register window class */
329     memset( &wc, 0, sizeof(wc) );
330     wc.style          = CS_HREDRAW | CS_VREDRAW;
331     wc.lpfnWndProc    = (WNDPROC)WndProc;
332     wc.cbClsExtra     = 0;
333     wc.cbWndExtra     = 0;
334     wc.hInstance      = instance;
335     wc.hIcon          = 0;
336     wc.hCursor        = 0;
337     wc.hbrBackground  = (HBRUSH)GetStockObject( BLACK_BRUSH );
338     wc.lpszMenuName   = 0;
339     wc.lpszClassName  = psz_class;
340
341     RegisterClass( &wc );
342
343     /* Create output window */
344     p_vout->p_sys->window =
345              CreateWindow( psz_class, psz_title,
346                            WS_VISIBLE | WS_SIZEBOX | WS_CAPTION,
347                            CW_USEDEFAULT, CW_USEDEFAULT,
348                            p_vout->render.i_width,
349                            p_vout->render.i_height + 10,
350                            NULL, NULL, instance, (LPVOID)p_vout );
351
352     /* Initialize offscreen buffer */
353     InitBuffers( p_vout );
354
355     /* Tell the video output we're ready to receive data */
356     vlc_thread_ready( p_event );
357
358     /* Display our window */
359     ShowWindow( p_vout->p_sys->window, SW_SHOWNORMAL );
360     UpdateWindow( p_vout->p_sys->window );
361
362     while( !p_event->b_die
363              && GetMessage( &msg, p_vout->p_sys->window, 0, 0 ) )
364     {
365         if( p_event->b_die )
366         {
367             break;
368         }
369
370         switch( msg.message )
371         {
372         case WM_KEYDOWN:
373             switch( msg.wParam )
374             {
375             case VK_ESCAPE:
376                 p_event->p_vlc->b_die = VLC_TRUE;
377                 break;
378             }
379             TranslateMessage( &msg );
380             break;
381
382         case WM_CHAR:
383             switch( msg.wParam )
384             {
385             case 'q':
386             case 'Q':
387                 p_event->p_vlc->b_die = VLC_TRUE;
388                 break;
389             }
390             break;
391
392         default:
393             TranslateMessage( &msg );
394             DispatchMessage( &msg );
395             break;
396         }
397     }
398
399     DestroyWindow( p_vout->p_sys->window );
400
401     DeleteDC( p_vout->p_sys->off_dc );
402     DeleteObject( p_vout->p_sys->off_bitmap );
403 }
404
405 /*****************************************************************************
406  * Message handler for the main window
407  *****************************************************************************/
408 static long FAR PASCAL WndProc ( HWND hWnd, UINT message,
409                                  WPARAM wParam, LPARAM lParam )
410 {
411     /* Caution: this only works */
412     vout_thread_t *p_vout;
413
414     if( message == WM_CREATE )
415     {
416         /* Store p_vout for future use */
417         p_vout = (vout_thread_t *)((CREATESTRUCT *)lParam)->lpCreateParams;
418         SetWindowLong( hWnd, GWL_USERDATA, (LONG)p_vout );
419     }
420     else
421     {
422         p_vout = (vout_thread_t *)GetWindowLong( hWnd, GWL_USERDATA );
423     }
424
425     switch( message )
426     {
427         case WM_LBUTTONDOWN:
428             break;
429         case WM_MOUSEMOVE:
430             break;
431         case WM_LBUTTONUP:
432             break;
433
434         case WM_CREATE:
435             break;
436
437         case WM_DESTROY:
438             PostQuitMessage( 0 );
439             break;
440
441         default:
442             return DefWindowProc( hWnd, message, wParam, lParam );
443    }
444
445    return 0;
446 }
447
448 /*****************************************************************************
449  * InitBuffers: initialize an offscreen bitmap for direct buffer operations.
450  *****************************************************************************/
451 static void InitBuffers( vout_thread_t *p_vout )
452 {
453     BITMAPINFOHEADER * p_header = &p_vout->p_sys->bitmapinfo.bmiHeader;
454     BITMAPINFO *       p_info = &p_vout->p_sys->bitmapinfo;
455     int   i_pixels = p_vout->render.i_height * p_vout->render.i_width;
456     HDC   window_dc;
457
458     window_dc = GetDC( p_vout->p_sys->window );
459
460     /* Get screen properties */
461     p_vout->p_sys->i_depth = GetDeviceCaps( window_dc, PLANES )
462                               * GetDeviceCaps( window_dc, BITSPIXEL );
463     msg_Dbg( p_vout, "GDI depth is %i", p_vout->p_sys->i_depth );
464
465     /* Initialize offscreen bitmap */
466     memset( p_info, 0, sizeof( BITMAPINFO ) + 3 * sizeof( RGBQUAD ) );
467
468     p_header->biSize = sizeof( BITMAPINFOHEADER );
469     p_header->biSizeImage = 0;
470     p_header->biPlanes = 1;
471     switch( p_vout->p_sys->i_depth )
472     {
473         case 8:
474             p_header->biBitCount = 8;
475             p_header->biCompression = BI_RGB;
476             /* FIXME: we need a palette here */
477             break;
478         case 24:
479             p_header->biBitCount = 24;
480             p_header->biCompression = BI_RGB;
481             ((DWORD*)p_info->bmiColors)[0] = 0x00ff0000;
482             ((DWORD*)p_info->bmiColors)[1] = 0x0000ff00;
483             ((DWORD*)p_info->bmiColors)[2] = 0x000000ff;
484             break;
485         case 32:
486             p_header->biBitCount = 32;
487             p_header->biCompression = BI_RGB;
488             ((DWORD*)p_info->bmiColors)[0] = 0x00ff0000;
489             ((DWORD*)p_info->bmiColors)[1] = 0x0000ff00;
490             ((DWORD*)p_info->bmiColors)[2] = 0x000000ff;
491             break;
492         case 16:
493         default:
494             p_header->biBitCount = 16;
495             p_header->biCompression = BI_RGB;
496             ((DWORD*)p_info->bmiColors)[0] = 0x00007c00;
497             ((DWORD*)p_info->bmiColors)[1] = 0x000003e0;
498             ((DWORD*)p_info->bmiColors)[2] = 0x0000001f;
499             break;
500     }
501     p_header->biWidth = p_vout->render.i_width;
502     p_header->biHeight = p_vout->render.i_height;
503     p_header->biClrImportant = 0;
504     p_header->biClrUsed = 0;
505     p_header->biXPelsPerMeter = 0;
506     p_header->biYPelsPerMeter = 0;
507
508     p_vout->p_sys->off_bitmap =
509           CreateDIBSection( window_dc, (BITMAPINFO *)p_header, DIB_RGB_COLORS,
510                             (void**)&p_vout->p_sys->p_buffer, NULL, 0 );
511
512     p_vout->p_sys->off_dc = CreateCompatibleDC( window_dc );
513
514     SelectObject( p_vout->p_sys->off_dc, p_vout->p_sys->off_bitmap );
515     ReleaseDC( 0, window_dc );
516 }
517