]> git.sesse.net Git - vlc/blob - modules/video_output/directx/events.c
44207162a5e3370317177ae2d22c89c2370c5b92
[vlc] / modules / video_output / directx / events.c
1 /*****************************************************************************
2  * events.c: Windows DirectX video output events handler
3  *****************************************************************************
4  * Copyright (C) 2001 VideoLAN
5  * $Id: events.c,v 1.33 2003/12/16 22:10:56 gbazin Exp $
6  *
7  * Authors: Gildas Bazin <gbazin@netcourrier.com>
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 void DirectXPopupMenu( event_thread_t *p_event, vlc_bool_t b_open )
57 {
58     playlist_t *p_playlist =
59         vlc_object_find( p_event, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
60     if( p_playlist != NULL )
61     {
62         vlc_value_t val;
63         val.b_bool = b_open;
64         var_Set( p_playlist, "intf-popupmenu", val );
65         vlc_object_release( p_playlist );
66     }
67 }
68
69 static int DirectXConvertKey( int i_key );
70
71 /*****************************************************************************
72  * DirectXEventThread: Create video window & handle its messages
73  *****************************************************************************
74  * This function creates a video window and then enters an infinite loop
75  * that handles the messages sent to that window.
76  * The main goal of this thread is to isolate the Win32 PeekMessage function
77  * because this one can block for a long time.
78  *****************************************************************************/
79 void DirectXEventThread( event_thread_t *p_event )
80 {
81     MSG msg;
82     POINT old_mouse_pos = {0,0};
83     vlc_value_t val;
84     int i_width, i_height, i_x, i_y;
85
86     /* Initialisation */
87
88     /* Create a window for the video */
89     /* Creating a window under Windows also initializes the thread's event
90      * message queue */
91     if( DirectXCreateWindow( p_event->p_vout ) )
92     {
93         msg_Err( p_event, "out of memory" );
94         p_event->b_dead = VLC_TRUE;
95     }
96
97     /* signal the creation of the window */
98     vlc_thread_ready( p_event );
99
100     /* Main loop */
101     /* GetMessage will sleep if there's no message in the queue */
102     while( !p_event->b_die && ( p_event->p_vout->p_sys->hparent ||
103            GetMessage( &msg, p_event->p_vout->p_sys->hwnd, 0, 0 ) ) )
104     {
105         /* Check if we are asked to exit */
106         if( p_event->b_die )
107             break;
108
109         if( p_event->p_vout->p_sys->hparent )
110         {
111             /* Parent window was created in another thread so we can't
112              * access the window messages. */
113             msleep( INTF_IDLE_SLEEP );
114             continue;
115         }
116
117         switch( msg.message )
118         {
119
120         case WM_NCMOUSEMOVE:
121         case WM_MOUSEMOVE:
122             vout_PlacePicture( p_event->p_vout,
123                                p_event->p_vout->p_sys->i_window_width,
124                                p_event->p_vout->p_sys->i_window_height,
125                                &i_x, &i_y, &i_width, &i_height );
126
127             val.i_int = ( GET_X_LPARAM(msg.lParam) - i_x )
128                          * p_event->p_vout->render.i_width / i_width;
129             var_Set( p_event->p_vout, "mouse-x", val );
130             val.i_int = ( GET_Y_LPARAM(msg.lParam) - i_y )
131                          * p_event->p_vout->render.i_height / i_height;
132             var_Set( p_event->p_vout, "mouse-y", val );
133
134             val.b_bool = VLC_TRUE;
135             var_Set( p_event->p_vout, "mouse-moved", val );
136
137             if( (abs(GET_X_LPARAM(msg.lParam) - old_mouse_pos.x) > 2 ||
138                 (abs(GET_Y_LPARAM(msg.lParam) - old_mouse_pos.y)) > 2 ) )
139             {
140                 GetCursorPos( &old_mouse_pos );
141                 p_event->p_vout->p_sys->i_lastmoved = mdate();
142
143                 if( p_event->p_vout->p_sys->b_cursor_hidden )
144                 {
145                     p_event->p_vout->p_sys->b_cursor_hidden = 0;
146                     ShowCursor( TRUE );
147                 }
148             }
149             break;
150
151         case WM_VLC_HIDE_MOUSE:
152             GetCursorPos( &old_mouse_pos );
153             ShowCursor( FALSE );
154             break;
155
156         case WM_LBUTTONDOWN:
157             var_Get( p_event->p_vout, "mouse-button-down", &val );
158             val.i_int |= 1;
159             var_Set( p_event->p_vout, "mouse-button-down", val );
160             DirectXPopupMenu( p_event, VLC_FALSE );
161             break;
162
163         case WM_LBUTTONUP:
164             var_Get( p_event->p_vout, "mouse-button-down", &val );
165             val.i_int &= ~1;
166             var_Set( p_event->p_vout, "mouse-button-down", val );
167
168             val.b_bool = VLC_TRUE;
169             var_Set( p_event->p_vout, "mouse-clicked", val );
170             break;
171
172         case WM_LBUTTONDBLCLK:
173             p_event->p_vout->p_sys->i_changes |= VOUT_FULLSCREEN_CHANGE;
174             break;
175
176         case WM_MBUTTONDOWN:
177             var_Get( p_event->p_vout, "mouse-button-down", &val );
178             val.i_int |= 2;
179             var_Set( p_event->p_vout, "mouse-button-down", val );
180             DirectXPopupMenu( p_event, VLC_FALSE );
181             break;
182
183         case WM_MBUTTONUP:
184             var_Get( p_event->p_vout, "mouse-button-down", &val );
185             val.i_int &= ~2;
186             var_Set( p_event->p_vout, "mouse-button-down", val );
187             break;
188
189         case WM_RBUTTONDOWN:
190             var_Get( p_event->p_vout, "mouse-button-down", &val );
191             val.i_int |= 4;
192             var_Set( p_event->p_vout, "mouse-button-down", val );
193             DirectXPopupMenu( p_event, VLC_FALSE );
194             break;
195
196         case WM_RBUTTONUP:
197             var_Get( p_event->p_vout, "mouse-button-down", &val );
198             val.i_int &= ~4;
199             var_Set( p_event->p_vout, "mouse-button-down", val );
200             DirectXPopupMenu( p_event, VLC_TRUE );
201             break;
202
203         case WM_KEYDOWN:
204         case WM_SYSKEYDOWN:
205             /* The key events are first processed here and not translated
206              * into WM_CHAR events because we need to know the status of the
207              * modifier keys. */
208             val.i_int = DirectXConvertKey( msg.wParam );
209             if( !val.i_int )
210             {
211                 /* This appears to be a "normal" (ascii) key */
212                 val.i_int = tolower( MapVirtualKey( msg.wParam, 2 ) );
213             }
214
215             if( val.i_int )
216             {
217                 if( GetKeyState(VK_CONTROL) & 0x8000 )
218                 {
219                     val.i_int |= KEY_MODIFIER_CTRL;
220                 }
221                 if( GetKeyState(VK_SHIFT) & 0x8000 )
222                 {
223                     val.i_int |= KEY_MODIFIER_SHIFT;
224                 }
225                 if( GetKeyState(VK_MENU) & 0x8000 )
226                 {
227                     val.i_int |= KEY_MODIFIER_ALT;
228                 }
229
230                 var_Set( p_event->p_vlc, "key-pressed", val );
231             }
232             break;
233
234         default:
235             /* Messages we don't handle directly are dispatched to the
236              * window procedure */
237             TranslateMessage(&msg);
238             DispatchMessage(&msg);
239             break;
240
241         } /* End Switch */
242
243     } /* End Main loop */
244
245     if( msg.message == WM_QUIT )
246     {
247         msg_Warn( p_event, "WM_QUIT... should not happen!!" );
248         p_event->p_vout->p_sys->hwnd = NULL; /* Window already destroyed */
249     }
250
251     msg_Dbg( p_event, "DirectXEventThread terminating" );
252
253     /* clear the changes formerly signaled */
254     p_event->p_vout->p_sys->i_changes = 0;
255
256     DirectXCloseWindow( p_event->p_vout );
257 }
258
259
260 /* following functions are local */
261
262 /*****************************************************************************
263  * DirectXCreateWindow: create a window for the video.
264  *****************************************************************************
265  * Before creating a direct draw surface, we need to create a window in which
266  * the video will be displayed. This window will also allow us to capture the
267  * events.
268  *****************************************************************************/
269 static int DirectXCreateWindow( vout_thread_t *p_vout )
270 {
271     HINSTANCE  hInstance;
272     HMENU      hMenu;
273     RECT       rect_window;
274
275     vlc_value_t val;
276
277     msg_Dbg( p_vout, "DirectXCreateWindow" );
278
279     /* Get this module's instance */
280     hInstance = GetModuleHandle(NULL);
281
282     /* If an external window was specified, we'll draw in it. */
283     var_Get( p_vout->p_vlc, "drawable", &val );
284     p_vout->p_sys->hparent = p_vout->p_sys->hwnd =
285              val.i_int ?  (void*)(ptrdiff_t) val.i_int : NULL;
286
287     if( p_vout->p_sys->hparent )
288     {
289         msg_Dbg( p_vout, "using external window %p\n", p_vout->p_sys->hwnd );
290
291         /* Set stuff in the window that we can not put directly in
292          * a class (see below). */
293         SetClassLong( p_vout->p_sys->hwnd,
294                       GCL_STYLE, CS_DBLCLKS );
295         SetClassLong( p_vout->p_sys->hwnd,
296                       GCL_HBRBACKGROUND, (LONG)GetStockObject(BLACK_BRUSH) );
297         SetClassLong( p_vout->p_sys->hwnd,
298                       GCL_HCURSOR, (LONG)LoadCursor(NULL, IDC_ARROW) );
299         /* Store a p_vout pointer into the window local storage (for later
300          * use in DirectXEventProc). */
301         SetWindowLong( p_vout->p_sys->hwnd, GWL_USERDATA, (LONG)p_vout );
302
303         p_vout->p_sys->pf_wndproc =
304                (WNDPROC)SetWindowLong( p_vout->p_sys->hwnd,
305                                        GWL_WNDPROC, (LONG)DirectXEventProc );
306
307         /* Blam! Erase everything that might have been there. */
308         InvalidateRect( p_vout->p_sys->hwnd, NULL, TRUE );
309     }
310     else
311     {
312         WNDCLASSEX wc;                            /* window class components */
313         HICON      vlc_icon = NULL;
314         char       vlc_path[MAX_PATH+1];
315
316         /* Get the Icon from the main app */
317         vlc_icon = NULL;
318         if( GetModuleFileName( NULL, vlc_path, MAX_PATH ) )
319         {
320             vlc_icon = ExtractIcon( hInstance, vlc_path, 0 );
321         }
322
323         /* Fill in the window class structure */
324         wc.cbSize        = sizeof(WNDCLASSEX);
325         wc.style         = CS_DBLCLKS;                   /* style: dbl click */
326         wc.lpfnWndProc   = (WNDPROC)DirectXEventProc;       /* event handler */
327         wc.cbClsExtra    = 0;                         /* no extra class data */
328         wc.cbWndExtra    = 0;                        /* no extra window data */
329         wc.hInstance     = hInstance;                            /* instance */
330         wc.hIcon         = vlc_icon;                /* load the vlc big icon */
331         wc.hCursor       = LoadCursor(NULL, IDC_ARROW);    /* default cursor */
332         wc.hbrBackground = GetStockObject(BLACK_BRUSH);  /* background color */
333         wc.lpszMenuName  = NULL;                                  /* no menu */
334         wc.lpszClassName = "VLC DirectX";             /* use a special class */
335         wc.hIconSm       = vlc_icon;              /* load the vlc small icon */
336
337         /* Register the window class */
338         if( !RegisterClassEx(&wc) )
339         {
340             WNDCLASS wndclass;
341
342             if( vlc_icon )
343             {
344                 DestroyIcon( vlc_icon );
345             }
346
347             /* Check why it failed. If it's because one already exists
348              * then fine, otherwise return with an error. */
349             if( !GetClassInfo( hInstance, "VLC DirectX", &wndclass ) )
350             {
351                 msg_Err( p_vout, "DirectXCreateWindow RegisterClass FAILED" );
352                 return VLC_EGENERIC;
353             }
354         }
355
356         /* When you create a window you give the dimensions you wish it to
357          * have. Unfortunatly these dimensions will include the borders and
358          * titlebar. We use the following function to find out the size of
359          * the window corresponding to the useable surface we want */
360         rect_window.top    = 10;
361         rect_window.left   = 10;
362         rect_window.right  = rect_window.left + p_vout->p_sys->i_window_width;
363         rect_window.bottom = rect_window.top + p_vout->p_sys->i_window_height;
364         AdjustWindowRect( &rect_window, WS_OVERLAPPEDWINDOW|WS_SIZEBOX, 0 );
365
366         /* Create the window */
367         p_vout->p_sys->hwnd =
368             CreateWindow( "VLC DirectX",             /* name of window class */
369                     VOUT_TITLE " (DirectX Output)", /* window title bar text */
370                     WS_OVERLAPPEDWINDOW | WS_SIZEBOX | WS_VISIBLE |
371                     WS_CLIPCHILDREN,                         /* window style */
372                     CW_USEDEFAULT,                   /* default X coordinate */
373                     0,                               /* default Y coordinate */
374                     rect_window.right - rect_window.left,    /* window width */
375                     rect_window.bottom - rect_window.top,   /* window height */
376                     NULL,                                /* no parent window */
377                     NULL,                          /* no menu in this window */
378                     hInstance,            /* handle of this program instance */
379                     (LPVOID)p_vout );            /* send p_vout to WM_CREATE */
380
381         if( !p_vout->p_sys->hwnd )
382         {
383             msg_Warn( p_vout, "DirectXCreateWindow create window FAILED" );
384             return VLC_EGENERIC;
385         }
386     }
387
388     /* Now display the window */
389     ShowWindow( p_vout->p_sys->hwnd, SW_SHOW );
390
391     /* Create video sub-window. This sub window will always exactly match
392      * the size of the video, which allows us to use crazy overlay colorkeys
393      * without having them shown outside of the video area. */
394     SendMessage( p_vout->p_sys->hwnd, WM_VLC_CREATE_VIDEO_WIN, 0, 0 );
395
396     /* Append a "Always On Top" entry in the system menu */
397     hMenu = GetSystemMenu( p_vout->p_sys->hwnd, FALSE );
398     AppendMenu( hMenu, MF_SEPARATOR, 0, "" );
399     AppendMenu( hMenu, MF_STRING | MF_UNCHECKED,
400                        IDM_TOGGLE_ON_TOP, "Always on &Top" );
401
402     return VLC_SUCCESS;
403 }
404
405 /*****************************************************************************
406  * DirectXCloseWindow: close the window created by DirectXCreateWindow
407  *****************************************************************************
408  * This function returns all resources allocated by DirectXCreateWindow.
409  *****************************************************************************/
410 static void DirectXCloseWindow( vout_thread_t *p_vout )
411 {
412     msg_Dbg( p_vout, "DirectXCloseWindow" );
413
414     if( p_vout->p_sys->hwnd && !p_vout->p_sys->hparent )
415     {
416         DestroyWindow( p_vout->p_sys->hwnd );
417     }
418     else if( p_vout->p_sys->hparent )
419     {
420         /* Get rid of the video sub-window */
421         PostMessage( p_vout->p_sys->hvideownd, WM_VLC_DESTROY_VIDEO_WIN, 0, 0);
422
423         /* We don't want our windowproc to be called anymore */
424         SetWindowLong( p_vout->p_sys->hwnd,
425                        GWL_WNDPROC, (LONG)p_vout->p_sys->pf_wndproc );
426         SetWindowLong( p_vout->p_sys->hwnd, GWL_USERDATA, (LONG)NULL );
427
428         /* Blam! Erase everything that might have been there. */
429         InvalidateRect( p_vout->p_sys->hwnd, NULL, TRUE );
430     }
431
432     p_vout->p_sys->hwnd = NULL;
433
434     /* We don't unregister the Window Class because it could lead to race
435      * conditions and it will be done anyway by the system when the app will
436      * exit */
437 }
438
439 /*****************************************************************************
440  * DirectXUpdateRects: update clipping rectangles
441  *****************************************************************************
442  * This function is called when the window position or size are changed, and
443  * its job is to update the source and destination RECTs used to display the
444  * picture.
445  *****************************************************************************/
446 void DirectXUpdateRects( vout_thread_t *p_vout, vlc_bool_t b_force )
447 {
448 #define rect_src p_vout->p_sys->rect_src
449 #define rect_src_clipped p_vout->p_sys->rect_src_clipped
450 #define rect_dest p_vout->p_sys->rect_dest
451 #define rect_dest_clipped p_vout->p_sys->rect_dest_clipped
452
453     int i_width, i_height, i_x, i_y;
454
455     RECT  rect;
456     POINT point;
457
458     /* Retrieve the window size */
459     GetClientRect( p_vout->p_sys->hwnd, &rect );
460
461     /* Retrieve the window position */
462     point.x = point.y = 0;
463     ClientToScreen( p_vout->p_sys->hwnd, &point );
464
465     /* If nothing changed, we can return */
466     if( !b_force
467          && p_vout->p_sys->i_window_width == rect.right
468          && p_vout->p_sys->i_window_height == rect.bottom
469          && p_vout->p_sys->i_window_x == point.x
470          && p_vout->p_sys->i_window_y == point.y )
471     {
472         return;
473     }
474
475     /* Update the window position and size */
476     p_vout->p_sys->i_window_x = point.x;
477     p_vout->p_sys->i_window_y = point.y;
478     p_vout->p_sys->i_window_width = rect.right;
479     p_vout->p_sys->i_window_height = rect.bottom;
480
481     vout_PlacePicture( p_vout, rect.right, rect.bottom,
482                        &i_x, &i_y, &i_width, &i_height );
483
484     SetWindowPos( p_vout->p_sys->hvideownd, HWND_TOP,
485                   i_x, i_y, i_width, i_height, 0 );
486
487     /* Destination image position and dimensions */
488     rect_dest.left = point.x + i_x;
489     rect_dest.right = rect_dest.left + i_width;
490     rect_dest.top = point.y + i_y;
491     rect_dest.bottom = rect_dest.top + i_height;
492
493     /* UpdateOverlay directdraw function doesn't automatically clip to the
494      * display size so we need to do it otherwise it will fail */
495
496     /* Clip the destination window */
497     if( !IntersectRect( &rect_dest_clipped, &rect_dest,
498                         &p_vout->p_sys->rect_display ) )
499     {
500         SetRectEmpty( &rect_src_clipped );
501         return;
502     }
503
504 #if 0
505     msg_Dbg( p_vout, "DirectXUpdateRects image_dst_clipped coords:"
506                      " %i,%i,%i,%i",
507                      rect_dest_clipped.left, rect_dest_clipped.top,
508                      rect_dest_clipped.right, rect_dest_clipped.bottom );
509 #endif
510
511     /* the 2 following lines are to fix a bug when clicking on the desktop */
512     if( (rect_dest_clipped.right - rect_dest_clipped.left)==0 ||
513         (rect_dest_clipped.bottom - rect_dest_clipped.top)==0 )
514     {
515         SetRectEmpty( &rect_src_clipped );
516         return;
517     }
518
519     /* src image dimensions */
520     rect_src.left = 0;
521     rect_src.top = 0;
522     rect_src.right = p_vout->render.i_width;
523     rect_src.bottom = p_vout->render.i_height;
524
525     /* Clip the source image */
526     rect_src_clipped.left = (rect_dest_clipped.left - rect_dest.left) *
527       p_vout->render.i_width / (rect_dest.right - rect_dest.left);
528     rect_src_clipped.right = p_vout->render.i_width -
529       (rect_dest.right - rect_dest_clipped.right) * p_vout->render.i_width /
530       (rect_dest.right - rect_dest.left);
531     rect_src_clipped.top = (rect_dest_clipped.top - rect_dest.top) *
532       p_vout->render.i_height / (rect_dest.bottom - rect_dest.top);
533     rect_src_clipped.bottom = p_vout->render.i_height -
534       (rect_dest.bottom - rect_dest_clipped.bottom) * p_vout->render.i_height /
535       (rect_dest.bottom - rect_dest.top);
536
537     /* The destination coordinates need to be relative to the current
538      * directdraw primary surface (display) */
539     rect_dest_clipped.left -= p_vout->p_sys->rect_display.left;
540     rect_dest_clipped.right -= p_vout->p_sys->rect_display.left;
541     rect_dest_clipped.top -= p_vout->p_sys->rect_display.top;
542     rect_dest_clipped.bottom -= p_vout->p_sys->rect_display.top;
543
544 #if 0
545     msg_Dbg( p_vout, "DirectXUpdateRects image_src_clipped"
546                      " coords: %i,%i,%i,%i",
547                      rect_src_clipped.left, rect_src_clipped.top,
548                      rect_src_clipped.right, rect_src_clipped.bottom );
549 #endif
550
551     /* Signal the change in size/position */
552     p_vout->p_sys->i_changes |= DX_POSITION_CHANGE;
553
554 #undef rect_src
555 #undef rect_src_clipped
556 #undef rect_dest
557 #undef rect_dest_clipped
558 }
559
560 /*****************************************************************************
561  * DirectXEventProc: This is the window event processing function.
562  *****************************************************************************
563  * On Windows, when you create a window you have to attach an event processing
564  * function to it. The aim of this function is to manage "Queued Messages" and
565  * "Nonqueued Messages".
566  * Queued Messages are those picked up and retransmitted by vout_Manage
567  * (using the GetMessage and DispatchMessage functions).
568  * Nonqueued Messages are those that Windows will send directly to this
569  * procedure (like WM_DESTROY, WM_WINDOWPOSCHANGED...)
570  *****************************************************************************/
571 static long FAR PASCAL DirectXEventProc( HWND hwnd, UINT message,
572                                          WPARAM wParam, LPARAM lParam )
573 {
574     vout_thread_t *p_vout;
575
576     if( message == WM_CREATE )
577     {
578         /* Store p_vout for future use */
579         p_vout = (vout_thread_t *)((CREATESTRUCT *)lParam)->lpCreateParams;
580         SetWindowLong( hwnd, GWL_USERDATA, (LONG)p_vout );
581     }
582     else
583     {
584         p_vout = (vout_thread_t *)GetWindowLong( hwnd, GWL_USERDATA );
585     }
586
587     if( !p_vout )
588     {
589         /* Hmmm mozilla does manage somehow to save the pointer to our
590          * windowproc and still calls it after the vout has been closed. */
591         return DefWindowProc(hwnd, message, wParam, lParam);
592     }
593
594     switch( message )
595     {
596
597     case WM_WINDOWPOSCHANGED:
598         DirectXUpdateRects( p_vout, VLC_TRUE );
599         return 0;
600
601     /* the user wants to close the window */
602     case WM_CLOSE:
603     {
604         playlist_t * p_playlist =
605             (playlist_t *)vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
606                                            FIND_ANYWHERE );
607         if( p_playlist == NULL )
608         {
609             return 0;
610         }
611
612         playlist_Stop( p_playlist );
613         vlc_object_release( p_playlist );
614         return 0;
615     }
616
617     /* the window has been closed so shut down everything now */
618     case WM_DESTROY:
619         msg_Dbg( p_vout, "WinProc WM_DESTROY" );
620         /* just destroy the window */
621         PostQuitMessage( 0 );
622         return 0;
623
624     case WM_SYSCOMMAND:
625         switch (wParam)
626         {
627             case SC_SCREENSAVE:                     /* catch the screensaver */
628             case SC_MONITORPOWER:              /* catch the monitor turn-off */
629                 msg_Dbg( p_vout, "WinProc WM_SYSCOMMAND" );
630                 return 0;                  /* this stops them from happening */
631             case IDM_TOGGLE_ON_TOP:            /* toggle the "on top" status */
632             {
633                 vlc_value_t val;
634                 msg_Dbg( p_vout, "WinProc WM_SYSCOMMAND: IDM_TOGGLE_ON_TOP");
635
636                 /* Get the current value... */
637                 if( var_Get( p_vout, "video-on-top", &val ) < 0 )
638                     return 0;
639                 /* ...and change it */
640                 val.b_bool = !val.b_bool;
641                 var_Set( p_vout, "video-on-top", val );
642                 return 0;
643                 break;
644             }
645         }
646         break;
647
648     case WM_VLC_CREATE_VIDEO_WIN:
649         /* Create video sub-window */
650         p_vout->p_sys->hvideownd =
651             CreateWindow( "STATIC", "",   /* window class and title bar text */
652                     WS_CHILD | WS_VISIBLE,                   /* window style */
653                     CW_USEDEFAULT, CW_USEDEFAULT,     /* default coordinates */
654                     CW_USEDEFAULT, CW_USEDEFAULT,
655                     hwnd,                                   /* parent window */
656                     NULL, GetModuleHandle(NULL), NULL );
657
658         if( !p_vout->p_sys->hvideownd )
659         {
660             msg_Warn( p_vout, "Can't create video sub-window" );
661         }
662         else
663         {
664             SetWindowLong( p_vout->p_sys->hvideownd,
665                            GWL_WNDPROC, (LONG)DirectXVideoEventProc );
666         }
667         break;
668
669     default:
670         //msg_Dbg( p_vout, "WinProc WM Default %i", message );
671         break;
672     }
673
674     return DefWindowProc(hwnd, message, wParam, lParam);
675 }
676 static long FAR PASCAL DirectXVideoEventProc( HWND hwnd, UINT message,
677                                               WPARAM wParam, LPARAM lParam )
678 {
679     switch( message )
680     {
681     case WM_VLC_DESTROY_VIDEO_WIN:
682         /* Destroy video sub-window */
683         DestroyWindow( hwnd );
684         break;
685     }
686
687     return DefWindowProc(hwnd, message, wParam, lParam);
688 }
689
690 static struct
691 {
692     int i_dxkey;
693     int i_vlckey;
694
695 } dxkeys_to_vlckeys[] =
696 {
697     { VK_F1, KEY_F1 }, { VK_F2, KEY_F2 }, { VK_F3, KEY_F3 }, { VK_F4, KEY_F4 },
698     { VK_F5, KEY_F5 }, { VK_F6, KEY_F6 }, { VK_F7, KEY_F7 }, { VK_F8, KEY_F8 },
699     { VK_F9, KEY_F9 }, { VK_F10, KEY_F10 }, { VK_F11, KEY_F11 },
700     { VK_F12, KEY_F12 },
701
702     { VK_RETURN, KEY_ENTER },
703     { VK_SPACE, KEY_SPACE },
704     { VK_ESCAPE, KEY_ESC },
705
706     { VK_LEFT, KEY_LEFT },
707     { VK_RIGHT, KEY_RIGHT },
708     { VK_UP, KEY_UP },
709     { VK_DOWN, KEY_DOWN },
710
711     { VK_HOME, KEY_HOME },
712     { VK_END, KEY_END },
713     { VK_PRIOR, KEY_PAGEUP },
714     { VK_NEXT, KEY_PAGEDOWN },
715
716     { VK_CONTROL, 0 },
717     { VK_SHIFT, 0 },
718     { VK_MENU, 0 },
719
720     { 0, 0 }
721 };
722
723 static int DirectXConvertKey( int i_key )
724 {
725     int i;
726
727     for( i = 0; dxkeys_to_vlckeys[i].i_dxkey != 0; i++ )
728     {
729         if( dxkeys_to_vlckeys[i].i_dxkey == i_key )
730         {
731             return dxkeys_to_vlckeys[i].i_vlckey;
732         }
733     }
734
735     return 0;
736 }