41f5ec3a7591a5cd631b18916fd9767176378ae2
[vlc.git] / modules / video_output / msw / wingdi.c
1 /*****************************************************************************
2  * wingdi.c : Win32 / WinCE GDI video output plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2002 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/vlc.h>
34 #include <vlc_interface.h>
35 #include <vlc_playlist.h>
36 #include <vlc_vout.h>
37
38 #include <commctrl.h>
39
40 #include "vout.h"
41
42 #ifdef MODULE_NAME_IS_wingapi
43     typedef struct GXDisplayProperties {
44         DWORD cxWidth;
45         DWORD cyHeight;
46         long cbxPitch;
47         long cbyPitch;
48         long cBPP;
49         DWORD ffFormat;
50     } GXDisplayProperties;
51
52     typedef struct GXScreenRect {
53         DWORD dwTop;
54         DWORD dwLeft;
55         DWORD dwWidth;
56         DWORD dwHeight;
57     } GXScreenRect;
58
59 #   define GX_FULLSCREEN    0x01
60 #   define GX_NORMALKEYS    0x02
61 #   define GX_LANDSCAPEKEYS 0x03
62
63 #   ifndef kfLandscape
64 #       define kfLandscape      0x8
65 #       define kfPalette        0x10
66 #       define kfDirect         0x20
67 #       define kfDirect555      0x40
68 #       define kfDirect565      0x80
69 #       define kfDirect888      0x100
70 #       define kfDirect444      0x200
71 #       define kfDirectInverted 0x400
72 #   endif
73 #endif /* MODULE_NAME_IS_wingapi */
74
75 #define MAX_DIRECTBUFFERS 10
76
77 #ifdef UNDER_CE
78 #ifndef WS_OVERLAPPEDWINDOW
79 #   define WS_OVERLAPPEDWINDOW 0xcf0000
80 #endif
81 #ifndef WS_EX_NOPARENTNOTIFY
82 #   define WS_EX_NOPARENTNOTIFY 4
83 #endif
84 #ifndef WS_EX_APPWINDOW
85 #define WS_EX_APPWINDOW 0x40000
86 #endif
87 #define SetWindowLongPtr SetWindowLong
88 #define GetWindowLongPtr GetWindowLong
89 #define GWLP_USERDATA GWL_USERDATA
90 #define AdjustWindowRect(a,b,c)
91 #endif //UNDER_CE
92
93 #ifndef WS_NONAVDONEBUTTON
94 #define WS_NONAVDONEBUTTON 0
95 #endif
96 /*****************************************************************************
97  * Local prototypes
98  *****************************************************************************/
99 static int  OpenVideo  ( vlc_object_t * );
100 static void CloseVideo ( vlc_object_t * );
101
102 static int  Init      ( vout_thread_t * );
103 static void End       ( vout_thread_t * );
104 static int  Manage    ( vout_thread_t * );
105 static void Render    ( vout_thread_t *, picture_t * );
106 #ifdef MODULE_NAME_IS_wingapi
107 static void FirstDisplayGAPI( vout_thread_t *, picture_t * );
108 static void DisplayGAPI( vout_thread_t *, picture_t * );
109 static int GAPILockSurface( vout_thread_t *, picture_t * );
110 static int GAPIUnlockSurface( vout_thread_t *, picture_t * );
111 #else
112 static void FirstDisplayGDI( vout_thread_t *, picture_t * );
113 static void DisplayGDI( vout_thread_t *, picture_t * );
114 #endif
115 static void SetPalette( vout_thread_t *, uint16_t *, uint16_t *, uint16_t * );
116
117 static void InitBuffers        ( vout_thread_t * );
118
119 #ifdef MODULE_NAME_IS_wingapi
120 #   define GXOpenDisplay p_vout->p_sys->GXOpenDisplay
121 #   define GXCloseDisplay p_vout->p_sys->GXCloseDisplay
122 #   define GXBeginDraw p_vout->p_sys->GXBeginDraw
123 #   define GXEndDraw p_vout->p_sys->GXEndDraw
124 #   define GXGetDisplayProperties p_vout->p_sys->GXGetDisplayProperties
125 #   define GXSuspend p_vout->p_sys->GXSuspend
126 #   define GXResume p_vout->p_sys->GXResume
127 #endif
128
129 #define DX_POSITION_CHANGE 0x1000
130
131 /*****************************************************************************
132  * Module descriptor
133  *****************************************************************************/
134 vlc_module_begin();
135     set_category( CAT_VIDEO );
136     set_subcategory( SUBCAT_VIDEO_VOUT );
137 #ifdef MODULE_NAME_IS_wingapi
138     set_shortname( "Windows GAPI" );
139     set_description( _("Windows GAPI video output") );
140     set_capability( "video output", 20 );
141 #else
142     set_shortname( "Windows GDI" );
143     set_description( _("Windows GDI video output") );
144     set_capability( "video output", 10 );
145 #endif
146     set_callbacks( OpenVideo, CloseVideo );
147 vlc_module_end();
148
149 /*****************************************************************************
150  * OpenVideo: activate GDI video thread output method
151  *****************************************************************************/
152 static int OpenVideo ( vlc_object_t *p_this )
153 {
154     vout_thread_t * p_vout = (vout_thread_t *)p_this;
155     vlc_value_t val;
156
157     p_vout->p_sys = (vout_sys_t *)malloc( sizeof(vout_sys_t) );
158     if( !p_vout->p_sys ) return VLC_ENOMEM;
159     memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) );
160
161 #ifdef MODULE_NAME_IS_wingapi
162     /* Load GAPI */
163     p_vout->p_sys->gapi_dll = LoadLibrary( _T("GX.DLL") );
164     if( p_vout->p_sys->gapi_dll == NULL )
165     {
166         msg_Warn( p_vout, "failed loading gx.dll" );
167         free( p_vout->p_sys );
168         return VLC_EGENERIC;
169     }
170
171     GXOpenDisplay = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
172         _T("?GXOpenDisplay@@YAHPAUHWND__@@K@Z") );
173     GXCloseDisplay = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
174         _T("?GXCloseDisplay@@YAHXZ") );
175     GXBeginDraw = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
176         _T("?GXBeginDraw@@YAPAXXZ") );
177     GXEndDraw = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
178         _T("?GXEndDraw@@YAHXZ") );
179     GXGetDisplayProperties = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
180         _T("?GXGetDisplayProperties@@YA?AUGXDisplayProperties@@XZ") );
181     GXSuspend = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
182         _T("?GXSuspend@@YAHXZ") );
183     GXResume = GetProcAddress( p_vout->p_sys->gapi_dll,
184         _T("?GXResume@@YAHXZ") );
185
186     if( !GXOpenDisplay || !GXCloseDisplay || !GXBeginDraw || !GXEndDraw ||
187         !GXGetDisplayProperties || !GXSuspend || !GXResume )
188     {
189         msg_Err( p_vout, "failed GetProcAddress on gapi.dll" );
190         free( p_vout->p_sys );
191         return VLC_EGENERIC;
192     }
193
194     msg_Dbg( p_vout, "GAPI DLL loaded" );
195
196     p_vout->p_sys->render_width = p_vout->render.i_width;
197     p_vout->p_sys->render_height = p_vout->render.i_height;
198 #endif
199
200     p_vout->p_sys->p_event = (vlc_object_t *)
201         vlc_object_create( p_vout, VLC_OBJECT_GENERIC );
202     if( !p_vout->p_sys->p_event )
203     {
204         free( p_vout->p_sys );
205         return VLC_ENOMEM;
206     }
207
208     p_vout->pf_init = Init;
209     p_vout->pf_end = End;
210     p_vout->pf_manage = Manage;
211     p_vout->pf_render = Render;
212 #ifdef MODULE_NAME_IS_wingapi
213     p_vout->pf_display = FirstDisplayGAPI;
214
215     p_vout->p_sys->b_focus = 0;
216     p_vout->p_sys->b_parent_focus = 0;
217
218 #else
219     p_vout->pf_display = FirstDisplayGDI;
220 #endif
221
222     p_vout->p_sys->hwnd = p_vout->p_sys->hvideownd = NULL;
223     p_vout->p_sys->hparent = p_vout->p_sys->hfswnd = NULL;
224     p_vout->p_sys->i_changes = 0;
225     vlc_mutex_init( p_vout, &p_vout->p_sys->lock );
226     SetRectEmpty( &p_vout->p_sys->rect_display );
227     SetRectEmpty( &p_vout->p_sys->rect_parent );
228
229     var_Create( p_vout, "video-title", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
230     var_Create( p_vout, "disable-screensaver", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
231
232     p_vout->p_sys->b_cursor_hidden = 0;
233     p_vout->p_sys->i_lastmoved = mdate();
234
235     /* Set main window's size */
236     p_vout->p_sys->i_window_width = p_vout->i_window_width;
237     p_vout->p_sys->i_window_height = p_vout->i_window_height;
238
239     /* Create the EventThread, this thread is created by us to isolate
240      * the Win32 PeekMessage function calls. We want to do this because
241      * Windows can stay blocked inside this call for a long time, and when
242      * this happens it thus blocks vlc's video_output thread.
243      * Vout EventThread will take care of the creation of the video
244      * window (because PeekMessage has to be called from the same thread which
245      * created the window). */
246     msg_Dbg( p_vout, "creating Vout EventThread" );
247     p_vout->p_sys->p_event =
248         vlc_object_create( p_vout, sizeof(event_thread_t) );
249     p_vout->p_sys->p_event->p_vout = p_vout;
250     if( vlc_thread_create( p_vout->p_sys->p_event, "VLC Vout Events Thread",
251                            E_(EventThread), 0, 1 ) )
252     {
253         msg_Err( p_vout, "cannot create Vout EventThread" );
254         vlc_object_release( p_vout->p_sys->p_event );
255         p_vout->p_sys->p_event = NULL;
256         goto error;
257     }
258
259     if( p_vout->p_sys->p_event->b_error )
260     {
261         msg_Err( p_vout, "Vout EventThread failed" );
262         goto error;
263     }
264
265     vlc_object_attach( p_vout->p_sys->p_event, p_vout );
266
267     msg_Dbg( p_vout, "Vout EventThread running" );
268
269 #ifndef UNDER_CE
270     /* Variable to indicate if the window should be on top of others */
271     /* Trigger a callback right now */
272     var_Get( p_vout, "video-on-top", &val );
273     var_Set( p_vout, "video-on-top", val );
274
275     /* disable screensaver by temporarily changing system settings */
276     p_vout->p_sys->i_spi_lowpowertimeout = 0;
277     p_vout->p_sys->i_spi_powerofftimeout = 0;
278     p_vout->p_sys->i_spi_screensavetimeout = 0;
279     var_Get( p_vout, "disable-screensaver", &val);
280     if( val.b_bool ) {
281         msg_Dbg(p_vout, "disabling screen saver");
282         SystemParametersInfo(SPI_GETLOWPOWERTIMEOUT,
283             0, &(p_vout->p_sys->i_spi_lowpowertimeout), 0);
284         if( 0 != p_vout->p_sys->i_spi_lowpowertimeout ) {
285             SystemParametersInfo(SPI_SETLOWPOWERTIMEOUT, 0, NULL, 0);
286         }
287         SystemParametersInfo(SPI_GETPOWEROFFTIMEOUT, 0,
288             &(p_vout->p_sys->i_spi_powerofftimeout), 0);
289         if( 0 != p_vout->p_sys->i_spi_powerofftimeout ) {
290             SystemParametersInfo(SPI_SETPOWEROFFTIMEOUT, 0, NULL, 0);
291         }
292         SystemParametersInfo(SPI_GETSCREENSAVETIMEOUT, 0,
293             &(p_vout->p_sys->i_spi_screensavetimeout), 0);
294         if( 0 != p_vout->p_sys->i_spi_screensavetimeout ) {
295             SystemParametersInfo(SPI_SETSCREENSAVETIMEOUT, 0, NULL, 0);
296         }
297     }
298 #endif
299     return VLC_SUCCESS;
300
301 error:
302     CloseVideo( VLC_OBJECT(p_vout) );
303     return VLC_EGENERIC;
304 }
305
306 /*****************************************************************************
307  * CloseVideo: deactivate the GDI video output
308  *****************************************************************************/
309 static void CloseVideo ( vlc_object_t *p_this )
310 {
311     vout_thread_t * p_vout = (vout_thread_t *)p_this;
312
313     if( p_vout->p_sys->p_event )
314     {
315         vlc_object_detach( p_vout->p_sys->p_event );
316
317         /* Kill Vout EventThread */
318         vlc_object_kill( p_vout->p_sys->p_event );
319
320         /* we need to be sure Vout EventThread won't stay stuck in
321          * GetMessage, so we send a fake message */
322         if( p_vout->p_sys->hwnd )
323         {
324             PostMessage( p_vout->p_sys->hwnd, WM_NULL, 0, 0);
325         }
326
327         vlc_thread_join( p_vout->p_sys->p_event );
328         vlc_object_release( p_vout->p_sys->p_event );
329     }
330     vlc_mutex_destroy( &p_vout->p_sys->lock );
331
332 #ifndef UNDER_CE
333     /* restore screensaver system settings */
334     if( 0 != p_vout->p_sys->i_spi_lowpowertimeout ) {
335         SystemParametersInfo(SPI_SETLOWPOWERTIMEOUT,
336             p_vout->p_sys->i_spi_lowpowertimeout, NULL, 0);
337     }
338     if( 0 != p_vout->p_sys->i_spi_powerofftimeout ) {
339         SystemParametersInfo(SPI_SETPOWEROFFTIMEOUT,
340             p_vout->p_sys->i_spi_powerofftimeout, NULL, 0);
341     }
342     if( 0 != p_vout->p_sys->i_spi_screensavetimeout ) {
343         SystemParametersInfo(SPI_SETSCREENSAVETIMEOUT,
344             p_vout->p_sys->i_spi_screensavetimeout, NULL, 0);
345     }
346 #endif
347
348 #ifdef MODULE_NAME_IS_wingapi
349     FreeLibrary( p_vout->p_sys->gapi_dll );
350 #endif
351
352     if( p_vout->p_sys )
353     {
354         free( p_vout->p_sys );
355         p_vout->p_sys = NULL;
356     }
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     E_(UpdateRects)( p_vout, VLC_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     /* Check for cropping / aspect changes */
522     if( p_vout->i_changes & VOUT_CROP_CHANGE ||
523         p_vout->i_changes & VOUT_ASPECT_CHANGE )
524     {
525         p_vout->i_changes &= ~VOUT_CROP_CHANGE;
526         p_vout->i_changes &= ~VOUT_ASPECT_CHANGE;
527
528         p_vout->fmt_out.i_x_offset = p_vout->fmt_in.i_x_offset;
529         p_vout->fmt_out.i_y_offset = p_vout->fmt_in.i_y_offset;
530         p_vout->fmt_out.i_visible_width = p_vout->fmt_in.i_visible_width;
531         p_vout->fmt_out.i_visible_height = p_vout->fmt_in.i_visible_height;
532         p_vout->fmt_out.i_aspect = p_vout->fmt_in.i_aspect;
533         p_vout->fmt_out.i_sar_num = p_vout->fmt_in.i_sar_num;
534         p_vout->fmt_out.i_sar_den = p_vout->fmt_in.i_sar_den;
535         p_vout->output.i_aspect = p_vout->fmt_in.i_aspect;
536         E_(UpdateRects)( p_vout, VLC_TRUE );
537     }
538
539     /*
540      * Position Change
541      */
542     if( p_vout->p_sys->i_changes & DX_POSITION_CHANGE )
543     {
544         p_vout->p_sys->i_changes &= ~DX_POSITION_CHANGE;
545     }
546
547     /* We used to call the Win32 PeekMessage function here to read the window
548      * messages. But since window can stay blocked into this function for a
549      * long time (for example when you move your window on the screen), I
550      * decided to isolate PeekMessage in another thread. */
551
552     /*
553      * Fullscreen change
554      */
555     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE
556         || p_vout->p_sys->i_changes & VOUT_FULLSCREEN_CHANGE )
557     {
558         Win32ToggleFullscreen( p_vout );
559
560         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
561         p_vout->p_sys->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
562     }
563
564     /*
565      * Pointer change
566      */
567     if( p_vout->b_fullscreen && !p_vout->p_sys->b_cursor_hidden &&
568         (mdate() - p_vout->p_sys->i_lastmoved) > 5000000 )
569     {
570         POINT point;
571         HWND hwnd;
572
573         /* Hide the cursor only if it is inside our window */
574         GetCursorPos( &point );
575         hwnd = WindowFromPoint(point);
576         if( hwnd == p_vout->p_sys->hwnd || hwnd == p_vout->p_sys->hvideownd )
577         {
578             PostMessage( p_vout->p_sys->hwnd, WM_VLC_HIDE_MOUSE, 0, 0 );
579         }
580         else
581         {
582             p_vout->p_sys->i_lastmoved = mdate();
583         }
584     }
585
586     /*
587      * "Always on top" status change
588      */
589     if( p_vout->p_sys->b_on_top_change )
590     {
591         vlc_value_t val;
592         HMENU hMenu = GetSystemMenu( p_vout->p_sys->hwnd, FALSE );
593
594         var_Get( p_vout, "video-on-top", &val );
595
596         /* Set the window on top if necessary */
597         if( val.b_bool && !( GetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE )
598                            & WS_EX_TOPMOST ) )
599         {
600             CheckMenuItem( hMenu, IDM_TOGGLE_ON_TOP,
601                            MF_BYCOMMAND | MFS_CHECKED );
602             SetWindowPos( p_vout->p_sys->hwnd, HWND_TOPMOST, 0, 0, 0, 0,
603                           SWP_NOSIZE | SWP_NOMOVE );
604         }
605         else
606         /* The window shouldn't be on top */
607         if( !val.b_bool && ( GetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE )
608                            & WS_EX_TOPMOST ) )
609         {
610             CheckMenuItem( hMenu, IDM_TOGGLE_ON_TOP,
611                            MF_BYCOMMAND | MFS_UNCHECKED );
612             SetWindowPos( p_vout->p_sys->hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
613                           SWP_NOSIZE | SWP_NOMOVE );
614         }
615
616         p_vout->p_sys->b_on_top_change = VLC_FALSE;
617     }
618
619     /* Check if the event thread is still running */
620     if( p_vout->p_sys->p_event->b_die )
621     {
622         return VLC_EGENERIC; /* exit */
623     }
624
625     return VLC_SUCCESS;
626 }
627
628 /*****************************************************************************
629  * Render: render previously calculated output
630  *****************************************************************************/
631 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
632 {
633     /* No need to do anything, the fake direct buffers stay as they are */
634 }
635
636 /*****************************************************************************
637  * Display: displays previously rendered output
638  *****************************************************************************/
639 #define rect_src p_vout->p_sys->rect_src
640 #define rect_src_clipped p_vout->p_sys->rect_src_clipped
641 #define rect_dest p_vout->p_sys->rect_dest
642 #define rect_dest_clipped p_vout->p_sys->rect_dest_clipped
643
644 #ifndef MODULE_NAME_IS_wingapi
645 static void DisplayGDI( vout_thread_t *p_vout, picture_t *p_pic )
646 {
647     vout_sys_t *p_sys = p_vout->p_sys;
648     RECT rect_dst = rect_dest_clipped;
649     HDC hdc = GetDC( p_sys->hvideownd );
650
651     OffsetRect( &rect_dst, -rect_dest.left, -rect_dest.top );
652     SelectObject( p_sys->off_dc, p_sys->off_bitmap );
653
654     if( rect_dest_clipped.right - rect_dest_clipped.left !=
655         rect_src_clipped.right - rect_src_clipped.left ||
656         rect_dest_clipped.bottom - rect_dest_clipped.top !=
657         rect_src_clipped.bottom - rect_src_clipped.top )
658     {
659         StretchBlt( hdc, rect_dst.left, rect_dst.top,
660                     rect_dst.right, rect_dst.bottom,
661                     p_sys->off_dc, rect_src_clipped.left, rect_src_clipped.top,
662                     rect_src_clipped.right, rect_src_clipped.bottom, SRCCOPY );
663     }
664     else
665     {
666         BitBlt( hdc, rect_dst.left, rect_dst.top,
667                 rect_dst.right, rect_dst.bottom,
668                 p_sys->off_dc, rect_src_clipped.left,
669                 rect_src_clipped.top, SRCCOPY );
670     }
671
672     ReleaseDC( p_sys->hvideownd, hdc );
673 }
674
675 static void FirstDisplayGDI( vout_thread_t *p_vout, picture_t *p_pic )
676 {
677     /*
678     ** Video window is initially hidden, show it now since we got a
679     ** picture to show.
680     */
681     SetWindowPos( p_vout->p_sys->hvideownd, 0, 0, 0, 0, 0,
682         SWP_ASYNCWINDOWPOS|
683         SWP_FRAMECHANGED|
684         SWP_SHOWWINDOW|
685         SWP_NOMOVE|
686         SWP_NOSIZE|
687         SWP_NOZORDER );
688
689     /* get initial picture presented */
690     DisplayGDI(p_vout, p_pic);
691
692     /* use and restores proper display function for further pictures */
693     p_vout->pf_display = DisplayGDI;
694 }
695
696 #else
697
698 static int GAPILockSurface( vout_thread_t *p_vout, picture_t *p_pic )
699 {
700     vout_sys_t *p_sys = p_vout->p_sys;
701     int i_x, i_y, i_width, i_height;
702     RECT video_rect;
703     POINT point;
704
705     GetClientRect( p_sys->hwnd, &video_rect);
706     vout_PlacePicture( p_vout, video_rect.right - video_rect.left,
707                        video_rect.bottom - video_rect.top,
708                        &i_x, &i_y, &i_width, &i_height );
709     point.x = point.y = 0;
710     ClientToScreen( p_sys->hwnd, &point );
711     i_x += point.x + video_rect.left;
712     i_y += point.y + video_rect.top;
713
714     if( i_width != p_vout->output.i_width ||
715         i_height != p_vout->output.i_height )
716     {
717         GXDisplayProperties gxdisplayprop = GXGetDisplayProperties();
718
719         p_sys->render_width = i_width;
720         p_sys->render_height = i_height;
721         p_vout->i_changes |= VOUT_SIZE_CHANGE;
722
723         msg_Dbg( p_vout, "vout size change (%ix%i -> %ix%i)",
724                  i_width, i_height, p_vout->output.i_width,
725                  p_vout->output.i_height );
726
727         p_vout->p_sys->i_pic_pixel_pitch = gxdisplayprop.cbxPitch;
728         p_vout->p_sys->i_pic_pitch = gxdisplayprop.cbyPitch;
729         return VLC_EGENERIC;
730     }
731     else
732     {
733         GXDisplayProperties gxdisplayprop;
734         RECT display_rect, dest_rect;
735         uint8_t *p_dest, *p_src = p_pic->p->p_pixels;
736
737         video_rect.left = i_x; video_rect.top = i_y;
738         video_rect.right = i_x + i_width;
739         video_rect.bottom = i_y + i_height;
740
741         gxdisplayprop = GXGetDisplayProperties();
742         display_rect.left = 0; display_rect.top = 0;
743         display_rect.right = gxdisplayprop.cxWidth;
744         display_rect.bottom = gxdisplayprop.cyHeight;
745
746         if( !IntersectRect( &dest_rect, &video_rect, &display_rect ) )
747         {
748             return VLC_EGENERIC;
749         }
750
751 #if 0
752         msg_Err( p_vout, "video (%d,%d,%d,%d) display (%d,%d,%d,%d) "
753                  "dest (%d,%d,%d,%d)",
754                  video_rect.left, video_rect.right,
755                  video_rect.top, video_rect.bottom,
756                  display_rect.left, display_rect.right,
757                  display_rect.top, display_rect.bottom,
758                  dest_rect.left, dest_rect.right,
759                  dest_rect.top, dest_rect.bottom );
760 #endif
761
762         if( !(p_dest = GXBeginDraw()) )
763         {
764 #if 0
765             msg_Err( p_vout, "GXBeginDraw error %d ", GetLastError() );
766 #endif
767             return VLC_EGENERIC;
768         }
769
770         p_src += (dest_rect.left - video_rect.left) * gxdisplayprop.cbxPitch +
771             (dest_rect.top - video_rect.top) * p_pic->p->i_pitch;
772         p_dest += dest_rect.left * gxdisplayprop.cbxPitch +
773             dest_rect.top * gxdisplayprop.cbyPitch;
774         i_width = dest_rect.right - dest_rect.left;
775         i_height = dest_rect.bottom - dest_rect.top;
776
777         p_pic->p->p_pixels = p_dest;
778     }
779
780     return VLC_SUCCESS;
781 }
782
783 static int GAPIUnlockSurface( vout_thread_t *p_vout, picture_t *p_pic )
784 {
785     GXEndDraw();
786     return VLC_SUCCESS;
787 }
788
789 static void DisplayGAPI( vout_thread_t *p_vout, picture_t *p_pic )
790 {
791 }
792
793 static void FirstDisplayGAPI( vout_thread_t *p_vout, picture_t *p_pic )
794 {
795     /* get initial picture presented through D3D */
796     DisplayGAPI(p_vout, p_pic);
797
798     /*
799     ** Video window is initially hidden, show it now since we got a
800     ** picture to show.
801     */
802     SetWindowPos( p_vout->p_sys->hvideownd, 0, 0, 0, 0, 0,
803         SWP_ASYNCWINDOWPOS|
804         SWP_FRAMECHANGED|
805         SWP_SHOWWINDOW|
806         SWP_NOMOVE|
807         SWP_NOSIZE|
808         SWP_NOZORDER );
809
810     /* use and restores proper display function for further pictures */
811     p_vout->pf_display = DisplayGAPI;
812 }
813
814 #endif
815
816 #undef rect_src
817 #undef rect_src_clipped
818 #undef rect_dest
819 #undef rect_dest_clipped
820 /*****************************************************************************
821  * SetPalette: sets an 8 bpp palette
822  *****************************************************************************/
823 static void SetPalette( vout_thread_t *p_vout,
824                         uint16_t *red, uint16_t *green, uint16_t *blue )
825 {
826     msg_Err( p_vout, "FIXME: SetPalette unimplemented" );
827 }
828
829 /*****************************************************************************
830  * InitBuffers: initialize an offscreen bitmap for direct buffer operations.
831  *****************************************************************************/
832 static void InitBuffers( vout_thread_t *p_vout )
833 {
834     BITMAPINFOHEADER *p_header = &p_vout->p_sys->bitmapinfo.bmiHeader;
835     BITMAPINFO *p_info = &p_vout->p_sys->bitmapinfo;
836     int i_pixels = p_vout->render.i_height * p_vout->render.i_width;
837     HDC window_dc = GetDC( p_vout->p_sys->hvideownd );
838
839     /* Get screen properties */
840 #ifdef MODULE_NAME_IS_wingapi
841     GXDisplayProperties gx_displayprop = GXGetDisplayProperties();
842     p_vout->p_sys->i_depth = gx_displayprop.cBPP;
843 #else
844     p_vout->p_sys->i_depth = GetDeviceCaps( window_dc, PLANES ) *
845         GetDeviceCaps( window_dc, BITSPIXEL );
846 #endif
847     msg_Dbg( p_vout, "GDI depth is %i", p_vout->p_sys->i_depth );
848
849 #ifdef MODULE_NAME_IS_wingapi
850     GXOpenDisplay( p_vout->p_sys->hvideownd, GX_FULLSCREEN );
851
852 #else
853
854     /* Initialize offscreen bitmap */
855     memset( p_info, 0, sizeof( BITMAPINFO ) + 3 * sizeof( RGBQUAD ) );
856
857     p_header->biSize = sizeof( BITMAPINFOHEADER );
858     p_header->biSizeImage = 0;
859     p_header->biPlanes = 1;
860     switch( p_vout->p_sys->i_depth )
861     {
862     case 8:
863         p_header->biBitCount = 8;
864         p_header->biCompression = BI_RGB;
865         /* FIXME: we need a palette here */
866         break;
867     case 15:
868         p_header->biBitCount = 15;
869         p_header->biCompression = BI_BITFIELDS;//BI_RGB;
870         ((DWORD*)p_info->bmiColors)[0] = 0x00007c00;
871         ((DWORD*)p_info->bmiColors)[1] = 0x000003e0;
872         ((DWORD*)p_info->bmiColors)[2] = 0x0000001f;
873         break;
874     case 16:
875         p_header->biBitCount = 16;
876         p_header->biCompression = BI_BITFIELDS;//BI_RGB;
877         ((DWORD*)p_info->bmiColors)[0] = 0x0000f800;
878         ((DWORD*)p_info->bmiColors)[1] = 0x000007e0;
879         ((DWORD*)p_info->bmiColors)[2] = 0x0000001f;
880         break;
881     case 24:
882         p_header->biBitCount = 24;
883         p_header->biCompression = BI_RGB;
884         ((DWORD*)p_info->bmiColors)[0] = 0x00ff0000;
885         ((DWORD*)p_info->bmiColors)[1] = 0x0000ff00;
886         ((DWORD*)p_info->bmiColors)[2] = 0x000000ff;
887         break;
888     case 32:
889         p_header->biBitCount = 32;
890         p_header->biCompression = BI_RGB;
891         ((DWORD*)p_info->bmiColors)[0] = 0x00ff0000;
892         ((DWORD*)p_info->bmiColors)[1] = 0x0000ff00;
893         ((DWORD*)p_info->bmiColors)[2] = 0x000000ff;
894         break;
895     default:
896         msg_Err( p_vout, "screen depth %i not supported",
897                  p_vout->p_sys->i_depth );
898         return;
899         break;
900     }
901     p_header->biWidth = p_vout->render.i_width;
902     p_header->biHeight = -p_vout->render.i_height;
903     p_header->biClrImportant = 0;
904     p_header->biClrUsed = 0;
905     p_header->biXPelsPerMeter = 0;
906     p_header->biYPelsPerMeter = 0;
907
908     p_vout->p_sys->i_pic_pixel_pitch = p_header->biBitCount / 8;
909     p_vout->p_sys->i_pic_pitch = p_header->biBitCount * p_header->biWidth / 8;
910
911     p_vout->p_sys->off_bitmap =
912         CreateDIBSection( window_dc, (BITMAPINFO *)p_header, DIB_RGB_COLORS,
913                           (void**)&p_vout->p_sys->p_pic_buffer, NULL, 0 );
914
915     p_vout->p_sys->off_dc = CreateCompatibleDC( window_dc );
916
917     SelectObject( p_vout->p_sys->off_dc, p_vout->p_sys->off_bitmap );
918     ReleaseDC( p_vout->p_sys->hvideownd, window_dc );
919 #endif
920 }
921