]> git.sesse.net Git - vlc/blob - modules/video_output/msw/wingdi.c
a4ac84ab0ed1348f93322615d6a73bd55673abe2
[vlc] / modules / video_output / msw / wingdi.c
1 /*****************************************************************************
2  * wingdi.c : Win32 / WinCE GDI video output plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2002-2009 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Gildas Bazin <gbazin@videolan.org>
8  *          Samuel Hocevar <sam@zoy.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_interface.h>
36 #include <vlc_playlist.h>
37 #include <vlc_vout.h>
38
39 #include <commctrl.h>
40
41 /*#ifdef MODULE_NAME_IS_wingapi
42     typedef struct GXDisplayProperties {
43         DWORD cxWidth;
44         DWORD cyHeight;
45         long cbxPitch;
46         long cbyPitch;
47         long cBPP;
48         DWORD ffFormat;
49     } GXDisplayProperties;
50
51     typedef struct GXScreenRect {
52         DWORD dwTop;
53         DWORD dwLeft;
54         DWORD dwWidth;
55         DWORD dwHeight;
56     } GXScreenRect;
57
58 #   define GX_FULLSCREEN    0x01
59 #   define GX_NORMALKEYS    0x02
60 #   define GX_LANDSCAPEKEYS 0x03
61
62 #   ifndef kfLandscape
63 #       define kfLandscape      0x8
64 #       define kfPalette        0x10
65 #       define kfDirect         0x20
66 #       define kfDirect555      0x40
67 #       define kfDirect565      0x80
68 #       define kfDirect888      0x100
69 #       define kfDirect444      0x200
70 #       define kfDirectInverted 0x400
71 #   endif
72 #endif /* MODULE_NAME_IS_wingapi */
73
74 #include "vout.h"
75
76 #define MAX_DIRECTBUFFERS 10
77
78 #ifdef UNDER_CE
79 #ifndef WS_OVERLAPPEDWINDOW
80 #   define WS_OVERLAPPEDWINDOW 0xcf0000
81 #endif
82 #ifndef WS_EX_NOPARENTNOTIFY
83 #   define WS_EX_NOPARENTNOTIFY 4
84 #endif
85 #ifndef WS_EX_APPWINDOW
86 #define WS_EX_APPWINDOW 0x40000
87 #endif
88 //#define SetWindowLongPtr SetWindowLong
89 //#define GetWindowLongPtr GetWindowLong
90 //#define GWLP_USERDATA GWL_USERDATA
91 #define AdjustWindowRect(a,b,c)
92 #endif //UNDER_CE
93
94 #ifndef WS_NONAVDONEBUTTON
95 #define WS_NONAVDONEBUTTON 0
96 #endif
97 /*****************************************************************************
98  * Local prototypes
99  *****************************************************************************/
100 static int  OpenVideo  ( vlc_object_t * );
101 static void CloseVideo ( vlc_object_t * );
102
103 static int  Init      ( vout_thread_t * );
104 static void End       ( vout_thread_t * );
105 static int  Manage    ( vout_thread_t * );
106 static void Render    ( vout_thread_t *, picture_t * );
107 #ifdef MODULE_NAME_IS_wingapi
108 static void FirstDisplayGAPI( vout_thread_t *, picture_t * );
109 static void DisplayGAPI( vout_thread_t *, picture_t * );
110 static int GAPILockSurface( vout_thread_t *, picture_t * );
111 static int GAPIUnlockSurface( vout_thread_t *, picture_t * );
112 #else
113 static void FirstDisplayGDI( vout_thread_t *, picture_t * );
114 static void DisplayGDI( vout_thread_t *, picture_t * );
115 #endif
116 static void SetPalette( vout_thread_t *, uint16_t *, uint16_t *, uint16_t * );
117
118 static void InitBuffers        ( vout_thread_t * );
119
120
121
122 #define DX_POSITION_CHANGE 0x1000
123
124 /*****************************************************************************
125  * Module descriptor
126  *****************************************************************************/
127 vlc_module_begin ()
128     set_category( CAT_VIDEO )
129     set_subcategory( SUBCAT_VIDEO_VOUT )
130 #ifdef MODULE_NAME_IS_wingapi
131     set_shortname( "Windows GAPI" )
132     set_description( N_("Windows GAPI video output") )
133     set_capability( "video output", 20 )
134 #else
135     set_shortname( "Windows GDI" )
136     set_description( N_("Windows GDI video output") )
137     set_capability( "video output", 10 )
138 #endif
139     set_callbacks( OpenVideo, CloseVideo )
140 vlc_module_end ()
141
142 /*****************************************************************************
143  * OpenVideo: activate GDI video thread output method
144  *****************************************************************************/
145 static int OpenVideo ( vlc_object_t *p_this )
146 {
147     vout_thread_t * p_vout = (vout_thread_t *)p_this;
148
149     p_vout->p_sys = (vout_sys_t *)calloc( 1, sizeof(vout_sys_t) );
150     if( !p_vout->p_sys ) return VLC_ENOMEM;
151
152 #ifdef MODULE_NAME_IS_wingapi
153     /* Load GAPI */
154     p_vout->p_sys->gapi_dll = LoadLibrary( _T("GX.DLL") );
155     if( p_vout->p_sys->gapi_dll == NULL )
156     {
157         msg_Warn( p_vout, "failed loading gx.dll" );
158         free( p_vout->p_sys );
159         return VLC_EGENERIC;
160     }
161
162     GXOpenDisplay = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
163         _T("?GXOpenDisplay@@YAHPAUHWND__@@K@Z") );
164     GXCloseDisplay = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
165         _T("?GXCloseDisplay@@YAHXZ") );
166     GXBeginDraw = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
167         _T("?GXBeginDraw@@YAPAXXZ") );
168     GXEndDraw = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
169         _T("?GXEndDraw@@YAHXZ") );
170     GXGetDisplayProperties = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
171         _T("?GXGetDisplayProperties@@YA?AUGXDisplayProperties@@XZ") );
172     GXSuspend = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
173         _T("?GXSuspend@@YAHXZ") );
174     GXResume = GetProcAddress( p_vout->p_sys->gapi_dll,
175         _T("?GXResume@@YAHXZ") );
176
177     if( !GXOpenDisplay || !GXCloseDisplay || !GXBeginDraw || !GXEndDraw ||
178         !GXGetDisplayProperties || !GXSuspend || !GXResume )
179     {
180         msg_Err( p_vout, "failed GetProcAddress on gapi.dll" );
181         free( p_vout->p_sys );
182         return VLC_EGENERIC;
183     }
184
185     msg_Dbg( p_vout, "GAPI DLL loaded" );
186
187     p_vout->p_sys->render_width = p_vout->render.i_width;
188     p_vout->p_sys->render_height = p_vout->render.i_height;
189 #endif
190
191     p_vout->p_sys->p_event = (vlc_object_t *)
192         vlc_object_create( p_vout, sizeof( vlc_object_t ) );
193     if( !p_vout->p_sys->p_event )
194     {
195         free( p_vout->p_sys );
196         return VLC_ENOMEM;
197     }
198
199     p_vout->pf_init = Init;
200     p_vout->pf_end = End;
201     p_vout->pf_manage = Manage;
202     p_vout->pf_render = Render;
203 #ifdef MODULE_NAME_IS_wingapi
204     p_vout->pf_display = FirstDisplayGAPI;
205
206     p_vout->p_sys->b_focus = 0;
207     p_vout->p_sys->b_parent_focus = 0;
208
209 #else
210     p_vout->pf_display = FirstDisplayGDI;
211 #endif
212
213     p_vout->p_sys->hwnd = p_vout->p_sys->hvideownd = NULL;
214     p_vout->p_sys->hparent = p_vout->p_sys->hfswnd = NULL;
215     p_vout->p_sys->i_changes = 0;
216     vlc_mutex_init( &p_vout->p_sys->lock );
217     SetRectEmpty( &p_vout->p_sys->rect_display );
218     SetRectEmpty( &p_vout->p_sys->rect_parent );
219
220     var_Create( p_vout, "video-title", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
221     var_Create( p_vout, "disable-screensaver", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
222
223     p_vout->p_sys->b_cursor_hidden = 0;
224     p_vout->p_sys->i_lastmoved = mdate();
225     p_vout->p_sys->i_mouse_hide_timeout =
226         var_GetInteger(p_vout, "mouse-hide-timeout") * 1000;
227
228     /* Set main window's size */
229     p_vout->p_sys->i_window_width = p_vout->i_window_width;
230     p_vout->p_sys->i_window_height = p_vout->i_window_height;
231
232     /* Create the EventThread, this thread is created by us to isolate
233      * the Win32 PeekMessage function calls. We want to do this because
234      * Windows can stay blocked inside this call for a long time, and when
235      * this happens it thus blocks vlc's video_output thread.
236      * Vout EventThread will take care of the creation of the video
237      * window (because PeekMessage has to be called from the same thread which
238      * created the window). */
239     msg_Dbg( p_vout, "creating Vout EventThread" );
240     p_vout->p_sys->p_event =
241         vlc_object_create( p_vout, sizeof(event_thread_t) );
242     p_vout->p_sys->p_event->p_vout = p_vout;
243     p_vout->p_sys->p_event->window_ready = CreateEvent( NULL, TRUE, FALSE, NULL );
244     if( vlc_thread_create( p_vout->p_sys->p_event, "Vout Events Thread",
245                            EventThread, 0 ) )
246     {
247         msg_Err( p_vout, "cannot create Vout EventThread" );
248         CloseHandle( p_vout->p_sys->p_event->window_ready );
249         vlc_object_release( p_vout->p_sys->p_event );
250         p_vout->p_sys->p_event = NULL;
251         goto error;
252     }
253     WaitForSingleObject( p_vout->p_sys->p_event->window_ready, INFINITE );
254     CloseHandle( p_vout->p_sys->p_event->window_ready );
255
256     if( p_vout->p_sys->p_event->b_error )
257     {
258         msg_Err( p_vout, "Vout EventThread failed" );
259         goto error;
260     }
261
262     vlc_object_attach( p_vout->p_sys->p_event, p_vout );
263
264     msg_Dbg( p_vout, "Vout EventThread running" );
265
266 #ifndef UNDER_CE
267     /* Variable to indicate if the window should be on top of others */
268     /* Trigger a callback right now */
269     var_TriggerCallback( p_vout, "video-on-top" );
270
271     /* disable screensaver by temporarily changing system settings */
272     p_vout->p_sys->i_spi_lowpowertimeout = 0;
273     p_vout->p_sys->i_spi_powerofftimeout = 0;
274     p_vout->p_sys->i_spi_screensavetimeout = 0;
275     if( var_GetBool( p_vout, "disable-screensaver" ) ) {
276         msg_Dbg(p_vout, "disabling screen saver");
277         SystemParametersInfo(SPI_GETLOWPOWERTIMEOUT,
278             0, &(p_vout->p_sys->i_spi_lowpowertimeout), 0);
279         if( 0 != p_vout->p_sys->i_spi_lowpowertimeout ) {
280             SystemParametersInfo(SPI_SETLOWPOWERTIMEOUT, 0, NULL, 0);
281         }
282         SystemParametersInfo(SPI_GETPOWEROFFTIMEOUT, 0,
283             &(p_vout->p_sys->i_spi_powerofftimeout), 0);
284         if( 0 != p_vout->p_sys->i_spi_powerofftimeout ) {
285             SystemParametersInfo(SPI_SETPOWEROFFTIMEOUT, 0, NULL, 0);
286         }
287         SystemParametersInfo(SPI_GETSCREENSAVETIMEOUT, 0,
288             &(p_vout->p_sys->i_spi_screensavetimeout), 0);
289         if( 0 != p_vout->p_sys->i_spi_screensavetimeout ) {
290             SystemParametersInfo(SPI_SETSCREENSAVETIMEOUT, 0, NULL, 0);
291         }
292     }
293 #endif
294     return VLC_SUCCESS;
295
296 error:
297     CloseVideo( VLC_OBJECT(p_vout) );
298     return VLC_EGENERIC;
299 }
300
301 /*****************************************************************************
302  * CloseVideo: deactivate the GDI video output
303  *****************************************************************************/
304 static void CloseVideo ( vlc_object_t *p_this )
305 {
306     vout_thread_t * p_vout = (vout_thread_t *)p_this;
307
308     if( p_vout->b_fullscreen )
309     {
310         msg_Dbg( p_vout, "Quitting fullscreen" );
311         Win32ToggleFullscreen( p_vout );
312         /* Force fullscreen in the core for the next video */
313         var_SetBool( p_vout, "fullscreen", true );
314     }
315
316     if( p_vout->p_sys->p_event )
317     {
318         vlc_object_detach( p_vout->p_sys->p_event );
319
320         /* Kill Vout EventThread */
321         vlc_object_kill( p_vout->p_sys->p_event );
322
323         /* we need to be sure Vout EventThread won't stay stuck in
324          * GetMessage, so we send a fake message */
325         if( p_vout->p_sys->hwnd )
326         {
327             PostMessage( p_vout->p_sys->hwnd, WM_NULL, 0, 0);
328         }
329
330         vlc_thread_join( p_vout->p_sys->p_event );
331         vlc_object_release( p_vout->p_sys->p_event );
332     }
333     vlc_mutex_destroy( &p_vout->p_sys->lock );
334
335 #ifndef UNDER_CE
336     /* restore screensaver system settings */
337     if( 0 != p_vout->p_sys->i_spi_lowpowertimeout ) {
338         SystemParametersInfo(SPI_SETLOWPOWERTIMEOUT,
339             p_vout->p_sys->i_spi_lowpowertimeout, NULL, 0);
340     }
341     if( 0 != p_vout->p_sys->i_spi_powerofftimeout ) {
342         SystemParametersInfo(SPI_SETPOWEROFFTIMEOUT,
343             p_vout->p_sys->i_spi_powerofftimeout, NULL, 0);
344     }
345     if( 0 != p_vout->p_sys->i_spi_screensavetimeout ) {
346         SystemParametersInfo(SPI_SETSCREENSAVETIMEOUT,
347             p_vout->p_sys->i_spi_screensavetimeout, NULL, 0);
348     }
349 #endif
350
351 #ifdef MODULE_NAME_IS_wingapi
352     FreeLibrary( p_vout->p_sys->gapi_dll );
353 #endif
354
355     free( p_vout->p_sys );
356     p_vout->p_sys = NULL;
357 }
358
359 /*****************************************************************************
360  * Init: initialize video thread output method
361  *****************************************************************************/
362 static int Init( vout_thread_t *p_vout )
363 {
364     picture_t *p_pic;
365
366     /* Initialize offscreen buffer */
367     InitBuffers( p_vout );
368
369     p_vout->p_sys->rect_display.left = 0;
370     p_vout->p_sys->rect_display.top = 0;
371     p_vout->p_sys->rect_display.right  = GetSystemMetrics(SM_CXSCREEN);
372     p_vout->p_sys->rect_display.bottom = GetSystemMetrics(SM_CYSCREEN);
373
374     I_OUTPUTPICTURES = 0;
375
376     /* Initialize the output structure */
377     switch( p_vout->p_sys->i_depth )
378     {
379     case 8:
380         p_vout->output.i_chroma = VLC_FOURCC('R','G','B','2');
381         p_vout->output.pf_setpalette = SetPalette;
382         break;
383     case 15:
384         p_vout->output.i_chroma = VLC_FOURCC('R','V','1','5');
385         p_vout->output.i_rmask  = 0x7c00;
386         p_vout->output.i_gmask  = 0x03e0;
387         p_vout->output.i_bmask  = 0x001f;
388         break;
389     case 16:
390         p_vout->output.i_chroma = VLC_FOURCC('R','V','1','6');
391         p_vout->output.i_rmask  = 0xf800;
392         p_vout->output.i_gmask  = 0x07e0;
393         p_vout->output.i_bmask  = 0x001f;
394         break;
395     case 24:
396         p_vout->output.i_chroma = VLC_FOURCC('R','V','2','4');
397         p_vout->output.i_rmask  = 0x00ff0000;
398         p_vout->output.i_gmask  = 0x0000ff00;
399         p_vout->output.i_bmask  = 0x000000ff;
400         break;
401     case 32:
402         p_vout->output.i_chroma = VLC_FOURCC('R','V','3','2');
403         p_vout->output.i_rmask  = 0x00ff0000;
404         p_vout->output.i_gmask  = 0x0000ff00;
405         p_vout->output.i_bmask  = 0x000000ff;
406         break;
407     default:
408         msg_Err( p_vout, "screen depth %i not supported",
409                  p_vout->p_sys->i_depth );
410         return VLC_EGENERIC;
411         break;
412     }
413
414     p_pic = &p_vout->p_picture[0];
415
416 #ifdef MODULE_NAME_IS_wingapi
417     p_vout->output.i_width  = 0;
418     p_vout->output.i_height = 0;
419     p_pic->pf_lock  = GAPILockSurface;
420     p_pic->pf_unlock = GAPIUnlockSurface;
421     Manage( p_vout );
422     GAPILockSurface( p_vout, p_pic );
423     p_vout->i_changes = 0;
424     p_vout->output.i_width  = p_vout->p_sys->render_width;
425     p_vout->output.i_height = p_vout->p_sys->render_height;
426
427 #else
428     p_vout->output.i_width  = p_vout->render.i_width;
429     p_vout->output.i_height = p_vout->render.i_height;
430
431     p_vout->fmt_out = p_vout->fmt_in;
432     p_vout->fmt_out.i_chroma = p_vout->output.i_chroma;
433 #endif
434
435     p_vout->output.i_aspect = p_vout->render.i_aspect;
436
437     p_pic->p->p_pixels = p_vout->p_sys->p_pic_buffer;
438     p_pic->p->i_lines = p_vout->output.i_height;
439     p_pic->p->i_visible_lines = p_vout->output.i_height;
440     p_pic->p->i_pitch = p_vout->p_sys->i_pic_pitch;
441     p_pic->p->i_pixel_pitch = p_vout->p_sys->i_pic_pixel_pitch;
442     p_pic->p->i_visible_pitch = p_vout->output.i_width *
443         p_pic->p->i_pixel_pitch;
444     p_pic->i_planes = 1;
445     p_pic->i_status = DESTROYED_PICTURE;
446     p_pic->i_type   = DIRECT_PICTURE;
447
448     PP_OUTPUTPICTURE[ I_OUTPUTPICTURES++ ] = p_pic;
449
450     /* Change the window title bar text */
451     PostMessage( p_vout->p_sys->hwnd, WM_VLC_CHANGE_TEXT, 0, 0 );
452     UpdateRects( p_vout, true );
453
454     return VLC_SUCCESS;
455 }
456
457 /*****************************************************************************
458  * End: terminate video thread output method
459  *****************************************************************************/
460 static void End( vout_thread_t *p_vout )
461 {
462 #ifdef MODULE_NAME_IS_wingapi
463     GXCloseDisplay();
464 #else
465     DeleteDC( p_vout->p_sys->off_dc );
466     DeleteObject( p_vout->p_sys->off_bitmap );
467 #endif
468 }
469
470 /*****************************************************************************
471  * Manage: handle events
472  *****************************************************************************
473  * This function should be called regularly by video output thread. It manages
474  * console events. It returns a non null value on error.
475  *****************************************************************************/
476 static int Manage( vout_thread_t *p_vout )
477 {
478     /* If we do not control our window, we check for geometry changes
479      * ourselves because the parent might not send us its events. */
480     vlc_mutex_lock( &p_vout->p_sys->lock );
481     if( p_vout->p_sys->hparent && !p_vout->b_fullscreen )
482     {
483         RECT rect_parent;
484         POINT point;
485
486         vlc_mutex_unlock( &p_vout->p_sys->lock );
487
488         GetClientRect( p_vout->p_sys->hparent, &rect_parent );
489         point.x = point.y = 0;
490         ClientToScreen( p_vout->p_sys->hparent, &point );
491         OffsetRect( &rect_parent, point.x, point.y );
492
493         if( !EqualRect( &rect_parent, &p_vout->p_sys->rect_parent ) )
494         {
495             int i_x, i_y, i_width, i_height;
496             p_vout->p_sys->rect_parent = rect_parent;
497
498             /* This one is to force the update even if only
499              * the position has changed */
500             SetWindowPos( p_vout->p_sys->hwnd, 0, 1, 1,
501                           rect_parent.right - rect_parent.left,
502                           rect_parent.bottom - rect_parent.top, 0 );
503
504             SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0,
505                           rect_parent.right - rect_parent.left,
506                           rect_parent.bottom - rect_parent.top, 0 );
507
508             vout_PlacePicture( p_vout, rect_parent.right - rect_parent.left,
509                                rect_parent.bottom - rect_parent.top,
510                                &i_x, &i_y, &i_width, &i_height );
511
512             SetWindowPos( p_vout->p_sys->hvideownd, HWND_TOP,
513                           i_x, i_y, i_width, i_height, 0 );
514         }
515     }
516     else
517     {
518         vlc_mutex_unlock( &p_vout->p_sys->lock );
519     }
520
521     /* autoscale toggle */
522     if( p_vout->i_changes & VOUT_SCALE_CHANGE )
523     {
524         p_vout->i_changes &= ~VOUT_SCALE_CHANGE;
525
526         p_vout->b_autoscale = var_GetBool( p_vout, "autoscale" );
527         p_vout->i_zoom = (int) ZOOM_FP_FACTOR;
528
529         UpdateRects( p_vout, true );
530     }
531
532     /* scaling factor */
533     if( p_vout->i_changes & VOUT_ZOOM_CHANGE )
534     {
535         p_vout->i_changes &= ~VOUT_ZOOM_CHANGE;
536
537         p_vout->b_autoscale = false;
538         p_vout->i_zoom =
539             (int)( ZOOM_FP_FACTOR * var_GetFloat( p_vout, "scale" ) );
540         UpdateRects( p_vout, true );
541     }
542
543     /* Check for cropping / aspect changes */
544     if( p_vout->i_changes & VOUT_CROP_CHANGE ||
545         p_vout->i_changes & VOUT_ASPECT_CHANGE )
546     {
547         p_vout->i_changes &= ~VOUT_CROP_CHANGE;
548         p_vout->i_changes &= ~VOUT_ASPECT_CHANGE;
549
550         p_vout->fmt_out.i_x_offset = p_vout->fmt_in.i_x_offset;
551         p_vout->fmt_out.i_y_offset = p_vout->fmt_in.i_y_offset;
552         p_vout->fmt_out.i_visible_width = p_vout->fmt_in.i_visible_width;
553         p_vout->fmt_out.i_visible_height = p_vout->fmt_in.i_visible_height;
554         p_vout->fmt_out.i_aspect = p_vout->fmt_in.i_aspect;
555         p_vout->fmt_out.i_sar_num = p_vout->fmt_in.i_sar_num;
556         p_vout->fmt_out.i_sar_den = p_vout->fmt_in.i_sar_den;
557         p_vout->output.i_aspect = p_vout->fmt_in.i_aspect;
558         UpdateRects( p_vout, true );
559     }
560
561     /*
562      * Position Change
563      */
564     if( p_vout->p_sys->i_changes & DX_POSITION_CHANGE )
565     {
566         p_vout->p_sys->i_changes &= ~DX_POSITION_CHANGE;
567     }
568
569     /* We used to call the Win32 PeekMessage function here to read the window
570      * messages. But since window can stay blocked into this function for a
571      * long time (for example when you move your window on the screen), I
572      * decided to isolate PeekMessage in another thread. */
573
574     /*
575      * Fullscreen change
576      */
577     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE
578         || p_vout->p_sys->i_changes & VOUT_FULLSCREEN_CHANGE )
579     {
580         Win32ToggleFullscreen( p_vout );
581
582         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
583         p_vout->p_sys->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
584     }
585
586     /*
587      * Pointer change
588      */
589     if( p_vout->b_fullscreen && !p_vout->p_sys->b_cursor_hidden &&
590         (mdate() - p_vout->p_sys->i_lastmoved) >
591             p_vout->p_sys->i_mouse_hide_timeout )
592     {
593         POINT point;
594         HWND hwnd;
595
596         /* Hide the cursor only if it is inside our window */
597         GetCursorPos( &point );
598         hwnd = WindowFromPoint(point);
599         if( hwnd == p_vout->p_sys->hwnd || hwnd == p_vout->p_sys->hvideownd )
600         {
601             PostMessage( p_vout->p_sys->hwnd, WM_VLC_HIDE_MOUSE, 0, 0 );
602         }
603         else
604         {
605             p_vout->p_sys->i_lastmoved = mdate();
606         }
607     }
608
609     /*
610      * "Always on top" status change
611      */
612     if( p_vout->p_sys->b_on_top_change )
613     {
614         vlc_value_t val;
615         HMENU hMenu = GetSystemMenu( p_vout->p_sys->hwnd, FALSE );
616
617         var_Get( p_vout, "video-on-top", &val );
618
619         /* Set the window on top if necessary */
620         if( val.b_bool && !( GetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE )
621                            & WS_EX_TOPMOST ) )
622         {
623             CheckMenuItem( hMenu, IDM_TOGGLE_ON_TOP,
624                            MF_BYCOMMAND | MFS_CHECKED );
625             SetWindowPos( p_vout->p_sys->hwnd, HWND_TOPMOST, 0, 0, 0, 0,
626                           SWP_NOSIZE | SWP_NOMOVE );
627         }
628         else
629         /* The window shouldn't be on top */
630         if( !val.b_bool && ( GetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE )
631                            & WS_EX_TOPMOST ) )
632         {
633             CheckMenuItem( hMenu, IDM_TOGGLE_ON_TOP,
634                            MF_BYCOMMAND | MFS_UNCHECKED );
635             SetWindowPos( p_vout->p_sys->hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
636                           SWP_NOSIZE | SWP_NOMOVE );
637         }
638
639         p_vout->p_sys->b_on_top_change = false;
640     }
641
642     /* Check if the event thread is still running */
643     if( !vlc_object_alive (p_vout->p_sys->p_event) )
644     {
645         return VLC_EGENERIC; /* exit */
646     }
647
648     return VLC_SUCCESS;
649 }
650
651 /*****************************************************************************
652  * Render: render previously calculated output
653  *****************************************************************************/
654 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
655 {
656     /* No need to do anything, the fake direct buffers stay as they are */
657     (void)p_vout;
658     (void)p_pic;
659 }
660
661 /*****************************************************************************
662  * Display: displays previously rendered output
663  *****************************************************************************/
664 #define rect_src p_vout->p_sys->rect_src
665 #define rect_src_clipped p_vout->p_sys->rect_src_clipped
666 #define rect_dest p_vout->p_sys->rect_dest
667 #define rect_dest_clipped p_vout->p_sys->rect_dest_clipped
668
669 #ifndef MODULE_NAME_IS_wingapi
670 static void DisplayGDI( vout_thread_t *p_vout, picture_t *p_pic )
671 {
672     vout_sys_t *p_sys = p_vout->p_sys;
673     RECT rect_dst = rect_dest_clipped;
674     HDC hdc = GetDC( p_sys->hvideownd );
675
676     OffsetRect( &rect_dst, -rect_dest.left, -rect_dest.top );
677     SelectObject( p_sys->off_dc, p_sys->off_bitmap );
678
679     if( rect_dest_clipped.right - rect_dest_clipped.left !=
680         rect_src_clipped.right - rect_src_clipped.left ||
681         rect_dest_clipped.bottom - rect_dest_clipped.top !=
682         rect_src_clipped.bottom - rect_src_clipped.top )
683     {
684         StretchBlt( hdc, rect_dst.left, rect_dst.top,
685                     rect_dst.right, rect_dst.bottom,
686                     p_sys->off_dc, rect_src_clipped.left, rect_src_clipped.top,
687                     rect_src_clipped.right, rect_src_clipped.bottom, SRCCOPY );
688     }
689     else
690     {
691         BitBlt( hdc, rect_dst.left, rect_dst.top,
692                 rect_dst.right, rect_dst.bottom,
693                 p_sys->off_dc, rect_src_clipped.left,
694                 rect_src_clipped.top, SRCCOPY );
695     }
696
697     ReleaseDC( p_sys->hvideownd, hdc );
698 }
699
700 static void FirstDisplayGDI( vout_thread_t *p_vout, picture_t *p_pic )
701 {
702     /*
703     ** Video window is initially hidden, show it now since we got a
704     ** picture to show.
705     */
706     SetWindowPos( p_vout->p_sys->hvideownd, 0, 0, 0, 0, 0,
707         SWP_ASYNCWINDOWPOS|
708         SWP_FRAMECHANGED|
709         SWP_SHOWWINDOW|
710         SWP_NOMOVE|
711         SWP_NOSIZE|
712         SWP_NOZORDER );
713
714     /* get initial picture presented */
715     DisplayGDI(p_vout, p_pic);
716
717     /* use and restores proper display function for further pictures */
718     p_vout->pf_display = DisplayGDI;
719 }
720
721 #else
722
723 static int GAPILockSurface( vout_thread_t *p_vout, picture_t *p_pic )
724 {
725     vout_sys_t *p_sys = p_vout->p_sys;
726     int i_x, i_y, i_width, i_height;
727     RECT video_rect;
728     POINT point;
729
730     GetClientRect( p_sys->hwnd, &video_rect);
731     vout_PlacePicture( p_vout, video_rect.right - video_rect.left,
732                        video_rect.bottom - video_rect.top,
733                        &i_x, &i_y, &i_width, &i_height );
734     point.x = point.y = 0;
735     ClientToScreen( p_sys->hwnd, &point );
736     i_x += point.x + video_rect.left;
737     i_y += point.y + video_rect.top;
738
739     if( i_width != p_vout->output.i_width ||
740         i_height != p_vout->output.i_height )
741     {
742         GXDisplayProperties gxdisplayprop = GXGetDisplayProperties();
743
744         p_sys->render_width = i_width;
745         p_sys->render_height = i_height;
746         p_vout->i_changes |= VOUT_SIZE_CHANGE;
747
748         msg_Dbg( p_vout, "vout size change (%ix%i -> %ix%i)",
749                  i_width, i_height, p_vout->output.i_width,
750                  p_vout->output.i_height );
751
752         p_vout->p_sys->i_pic_pixel_pitch = gxdisplayprop.cbxPitch;
753         p_vout->p_sys->i_pic_pitch = gxdisplayprop.cbyPitch;
754         return VLC_EGENERIC;
755     }
756     else
757     {
758         GXDisplayProperties gxdisplayprop;
759         RECT display_rect, dest_rect;
760         uint8_t *p_dest, *p_src = p_pic->p->p_pixels;
761
762         video_rect.left = i_x; video_rect.top = i_y;
763         video_rect.right = i_x + i_width;
764         video_rect.bottom = i_y + i_height;
765
766         gxdisplayprop = GXGetDisplayProperties();
767         display_rect.left = 0; display_rect.top = 0;
768         display_rect.right = gxdisplayprop.cxWidth;
769         display_rect.bottom = gxdisplayprop.cyHeight;
770
771         if( !IntersectRect( &dest_rect, &video_rect, &display_rect ) )
772         {
773             return VLC_EGENERIC;
774         }
775
776 #if 0
777         msg_Err( p_vout, "video (%d,%d,%d,%d) display (%d,%d,%d,%d) "
778                  "dest (%d,%d,%d,%d)",
779                  video_rect.left, video_rect.right,
780                  video_rect.top, video_rect.bottom,
781                  display_rect.left, display_rect.right,
782                  display_rect.top, display_rect.bottom,
783                  dest_rect.left, dest_rect.right,
784                  dest_rect.top, dest_rect.bottom );
785 #endif
786
787         if( !(p_dest = GXBeginDraw()) )
788         {
789 #if 0
790             msg_Err( p_vout, "GXBeginDraw error %d ", GetLastError() );
791 #endif
792             return VLC_EGENERIC;
793         }
794
795         p_src += (dest_rect.left - video_rect.left) * gxdisplayprop.cbxPitch +
796             (dest_rect.top - video_rect.top) * p_pic->p->i_pitch;
797         p_dest += dest_rect.left * gxdisplayprop.cbxPitch +
798             dest_rect.top * gxdisplayprop.cbyPitch;
799         i_width = dest_rect.right - dest_rect.left;
800         i_height = dest_rect.bottom - dest_rect.top;
801
802         p_pic->p->p_pixels = p_dest;
803     }
804
805     return VLC_SUCCESS;
806 }
807
808 static int GAPIUnlockSurface( vout_thread_t *p_vout, picture_t *p_pic )
809 {
810     GXEndDraw();
811     return VLC_SUCCESS;
812 }
813
814 static void DisplayGAPI( vout_thread_t *p_vout, picture_t *p_pic )
815 {
816 }
817
818 static void FirstDisplayGAPI( vout_thread_t *p_vout, picture_t *p_pic )
819 {
820     /* get initial picture presented through D3D */
821     DisplayGAPI(p_vout, p_pic);
822
823     /*
824     ** Video window is initially hidden, show it now since we got a
825     ** picture to show.
826     */
827     SetWindowPos( p_vout->p_sys->hvideownd, 0, 0, 0, 0, 0,
828         SWP_ASYNCWINDOWPOS|
829         SWP_FRAMECHANGED|
830         SWP_SHOWWINDOW|
831         SWP_NOMOVE|
832         SWP_NOSIZE|
833         SWP_NOZORDER );
834
835     /* use and restores proper display function for further pictures */
836     p_vout->pf_display = DisplayGAPI;
837 }
838
839 #endif
840
841 #undef rect_src
842 #undef rect_src_clipped
843 #undef rect_dest
844 #undef rect_dest_clipped
845 /*****************************************************************************
846  * SetPalette: sets an 8 bpp palette
847  *****************************************************************************/
848 static void SetPalette( vout_thread_t *p_vout,
849                         uint16_t *red, uint16_t *green, uint16_t *blue )
850 {
851     msg_Err( p_vout, "FIXME: SetPalette unimplemented" );
852 }
853
854 /*****************************************************************************
855  * InitBuffers: initialize an offscreen bitmap for direct buffer operations.
856  *****************************************************************************/
857 static void InitBuffers( vout_thread_t *p_vout )
858 {
859     BITMAPINFOHEADER *p_header = &p_vout->p_sys->bitmapinfo.bmiHeader;
860     BITMAPINFO *p_info = &p_vout->p_sys->bitmapinfo;
861     HDC window_dc = GetDC( p_vout->p_sys->hvideownd );
862
863     /* Get screen properties */
864 #ifdef MODULE_NAME_IS_wingapi
865     GXDisplayProperties gx_displayprop = GXGetDisplayProperties();
866     p_vout->p_sys->i_depth = gx_displayprop.cBPP;
867 #else
868     p_vout->p_sys->i_depth = GetDeviceCaps( window_dc, PLANES ) *
869         GetDeviceCaps( window_dc, BITSPIXEL );
870 #endif
871     msg_Dbg( p_vout, "GDI depth is %i", p_vout->p_sys->i_depth );
872
873 #ifdef MODULE_NAME_IS_wingapi
874     GXOpenDisplay( p_vout->p_sys->hvideownd, GX_FULLSCREEN );
875
876 #else
877
878     /* Initialize offscreen bitmap */
879     memset( p_info, 0, sizeof( BITMAPINFO ) + 3 * sizeof( RGBQUAD ) );
880
881     p_header->biSize = sizeof( BITMAPINFOHEADER );
882     p_header->biSizeImage = 0;
883     p_header->biPlanes = 1;
884     switch( p_vout->p_sys->i_depth )
885     {
886     case 8:
887         p_header->biBitCount = 8;
888         p_header->biCompression = BI_RGB;
889         /* FIXME: we need a palette here */
890         break;
891     case 15:
892         p_header->biBitCount = 15;
893         p_header->biCompression = BI_BITFIELDS;//BI_RGB;
894         ((DWORD*)p_info->bmiColors)[0] = 0x00007c00;
895         ((DWORD*)p_info->bmiColors)[1] = 0x000003e0;
896         ((DWORD*)p_info->bmiColors)[2] = 0x0000001f;
897         break;
898     case 16:
899         p_header->biBitCount = 16;
900         p_header->biCompression = BI_BITFIELDS;//BI_RGB;
901         ((DWORD*)p_info->bmiColors)[0] = 0x0000f800;
902         ((DWORD*)p_info->bmiColors)[1] = 0x000007e0;
903         ((DWORD*)p_info->bmiColors)[2] = 0x0000001f;
904         break;
905     case 24:
906         p_header->biBitCount = 24;
907         p_header->biCompression = BI_RGB;
908         ((DWORD*)p_info->bmiColors)[0] = 0x00ff0000;
909         ((DWORD*)p_info->bmiColors)[1] = 0x0000ff00;
910         ((DWORD*)p_info->bmiColors)[2] = 0x000000ff;
911         break;
912     case 32:
913         p_header->biBitCount = 32;
914         p_header->biCompression = BI_RGB;
915         ((DWORD*)p_info->bmiColors)[0] = 0x00ff0000;
916         ((DWORD*)p_info->bmiColors)[1] = 0x0000ff00;
917         ((DWORD*)p_info->bmiColors)[2] = 0x000000ff;
918         break;
919     default:
920         msg_Err( p_vout, "screen depth %i not supported",
921                  p_vout->p_sys->i_depth );
922         return;
923         break;
924     }
925     p_header->biWidth = p_vout->render.i_width;
926     p_header->biHeight = -p_vout->render.i_height;
927     p_header->biClrImportant = 0;
928     p_header->biClrUsed = 0;
929     p_header->biXPelsPerMeter = 0;
930     p_header->biYPelsPerMeter = 0;
931
932     p_vout->p_sys->i_pic_pixel_pitch = p_header->biBitCount / 8;
933     p_vout->p_sys->i_pic_pitch = p_header->biBitCount * p_header->biWidth / 8;
934
935     p_vout->p_sys->off_bitmap =
936         CreateDIBSection( window_dc, (BITMAPINFO *)p_header, DIB_RGB_COLORS,
937                           (void**)&p_vout->p_sys->p_pic_buffer, NULL, 0 );
938
939     p_vout->p_sys->off_dc = CreateCompatibleDC( window_dc );
940
941     SelectObject( p_vout->p_sys->off_dc, p_vout->p_sys->off_bitmap );
942     ReleaseDC( p_vout->p_sys->hvideownd, window_dc );
943 #endif
944 }
945