706aded1725d59e795381d183d84c8e4e55f4842
[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-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 <windows.h>
40 #include <tchar.h>
41 #include <commctrl.h>
42
43 /*#ifdef MODULE_NAME_IS_wingapi
44     typedef struct GXDisplayProperties {
45         DWORD cxWidth;
46         DWORD cyHeight;
47         long cbxPitch;
48         long cbyPitch;
49         long cBPP;
50         DWORD ffFormat;
51     } GXDisplayProperties;
52
53     typedef struct GXScreenRect {
54         DWORD dwTop;
55         DWORD dwLeft;
56         DWORD dwWidth;
57         DWORD dwHeight;
58     } GXScreenRect;
59
60 #   define GX_FULLSCREEN    0x01
61 #   define GX_NORMALKEYS    0x02
62 #   define GX_LANDSCAPEKEYS 0x03
63
64 #   ifndef kfLandscape
65 #       define kfLandscape      0x8
66 #       define kfPalette        0x10
67 #       define kfDirect         0x20
68 #       define kfDirect555      0x40
69 #       define kfDirect565      0x80
70 #       define kfDirect888      0x100
71 #       define kfDirect444      0x200
72 #       define kfDirectInverted 0x400
73 #   endif
74 #endif */ /* MODULE_NAME_IS_wingapi */
75
76 #include "vout.h"
77
78 #define MAX_DIRECTBUFFERS 10
79
80 #ifdef UNDER_CE
81 #ifndef WS_OVERLAPPEDWINDOW
82 #   define WS_OVERLAPPEDWINDOW 0xcf0000
83 #endif
84 #ifndef WS_EX_NOPARENTNOTIFY
85 #   define WS_EX_NOPARENTNOTIFY 4
86 #endif
87 #ifndef WS_EX_APPWINDOW
88 #define WS_EX_APPWINDOW 0x40000
89 #endif
90 //#define SetWindowLongPtr SetWindowLong
91 //#define GetWindowLongPtr GetWindowLong
92 //#define GWLP_USERDATA GWL_USERDATA
93 #define AdjustWindowRect(a,b,c)
94 #endif //UNDER_CE
95
96 #ifndef WS_NONAVDONEBUTTON
97 #define WS_NONAVDONEBUTTON 0
98 #endif
99 /*****************************************************************************
100  * Local prototypes
101  *****************************************************************************/
102 static int  OpenVideo  ( vlc_object_t * );
103 static void CloseVideo ( vlc_object_t * );
104
105 static int  Init      ( vout_thread_t * );
106 static void End       ( vout_thread_t * );
107 static int  Manage    ( vout_thread_t * );
108 static void Render    ( vout_thread_t *, picture_t * );
109 #ifdef MODULE_NAME_IS_wingapi
110 static void FirstDisplayGAPI( vout_thread_t *, picture_t * );
111 static void DisplayGAPI( vout_thread_t *, picture_t * );
112 static int GAPILockSurface( vout_thread_t *, picture_t * );
113 static int GAPIUnlockSurface( vout_thread_t *, picture_t * );
114 #else
115 static void FirstDisplayGDI( vout_thread_t *, picture_t * );
116 static void DisplayGDI( vout_thread_t *, picture_t * );
117 #endif
118 static void SetPalette( vout_thread_t *, uint16_t *, uint16_t *, uint16_t * );
119
120 static void InitBuffers        ( vout_thread_t * );
121
122
123
124 #define DX_POSITION_CHANGE 0x1000
125
126 /*****************************************************************************
127  * Module descriptor
128  *****************************************************************************/
129 vlc_module_begin ()
130     set_category( CAT_VIDEO )
131     set_subcategory( SUBCAT_VIDEO_VOUT )
132 #ifdef MODULE_NAME_IS_wingapi
133     set_shortname( "Windows GAPI" )
134     set_description( N_("Windows GAPI video output") )
135     set_capability( "video output", 20 )
136 #else
137     set_shortname( "Windows GDI" )
138     set_description( N_("Windows GDI video output") )
139     set_capability( "video output", 10 )
140 #endif
141     set_callbacks( OpenVideo, CloseVideo )
142 vlc_module_end ()
143
144 /*****************************************************************************
145  * OpenVideo: activate GDI video thread output method
146  *****************************************************************************/
147 static int OpenVideo ( vlc_object_t *p_this )
148 {
149     vout_thread_t * p_vout = (vout_thread_t *)p_this;
150
151     p_vout->p_sys = (vout_sys_t *)calloc( 1, sizeof(vout_sys_t) );
152     if( !p_vout->p_sys ) return VLC_ENOMEM;
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_TriggerCallback( p_vout, "video-on-top" );
272
273     /* disable screensaver by temporarily changing system settings */
274     p_vout->p_sys->i_spi_lowpowertimeout = 0;
275     p_vout->p_sys->i_spi_powerofftimeout = 0;
276     p_vout->p_sys->i_spi_screensavetimeout = 0;
277     if( var_GetBool( p_vout, "disable-screensaver" ) ) {
278         msg_Dbg(p_vout, "disabling screen saver");
279         SystemParametersInfo(SPI_GETLOWPOWERTIMEOUT,
280             0, &(p_vout->p_sys->i_spi_lowpowertimeout), 0);
281         if( 0 != p_vout->p_sys->i_spi_lowpowertimeout ) {
282             SystemParametersInfo(SPI_SETLOWPOWERTIMEOUT, 0, NULL, 0);
283         }
284         SystemParametersInfo(SPI_GETPOWEROFFTIMEOUT, 0,
285             &(p_vout->p_sys->i_spi_powerofftimeout), 0);
286         if( 0 != p_vout->p_sys->i_spi_powerofftimeout ) {
287             SystemParametersInfo(SPI_SETPOWEROFFTIMEOUT, 0, NULL, 0);
288         }
289         SystemParametersInfo(SPI_GETSCREENSAVETIMEOUT, 0,
290             &(p_vout->p_sys->i_spi_screensavetimeout), 0);
291         if( 0 != p_vout->p_sys->i_spi_screensavetimeout ) {
292             SystemParametersInfo(SPI_SETSCREENSAVETIMEOUT, 0, NULL, 0);
293         }
294     }
295 #endif
296     return VLC_SUCCESS;
297
298 error:
299     CloseVideo( VLC_OBJECT(p_vout) );
300     return VLC_EGENERIC;
301 }
302
303 /*****************************************************************************
304  * CloseVideo: deactivate the GDI video output
305  *****************************************************************************/
306 static void CloseVideo ( vlc_object_t *p_this )
307 {
308     vout_thread_t * p_vout = (vout_thread_t *)p_this;
309
310     if( p_vout->b_fullscreen )
311     {
312         msg_Dbg( p_vout, "Quitting fullscreen" );
313         Win32ToggleFullscreen( p_vout );
314         /* Force fullscreen in the core for the next video */
315         var_SetBool( p_vout, "fullscreen", true );
316     }
317
318     if( p_vout->p_sys->p_event )
319     {
320         vlc_object_detach( p_vout->p_sys->p_event );
321
322         /* Kill Vout EventThread */
323         vlc_object_kill( p_vout->p_sys->p_event );
324
325         /* we need to be sure Vout EventThread won't stay stuck in
326          * GetMessage, so we send a fake message */
327         if( p_vout->p_sys->hwnd )
328         {
329             PostMessage( p_vout->p_sys->hwnd, WM_NULL, 0, 0);
330         }
331
332         vlc_thread_join( p_vout->p_sys->p_event );
333         vlc_object_release( p_vout->p_sys->p_event );
334     }
335     vlc_mutex_destroy( &p_vout->p_sys->lock );
336
337 #ifndef UNDER_CE
338     /* restore screensaver system settings */
339     if( 0 != p_vout->p_sys->i_spi_lowpowertimeout ) {
340         SystemParametersInfo(SPI_SETLOWPOWERTIMEOUT,
341             p_vout->p_sys->i_spi_lowpowertimeout, NULL, 0);
342     }
343     if( 0 != p_vout->p_sys->i_spi_powerofftimeout ) {
344         SystemParametersInfo(SPI_SETPOWEROFFTIMEOUT,
345             p_vout->p_sys->i_spi_powerofftimeout, NULL, 0);
346     }
347     if( 0 != p_vout->p_sys->i_spi_screensavetimeout ) {
348         SystemParametersInfo(SPI_SETSCREENSAVETIMEOUT,
349             p_vout->p_sys->i_spi_screensavetimeout, NULL, 0);
350     }
351 #endif
352
353 #ifdef MODULE_NAME_IS_wingapi
354     FreeLibrary( p_vout->p_sys->gapi_dll );
355 #endif
356
357     free( p_vout->p_sys );
358     p_vout->p_sys = NULL;
359 }
360
361 /*****************************************************************************
362  * Init: initialize video thread output method
363  *****************************************************************************/
364 static int Init( vout_thread_t *p_vout )
365 {
366     picture_t *p_pic;
367
368     /* Initialize offscreen buffer */
369     InitBuffers( p_vout );
370
371     p_vout->p_sys->rect_display.left = 0;
372     p_vout->p_sys->rect_display.top = 0;
373     p_vout->p_sys->rect_display.right  = GetSystemMetrics(SM_CXSCREEN);
374     p_vout->p_sys->rect_display.bottom = GetSystemMetrics(SM_CYSCREEN);
375
376     I_OUTPUTPICTURES = 0;
377
378     /* Initialize the output structure */
379     switch( p_vout->p_sys->i_depth )
380     {
381     case 8:
382         p_vout->output.i_chroma = VLC_CODEC_RGB8;
383         p_vout->output.pf_setpalette = SetPalette;
384         break;
385     case 15:
386         p_vout->output.i_chroma = VLC_CODEC_RGB15;
387         p_vout->output.i_rmask  = 0x7c00;
388         p_vout->output.i_gmask  = 0x03e0;
389         p_vout->output.i_bmask  = 0x001f;
390         break;
391     case 16:
392         p_vout->output.i_chroma = VLC_CODEC_RGB16;
393         p_vout->output.i_rmask  = 0xf800;
394         p_vout->output.i_gmask  = 0x07e0;
395         p_vout->output.i_bmask  = 0x001f;
396         break;
397     case 24:
398         p_vout->output.i_chroma = VLC_CODEC_RGB24;
399         p_vout->output.i_rmask  = 0x00ff0000;
400         p_vout->output.i_gmask  = 0x0000ff00;
401         p_vout->output.i_bmask  = 0x000000ff;
402         break;
403     case 32:
404         p_vout->output.i_chroma = VLC_CODEC_RGB32;
405         p_vout->output.i_rmask  = 0x00ff0000;
406         p_vout->output.i_gmask  = 0x0000ff00;
407         p_vout->output.i_bmask  = 0x000000ff;
408         break;
409     default:
410         msg_Err( p_vout, "screen depth %i not supported",
411                  p_vout->p_sys->i_depth );
412         return VLC_EGENERIC;
413         break;
414     }
415
416     p_pic = &p_vout->p_picture[0];
417
418 #ifdef MODULE_NAME_IS_wingapi
419     p_vout->output.i_width  = 0;
420     p_vout->output.i_height = 0;
421     p_pic->pf_lock  = GAPILockSurface;
422     p_pic->pf_unlock = GAPIUnlockSurface;
423     Manage( p_vout );
424     GAPILockSurface( p_vout, p_pic );
425     p_vout->i_changes = 0;
426     p_vout->output.i_width  = p_vout->p_sys->render_width;
427     p_vout->output.i_height = p_vout->p_sys->render_height;
428
429 #else
430     p_vout->output.i_width  = p_vout->render.i_width;
431     p_vout->output.i_height = p_vout->render.i_height;
432
433     p_vout->fmt_out = p_vout->fmt_in;
434     p_vout->fmt_out.i_chroma = p_vout->output.i_chroma;
435 #endif
436
437     p_vout->output.i_aspect = p_vout->render.i_aspect;
438
439     p_pic->p->p_pixels = p_vout->p_sys->p_pic_buffer;
440     p_pic->p->i_lines = p_vout->output.i_height;
441     p_pic->p->i_visible_lines = p_vout->output.i_height;
442     p_pic->p->i_pitch = p_vout->p_sys->i_pic_pitch;
443     p_pic->p->i_pixel_pitch = p_vout->p_sys->i_pic_pixel_pitch;
444     p_pic->p->i_visible_pitch = p_vout->output.i_width *
445         p_pic->p->i_pixel_pitch;
446     p_pic->i_planes = 1;
447     p_pic->i_status = DESTROYED_PICTURE;
448     p_pic->i_type   = DIRECT_PICTURE;
449
450     PP_OUTPUTPICTURE[ I_OUTPUTPICTURES++ ] = p_pic;
451
452     /* Change the window title bar text */
453     PostMessage( p_vout->p_sys->hwnd, WM_VLC_CHANGE_TEXT, 0, 0 );
454     UpdateRects( p_vout, true );
455
456     return VLC_SUCCESS;
457 }
458
459 /*****************************************************************************
460  * End: terminate video thread output method
461  *****************************************************************************/
462 static void End( vout_thread_t *p_vout )
463 {
464 #ifdef MODULE_NAME_IS_wingapi
465     GXCloseDisplay();
466 #else
467     DeleteDC( p_vout->p_sys->off_dc );
468     DeleteObject( p_vout->p_sys->off_bitmap );
469 #endif
470 }
471
472 /*****************************************************************************
473  * Manage: handle events
474  *****************************************************************************
475  * This function should be called regularly by video output thread. It manages
476  * console events. It returns a non null value on error.
477  *****************************************************************************/
478 static int Manage( vout_thread_t *p_vout )
479 {
480     /* If we do not control our window, we check for geometry changes
481      * ourselves because the parent might not send us its events. */
482     vlc_mutex_lock( &p_vout->p_sys->lock );
483     if( p_vout->p_sys->hparent && !p_vout->b_fullscreen )
484     {
485         RECT rect_parent;
486         POINT point;
487
488         vlc_mutex_unlock( &p_vout->p_sys->lock );
489
490         GetClientRect( p_vout->p_sys->hparent, &rect_parent );
491         point.x = point.y = 0;
492         ClientToScreen( p_vout->p_sys->hparent, &point );
493         OffsetRect( &rect_parent, point.x, point.y );
494
495         if( !EqualRect( &rect_parent, &p_vout->p_sys->rect_parent ) )
496         {
497             int i_x, i_y, i_width, i_height;
498             p_vout->p_sys->rect_parent = rect_parent;
499
500             /* This one is to force the update even if only
501              * the position has changed */
502             SetWindowPos( p_vout->p_sys->hwnd, 0, 1, 1,
503                           rect_parent.right - rect_parent.left,
504                           rect_parent.bottom - rect_parent.top, 0 );
505
506             SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0,
507                           rect_parent.right - rect_parent.left,
508                           rect_parent.bottom - rect_parent.top, 0 );
509
510             vout_PlacePicture( p_vout, rect_parent.right - rect_parent.left,
511                                rect_parent.bottom - rect_parent.top,
512                                &i_x, &i_y, &i_width, &i_height );
513
514             SetWindowPos( p_vout->p_sys->hvideownd, HWND_TOP,
515                           i_x, i_y, i_width, i_height, 0 );
516         }
517     }
518     else
519     {
520         vlc_mutex_unlock( &p_vout->p_sys->lock );
521     }
522
523     /* autoscale toggle */
524     if( p_vout->i_changes & VOUT_SCALE_CHANGE )
525     {
526         p_vout->i_changes &= ~VOUT_SCALE_CHANGE;
527
528         p_vout->b_autoscale = var_GetBool( p_vout, "autoscale" );
529         p_vout->i_zoom = (int) ZOOM_FP_FACTOR;
530
531         UpdateRects( p_vout, true );
532     }
533
534     /* scaling factor */
535     if( p_vout->i_changes & VOUT_ZOOM_CHANGE )
536     {
537         p_vout->i_changes &= ~VOUT_ZOOM_CHANGE;
538
539         p_vout->b_autoscale = false;
540         p_vout->i_zoom =
541             (int)( ZOOM_FP_FACTOR * var_GetFloat( p_vout, "scale" ) );
542         UpdateRects( p_vout, true );
543     }
544
545     /* Check for cropping / aspect changes */
546     if( p_vout->i_changes & VOUT_CROP_CHANGE ||
547         p_vout->i_changes & VOUT_ASPECT_CHANGE )
548     {
549         p_vout->i_changes &= ~VOUT_CROP_CHANGE;
550         p_vout->i_changes &= ~VOUT_ASPECT_CHANGE;
551
552         p_vout->fmt_out.i_x_offset = p_vout->fmt_in.i_x_offset;
553         p_vout->fmt_out.i_y_offset = p_vout->fmt_in.i_y_offset;
554         p_vout->fmt_out.i_visible_width = p_vout->fmt_in.i_visible_width;
555         p_vout->fmt_out.i_visible_height = p_vout->fmt_in.i_visible_height;
556         p_vout->fmt_out.i_aspect = p_vout->fmt_in.i_aspect;
557         p_vout->fmt_out.i_sar_num = p_vout->fmt_in.i_sar_num;
558         p_vout->fmt_out.i_sar_den = p_vout->fmt_in.i_sar_den;
559         p_vout->output.i_aspect = p_vout->fmt_in.i_aspect;
560         UpdateRects( p_vout, true );
561     }
562
563     /*
564      * Position Change
565      */
566     if( p_vout->p_sys->i_changes & DX_POSITION_CHANGE )
567     {
568         p_vout->p_sys->i_changes &= ~DX_POSITION_CHANGE;
569     }
570
571     /* We used to call the Win32 PeekMessage function here to read the window
572      * messages. But since window can stay blocked into this function for a
573      * long time (for example when you move your window on the screen), I
574      * decided to isolate PeekMessage in another thread. */
575
576     /*
577      * Fullscreen change
578      */
579     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE
580         || p_vout->p_sys->i_changes & VOUT_FULLSCREEN_CHANGE )
581     {
582         Win32ToggleFullscreen( p_vout );
583
584         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
585         p_vout->p_sys->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
586     }
587
588     /*
589      * Pointer change
590      */
591     if( p_vout->b_fullscreen && !p_vout->p_sys->b_cursor_hidden &&
592         (mdate() - p_vout->p_sys->i_lastmoved) >
593             p_vout->p_sys->i_mouse_hide_timeout )
594     {
595         POINT point;
596         HWND hwnd;
597
598         /* Hide the cursor only if it is inside our window */
599         GetCursorPos( &point );
600         hwnd = WindowFromPoint(point);
601         if( hwnd == p_vout->p_sys->hwnd || hwnd == p_vout->p_sys->hvideownd )
602         {
603             PostMessage( p_vout->p_sys->hwnd, WM_VLC_HIDE_MOUSE, 0, 0 );
604         }
605         else
606         {
607             p_vout->p_sys->i_lastmoved = mdate();
608         }
609     }
610
611     /*
612      * "Always on top" status change
613      */
614     if( p_vout->p_sys->b_on_top_change )
615     {
616         vlc_value_t val;
617         HMENU hMenu = GetSystemMenu( p_vout->p_sys->hwnd, FALSE );
618
619         var_Get( p_vout, "video-on-top", &val );
620
621         /* Set the window on top if necessary */
622         if( val.b_bool && !( GetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE )
623                            & WS_EX_TOPMOST ) )
624         {
625             CheckMenuItem( hMenu, IDM_TOGGLE_ON_TOP,
626                            MF_BYCOMMAND | MFS_CHECKED );
627             SetWindowPos( p_vout->p_sys->hwnd, HWND_TOPMOST, 0, 0, 0, 0,
628                           SWP_NOSIZE | SWP_NOMOVE );
629         }
630         else
631         /* The window shouldn't be on top */
632         if( !val.b_bool && ( GetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE )
633                            & WS_EX_TOPMOST ) )
634         {
635             CheckMenuItem( hMenu, IDM_TOGGLE_ON_TOP,
636                            MF_BYCOMMAND | MFS_UNCHECKED );
637             SetWindowPos( p_vout->p_sys->hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
638                           SWP_NOSIZE | SWP_NOMOVE );
639         }
640
641         p_vout->p_sys->b_on_top_change = false;
642     }
643
644     /* Check if the event thread is still running */
645     if( !vlc_object_alive (p_vout->p_sys->p_event) )
646     {
647         return VLC_EGENERIC; /* exit */
648     }
649
650     return VLC_SUCCESS;
651 }
652
653 /*****************************************************************************
654  * Render: render previously calculated output
655  *****************************************************************************/
656 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
657 {
658     /* No need to do anything, the fake direct buffers stay as they are */
659     (void)p_vout;
660     (void)p_pic;
661 }
662
663 /*****************************************************************************
664  * Display: displays previously rendered output
665  *****************************************************************************/
666 #define rect_src p_vout->p_sys->rect_src
667 #define rect_src_clipped p_vout->p_sys->rect_src_clipped
668 #define rect_dest p_vout->p_sys->rect_dest
669 #define rect_dest_clipped p_vout->p_sys->rect_dest_clipped
670
671 #ifndef MODULE_NAME_IS_wingapi
672 static void DisplayGDI( vout_thread_t *p_vout, picture_t *p_pic )
673 {
674     vout_sys_t *p_sys = p_vout->p_sys;
675     RECT rect_dst = rect_dest_clipped;
676     HDC hdc = GetDC( p_sys->hvideownd );
677
678     OffsetRect( &rect_dst, -rect_dest.left, -rect_dest.top );
679     SelectObject( p_sys->off_dc, p_sys->off_bitmap );
680
681     if( rect_dest_clipped.right - rect_dest_clipped.left !=
682         rect_src_clipped.right - rect_src_clipped.left ||
683         rect_dest_clipped.bottom - rect_dest_clipped.top !=
684         rect_src_clipped.bottom - rect_src_clipped.top )
685     {
686         StretchBlt( hdc, rect_dst.left, rect_dst.top,
687                     rect_dst.right, rect_dst.bottom,
688                     p_sys->off_dc, rect_src_clipped.left, rect_src_clipped.top,
689                     rect_src_clipped.right, rect_src_clipped.bottom, SRCCOPY );
690     }
691     else
692     {
693         BitBlt( hdc, rect_dst.left, rect_dst.top,
694                 rect_dst.right, rect_dst.bottom,
695                 p_sys->off_dc, rect_src_clipped.left,
696                 rect_src_clipped.top, SRCCOPY );
697     }
698
699     ReleaseDC( p_sys->hvideownd, hdc );
700 }
701
702 static void FirstDisplayGDI( vout_thread_t *p_vout, picture_t *p_pic )
703 {
704     /*
705     ** Video window is initially hidden, show it now since we got a
706     ** picture to show.
707     */
708     SetWindowPos( p_vout->p_sys->hvideownd, 0, 0, 0, 0, 0,
709         SWP_ASYNCWINDOWPOS|
710         SWP_FRAMECHANGED|
711         SWP_SHOWWINDOW|
712         SWP_NOMOVE|
713         SWP_NOSIZE|
714         SWP_NOZORDER );
715
716     /* get initial picture presented */
717     DisplayGDI(p_vout, p_pic);
718
719     /* use and restores proper display function for further pictures */
720     p_vout->pf_display = DisplayGDI;
721 }
722
723 #else
724
725 static int GAPILockSurface( vout_thread_t *p_vout, picture_t *p_pic )
726 {
727     vout_sys_t *p_sys = p_vout->p_sys;
728     int i_x, i_y, i_width, i_height;
729     RECT video_rect;
730     POINT point;
731
732     GetClientRect( p_sys->hwnd, &video_rect);
733     vout_PlacePicture( p_vout, video_rect.right - video_rect.left,
734                        video_rect.bottom - video_rect.top,
735                        &i_x, &i_y, &i_width, &i_height );
736     point.x = point.y = 0;
737     ClientToScreen( p_sys->hwnd, &point );
738     i_x += point.x + video_rect.left;
739     i_y += point.y + video_rect.top;
740
741     if( i_width != p_vout->output.i_width ||
742         i_height != p_vout->output.i_height )
743     {
744         GXDisplayProperties gxdisplayprop = GXGetDisplayProperties();
745
746         p_sys->render_width = i_width;
747         p_sys->render_height = i_height;
748         p_vout->i_changes |= VOUT_SIZE_CHANGE;
749
750         msg_Dbg( p_vout, "vout size change (%ix%i -> %ix%i)",
751                  i_width, i_height, p_vout->output.i_width,
752                  p_vout->output.i_height );
753
754         p_vout->p_sys->i_pic_pixel_pitch = gxdisplayprop.cbxPitch;
755         p_vout->p_sys->i_pic_pitch = gxdisplayprop.cbyPitch;
756         return VLC_EGENERIC;
757     }
758     else
759     {
760         GXDisplayProperties gxdisplayprop;
761         RECT display_rect, dest_rect;
762         uint8_t *p_dest, *p_src = p_pic->p->p_pixels;
763
764         video_rect.left = i_x; video_rect.top = i_y;
765         video_rect.right = i_x + i_width;
766         video_rect.bottom = i_y + i_height;
767
768         gxdisplayprop = GXGetDisplayProperties();
769         display_rect.left = 0; display_rect.top = 0;
770         display_rect.right = gxdisplayprop.cxWidth;
771         display_rect.bottom = gxdisplayprop.cyHeight;
772
773         if( !IntersectRect( &dest_rect, &video_rect, &display_rect ) )
774         {
775             return VLC_EGENERIC;
776         }
777
778 #if 0
779         msg_Err( p_vout, "video (%d,%d,%d,%d) display (%d,%d,%d,%d) "
780                  "dest (%d,%d,%d,%d)",
781                  video_rect.left, video_rect.right,
782                  video_rect.top, video_rect.bottom,
783                  display_rect.left, display_rect.right,
784                  display_rect.top, display_rect.bottom,
785                  dest_rect.left, dest_rect.right,
786                  dest_rect.top, dest_rect.bottom );
787 #endif
788
789         if( !(p_dest = GXBeginDraw()) )
790         {
791 #if 0
792             msg_Err( p_vout, "GXBeginDraw error %d ", GetLastError() );
793 #endif
794             return VLC_EGENERIC;
795         }
796
797         p_src += (dest_rect.left - video_rect.left) * gxdisplayprop.cbxPitch +
798             (dest_rect.top - video_rect.top) * p_pic->p->i_pitch;
799         p_dest += dest_rect.left * gxdisplayprop.cbxPitch +
800             dest_rect.top * gxdisplayprop.cbyPitch;
801         i_width = dest_rect.right - dest_rect.left;
802         i_height = dest_rect.bottom - dest_rect.top;
803
804         p_pic->p->p_pixels = p_dest;
805     }
806
807     return VLC_SUCCESS;
808 }
809
810 static int GAPIUnlockSurface( vout_thread_t *p_vout, picture_t *p_pic )
811 {
812     GXEndDraw();
813     return VLC_SUCCESS;
814 }
815
816 static void DisplayGAPI( vout_thread_t *p_vout, picture_t *p_pic )
817 {
818 }
819
820 static void FirstDisplayGAPI( vout_thread_t *p_vout, picture_t *p_pic )
821 {
822     /* get initial picture presented through D3D */
823     DisplayGAPI(p_vout, p_pic);
824
825     /*
826     ** Video window is initially hidden, show it now since we got a
827     ** picture to show.
828     */
829     SetWindowPos( p_vout->p_sys->hvideownd, 0, 0, 0, 0, 0,
830         SWP_ASYNCWINDOWPOS|
831         SWP_FRAMECHANGED|
832         SWP_SHOWWINDOW|
833         SWP_NOMOVE|
834         SWP_NOSIZE|
835         SWP_NOZORDER );
836
837     /* use and restores proper display function for further pictures */
838     p_vout->pf_display = DisplayGAPI;
839 }
840
841 #endif
842
843 #undef rect_src
844 #undef rect_src_clipped
845 #undef rect_dest
846 #undef rect_dest_clipped
847 /*****************************************************************************
848  * SetPalette: sets an 8 bpp palette
849  *****************************************************************************/
850 static void SetPalette( vout_thread_t *p_vout,
851                         uint16_t *red, uint16_t *green, uint16_t *blue )
852 {
853     msg_Err( p_vout, "FIXME: SetPalette unimplemented" );
854 }
855
856 /*****************************************************************************
857  * InitBuffers: initialize an offscreen bitmap for direct buffer operations.
858  *****************************************************************************/
859 static void InitBuffers( vout_thread_t *p_vout )
860 {
861     BITMAPINFOHEADER *p_header = &p_vout->p_sys->bitmapinfo.bmiHeader;
862     BITMAPINFO *p_info = &p_vout->p_sys->bitmapinfo;
863     HDC window_dc = GetDC( p_vout->p_sys->hvideownd );
864
865     /* Get screen properties */
866 #ifdef MODULE_NAME_IS_wingapi
867     GXDisplayProperties gx_displayprop = GXGetDisplayProperties();
868     p_vout->p_sys->i_depth = gx_displayprop.cBPP;
869 #else
870     p_vout->p_sys->i_depth = GetDeviceCaps( window_dc, PLANES ) *
871         GetDeviceCaps( window_dc, BITSPIXEL );
872 #endif
873     msg_Dbg( p_vout, "GDI depth is %i", p_vout->p_sys->i_depth );
874
875 #ifdef MODULE_NAME_IS_wingapi
876     GXOpenDisplay( p_vout->p_sys->hvideownd, GX_FULLSCREEN );
877
878 #else
879
880     /* Initialize offscreen bitmap */
881     memset( p_info, 0, sizeof( BITMAPINFO ) + 3 * sizeof( RGBQUAD ) );
882
883     p_header->biSize = sizeof( BITMAPINFOHEADER );
884     p_header->biSizeImage = 0;
885     p_header->biPlanes = 1;
886     switch( p_vout->p_sys->i_depth )
887     {
888     case 8:
889         p_header->biBitCount = 8;
890         p_header->biCompression = BI_RGB;
891         /* FIXME: we need a palette here */
892         break;
893     case 15:
894         p_header->biBitCount = 15;
895         p_header->biCompression = BI_BITFIELDS;//BI_RGB;
896         ((DWORD*)p_info->bmiColors)[0] = 0x00007c00;
897         ((DWORD*)p_info->bmiColors)[1] = 0x000003e0;
898         ((DWORD*)p_info->bmiColors)[2] = 0x0000001f;
899         break;
900     case 16:
901         p_header->biBitCount = 16;
902         p_header->biCompression = BI_BITFIELDS;//BI_RGB;
903         ((DWORD*)p_info->bmiColors)[0] = 0x0000f800;
904         ((DWORD*)p_info->bmiColors)[1] = 0x000007e0;
905         ((DWORD*)p_info->bmiColors)[2] = 0x0000001f;
906         break;
907     case 24:
908         p_header->biBitCount = 24;
909         p_header->biCompression = BI_RGB;
910         ((DWORD*)p_info->bmiColors)[0] = 0x00ff0000;
911         ((DWORD*)p_info->bmiColors)[1] = 0x0000ff00;
912         ((DWORD*)p_info->bmiColors)[2] = 0x000000ff;
913         break;
914     case 32:
915         p_header->biBitCount = 32;
916         p_header->biCompression = BI_RGB;
917         ((DWORD*)p_info->bmiColors)[0] = 0x00ff0000;
918         ((DWORD*)p_info->bmiColors)[1] = 0x0000ff00;
919         ((DWORD*)p_info->bmiColors)[2] = 0x000000ff;
920         break;
921     default:
922         msg_Err( p_vout, "screen depth %i not supported",
923                  p_vout->p_sys->i_depth );
924         return;
925         break;
926     }
927     p_header->biWidth = p_vout->render.i_width;
928     p_header->biHeight = -p_vout->render.i_height;
929     p_header->biClrImportant = 0;
930     p_header->biClrUsed = 0;
931     p_header->biXPelsPerMeter = 0;
932     p_header->biYPelsPerMeter = 0;
933
934     p_vout->p_sys->i_pic_pixel_pitch = p_header->biBitCount / 8;
935     p_vout->p_sys->i_pic_pitch = p_header->biBitCount * p_header->biWidth / 8;
936
937     p_vout->p_sys->off_bitmap =
938         CreateDIBSection( window_dc, (BITMAPINFO *)p_header, DIB_RGB_COLORS,
939                           (void**)&p_vout->p_sys->p_pic_buffer, NULL, 0 );
940
941     p_vout->p_sys->off_dc = CreateCompatibleDC( window_dc );
942
943     SelectObject( p_vout->p_sys->off_dc, p_vout->p_sys->off_bitmap );
944     ReleaseDC( p_vout->p_sys->hvideownd, window_dc );
945 #endif
946 }
947