win32 vout: factorize code
[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     if ( CreateEventThread( p_vout ) )
235     {
236
237 #ifndef UNDER_CE
238         /* Variable to indicate if the window should be on top of others */
239         /* Trigger a callback right now */
240         var_TriggerCallback( p_vout, "video-on-top" );
241
242         DisableScreensaver ( p_vout );
243 #endif
244         return VLC_SUCCESS;
245     }
246     else
247     {
248         CloseVideo( VLC_OBJECT(p_vout) );
249         return VLC_EGENERIC;
250     }
251 }
252
253 /*****************************************************************************
254  * CloseVideo: deactivate the GDI video output
255  *****************************************************************************/
256 static void CloseVideo ( vlc_object_t *p_this )
257 {
258     vout_thread_t * p_vout = (vout_thread_t *)p_this;
259
260     StopEventThread( p_vout );
261
262 #ifndef UNDER_CE
263     RestoreScreensaver( p_vout );
264 #endif
265
266 #ifdef MODULE_NAME_IS_wingapi
267     FreeLibrary( p_vout->p_sys->gapi_dll );
268 #endif
269
270     free( p_vout->p_sys );
271     p_vout->p_sys = NULL;
272 }
273
274 /*****************************************************************************
275  * Init: initialize video thread output method
276  *****************************************************************************/
277 static int Init( vout_thread_t *p_vout )
278 {
279     picture_t *p_pic;
280
281     /* Initialize offscreen buffer */
282     InitBuffers( p_vout );
283
284     p_vout->p_sys->rect_display.left = 0;
285     p_vout->p_sys->rect_display.top = 0;
286     p_vout->p_sys->rect_display.right  = GetSystemMetrics(SM_CXSCREEN);
287     p_vout->p_sys->rect_display.bottom = GetSystemMetrics(SM_CYSCREEN);
288
289     I_OUTPUTPICTURES = 0;
290
291     /* Initialize the output structure */
292     switch( p_vout->p_sys->i_depth )
293     {
294     case 8:
295         p_vout->output.i_chroma = VLC_CODEC_RGB8;
296         p_vout->output.pf_setpalette = SetPalette;
297         break;
298     case 15:
299         p_vout->output.i_chroma = VLC_CODEC_RGB15;
300         p_vout->output.i_rmask  = 0x7c00;
301         p_vout->output.i_gmask  = 0x03e0;
302         p_vout->output.i_bmask  = 0x001f;
303         break;
304     case 16:
305         p_vout->output.i_chroma = VLC_CODEC_RGB16;
306         p_vout->output.i_rmask  = 0xf800;
307         p_vout->output.i_gmask  = 0x07e0;
308         p_vout->output.i_bmask  = 0x001f;
309         break;
310     case 24:
311         p_vout->output.i_chroma = VLC_CODEC_RGB24;
312         p_vout->output.i_rmask  = 0x00ff0000;
313         p_vout->output.i_gmask  = 0x0000ff00;
314         p_vout->output.i_bmask  = 0x000000ff;
315         break;
316     case 32:
317         p_vout->output.i_chroma = VLC_CODEC_RGB32;
318         p_vout->output.i_rmask  = 0x00ff0000;
319         p_vout->output.i_gmask  = 0x0000ff00;
320         p_vout->output.i_bmask  = 0x000000ff;
321         break;
322     default:
323         msg_Err( p_vout, "screen depth %i not supported",
324                  p_vout->p_sys->i_depth );
325         return VLC_EGENERIC;
326         break;
327     }
328
329     p_pic = &p_vout->p_picture[0];
330
331 #ifdef MODULE_NAME_IS_wingapi
332     p_vout->output.i_width  = 0;
333     p_vout->output.i_height = 0;
334     p_pic->pf_lock  = GAPILockSurface;
335     p_pic->pf_unlock = GAPIUnlockSurface;
336     Manage( p_vout );
337     GAPILockSurface( p_vout, p_pic );
338     p_vout->i_changes = 0;
339     p_vout->output.i_width  = p_vout->p_sys->render_width;
340     p_vout->output.i_height = p_vout->p_sys->render_height;
341
342 #else
343     p_vout->output.i_width  = p_vout->render.i_width;
344     p_vout->output.i_height = p_vout->render.i_height;
345
346     p_vout->fmt_out = p_vout->fmt_in;
347     p_vout->fmt_out.i_chroma = p_vout->output.i_chroma;
348 #endif
349
350     p_vout->output.i_aspect = p_vout->render.i_aspect;
351
352     p_pic->p->p_pixels = p_vout->p_sys->p_pic_buffer;
353     p_pic->p->i_lines = p_vout->output.i_height;
354     p_pic->p->i_visible_lines = p_vout->output.i_height;
355     p_pic->p->i_pitch = p_vout->p_sys->i_pic_pitch;
356     p_pic->p->i_pixel_pitch = p_vout->p_sys->i_pic_pixel_pitch;
357     p_pic->p->i_visible_pitch = p_vout->output.i_width *
358         p_pic->p->i_pixel_pitch;
359     p_pic->i_planes = 1;
360     p_pic->i_status = DESTROYED_PICTURE;
361     p_pic->i_type   = DIRECT_PICTURE;
362
363     PP_OUTPUTPICTURE[ I_OUTPUTPICTURES++ ] = p_pic;
364
365     /* Change the window title bar text */
366     PostMessage( p_vout->p_sys->hwnd, WM_VLC_CHANGE_TEXT, 0, 0 );
367     UpdateRects( p_vout, true );
368
369     return VLC_SUCCESS;
370 }
371
372 /*****************************************************************************
373  * End: terminate video thread output method
374  *****************************************************************************/
375 static void End( vout_thread_t *p_vout )
376 {
377 #ifdef MODULE_NAME_IS_wingapi
378     GXCloseDisplay();
379 #else
380     DeleteDC( p_vout->p_sys->off_dc );
381     DeleteObject( p_vout->p_sys->off_bitmap );
382 #endif
383 }
384
385 /*****************************************************************************
386  * Manage: handle events
387  *****************************************************************************
388  * This function should be called regularly by video output thread. It manages
389  * console events. It returns a non null value on error.
390  *****************************************************************************/
391 static int Manage( vout_thread_t *p_vout )
392 {
393     /* If we do not control our window, we check for geometry changes
394      * ourselves because the parent might not send us its events. */
395     vlc_mutex_lock( &p_vout->p_sys->lock );
396     if( p_vout->p_sys->hparent && !p_vout->b_fullscreen )
397     {
398         RECT rect_parent;
399         POINT point;
400
401         vlc_mutex_unlock( &p_vout->p_sys->lock );
402
403         GetClientRect( p_vout->p_sys->hparent, &rect_parent );
404         point.x = point.y = 0;
405         ClientToScreen( p_vout->p_sys->hparent, &point );
406         OffsetRect( &rect_parent, point.x, point.y );
407
408         if( !EqualRect( &rect_parent, &p_vout->p_sys->rect_parent ) )
409         {
410             int i_x, i_y, i_width, i_height;
411             p_vout->p_sys->rect_parent = rect_parent;
412
413             /* This one is to force the update even if only
414              * the position has changed */
415             SetWindowPos( p_vout->p_sys->hwnd, 0, 1, 1,
416                           rect_parent.right - rect_parent.left,
417                           rect_parent.bottom - rect_parent.top, 0 );
418
419             SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0,
420                           rect_parent.right - rect_parent.left,
421                           rect_parent.bottom - rect_parent.top, 0 );
422
423             vout_PlacePicture( p_vout, rect_parent.right - rect_parent.left,
424                                rect_parent.bottom - rect_parent.top,
425                                &i_x, &i_y, &i_width, &i_height );
426
427             SetWindowPos( p_vout->p_sys->hvideownd, HWND_TOP,
428                           i_x, i_y, i_width, i_height, 0 );
429         }
430     }
431     else
432     {
433         vlc_mutex_unlock( &p_vout->p_sys->lock );
434     }
435
436     /* autoscale toggle */
437     if( p_vout->i_changes & VOUT_SCALE_CHANGE )
438     {
439         p_vout->i_changes &= ~VOUT_SCALE_CHANGE;
440
441         p_vout->b_autoscale = var_GetBool( p_vout, "autoscale" );
442         p_vout->i_zoom = (int) ZOOM_FP_FACTOR;
443
444         UpdateRects( p_vout, true );
445     }
446
447     /* scaling factor */
448     if( p_vout->i_changes & VOUT_ZOOM_CHANGE )
449     {
450         p_vout->i_changes &= ~VOUT_ZOOM_CHANGE;
451
452         p_vout->b_autoscale = false;
453         p_vout->i_zoom =
454             (int)( ZOOM_FP_FACTOR * var_GetFloat( p_vout, "scale" ) );
455         UpdateRects( p_vout, true );
456     }
457
458     /* Check for cropping / aspect changes */
459     if( p_vout->i_changes & VOUT_CROP_CHANGE ||
460         p_vout->i_changes & VOUT_ASPECT_CHANGE )
461     {
462         p_vout->i_changes &= ~VOUT_CROP_CHANGE;
463         p_vout->i_changes &= ~VOUT_ASPECT_CHANGE;
464
465         p_vout->fmt_out.i_x_offset = p_vout->fmt_in.i_x_offset;
466         p_vout->fmt_out.i_y_offset = p_vout->fmt_in.i_y_offset;
467         p_vout->fmt_out.i_visible_width = p_vout->fmt_in.i_visible_width;
468         p_vout->fmt_out.i_visible_height = p_vout->fmt_in.i_visible_height;
469         p_vout->fmt_out.i_aspect = p_vout->fmt_in.i_aspect;
470         p_vout->fmt_out.i_sar_num = p_vout->fmt_in.i_sar_num;
471         p_vout->fmt_out.i_sar_den = p_vout->fmt_in.i_sar_den;
472         p_vout->output.i_aspect = p_vout->fmt_in.i_aspect;
473         UpdateRects( p_vout, true );
474     }
475
476     /*
477      * Position Change
478      */
479     if( p_vout->p_sys->i_changes & DX_POSITION_CHANGE )
480     {
481         p_vout->p_sys->i_changes &= ~DX_POSITION_CHANGE;
482     }
483
484     /* We used to call the Win32 PeekMessage function here to read the window
485      * messages. But since window can stay blocked into this function for a
486      * long time (for example when you move your window on the screen), I
487      * decided to isolate PeekMessage in another thread. */
488
489     /*
490      * Fullscreen change
491      */
492     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE
493         || p_vout->p_sys->i_changes & VOUT_FULLSCREEN_CHANGE )
494     {
495         Win32ToggleFullscreen( p_vout );
496
497         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
498         p_vout->p_sys->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
499     }
500
501     /*
502      * Pointer change
503      */
504     if( p_vout->b_fullscreen && !p_vout->p_sys->b_cursor_hidden &&
505         (mdate() - p_vout->p_sys->i_lastmoved) >
506             p_vout->p_sys->i_mouse_hide_timeout )
507     {
508         POINT point;
509         HWND hwnd;
510
511         /* Hide the cursor only if it is inside our window */
512         GetCursorPos( &point );
513         hwnd = WindowFromPoint(point);
514         if( hwnd == p_vout->p_sys->hwnd || hwnd == p_vout->p_sys->hvideownd )
515         {
516             PostMessage( p_vout->p_sys->hwnd, WM_VLC_HIDE_MOUSE, 0, 0 );
517         }
518         else
519         {
520             p_vout->p_sys->i_lastmoved = mdate();
521         }
522     }
523
524     /*
525      * "Always on top" status change
526      */
527     if( p_vout->p_sys->b_on_top_change )
528     {
529         vlc_value_t val;
530         HMENU hMenu = GetSystemMenu( p_vout->p_sys->hwnd, FALSE );
531
532         var_Get( p_vout, "video-on-top", &val );
533
534         /* Set the window on top if necessary */
535         if( val.b_bool && !( GetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE )
536                            & WS_EX_TOPMOST ) )
537         {
538             CheckMenuItem( hMenu, IDM_TOGGLE_ON_TOP,
539                            MF_BYCOMMAND | MFS_CHECKED );
540             SetWindowPos( p_vout->p_sys->hwnd, HWND_TOPMOST, 0, 0, 0, 0,
541                           SWP_NOSIZE | SWP_NOMOVE );
542         }
543         else
544         /* The window shouldn't be on top */
545         if( !val.b_bool && ( GetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE )
546                            & WS_EX_TOPMOST ) )
547         {
548             CheckMenuItem( hMenu, IDM_TOGGLE_ON_TOP,
549                            MF_BYCOMMAND | MFS_UNCHECKED );
550             SetWindowPos( p_vout->p_sys->hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
551                           SWP_NOSIZE | SWP_NOMOVE );
552         }
553
554         p_vout->p_sys->b_on_top_change = false;
555     }
556
557     /* Check if the event thread is still running */
558     if( !vlc_object_alive (p_vout->p_sys->p_event) )
559     {
560         return VLC_EGENERIC; /* exit */
561     }
562
563     return VLC_SUCCESS;
564 }
565
566 /*****************************************************************************
567  * Render: render previously calculated output
568  *****************************************************************************/
569 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
570 {
571     /* No need to do anything, the fake direct buffers stay as they are */
572     (void)p_vout;
573     (void)p_pic;
574 }
575
576 /*****************************************************************************
577  * Display: displays previously rendered output
578  *****************************************************************************/
579 #define rect_src p_vout->p_sys->rect_src
580 #define rect_src_clipped p_vout->p_sys->rect_src_clipped
581 #define rect_dest p_vout->p_sys->rect_dest
582 #define rect_dest_clipped p_vout->p_sys->rect_dest_clipped
583
584 #ifndef MODULE_NAME_IS_wingapi
585 static void DisplayGDI( vout_thread_t *p_vout, picture_t *p_pic )
586 {
587     vout_sys_t *p_sys = p_vout->p_sys;
588     RECT rect_dst = rect_dest_clipped;
589     HDC hdc = GetDC( p_sys->hvideownd );
590
591     OffsetRect( &rect_dst, -rect_dest.left, -rect_dest.top );
592     SelectObject( p_sys->off_dc, p_sys->off_bitmap );
593
594     if( rect_dest_clipped.right - rect_dest_clipped.left !=
595         rect_src_clipped.right - rect_src_clipped.left ||
596         rect_dest_clipped.bottom - rect_dest_clipped.top !=
597         rect_src_clipped.bottom - rect_src_clipped.top )
598     {
599         StretchBlt( hdc, rect_dst.left, rect_dst.top,
600                     rect_dst.right, rect_dst.bottom,
601                     p_sys->off_dc, rect_src_clipped.left, rect_src_clipped.top,
602                     rect_src_clipped.right, rect_src_clipped.bottom, SRCCOPY );
603     }
604     else
605     {
606         BitBlt( hdc, rect_dst.left, rect_dst.top,
607                 rect_dst.right, rect_dst.bottom,
608                 p_sys->off_dc, rect_src_clipped.left,
609                 rect_src_clipped.top, SRCCOPY );
610     }
611
612     ReleaseDC( p_sys->hvideownd, hdc );
613 }
614
615 static void FirstDisplayGDI( vout_thread_t *p_vout, picture_t *p_pic )
616 {
617     /*
618     ** Video window is initially hidden, show it now since we got a
619     ** picture to show.
620     */
621     SetWindowPos( p_vout->p_sys->hvideownd, 0, 0, 0, 0, 0,
622         SWP_ASYNCWINDOWPOS|
623         SWP_FRAMECHANGED|
624         SWP_SHOWWINDOW|
625         SWP_NOMOVE|
626         SWP_NOSIZE|
627         SWP_NOZORDER );
628
629     /* get initial picture presented */
630     DisplayGDI(p_vout, p_pic);
631
632     /* use and restores proper display function for further pictures */
633     p_vout->pf_display = DisplayGDI;
634 }
635
636 #else
637
638 static int GAPILockSurface( vout_thread_t *p_vout, picture_t *p_pic )
639 {
640     vout_sys_t *p_sys = p_vout->p_sys;
641     int i_x, i_y, i_width, i_height;
642     RECT video_rect;
643     POINT point;
644
645     GetClientRect( p_sys->hwnd, &video_rect);
646     vout_PlacePicture( p_vout, video_rect.right - video_rect.left,
647                        video_rect.bottom - video_rect.top,
648                        &i_x, &i_y, &i_width, &i_height );
649     point.x = point.y = 0;
650     ClientToScreen( p_sys->hwnd, &point );
651     i_x += point.x + video_rect.left;
652     i_y += point.y + video_rect.top;
653
654     if( i_width != p_vout->output.i_width ||
655         i_height != p_vout->output.i_height )
656     {
657         GXDisplayProperties gxdisplayprop = GXGetDisplayProperties();
658
659         p_sys->render_width = i_width;
660         p_sys->render_height = i_height;
661         p_vout->i_changes |= VOUT_SIZE_CHANGE;
662
663         msg_Dbg( p_vout, "vout size change (%ix%i -> %ix%i)",
664                  i_width, i_height, p_vout->output.i_width,
665                  p_vout->output.i_height );
666
667         p_vout->p_sys->i_pic_pixel_pitch = gxdisplayprop.cbxPitch;
668         p_vout->p_sys->i_pic_pitch = gxdisplayprop.cbyPitch;
669         return VLC_EGENERIC;
670     }
671     else
672     {
673         GXDisplayProperties gxdisplayprop;
674         RECT display_rect, dest_rect;
675         uint8_t *p_dest, *p_src = p_pic->p->p_pixels;
676
677         video_rect.left = i_x; video_rect.top = i_y;
678         video_rect.right = i_x + i_width;
679         video_rect.bottom = i_y + i_height;
680
681         gxdisplayprop = GXGetDisplayProperties();
682         display_rect.left = 0; display_rect.top = 0;
683         display_rect.right = gxdisplayprop.cxWidth;
684         display_rect.bottom = gxdisplayprop.cyHeight;
685
686         if( !IntersectRect( &dest_rect, &video_rect, &display_rect ) )
687         {
688             return VLC_EGENERIC;
689         }
690
691 #if 0
692         msg_Err( p_vout, "video (%d,%d,%d,%d) display (%d,%d,%d,%d) "
693                  "dest (%d,%d,%d,%d)",
694                  video_rect.left, video_rect.right,
695                  video_rect.top, video_rect.bottom,
696                  display_rect.left, display_rect.right,
697                  display_rect.top, display_rect.bottom,
698                  dest_rect.left, dest_rect.right,
699                  dest_rect.top, dest_rect.bottom );
700 #endif
701
702         if( !(p_dest = GXBeginDraw()) )
703         {
704 #if 0
705             msg_Err( p_vout, "GXBeginDraw error %d ", GetLastError() );
706 #endif
707             return VLC_EGENERIC;
708         }
709
710         p_src += (dest_rect.left - video_rect.left) * gxdisplayprop.cbxPitch +
711             (dest_rect.top - video_rect.top) * p_pic->p->i_pitch;
712         p_dest += dest_rect.left * gxdisplayprop.cbxPitch +
713             dest_rect.top * gxdisplayprop.cbyPitch;
714         i_width = dest_rect.right - dest_rect.left;
715         i_height = dest_rect.bottom - dest_rect.top;
716
717         p_pic->p->p_pixels = p_dest;
718     }
719
720     return VLC_SUCCESS;
721 }
722
723 static int GAPIUnlockSurface( vout_thread_t *p_vout, picture_t *p_pic )
724 {
725     GXEndDraw();
726     return VLC_SUCCESS;
727 }
728
729 static void DisplayGAPI( vout_thread_t *p_vout, picture_t *p_pic )
730 {
731 }
732
733 static void FirstDisplayGAPI( vout_thread_t *p_vout, picture_t *p_pic )
734 {
735     /* get initial picture presented through D3D */
736     DisplayGAPI(p_vout, p_pic);
737
738     /*
739     ** Video window is initially hidden, show it now since we got a
740     ** picture to show.
741     */
742     SetWindowPos( p_vout->p_sys->hvideownd, 0, 0, 0, 0, 0,
743         SWP_ASYNCWINDOWPOS|
744         SWP_FRAMECHANGED|
745         SWP_SHOWWINDOW|
746         SWP_NOMOVE|
747         SWP_NOSIZE|
748         SWP_NOZORDER );
749
750     /* use and restores proper display function for further pictures */
751     p_vout->pf_display = DisplayGAPI;
752 }
753
754 #endif
755
756 #undef rect_src
757 #undef rect_src_clipped
758 #undef rect_dest
759 #undef rect_dest_clipped
760 /*****************************************************************************
761  * SetPalette: sets an 8 bpp palette
762  *****************************************************************************/
763 static void SetPalette( vout_thread_t *p_vout,
764                         uint16_t *red, uint16_t *green, uint16_t *blue )
765 {
766     msg_Err( p_vout, "FIXME: SetPalette unimplemented" );
767 }
768
769 /*****************************************************************************
770  * InitBuffers: initialize an offscreen bitmap for direct buffer operations.
771  *****************************************************************************/
772 static void InitBuffers( vout_thread_t *p_vout )
773 {
774     BITMAPINFOHEADER *p_header = &p_vout->p_sys->bitmapinfo.bmiHeader;
775     BITMAPINFO *p_info = &p_vout->p_sys->bitmapinfo;
776     HDC window_dc = GetDC( p_vout->p_sys->hvideownd );
777
778     /* Get screen properties */
779 #ifdef MODULE_NAME_IS_wingapi
780     GXDisplayProperties gx_displayprop = GXGetDisplayProperties();
781     p_vout->p_sys->i_depth = gx_displayprop.cBPP;
782 #else
783     p_vout->p_sys->i_depth = GetDeviceCaps( window_dc, PLANES ) *
784         GetDeviceCaps( window_dc, BITSPIXEL );
785 #endif
786     msg_Dbg( p_vout, "GDI depth is %i", p_vout->p_sys->i_depth );
787
788 #ifdef MODULE_NAME_IS_wingapi
789     GXOpenDisplay( p_vout->p_sys->hvideownd, GX_FULLSCREEN );
790
791 #else
792
793     /* Initialize offscreen bitmap */
794     memset( p_info, 0, sizeof( BITMAPINFO ) + 3 * sizeof( RGBQUAD ) );
795
796     p_header->biSize = sizeof( BITMAPINFOHEADER );
797     p_header->biSizeImage = 0;
798     p_header->biPlanes = 1;
799     switch( p_vout->p_sys->i_depth )
800     {
801     case 8:
802         p_header->biBitCount = 8;
803         p_header->biCompression = BI_RGB;
804         /* FIXME: we need a palette here */
805         break;
806     case 15:
807         p_header->biBitCount = 15;
808         p_header->biCompression = BI_BITFIELDS;//BI_RGB;
809         ((DWORD*)p_info->bmiColors)[0] = 0x00007c00;
810         ((DWORD*)p_info->bmiColors)[1] = 0x000003e0;
811         ((DWORD*)p_info->bmiColors)[2] = 0x0000001f;
812         break;
813     case 16:
814         p_header->biBitCount = 16;
815         p_header->biCompression = BI_BITFIELDS;//BI_RGB;
816         ((DWORD*)p_info->bmiColors)[0] = 0x0000f800;
817         ((DWORD*)p_info->bmiColors)[1] = 0x000007e0;
818         ((DWORD*)p_info->bmiColors)[2] = 0x0000001f;
819         break;
820     case 24:
821         p_header->biBitCount = 24;
822         p_header->biCompression = BI_RGB;
823         ((DWORD*)p_info->bmiColors)[0] = 0x00ff0000;
824         ((DWORD*)p_info->bmiColors)[1] = 0x0000ff00;
825         ((DWORD*)p_info->bmiColors)[2] = 0x000000ff;
826         break;
827     case 32:
828         p_header->biBitCount = 32;
829         p_header->biCompression = BI_RGB;
830         ((DWORD*)p_info->bmiColors)[0] = 0x00ff0000;
831         ((DWORD*)p_info->bmiColors)[1] = 0x0000ff00;
832         ((DWORD*)p_info->bmiColors)[2] = 0x000000ff;
833         break;
834     default:
835         msg_Err( p_vout, "screen depth %i not supported",
836                  p_vout->p_sys->i_depth );
837         return;
838         break;
839     }
840     p_header->biWidth = p_vout->render.i_width;
841     p_header->biHeight = -p_vout->render.i_height;
842     p_header->biClrImportant = 0;
843     p_header->biClrUsed = 0;
844     p_header->biXPelsPerMeter = 0;
845     p_header->biYPelsPerMeter = 0;
846
847     p_vout->p_sys->i_pic_pixel_pitch = p_header->biBitCount / 8;
848     p_vout->p_sys->i_pic_pitch = p_header->biBitCount * p_header->biWidth / 8;
849
850     p_vout->p_sys->off_bitmap =
851         CreateDIBSection( window_dc, (BITMAPINFO *)p_header, DIB_RGB_COLORS,
852                           (void**)&p_vout->p_sys->p_pic_buffer, NULL, 0 );
853
854     p_vout->p_sys->off_dc = CreateCompatibleDC( window_dc );
855
856     SelectObject( p_vout->p_sys->off_dc, p_vout->p_sys->off_bitmap );
857     ReleaseDC( p_vout->p_sys->hvideownd, window_dc );
858 #endif
859 }
860