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