]> git.sesse.net Git - vlc/blob - modules/video_output/directx/events.c
d2f8c04bb00a2a9a421f9a3d7f7be54c82354745
[vlc] / modules / video_output / directx / events.c
1 /*****************************************************************************
2  * events.c: Windows DirectX video output events handler
3  *****************************************************************************
4  * Copyright (C) 2001-2004 VideoLAN
5  * $Id$
6  *
7  * Authors: Gildas Bazin <gbazin@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24
25 /*****************************************************************************
26  * Preamble: This file contains the functions related to the creation of
27  *             a window and the handling of its messages (events).
28  *****************************************************************************/
29 #include <errno.h>                                                 /* ENOMEM */
30 #include <stdlib.h>                                                /* free() */
31 #include <ctype.h>                                              /* tolower() */
32 #include <string.h>                                            /* strerror() */
33
34 #include <vlc/vlc.h>
35 #include <vlc/intf.h>
36 #include <vlc/input.h>
37 #include <vlc/vout.h>
38
39 #include <windows.h>
40 #include <windowsx.h>
41 #include <shellapi.h>
42
43 #include <ddraw.h>
44
45 #include "vlc_keys.h"
46 #include "vout.h"
47
48 /*****************************************************************************
49  * Local prototypes.
50  *****************************************************************************/
51 static int  DirectXCreateWindow( vout_thread_t *p_vout );
52 static void DirectXCloseWindow ( vout_thread_t *p_vout );
53 static long FAR PASCAL DirectXEventProc( HWND, UINT, WPARAM, LPARAM );
54 static long FAR PASCAL DirectXVideoEventProc( HWND, UINT, WPARAM, LPARAM );
55
56 static int Control( vout_thread_t *p_vout, int i_query, va_list args );
57
58 static void DirectXPopupMenu( event_thread_t *p_event, vlc_bool_t b_open )
59 {
60     playlist_t *p_playlist =
61         vlc_object_find( p_event, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
62     if( p_playlist != NULL )
63     {
64         vlc_value_t val;
65         val.b_bool = b_open;
66         var_Set( p_playlist, "intf-popupmenu", val );
67         vlc_object_release( p_playlist );
68     }
69 }
70
71 static int DirectXConvertKey( int i_key );
72
73 /*****************************************************************************
74  * DirectXEventThread: Create video window & handle its messages
75  *****************************************************************************
76  * This function creates a video window and then enters an infinite loop
77  * that handles the messages sent to that window.
78  * The main goal of this thread is to isolate the Win32 PeekMessage function
79  * because this one can block for a long time.
80  *****************************************************************************/
81 void DirectXEventThread( event_thread_t *p_event )
82 {
83     MSG msg;
84     POINT old_mouse_pos = {0,0}, mouse_pos;
85     vlc_value_t val;
86     int i_width, i_height, i_x, i_y;
87     HMODULE hkernel32;
88
89     /* Initialisation */
90     p_event->p_vout->pf_control = Control;
91
92     /* Create a window for the video */
93     /* Creating a window under Windows also initializes the thread's event
94      * message queue */
95     if( DirectXCreateWindow( p_event->p_vout ) )
96     {
97         msg_Err( p_event, "out of memory" );
98         p_event->b_dead = VLC_TRUE;
99     }
100
101     /* Signal the creation of the window */
102     vlc_thread_ready( p_event );
103
104     /* Set power management stuff */
105     if( (hkernel32 = GetModuleHandle( "KERNEL32" ) ) )
106     {
107         ULONG (WINAPI* OurSetThreadExecutionState)( ULONG );
108
109         OurSetThreadExecutionState =
110             GetProcAddress( hkernel32, "SetThreadExecutionState" );
111
112         if( OurSetThreadExecutionState )
113             /* Prevent monitor from powering off */
114             OurSetThreadExecutionState( ES_DISPLAY_REQUIRED | ES_CONTINUOUS );
115         else
116             msg_Dbg( p_event, "no support for SetThreadExecutionState()" );
117     }
118
119     /* Main loop */
120     /* GetMessage will sleep if there's no message in the queue */
121     while( !p_event->b_die &&
122            GetMessage( &msg, p_event->p_vout->p_sys->hwnd, 0, 0 ) )
123     {
124         /* Check if we are asked to exit */
125         if( p_event->b_die )
126             break;
127
128         switch( msg.message )
129         {
130
131         case WM_MOUSEMOVE:
132             vout_PlacePicture( p_event->p_vout,
133                                p_event->p_vout->p_sys->i_window_width,
134                                p_event->p_vout->p_sys->i_window_height,
135                                &i_x, &i_y, &i_width, &i_height );
136
137             if( msg.hwnd != p_event->p_vout->p_sys->hwnd )
138             {
139                 /* Child window */
140                 i_x = i_y = 0;
141             }
142
143             if( i_width && i_height )
144             {
145                 val.i_int = ( GET_X_LPARAM(msg.lParam) - i_x )
146                              * p_event->p_vout->render.i_width / i_width;
147                 var_Set( p_event->p_vout, "mouse-x", val );
148                 val.i_int = ( GET_Y_LPARAM(msg.lParam) - i_y )
149                              * p_event->p_vout->render.i_height / i_height;
150                 var_Set( p_event->p_vout, "mouse-y", val );
151
152                 val.b_bool = VLC_TRUE;
153                 var_Set( p_event->p_vout, "mouse-moved", val );
154             }
155
156         case WM_NCMOUSEMOVE:
157             GetCursorPos( &mouse_pos );
158             if( (abs(mouse_pos.x - old_mouse_pos.x) > 2 ||
159                 (abs(mouse_pos.y - old_mouse_pos.y)) > 2 ) )
160             {
161                 GetCursorPos( &old_mouse_pos );
162                 p_event->p_vout->p_sys->i_lastmoved = mdate();
163
164                 if( p_event->p_vout->p_sys->b_cursor_hidden )
165                 {
166                     p_event->p_vout->p_sys->b_cursor_hidden = 0;
167                     ShowCursor( TRUE );
168                 }
169             }
170             break;
171
172         case WM_VLC_HIDE_MOUSE:
173             if( p_event->p_vout->p_sys->b_cursor_hidden ) break;
174             p_event->p_vout->p_sys->b_cursor_hidden = VLC_TRUE;
175             GetCursorPos( &old_mouse_pos );
176             ShowCursor( FALSE );
177             break;
178
179         case WM_VLC_SHOW_MOUSE:
180             if( !p_event->p_vout->p_sys->b_cursor_hidden ) break;
181             p_event->p_vout->p_sys->b_cursor_hidden = VLC_FALSE;
182             GetCursorPos( &old_mouse_pos );
183             ShowCursor( TRUE );
184             break;
185
186         case WM_LBUTTONDOWN:
187             var_Get( p_event->p_vout, "mouse-button-down", &val );
188             val.i_int |= 1;
189             var_Set( p_event->p_vout, "mouse-button-down", val );
190             DirectXPopupMenu( p_event, VLC_FALSE );
191             break;
192
193         case WM_LBUTTONUP:
194             var_Get( p_event->p_vout, "mouse-button-down", &val );
195             val.i_int &= ~1;
196             var_Set( p_event->p_vout, "mouse-button-down", val );
197
198             val.b_bool = VLC_TRUE;
199             var_Set( p_event->p_vout, "mouse-clicked", val );
200             break;
201
202         case WM_LBUTTONDBLCLK:
203             p_event->p_vout->p_sys->i_changes |= VOUT_FULLSCREEN_CHANGE;
204             break;
205
206         case WM_MBUTTONDOWN:
207             var_Get( p_event->p_vout, "mouse-button-down", &val );
208             val.i_int |= 2;
209             var_Set( p_event->p_vout, "mouse-button-down", val );
210             DirectXPopupMenu( p_event, VLC_FALSE );
211             break;
212
213         case WM_MBUTTONUP:
214             var_Get( p_event->p_vout, "mouse-button-down", &val );
215             val.i_int &= ~2;
216             var_Set( p_event->p_vout, "mouse-button-down", val );
217             break;
218
219         case WM_RBUTTONDOWN:
220             var_Get( p_event->p_vout, "mouse-button-down", &val );
221             val.i_int |= 4;
222             var_Set( p_event->p_vout, "mouse-button-down", val );
223             DirectXPopupMenu( p_event, VLC_FALSE );
224             break;
225
226         case WM_RBUTTONUP:
227             var_Get( p_event->p_vout, "mouse-button-down", &val );
228             val.i_int &= ~4;
229             var_Set( p_event->p_vout, "mouse-button-down", val );
230             DirectXPopupMenu( p_event, VLC_TRUE );
231             break;
232
233         case WM_KEYDOWN:
234         case WM_SYSKEYDOWN:
235             /* The key events are first processed here and not translated
236              * into WM_CHAR events because we need to know the status of the
237              * modifier keys. */
238             val.i_int = DirectXConvertKey( msg.wParam );
239             if( !val.i_int )
240             {
241                 /* This appears to be a "normal" (ascii) key */
242                 val.i_int = tolower( MapVirtualKey( msg.wParam, 2 ) );
243             }
244
245             if( val.i_int )
246             {
247                 if( GetKeyState(VK_CONTROL) & 0x8000 )
248                 {
249                     val.i_int |= KEY_MODIFIER_CTRL;
250                 }
251                 if( GetKeyState(VK_SHIFT) & 0x8000 )
252                 {
253                     val.i_int |= KEY_MODIFIER_SHIFT;
254                 }
255                 if( GetKeyState(VK_MENU) & 0x8000 )
256                 {
257                     val.i_int |= KEY_MODIFIER_ALT;
258                 }
259
260                 var_Set( p_event->p_vlc, "key-pressed", val );
261             }
262             break;
263
264         case WM_MOUSEWHEEL:
265             if( GET_WHEEL_DELTA_WPARAM( msg.wParam ) > 0 )
266             {
267                 val.i_int = KEY_MOUSEWHEELUP;
268             }
269             else
270             {
271                 val.i_int = KEY_MOUSEWHEELDOWN;
272             }
273             if( val.i_int )
274             {
275                 if( GetKeyState(VK_CONTROL) & 0x8000 )
276                 {
277                     val.i_int |= KEY_MODIFIER_CTRL;
278                 }
279                 if( GetKeyState(VK_SHIFT) & 0x8000 )
280                 {
281                     val.i_int |= KEY_MODIFIER_SHIFT;
282                 }
283                 if( GetKeyState(VK_MENU) & 0x8000 )
284                 {
285                     val.i_int |= KEY_MODIFIER_ALT;
286                 }
287
288                 var_Set( p_event->p_vlc, "key-pressed", val );
289             }
290             break;
291
292         case WM_VLC_CHANGE_TEXT:
293             var_Get( p_event->p_vout, "video-title", &val );
294
295             if( !val.psz_string || !*val.psz_string ) /* Default video title */
296             {
297                 if( p_event->p_vout->p_sys->b_using_overlay )
298                     SetWindowText( p_event->p_vout->p_sys->hwnd,
299                         VOUT_TITLE " (hardware YUV overlay DirectX output)" );
300                 else if( p_event->p_vout->p_sys->b_hw_yuv )
301                     SetWindowText( p_event->p_vout->p_sys->hwnd,
302                         VOUT_TITLE " (hardware YUV DirectX output)" );
303                 else SetWindowText( p_event->p_vout->p_sys->hwnd,
304                         VOUT_TITLE " (software RGB DirectX output)" );
305             }
306             else
307             {
308                 SetWindowText( p_event->p_vout->p_sys->hwnd, val.psz_string );
309             }
310             break;
311
312         default:
313             /* Messages we don't handle directly are dispatched to the
314              * window procedure */
315             TranslateMessage(&msg);
316             DispatchMessage(&msg);
317             break;
318
319         } /* End Switch */
320
321     } /* End Main loop */
322
323     /* Check for WM_QUIT if we created the window */
324     if( !p_event->p_vout->p_sys->hparent && msg.message == WM_QUIT )
325     {
326         msg_Warn( p_event, "WM_QUIT... should not happen!!" );
327         p_event->p_vout->p_sys->hwnd = NULL; /* Window already destroyed */
328     }
329
330     msg_Dbg( p_event, "DirectXEventThread terminating" );
331
332     /* clear the changes formerly signaled */
333     p_event->p_vout->p_sys->i_changes = 0;
334
335     DirectXCloseWindow( p_event->p_vout );
336 }
337
338
339 /* following functions are local */
340
341 /*****************************************************************************
342  * DirectXCreateWindow: create a window for the video.
343  *****************************************************************************
344  * Before creating a direct draw surface, we need to create a window in which
345  * the video will be displayed. This window will also allow us to capture the
346  * events.
347  *****************************************************************************/
348 static int DirectXCreateWindow( vout_thread_t *p_vout )
349 {
350     HINSTANCE  hInstance;
351     HMENU      hMenu;
352     RECT       rect_window;
353     WNDCLASSEX wc;                            /* window class components */
354     HICON      vlc_icon = NULL;
355     char       vlc_path[MAX_PATH+1];
356     int        i_style;
357
358     msg_Dbg( p_vout, "DirectXCreateWindow" );
359
360     /* Get this module's instance */
361     hInstance = GetModuleHandle(NULL);
362
363     /* If an external window was specified, we'll draw in it. */
364     p_vout->p_sys->hparent =
365         vout_RequestWindow( p_vout, &p_vout->p_sys->i_window_x,
366                             &p_vout->p_sys->i_window_y,
367                             &p_vout->p_sys->i_window_width,
368                             &p_vout->p_sys->i_window_height );
369
370     /* We create the window ourself, there is no previous window proc. */
371     p_vout->p_sys->pf_wndproc = NULL;
372
373     /* Get the Icon from the main app */
374     vlc_icon = NULL;
375     if( GetModuleFileName( NULL, vlc_path, MAX_PATH ) )
376     {
377         vlc_icon = ExtractIcon( hInstance, vlc_path, 0 );
378     }
379
380     /* Fill in the window class structure */
381     wc.cbSize        = sizeof(WNDCLASSEX);
382     wc.style         = CS_DBLCLKS;                   /* style: dbl click */
383     wc.lpfnWndProc   = (WNDPROC)DirectXEventProc;       /* event handler */
384     wc.cbClsExtra    = 0;                         /* no extra class data */
385     wc.cbWndExtra    = 0;                        /* no extra window data */
386     wc.hInstance     = hInstance;                            /* instance */
387     wc.hIcon         = vlc_icon;                /* load the vlc big icon */
388     wc.hCursor       = LoadCursor(NULL, IDC_ARROW);    /* default cursor */
389     wc.hbrBackground = GetStockObject(BLACK_BRUSH);  /* background color */
390     wc.lpszMenuName  = NULL;                                  /* no menu */
391     wc.lpszClassName = "VLC DirectX";             /* use a special class */
392     wc.hIconSm       = vlc_icon;              /* load the vlc small icon */
393
394     /* Register the window class */
395     if( !RegisterClassEx(&wc) )
396     {
397         WNDCLASS wndclass;
398
399         if( vlc_icon ) DestroyIcon( vlc_icon );
400
401         /* Check why it failed. If it's because one already exists
402          * then fine, otherwise return with an error. */
403         if( !GetClassInfo( hInstance, "VLC DirectX", &wndclass ) )
404         {
405             msg_Err( p_vout, "DirectXCreateWindow RegisterClass FAILED" );
406             return VLC_EGENERIC;
407         }
408     }
409
410     /* When you create a window you give the dimensions you wish it to
411      * have. Unfortunatly these dimensions will include the borders and
412      * titlebar. We use the following function to find out the size of
413      * the window corresponding to the useable surface we want */
414     rect_window.top    = 10;
415     rect_window.left   = 10;
416     rect_window.right  = rect_window.left + p_vout->p_sys->i_window_width;
417     rect_window.bottom = rect_window.top + p_vout->p_sys->i_window_height;
418     AdjustWindowRect( &rect_window, WS_OVERLAPPEDWINDOW|WS_SIZEBOX, 0 );
419
420     if( p_vout->p_sys->hparent )
421         i_style = WS_VISIBLE|WS_CLIPCHILDREN|WS_CHILD;
422     else
423         i_style = WS_OVERLAPPEDWINDOW|WS_SIZEBOX|WS_VISIBLE|WS_CLIPCHILDREN;
424
425     /* Create the window */
426     p_vout->p_sys->hwnd =
427         CreateWindowEx( WS_EX_NOPARENTNOTIFY,
428                     "VLC DirectX",                   /* name of window class */
429                     VOUT_TITLE " (DirectX Output)", /* window title bar text */
430                     i_style,                                 /* window style */
431                     (p_vout->p_sys->i_window_x < 0) ? CW_USEDEFAULT :
432                         p_vout->p_sys->i_window_x,   /* default X coordinate */
433                     (p_vout->p_sys->i_window_y < 0) ? CW_USEDEFAULT :
434                         p_vout->p_sys->i_window_y,   /* default Y coordinate */
435                     rect_window.right - rect_window.left,    /* window width */
436                     rect_window.bottom - rect_window.top,   /* window height */
437                     p_vout->p_sys->hparent,                 /* parent window */
438                     NULL,                          /* no menu in this window */
439                     hInstance,            /* handle of this program instance */
440                     (LPVOID)p_vout );            /* send p_vout to WM_CREATE */
441
442     if( !p_vout->p_sys->hwnd )
443     {
444         msg_Warn( p_vout, "DirectXCreateWindow create window FAILED" );
445         return VLC_EGENERIC;
446     }
447
448     if( p_vout->p_sys->hparent )
449     {
450         LONG i_style;
451
452         /* We don't want the window owner to overwrite our client area */
453         i_style = GetWindowLong( p_vout->p_sys->hparent, GWL_STYLE );
454         SetWindowLong( p_vout->p_sys->hparent, GWL_STYLE,
455                        i_style | WS_CLIPCHILDREN );
456
457     }
458
459     /* Now display the window */
460     ShowWindow( p_vout->p_sys->hwnd, SW_SHOW );
461
462     /* Create video sub-window. This sub window will always exactly match
463      * the size of the video, which allows us to use crazy overlay colorkeys
464      * without having them shown outside of the video area. */
465     SendMessage( p_vout->p_sys->hwnd, WM_VLC_CREATE_VIDEO_WIN, 0, 0 );
466
467     /* Append a "Always On Top" entry in the system menu */
468     hMenu = GetSystemMenu( p_vout->p_sys->hwnd, FALSE );
469     AppendMenu( hMenu, MF_SEPARATOR, 0, "" );
470     AppendMenu( hMenu, MF_STRING | MF_UNCHECKED,
471                        IDM_TOGGLE_ON_TOP, "Always on &Top" );
472
473     return VLC_SUCCESS;
474 }
475
476 /*****************************************************************************
477  * DirectXCloseWindow: close the window created by DirectXCreateWindow
478  *****************************************************************************
479  * This function returns all resources allocated by DirectXCreateWindow.
480  *****************************************************************************/
481 static void DirectXCloseWindow( vout_thread_t *p_vout )
482 {
483     msg_Dbg( p_vout, "DirectXCloseWindow" );
484
485     DestroyWindow( p_vout->p_sys->hwnd );
486
487     if( p_vout->p_sys->hparent )
488         vout_ReleaseWindow( p_vout, (void *)p_vout->p_sys->hparent );
489
490     p_vout->p_sys->hwnd = NULL;
491
492     /* We don't unregister the Window Class because it could lead to race
493      * conditions and it will be done anyway by the system when the app will
494      * exit */
495 }
496
497 /*****************************************************************************
498  * DirectXUpdateRects: update clipping rectangles
499  *****************************************************************************
500  * This function is called when the window position or size are changed, and
501  * its job is to update the source and destination RECTs used to display the
502  * picture.
503  *****************************************************************************/
504 void DirectXUpdateRects( vout_thread_t *p_vout, vlc_bool_t b_force )
505 {
506 #define rect_src p_vout->p_sys->rect_src
507 #define rect_src_clipped p_vout->p_sys->rect_src_clipped
508 #define rect_dest p_vout->p_sys->rect_dest
509 #define rect_dest_clipped p_vout->p_sys->rect_dest_clipped
510
511     int i_width, i_height, i_x, i_y;
512
513     RECT  rect;
514     POINT point;
515
516     /* Retrieve the window size */
517     GetClientRect( p_vout->p_sys->hwnd, &rect );
518
519     /* Retrieve the window position */
520     point.x = point.y = 0;
521     ClientToScreen( p_vout->p_sys->hwnd, &point );
522
523     /* If nothing changed, we can return */
524     if( !b_force
525          && p_vout->p_sys->i_window_width == rect.right
526          && p_vout->p_sys->i_window_height == rect.bottom
527          && p_vout->p_sys->i_window_x == point.x
528          && p_vout->p_sys->i_window_y == point.y )
529     {
530         return;
531     }
532
533     /* Update the window position and size */
534     p_vout->p_sys->i_window_x = point.x;
535     p_vout->p_sys->i_window_y = point.y;
536     p_vout->p_sys->i_window_width = rect.right;
537     p_vout->p_sys->i_window_height = rect.bottom;
538
539     vout_PlacePicture( p_vout, rect.right, rect.bottom,
540                        &i_x, &i_y, &i_width, &i_height );
541
542     if( p_vout->p_sys->hvideownd )
543         SetWindowPos( p_vout->p_sys->hvideownd, HWND_TOP,
544                       i_x, i_y, i_width, i_height, 0 );
545
546     /* Destination image position and dimensions */
547     rect_dest.left = point.x + i_x;
548     rect_dest.right = rect_dest.left + i_width;
549     rect_dest.top = point.y + i_y;
550     rect_dest.bottom = rect_dest.top + i_height;
551
552     /* Apply overlay hardware constraints */
553     if( p_vout->p_sys->b_using_overlay )
554     {
555         if( p_vout->p_sys->i_align_dest_boundary )
556             rect_dest.left = ( rect_dest.left +
557                 p_vout->p_sys->i_align_dest_boundary / 2 ) & 
558                 ~p_vout->p_sys->i_align_dest_boundary;
559
560         if( p_vout->p_sys->i_align_dest_size )
561             rect_dest.right = (( rect_dest.right - rect_dest.left +
562                 p_vout->p_sys->i_align_dest_size / 2 ) & 
563                 ~p_vout->p_sys->i_align_dest_size) + rect_dest.left;
564     }
565
566     /* UpdateOverlay directdraw function doesn't automatically clip to the
567      * display size so we need to do it otherwise it will fail */
568
569     /* Clip the destination window */
570     if( !IntersectRect( &rect_dest_clipped, &rect_dest,
571                         &p_vout->p_sys->rect_display ) )
572     {
573         SetRectEmpty( &rect_src_clipped );
574         return;
575     }
576
577 #if 0
578     msg_Dbg( p_vout, "DirectXUpdateRects image_dst_clipped coords:"
579                      " %i,%i,%i,%i",
580                      rect_dest_clipped.left, rect_dest_clipped.top,
581                      rect_dest_clipped.right, rect_dest_clipped.bottom );
582 #endif
583
584     /* the 2 following lines are to fix a bug when clicking on the desktop */
585     if( (rect_dest_clipped.right - rect_dest_clipped.left)==0 ||
586         (rect_dest_clipped.bottom - rect_dest_clipped.top)==0 )
587     {
588         SetRectEmpty( &rect_src_clipped );
589         return;
590     }
591
592     /* src image dimensions */
593     rect_src.left = 0;
594     rect_src.top = 0;
595     rect_src.right = p_vout->render.i_width;
596     rect_src.bottom = p_vout->render.i_height;
597
598     /* Clip the source image */
599     rect_src_clipped.left = (rect_dest_clipped.left - rect_dest.left) *
600       p_vout->render.i_width / (rect_dest.right - rect_dest.left);
601     rect_src_clipped.right = p_vout->render.i_width -
602       (rect_dest.right - rect_dest_clipped.right) * p_vout->render.i_width /
603       (rect_dest.right - rect_dest.left);
604     rect_src_clipped.top = (rect_dest_clipped.top - rect_dest.top) *
605       p_vout->render.i_height / (rect_dest.bottom - rect_dest.top);
606     rect_src_clipped.bottom = p_vout->render.i_height -
607       (rect_dest.bottom - rect_dest_clipped.bottom) * p_vout->render.i_height /
608       (rect_dest.bottom - rect_dest.top);
609
610     /* Apply overlay hardware constraints */
611     if( p_vout->p_sys->b_using_overlay )
612     {
613         if( p_vout->p_sys->i_align_src_boundary )
614             rect_src_clipped.left = ( rect_src_clipped.left +
615                 p_vout->p_sys->i_align_src_boundary / 2 ) & 
616                 ~p_vout->p_sys->i_align_src_boundary;
617
618         if( p_vout->p_sys->i_align_src_size )
619             rect_src_clipped.right = (( rect_src_clipped.right -
620                 rect_src_clipped.left +
621                 p_vout->p_sys->i_align_src_size / 2 ) & 
622                 ~p_vout->p_sys->i_align_src_size) + rect_src_clipped.left;
623     }
624
625 #if 0
626     msg_Dbg( p_vout, "DirectXUpdateRects image_src_clipped"
627                      " coords: %i,%i,%i,%i",
628                      rect_src_clipped.left, rect_src_clipped.top,
629                      rect_src_clipped.right, rect_src_clipped.bottom );
630 #endif
631
632     /* The destination coordinates need to be relative to the current
633      * directdraw primary surface (display) */
634     rect_dest_clipped.left -= p_vout->p_sys->rect_display.left;
635     rect_dest_clipped.right -= p_vout->p_sys->rect_display.left;
636     rect_dest_clipped.top -= p_vout->p_sys->rect_display.top;
637     rect_dest_clipped.bottom -= p_vout->p_sys->rect_display.top;
638
639     if( p_vout->p_sys->b_using_overlay )
640         DirectXUpdateOverlay( p_vout );
641
642     /* Signal the change in size/position */
643     p_vout->p_sys->i_changes |= DX_POSITION_CHANGE;
644
645 #undef rect_src
646 #undef rect_src_clipped
647 #undef rect_dest
648 #undef rect_dest_clipped
649 }
650
651 /*****************************************************************************
652  * DirectXEventProc: This is the window event processing function.
653  *****************************************************************************
654  * On Windows, when you create a window you have to attach an event processing
655  * function to it. The aim of this function is to manage "Queued Messages" and
656  * "Nonqueued Messages".
657  * Queued Messages are those picked up and retransmitted by vout_Manage
658  * (using the GetMessage and DispatchMessage functions).
659  * Nonqueued Messages are those that Windows will send directly to this
660  * procedure (like WM_DESTROY, WM_WINDOWPOSCHANGED...)
661  *****************************************************************************/
662 static long FAR PASCAL DirectXEventProc( HWND hwnd, UINT message,
663                                          WPARAM wParam, LPARAM lParam )
664 {
665     vout_thread_t *p_vout;
666
667     if( message == WM_CREATE )
668     {
669         /* Store p_vout for future use */
670         p_vout = (vout_thread_t *)((CREATESTRUCT *)lParam)->lpCreateParams;
671         SetWindowLongPtr( hwnd, GWLP_USERDATA, (LONG_PTR)p_vout );
672     }
673     else
674     {
675         p_vout = (vout_thread_t *)GetWindowLongPtr( hwnd, GWLP_USERDATA );
676     }
677
678     if( !p_vout )
679     {
680         /* Hmmm mozilla does manage somehow to save the pointer to our
681          * windowproc and still calls it after the vout has been closed. */
682         return DefWindowProc(hwnd, message, wParam, lParam);
683     }
684
685     switch( message )
686     {
687
688     case WM_WINDOWPOSCHANGED:
689         DirectXUpdateRects( p_vout, VLC_TRUE );
690         return 0;
691
692     /* the user wants to close the window */
693     case WM_CLOSE:
694     {
695         playlist_t * p_playlist =
696             (playlist_t *)vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
697                                            FIND_ANYWHERE );
698         if( p_playlist == NULL )
699         {
700             return 0;
701         }
702
703         playlist_Stop( p_playlist );
704         vlc_object_release( p_playlist );
705         return 0;
706     }
707
708     /* the window has been closed so shut down everything now */
709     case WM_DESTROY:
710         msg_Dbg( p_vout, "WinProc WM_DESTROY" );
711         /* just destroy the window */
712         PostQuitMessage( 0 );
713         return 0;
714
715     case WM_SYSCOMMAND:
716         switch (wParam)
717         {
718             case SC_SCREENSAVE:                     /* catch the screensaver */
719             case SC_MONITORPOWER:              /* catch the monitor turn-off */
720                 msg_Dbg( p_vout, "WinProc WM_SYSCOMMAND" );
721                 return 0;                  /* this stops them from happening */
722             case IDM_TOGGLE_ON_TOP:            /* toggle the "on top" status */
723             {
724                 vlc_value_t val;
725                 msg_Dbg( p_vout, "WinProc WM_SYSCOMMAND: IDM_TOGGLE_ON_TOP");
726
727                 /* Get the current value... */
728                 if( var_Get( p_vout, "video-on-top", &val ) < 0 )
729                     return 0;
730                 /* ...and change it */
731                 val.b_bool = !val.b_bool;
732                 var_Set( p_vout, "video-on-top", val );
733                 return 0;
734                 break;
735             }
736         }
737         break;
738
739     case WM_VLC_CREATE_VIDEO_WIN:
740         /* Create video sub-window */
741         p_vout->p_sys->hvideownd =
742             CreateWindow( "STATIC", "",   /* window class and title bar text */
743                     WS_CHILD | WS_VISIBLE,                   /* window style */
744                     CW_USEDEFAULT, CW_USEDEFAULT,     /* default coordinates */
745                     CW_USEDEFAULT, CW_USEDEFAULT,
746                     hwnd,                                   /* parent window */
747                     NULL, GetModuleHandle(NULL), NULL );
748
749         if( !p_vout->p_sys->hvideownd )
750         {
751             msg_Warn( p_vout, "Can't create video sub-window" );
752         }
753         else
754         {
755             msg_Dbg( p_vout, "Created video sub-window" );
756             SetWindowLongPtr( p_vout->p_sys->hvideownd,
757                               GWLP_WNDPROC, (LONG_PTR)DirectXVideoEventProc );
758             /* Store the previous window proc of _this_ window with the video
759              * window so we can use it in DirectXVideoEventProc to pass
760              * messages to the creator of _this_ window */
761             SetWindowLongPtr( p_vout->p_sys->hvideownd, GWLP_USERDATA,
762                               (LONG_PTR)p_vout->p_sys->pf_wndproc );
763         }
764         break;
765
766     case WM_PAINT:
767     case WM_NCPAINT:
768     case WM_ERASEBKGND:
769         /* We do not want to relay these messages to the parent window
770          * because we rely on the background color for the overlay. */
771         return DefWindowProc(hwnd, message, wParam, lParam);
772         break;
773
774     default:
775         //msg_Dbg( p_vout, "WinProc WM Default %i", message );
776         break;
777     }
778
779     /* Let windows handle the message */
780     return DefWindowProc(hwnd, message, wParam, lParam);
781 }
782
783 static long FAR PASCAL DirectXVideoEventProc( HWND hwnd, UINT message,
784                                               WPARAM wParam, LPARAM lParam )
785 {
786     WNDPROC pf_parentwndproc;
787     POINT pt;
788
789     switch( message )
790     {
791     case WM_MOUSEMOVE:
792     case WM_LBUTTONDOWN:
793     case WM_LBUTTONUP:
794     case WM_LBUTTONDBLCLK:
795     case WM_MBUTTONDOWN:
796     case WM_MBUTTONUP:
797     case WM_RBUTTONDOWN:
798     case WM_RBUTTONUP:
799         /* Translate mouse cursor position to parent window coordinates. */
800         pt.x = LOWORD( lParam );
801         pt.y = HIWORD( lParam );
802         MapWindowPoints( hwnd, GetParent( hwnd ), &pt, 1 );
803         lParam = MAKELPARAM( pt.x, pt.y );
804         /* Fall through. */
805     case WM_KEYDOWN:
806     case WM_KEYUP:
807     case WM_SYSKEYDOWN:
808     case WM_SYSKEYUP:
809         /* Foward these to the original window proc of the parent so the
810          * creator of the window gets a chance to process them. If we created
811          * the parent window ourself DirectXEventThread will process these
812          * and they will never make it here.
813          * Note that we fake the hwnd to be our parent in order to prevent
814          * confusion in the creator's window proc. */
815         pf_parentwndproc = (WNDPROC)GetWindowLongPtr( hwnd, GWLP_USERDATA );
816
817         if( pf_parentwndproc )
818         {
819             LRESULT i_ret;
820             LONG_PTR p_backup;
821
822             pf_parentwndproc = (WNDPROC)GetWindowLongPtr( hwnd, GWLP_USERDATA);
823
824             p_backup = SetWindowLongPtr( GetParent( hwnd ), GWLP_USERDATA, 0 );
825             i_ret = CallWindowProc( pf_parentwndproc, GetParent( hwnd ),
826                                     message, wParam, lParam );
827             SetWindowLongPtr( GetParent( hwnd ), GWLP_USERDATA, p_backup );
828
829             return i_ret;
830         }
831         break;
832     }
833
834     return DefWindowProc(hwnd, message, wParam, lParam);
835 }
836
837 static struct
838 {
839     int i_dxkey;
840     int i_vlckey;
841
842 } dxkeys_to_vlckeys[] =
843 {
844     { VK_F1, KEY_F1 }, { VK_F2, KEY_F2 }, { VK_F3, KEY_F3 }, { VK_F4, KEY_F4 },
845     { VK_F5, KEY_F5 }, { VK_F6, KEY_F6 }, { VK_F7, KEY_F7 }, { VK_F8, KEY_F8 },
846     { VK_F9, KEY_F9 }, { VK_F10, KEY_F10 }, { VK_F11, KEY_F11 },
847     { VK_F12, KEY_F12 },
848
849     { VK_RETURN, KEY_ENTER },
850     { VK_SPACE, KEY_SPACE },
851     { VK_ESCAPE, KEY_ESC },
852
853     { VK_LEFT, KEY_LEFT },
854     { VK_RIGHT, KEY_RIGHT },
855     { VK_UP, KEY_UP },
856     { VK_DOWN, KEY_DOWN },
857
858     { VK_HOME, KEY_HOME },
859     { VK_END, KEY_END },
860     { VK_PRIOR, KEY_PAGEUP },
861     { VK_NEXT, KEY_PAGEDOWN },
862
863     { VK_CONTROL, 0 },
864     { VK_SHIFT, 0 },
865     { VK_MENU, 0 },
866
867     { 0, 0 }
868 };
869
870 static int DirectXConvertKey( int i_key )
871 {
872     int i;
873
874     for( i = 0; dxkeys_to_vlckeys[i].i_dxkey != 0; i++ )
875     {
876         if( dxkeys_to_vlckeys[i].i_dxkey == i_key )
877         {
878             return dxkeys_to_vlckeys[i].i_vlckey;
879         }
880     }
881
882     return 0;
883 }
884
885 /*****************************************************************************
886  * Control: control facility for the vout
887  *****************************************************************************/
888 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
889 {
890     double f_arg;
891     RECT rect_window;
892
893     switch( i_query )
894     {
895     case VOUT_SET_ZOOM:
896         if( p_vout->p_sys->hparent )
897             return vout_ControlWindow( p_vout,
898                     (void *)p_vout->p_sys->hparent, i_query, args );
899
900         f_arg = va_arg( args, double );
901
902         /* Update dimensions */
903         rect_window.top = rect_window.left = 0;
904         rect_window.right  = p_vout->i_window_width * f_arg;
905         rect_window.bottom = p_vout->i_window_height * f_arg;
906         AdjustWindowRect( &rect_window, WS_OVERLAPPEDWINDOW|WS_SIZEBOX, 0 );
907
908         SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0,
909                       rect_window.right - rect_window.left,
910                       rect_window.bottom - rect_window.top, SWP_NOMOVE );
911
912         return VLC_SUCCESS;
913
914     case VOUT_REPARENT:
915         /* Change window style, borders and title bar */
916         vlc_mutex_lock( &p_vout->p_sys->lock );
917         p_vout->p_sys->hparent = 0;
918         vlc_mutex_unlock( &p_vout->p_sys->lock );
919
920         SetParent( p_vout->p_sys->hwnd, GetDesktopWindow() );
921         SetWindowLong( p_vout->p_sys->hwnd, GWL_STYLE,
922                        WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW |
923                        WS_SIZEBOX | WS_VISIBLE );
924         SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0, 0, 0,
925                       SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED );
926
927       return VLC_SUCCESS;
928
929     case VOUT_CLOSE:
930         return VLC_SUCCESS;
931
932     case VOUT_SET_STAY_ON_TOP:
933         if( p_vout->p_sys->hparent )
934             return vout_ControlWindow( p_vout,
935                     (void *)p_vout->p_sys->hparent, i_query, args );
936
937         p_vout->p_sys->b_on_top_change = VLC_TRUE;
938         return VLC_SUCCESS;
939
940     default:
941         msg_Dbg( p_vout, "control query not supported" );
942         return VLC_EGENERIC;
943     }
944 }