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