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