07c9aa73d28521f9e591e513b7c9363c7215a5c2
[vlc.git] / modules / video_output / msw / events.c
1 /*****************************************************************************
2  * events.c: Windows DirectX video output events handler
3  *****************************************************************************
4  * Copyright (C) 2001-2009 the VideoLAN team
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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, 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 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <errno.h>                                                 /* ENOMEM */
34 #include <ctype.h>                                              /* tolower() */
35
36 #ifndef _WIN32_WINNT
37 #   define _WIN32_WINNT 0x0500
38 #endif
39
40 #include <vlc_common.h>
41 #include <vlc_interface.h>
42 #include <vlc_playlist.h>
43 #include <vlc_vout.h>
44 #include <vlc_vout_window.h>
45
46 #include <windows.h>
47 #include <tchar.h>
48 #include <windowsx.h>
49 #include <shellapi.h>
50
51 #ifdef MODULE_NAME_IS_directx
52 #include <ddraw.h>
53 #endif
54 #ifdef MODULE_NAME_IS_direct3d
55 #include <d3d9.h>
56 #endif
57 #ifdef MODULE_NAME_IS_glwin32
58 #include <GL/gl.h>
59 #endif
60
61 #include <vlc_keys.h>
62 #include "vout.h"
63
64 #ifdef UNDER_CE
65 #include <aygshell.h>
66     //WINSHELLAPI BOOL WINAPI SHFullScreen(HWND hwndRequester, DWORD dwState);
67 #endif
68
69 /*#if defined(UNDER_CE) && !defined(__PLUGIN__) --FIXME*/
70 /*#   define SHFS_SHOWSIPBUTTON 0x0004
71 #   define SHFS_HIDESIPBUTTON 0x0008
72 #   define MENU_HEIGHT 26
73     BOOL SHFullScreen(HWND hwndRequester, DWORD dwState);
74 #endif*/
75
76
77 /*****************************************************************************
78  * Local prototypes.
79  *****************************************************************************/
80 static int  DirectXCreateWindow( vout_thread_t *p_vout );
81 static void DirectXCloseWindow ( vout_thread_t *p_vout );
82 static long FAR PASCAL DirectXEventProc( HWND, UINT, WPARAM, LPARAM );
83
84 static void DirectXPopupMenu( event_thread_t *p_event, bool b_open )
85 {
86     vlc_value_t val;
87     val.b_bool = b_open;
88     var_Set( p_event->p_libvlc, "intf-popupmenu", val );
89 }
90
91 static int DirectXConvertKey( int i_key );
92
93 /*****************************************************************************
94  * EventThread: Create video window & handle its messages
95  *****************************************************************************
96  * This function creates a video window and then enters an infinite loop
97  * that handles the messages sent to that window.
98  * The main goal of this thread is to isolate the Win32 PeekMessage function
99  * because this one can block for a long time.
100  *****************************************************************************/
101 static void* EventThread( vlc_object_t *p_this )
102 {
103     event_thread_t *p_event = (event_thread_t *)p_this;
104     MSG msg;
105     POINT old_mouse_pos = {0,0}, mouse_pos;
106     vlc_value_t val;
107     unsigned int i_width, i_height, i_x, i_y;
108     HMODULE hkernel32;
109     int canc = vlc_savecancel ();
110
111     /* Create a window for the video */
112     /* Creating a window under Windows also initializes the thread's event
113      * message queue */
114     if( DirectXCreateWindow( p_event->p_vout ) )
115     {
116         vlc_restorecancel (canc);
117         return NULL;
118     }
119
120     /* Signal the creation of the window */
121     SetEvent( p_event->window_ready );
122
123 #ifndef UNDER_CE
124     /* Set power management stuff */
125     if( (hkernel32 = GetModuleHandle( _T("KERNEL32") ) ) )
126     {
127         ULONG (WINAPI* OurSetThreadExecutionState)( ULONG );
128
129         OurSetThreadExecutionState = (ULONG (WINAPI*)( ULONG ))
130             GetProcAddress( hkernel32, _T("SetThreadExecutionState") );
131
132         if( OurSetThreadExecutionState )
133             /* Prevent monitor from powering off */
134             OurSetThreadExecutionState( ES_DISPLAY_REQUIRED | ES_CONTINUOUS );
135         else
136             msg_Dbg( p_event, "no support for SetThreadExecutionState()" );
137     }
138 #endif
139
140     /* Main loop */
141     /* GetMessage will sleep if there's no message in the queue */
142     while( vlc_object_alive (p_event) && GetMessage( &msg, 0, 0, 0 ) )
143     {
144         /* Check if we are asked to exit */
145         if( !vlc_object_alive (p_event) )
146             break;
147
148         switch( msg.message )
149         {
150
151         case WM_MOUSEMOVE:
152             vout_PlacePicture( p_event->p_vout,
153                                p_event->p_vout->p_sys->i_window_width,
154                                p_event->p_vout->p_sys->i_window_height,
155                                &i_x, &i_y, &i_width, &i_height );
156
157             if( msg.hwnd == p_event->p_vout->p_sys->hvideownd )
158             {
159                 /* Child window */
160                 i_x = i_y = 0;
161             }
162
163             if( i_width && i_height )
164             {
165                 val.i_int = ( GET_X_LPARAM(msg.lParam) - i_x ) *
166                     p_event->p_vout->fmt_in.i_visible_width / i_width +
167                     p_event->p_vout->fmt_in.i_x_offset;
168                 var_Set( p_event->p_vout, "mouse-x", val );
169                 val.i_int = ( GET_Y_LPARAM(msg.lParam) - i_y ) *
170                     p_event->p_vout->fmt_in.i_visible_height / i_height +
171                     p_event->p_vout->fmt_in.i_y_offset;
172                 var_Set( p_event->p_vout, "mouse-y", val );
173
174                 var_SetBool( p_event->p_vout, "mouse-moved", true );
175             }
176
177         case WM_NCMOUSEMOVE:
178             GetCursorPos( &mouse_pos );
179             if( (abs(mouse_pos.x - old_mouse_pos.x) > 2 ||
180                 (abs(mouse_pos.y - old_mouse_pos.y)) > 2 ) )
181             {
182                 GetCursorPos( &old_mouse_pos );
183                 p_event->p_vout->p_sys->i_lastmoved = mdate();
184
185                 if( p_event->p_vout->p_sys->b_cursor_hidden )
186                 {
187                     p_event->p_vout->p_sys->b_cursor_hidden = 0;
188                     ShowCursor( TRUE );
189                 }
190             }
191             break;
192
193         case WM_VLC_HIDE_MOUSE:
194             if( p_event->p_vout->p_sys->b_cursor_hidden ) break;
195             p_event->p_vout->p_sys->b_cursor_hidden = true;
196             GetCursorPos( &old_mouse_pos );
197             ShowCursor( FALSE );
198             break;
199
200         case WM_VLC_SHOW_MOUSE:
201             if( !p_event->p_vout->p_sys->b_cursor_hidden ) break;
202             p_event->p_vout->p_sys->b_cursor_hidden = false;
203             GetCursorPos( &old_mouse_pos );
204             ShowCursor( TRUE );
205             break;
206
207         case WM_LBUTTONDOWN:
208             var_Get( p_event->p_vout, "mouse-button-down", &val );
209             val.i_int |= 1;
210             var_Set( p_event->p_vout, "mouse-button-down", val );
211             DirectXPopupMenu( p_event, false );
212             break;
213
214         case WM_LBUTTONUP:
215             var_Get( p_event->p_vout, "mouse-button-down", &val );
216             val.i_int &= ~1;
217             var_Set( p_event->p_vout, "mouse-button-down", val );
218
219             var_SetBool( p_event->p_vout, "mouse-clicked", true );
220             break;
221
222         case WM_LBUTTONDBLCLK:
223             p_event->p_vout->p_sys->i_changes |= VOUT_FULLSCREEN_CHANGE;
224             break;
225
226         case WM_MBUTTONDOWN:
227             var_Get( p_event->p_vout, "mouse-button-down", &val );
228             val.i_int |= 2;
229             var_Set( p_event->p_vout, "mouse-button-down", val );
230             DirectXPopupMenu( p_event, false );
231             break;
232
233         case WM_MBUTTONUP:
234             var_Get( p_event->p_vout, "mouse-button-down", &val );
235             val.i_int &= ~2;
236             var_Set( p_event->p_vout, "mouse-button-down", val );
237             break;
238
239         case WM_RBUTTONDOWN:
240             var_Get( p_event->p_vout, "mouse-button-down", &val );
241             val.i_int |= 4;
242             var_Set( p_event->p_vout, "mouse-button-down", val );
243             DirectXPopupMenu( p_event, false );
244             break;
245
246         case WM_RBUTTONUP:
247             var_Get( p_event->p_vout, "mouse-button-down", &val );
248             val.i_int &= ~4;
249             var_Set( p_event->p_vout, "mouse-button-down", val );
250             DirectXPopupMenu( p_event, true );
251             break;
252
253         case WM_KEYDOWN:
254         case WM_SYSKEYDOWN:
255             /* The key events are first processed here and not translated
256              * into WM_CHAR events because we need to know the status of the
257              * modifier keys. */
258             val.i_int = DirectXConvertKey( msg.wParam );
259             if( !val.i_int )
260             {
261                 /* This appears to be a "normal" (ascii) key */
262                 val.i_int = tolower( MapVirtualKey( msg.wParam, 2 ) );
263             }
264
265             if( val.i_int )
266             {
267                 if( GetKeyState(VK_CONTROL) & 0x8000 )
268                 {
269                     val.i_int |= KEY_MODIFIER_CTRL;
270                 }
271                 if( GetKeyState(VK_SHIFT) & 0x8000 )
272                 {
273                     val.i_int |= KEY_MODIFIER_SHIFT;
274                 }
275                 if( GetKeyState(VK_MENU) & 0x8000 )
276                 {
277                     val.i_int |= KEY_MODIFIER_ALT;
278                 }
279
280                 var_Set( p_event->p_libvlc, "key-pressed", val );
281             }
282             break;
283
284         case WM_MOUSEWHEEL:
285             if( GET_WHEEL_DELTA_WPARAM( msg.wParam ) > 0 )
286             {
287                 val.i_int = KEY_MOUSEWHEELUP;
288             }
289             else
290             {
291                 val.i_int = KEY_MOUSEWHEELDOWN;
292             }
293             if( val.i_int )
294             {
295                 if( GetKeyState(VK_CONTROL) & 0x8000 )
296                 {
297                     val.i_int |= KEY_MODIFIER_CTRL;
298                 }
299                 if( GetKeyState(VK_SHIFT) & 0x8000 )
300                 {
301                     val.i_int |= KEY_MODIFIER_SHIFT;
302                 }
303                 if( GetKeyState(VK_MENU) & 0x8000 )
304                 {
305                     val.i_int |= KEY_MODIFIER_ALT;
306                 }
307
308                 var_Set( p_event->p_libvlc, "key-pressed", val );
309             }
310             break;
311
312         case WM_VLC_CHANGE_TEXT:
313             var_Get( p_event->p_vout, "video-title", &val );
314             if( !val.psz_string || !*val.psz_string ) /* Default video title */
315             {
316                 free( val.psz_string );
317
318 #ifdef MODULE_NAME_IS_wingdi
319                 val.psz_string = strdup( VOUT_TITLE " (WinGDI output)" );
320 #endif
321 #ifdef MODULE_NAME_IS_wingapi
322                 val.psz_string = strdup( VOUT_TITLE " (WinGAPI output)" );
323 #endif
324 #ifdef MODULE_NAME_IS_glwin32
325                 val.psz_string = strdup( VOUT_TITLE " (OpenGL output)" );
326 #endif
327 #ifdef MODULE_NAME_IS_direct3d
328                 val.psz_string = strdup( VOUT_TITLE " (Direct3D output)" );
329 #endif
330 #ifdef MODULE_NAME_IS_directx
331                 if( p_event->p_vout->p_sys->b_using_overlay ) val.psz_string =
332                     strdup( VOUT_TITLE " (hardware YUV overlay DirectX output)" );
333                 else if( p_event->p_vout->p_sys->b_hw_yuv ) val.psz_string =
334                     strdup( VOUT_TITLE " (hardware YUV DirectX output)" );
335                 else val.psz_string =
336                     strdup( VOUT_TITLE " (software RGB DirectX output)" );
337 #endif
338             }
339
340             {
341                 wchar_t *psz_title = malloc( strlen(val.psz_string) * 2 + 2 );
342                 if( psz_title )
343                 {
344                     mbstowcs( psz_title, val.psz_string, strlen(val.psz_string)*2);
345                     psz_title[strlen(val.psz_string)] = 0;
346                     free( val.psz_string ); val.psz_string = (char *)psz_title;
347                 }
348             }
349
350             SetWindowText( p_event->p_vout->p_sys->hwnd,
351                            (LPCTSTR)val.psz_string );
352             if( p_event->p_vout->p_sys->hfswnd )
353                 SetWindowText( p_event->p_vout->p_sys->hfswnd,
354                                (LPCTSTR)val.psz_string );
355             free( val.psz_string );
356             break;
357
358         default:
359             /* Messages we don't handle directly are dispatched to the
360              * window procedure */
361             TranslateMessage(&msg);
362             DispatchMessage(&msg);
363             break;
364
365         } /* End Switch */
366
367     } /* End Main loop */
368
369     /* Check for WM_QUIT if we created the window */
370     if( !p_event->p_vout->p_sys->hparent && msg.message == WM_QUIT )
371     {
372         msg_Warn( p_event, "WM_QUIT... should not happen!!" );
373         p_event->p_vout->p_sys->hwnd = NULL; /* Window already destroyed */
374     }
375
376     msg_Dbg( p_event, "DirectXEventThread terminating" );
377
378     DirectXCloseWindow( p_event->p_vout );
379     vlc_restorecancel (canc);
380
381     /* clear the changes formerly signaled */
382     p_event->p_vout->p_sys->i_changes = EVENT_THREAD_ENDED;
383     return NULL;
384 }
385
386
387 /* following functions are local */
388
389 /*****************************************************************************
390  * DirectXCreateWindow: create a window for the video.
391  *****************************************************************************
392  * Before creating a direct draw surface, we need to create a window in which
393  * the video will be displayed. This window will also allow us to capture the
394  * events.
395  *****************************************************************************/
396 static int DirectXCreateWindow( vout_thread_t *p_vout )
397 {
398     HINSTANCE  hInstance;
399     HMENU      hMenu;
400     RECT       rect_window;
401     WNDCLASS   wc;                            /* window class components */
402     HICON      vlc_icon = NULL;
403     char       vlc_path[MAX_PATH+1];
404     int        i_style, i_stylex;
405
406     msg_Dbg( p_vout, "DirectXCreateWindow" );
407
408     /* Get this module's instance */
409     hInstance = GetModuleHandle(NULL);
410
411     #ifdef MODULE_NAME_IS_direct3d
412     if( !p_vout->p_sys->b_desktop )
413     {
414     #endif
415         vout_window_cfg_t wnd_cfg;
416         memset( &wnd_cfg, 0, sizeof(wnd_cfg) );
417         wnd_cfg.type   = VOUT_WINDOW_TYPE_HWND;
418         wnd_cfg.x      = p_vout->p_sys->i_window_x;
419         wnd_cfg.y      = p_vout->p_sys->i_window_y;
420         wnd_cfg.width  = p_vout->p_sys->i_window_width;
421         wnd_cfg.height = p_vout->p_sys->i_window_height;
422
423         /* If an external window was specified, we'll draw in it. */
424         p_vout->p_sys->parent_window = vout_window_New( VLC_OBJECT(p_vout), NULL, &wnd_cfg );
425         if( p_vout->p_sys->parent_window )
426             p_vout->p_sys->hparent = p_vout->p_sys->parent_window->handle.hwnd;
427     #ifdef MODULE_NAME_IS_direct3d
428     }
429     else
430     {
431         /* Find Program Manager */
432         HWND hwnd = FindWindow( _T("Progman"), NULL );
433         if( hwnd ) hwnd = FindWindowEx( hwnd, NULL, _T("SHELLDLL_DefView"), NULL );
434         if( hwnd ) hwnd = FindWindowEx( hwnd, NULL, _T("SysListView32"), NULL );
435         if( !hwnd )
436             msg_Err( p_vout, "Couldn't find desktop icon window. Desktop mode can't be established." );
437         p_vout->p_sys->hparent = hwnd;
438     }
439     #endif
440
441     /* We create the window ourself, there is no previous window proc. */
442     p_vout->p_sys->pf_wndproc = NULL;
443
444     /* Get the Icon from the main app */
445     vlc_icon = NULL;
446 #ifndef UNDER_CE
447     if( GetModuleFileName( NULL, vlc_path, MAX_PATH ) )
448     {
449         vlc_icon = ExtractIcon( hInstance, vlc_path, 0 );
450     }
451 #endif
452
453     /* Fill in the window class structure */
454     wc.style         = CS_OWNDC|CS_DBLCLKS;          /* style: dbl click */
455     wc.lpfnWndProc   = (WNDPROC)DirectXEventProc;       /* event handler */
456     wc.cbClsExtra    = 0;                         /* no extra class data */
457     wc.cbWndExtra    = 0;                        /* no extra window data */
458     wc.hInstance     = hInstance;                            /* instance */
459     wc.hIcon         = vlc_icon;                /* load the vlc big icon */
460     wc.hCursor       = LoadCursor(NULL, IDC_ARROW);    /* default cursor */
461     wc.hbrBackground = GetStockObject(BLACK_BRUSH);  /* background color */
462     wc.lpszMenuName  = NULL;                                  /* no menu */
463     wc.lpszClassName = _T("VLC DirectX");         /* use a special class */
464
465     /* Register the window class */
466     if( !RegisterClass(&wc) )
467     {
468         WNDCLASS wndclass;
469
470         if( vlc_icon ) DestroyIcon( vlc_icon );
471
472         /* Check why it failed. If it's because one already exists
473          * then fine, otherwise return with an error. */
474         if( !GetClassInfo( hInstance, _T("VLC DirectX"), &wndclass ) )
475         {
476             msg_Err( p_vout, "DirectXCreateWindow RegisterClass FAILED (err=%lu)", GetLastError() );
477             return VLC_EGENERIC;
478         }
479     }
480
481     /* Register the video sub-window class */
482     wc.lpszClassName = _T("VLC DirectX video"); wc.hIcon = 0;
483     wc.hbrBackground = NULL; /* no background color */
484     if( !RegisterClass(&wc) )
485     {
486         WNDCLASS wndclass;
487
488         /* Check why it failed. If it's because one already exists
489          * then fine, otherwise return with an error. */
490         if( !GetClassInfo( hInstance, _T("VLC DirectX video"), &wndclass ) )
491         {
492             msg_Err( p_vout, "DirectXCreateWindow RegisterClass FAILED (err=%lu)", GetLastError() );
493             return VLC_EGENERIC;
494         }
495     }
496
497     /* When you create a window you give the dimensions you wish it to
498      * have. Unfortunatly these dimensions will include the borders and
499      * titlebar. We use the following function to find out the size of
500      * the window corresponding to the useable surface we want */
501     rect_window.top    = 10;
502     rect_window.left   = 10;
503     rect_window.right  = rect_window.left + p_vout->p_sys->i_window_width;
504     rect_window.bottom = rect_window.top + p_vout->p_sys->i_window_height;
505
506     if( var_GetBool( p_vout, "video-deco" ) )
507     {
508         /* Open with window decoration */
509         AdjustWindowRect( &rect_window, WS_OVERLAPPEDWINDOW|WS_SIZEBOX, 0 );
510         i_style = WS_OVERLAPPEDWINDOW|WS_SIZEBOX|WS_VISIBLE|WS_CLIPCHILDREN;
511         i_stylex = 0;
512     }
513     else
514     {
515         /* No window decoration */
516         AdjustWindowRect( &rect_window, WS_POPUP, 0 );
517         i_style = WS_POPUP|WS_VISIBLE|WS_CLIPCHILDREN;
518         i_stylex = 0; // WS_EX_TOOLWINDOW; Is TOOLWINDOW really needed ?
519                       // It messes up the fullscreen window.
520     }
521
522     if( p_vout->p_sys->hparent )
523     {
524         i_style = WS_VISIBLE|WS_CLIPCHILDREN|WS_CHILD;
525         i_stylex = 0;
526     }
527
528     p_vout->p_sys->i_window_style = i_style;
529
530     /* Create the window */
531     p_vout->p_sys->hwnd =
532         CreateWindowEx( WS_EX_NOPARENTNOTIFY | i_stylex,
533                     _T("VLC DirectX"),               /* name of window class */
534                     _T(VOUT_TITLE) _T(" (DirectX Output)"),  /* window title */
535                     i_style,                                 /* window style */
536                     (p_vout->p_sys->i_window_x < 0) ? CW_USEDEFAULT :
537                         (UINT)p_vout->p_sys->i_window_x,   /* default X coordinate */
538                     (p_vout->p_sys->i_window_y < 0) ? CW_USEDEFAULT :
539                         (UINT)p_vout->p_sys->i_window_y,   /* default Y coordinate */
540                     rect_window.right - rect_window.left,    /* window width */
541                     rect_window.bottom - rect_window.top,   /* window height */
542                     p_vout->p_sys->hparent,                 /* parent window */
543                     NULL,                          /* no menu in this window */
544                     hInstance,            /* handle of this program instance */
545                     (LPVOID)p_vout );            /* send p_vout to WM_CREATE */
546
547     if( !p_vout->p_sys->hwnd )
548     {
549         msg_Warn( p_vout, "DirectXCreateWindow create window FAILED (err=%lu)", GetLastError() );
550         return VLC_EGENERIC;
551     }
552
553     if( p_vout->p_sys->hparent )
554     {
555         LONG i_style;
556
557         /* We don't want the window owner to overwrite our client area */
558         i_style = GetWindowLong( p_vout->p_sys->hparent, GWL_STYLE );
559
560         if( !(i_style & WS_CLIPCHILDREN) )
561             /* Hmmm, apparently this is a blocking call... */
562             SetWindowLong( p_vout->p_sys->hparent, GWL_STYLE,
563                            i_style | WS_CLIPCHILDREN );
564
565         /* Create our fullscreen window */
566         p_vout->p_sys->hfswnd =
567             CreateWindowEx( WS_EX_APPWINDOW, _T("VLC DirectX"),
568                             _T(VOUT_TITLE) _T(" (DirectX Output)"),
569                             WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN|WS_SIZEBOX,
570                             CW_USEDEFAULT, CW_USEDEFAULT,
571                             CW_USEDEFAULT, CW_USEDEFAULT,
572                             NULL, NULL, hInstance, NULL );
573     }
574
575     /* Append a "Always On Top" entry in the system menu */
576     hMenu = GetSystemMenu( p_vout->p_sys->hwnd, FALSE );
577     AppendMenu( hMenu, MF_SEPARATOR, 0, _T("") );
578     AppendMenu( hMenu, MF_STRING | MF_UNCHECKED,
579                        IDM_TOGGLE_ON_TOP, _T("Always on &Top") );
580
581     /* Create video sub-window. This sub window will always exactly match
582      * the size of the video, which allows us to use crazy overlay colorkeys
583      * without having them shown outside of the video area. */
584     p_vout->p_sys->hvideownd =
585     CreateWindow( _T("VLC DirectX video"), _T(""),   /* window class */
586         WS_CHILD,                   /* window style, not visible initially */
587         0, 0,
588         p_vout->render.i_width,         /* default width */
589         p_vout->render.i_height,        /* default height */
590         p_vout->p_sys->hwnd,                    /* parent window */
591         NULL, hInstance,
592         (LPVOID)p_vout );            /* send p_vout to WM_CREATE */
593
594     if( !p_vout->p_sys->hvideownd )
595         msg_Warn( p_vout, "can't create video sub-window" );
596     else
597         msg_Dbg( p_vout, "created video sub-window" );
598
599     /* Now display the window */
600     ShowWindow( p_vout->p_sys->hwnd, SW_SHOW );
601
602     return VLC_SUCCESS;
603 }
604
605 /*****************************************************************************
606  * DirectXCloseWindow: close the window created by DirectXCreateWindow
607  *****************************************************************************
608  * This function returns all resources allocated by DirectXCreateWindow.
609  *****************************************************************************/
610 static void DirectXCloseWindow( vout_thread_t *p_vout )
611 {
612     msg_Dbg( p_vout, "DirectXCloseWindow" );
613
614     DestroyWindow( p_vout->p_sys->hwnd );
615     if( p_vout->p_sys->hfswnd ) DestroyWindow( p_vout->p_sys->hfswnd );
616
617     #ifdef MODULE_NAME_IS_direct3d
618     if( !p_vout->p_sys->b_desktop )
619     #endif
620         vout_window_Delete( p_vout->p_sys->parent_window );
621     p_vout->p_sys->hwnd = NULL;
622
623     /* We don't unregister the Window Class because it could lead to race
624      * conditions and it will be done anyway by the system when the app will
625      * exit */
626 }
627
628 /*****************************************************************************
629  * DirectXEventProc: This is the window event processing function.
630  *****************************************************************************
631  * On Windows, when you create a window you have to attach an event processing
632  * function to it. The aim of this function is to manage "Queued Messages" and
633  * "Nonqueued Messages".
634  * Queued Messages are those picked up and retransmitted by vout_Manage
635  * (using the GetMessage and DispatchMessage functions).
636  * Nonqueued Messages are those that Windows will send directly to this
637  * procedure (like WM_DESTROY, WM_WINDOWPOSCHANGED...)
638  *****************************************************************************/
639 static long FAR PASCAL DirectXEventProc( HWND hwnd, UINT message,
640                                          WPARAM wParam, LPARAM lParam )
641 {
642     vout_thread_t *p_vout;
643
644     if( message == WM_CREATE )
645     {
646         /* Store p_vout for future use */
647         p_vout = (vout_thread_t *)((CREATESTRUCT *)lParam)->lpCreateParams;
648         SetWindowLongPtr( hwnd, GWLP_USERDATA, (LONG_PTR)p_vout );
649         return TRUE;
650     }
651     else
652     {
653         LONG_PTR p_user_data = GetWindowLongPtr( hwnd, GWLP_USERDATA );
654         p_vout = (vout_thread_t *)p_user_data;
655         if( !p_vout )
656         {
657             /* Hmmm mozilla does manage somehow to save the pointer to our
658              * windowproc and still calls it after the vout has been closed. */
659             return DefWindowProc(hwnd, message, wParam, lParam);
660         }
661     }
662
663 #ifndef UNDER_CE
664     /* Catch the screensaver and the monitor turn-off */
665     if( message == WM_SYSCOMMAND &&
666         ( (wParam & 0xFFF0) == SC_SCREENSAVE || (wParam & 0xFFF0) == SC_MONITORPOWER ) )
667     {
668         //if( p_vout ) msg_Dbg( p_vout, "WinProc WM_SYSCOMMAND screensaver" );
669         return 0; /* this stops them from happening */
670     }
671 #endif
672
673     if( hwnd == p_vout->p_sys->hvideownd )
674     {
675         switch( message )
676         {
677 #ifdef MODULE_NAME_IS_directx
678         case WM_ERASEBKGND:
679         /* For overlay, we need to erase background */
680             return !p_vout->p_sys->b_using_overlay ?
681                 1 : DefWindowProc(hwnd, message, wParam, lParam);
682         case WM_PAINT:
683         /*
684         ** For overlay, DefWindowProc() will erase dirty regions
685         ** with colorkey.
686         ** For non-overlay, vout will paint the whole window at
687         ** regular interval, therefore dirty regions can be ignored
688         ** to minimize repaint.
689         */
690             if( !p_vout->p_sys->b_using_overlay )
691             {
692                 ValidateRect(hwnd, NULL);
693             }
694             // fall through to default
695 #else
696         /*
697         ** For OpenGL and Direct3D, vout will update the whole
698         ** window at regular interval, therefore dirty region
699         ** can be ignored to minimize repaint.
700         */
701         case WM_ERASEBKGND:
702             /* nothing to erase */
703             return 1;
704         case WM_PAINT:
705             /* nothing to repaint */
706             ValidateRect(hwnd, NULL);
707             // fall through
708 #endif
709         default:
710             return DefWindowProc(hwnd, message, wParam, lParam);
711         }
712     }
713
714     switch( message )
715     {
716
717     case WM_WINDOWPOSCHANGED:
718         UpdateRects( p_vout, true );
719         return 0;
720
721     /* the user wants to close the window */
722     case WM_CLOSE:
723     {
724         playlist_t * p_playlist = pl_Hold( p_vout );
725         if( p_playlist )
726         {
727             playlist_Stop( p_playlist );
728             pl_Release( p_vout );
729         }
730         return 0;
731     }
732
733     /* the window has been closed so shut down everything now */
734     case WM_DESTROY:
735         msg_Dbg( p_vout, "WinProc WM_DESTROY" );
736         /* just destroy the window */
737         PostQuitMessage( 0 );
738         return 0;
739
740     case WM_SYSCOMMAND:
741         switch (wParam)
742         {
743             case IDM_TOGGLE_ON_TOP:            /* toggle the "on top" status */
744             {
745                 vlc_value_t val;
746                 msg_Dbg( p_vout, "WinProc WM_SYSCOMMAND: IDM_TOGGLE_ON_TOP");
747
748                 /* Change the current value */
749                 var_Get( p_vout, "video-on-top", &val );
750                 val.b_bool = !val.b_bool;
751                 var_Set( p_vout, "video-on-top", val );
752                 return 0;
753             }
754         }
755         break;
756
757     case WM_PAINT:
758     case WM_NCPAINT:
759     case WM_ERASEBKGND:
760         return DefWindowProc(hwnd, message, wParam, lParam);
761
762     case WM_KILLFOCUS:
763 #ifdef MODULE_NAME_IS_wingapi
764         p_vout->p_sys->b_focus = false;
765         if( !p_vout->p_sys->b_parent_focus ) GXSuspend();
766 #endif
767 #ifdef UNDER_CE
768         if( hwnd == p_vout->p_sys->hfswnd )
769         {
770             HWND htbar = FindWindow( _T("HHTaskbar"), NULL );
771             ShowWindow( htbar, SW_SHOW );
772         }
773
774         if( !p_vout->p_sys->hparent ||
775             hwnd == p_vout->p_sys->hfswnd )
776         {
777             SHFullScreen( hwnd, SHFS_SHOWSIPBUTTON );
778         }
779 #endif
780         return 0;
781
782     case WM_SETFOCUS:
783 #ifdef MODULE_NAME_IS_wingapi
784         p_vout->p_sys->b_focus = true;
785         GXResume();
786 #endif
787 #ifdef UNDER_CE
788         if( p_vout->p_sys->hparent &&
789             hwnd != p_vout->p_sys->hfswnd && p_vout->b_fullscreen )
790             p_vout->p_sys->i_changes |= VOUT_FULLSCREEN_CHANGE;
791
792         if( hwnd == p_vout->p_sys->hfswnd )
793         {
794             HWND htbar = FindWindow( _T("HHTaskbar"), NULL );
795             ShowWindow( htbar, SW_HIDE );
796         }
797
798         if( !p_vout->p_sys->hparent ||
799             hwnd == p_vout->p_sys->hfswnd )
800         {
801             SHFullScreen( hwnd, SHFS_HIDESIPBUTTON );
802         }
803 #endif
804         return 0;
805
806     default:
807         //msg_Dbg( p_vout, "WinProc WM Default %i", message );
808         break;
809     }
810
811     /* Let windows handle the message */
812     return DefWindowProc(hwnd, message, wParam, lParam);
813 }
814
815 static struct
816 {
817     int i_dxkey;
818     int i_vlckey;
819
820 } dxkeys_to_vlckeys[] =
821 {
822     { VK_F1, KEY_F1 }, { VK_F2, KEY_F2 }, { VK_F3, KEY_F3 }, { VK_F4, KEY_F4 },
823     { VK_F5, KEY_F5 }, { VK_F6, KEY_F6 }, { VK_F7, KEY_F7 }, { VK_F8, KEY_F8 },
824     { VK_F9, KEY_F9 }, { VK_F10, KEY_F10 }, { VK_F11, KEY_F11 },
825     { VK_F12, KEY_F12 },
826
827     { VK_RETURN, KEY_ENTER },
828     { VK_SPACE, KEY_SPACE },
829     { VK_ESCAPE, KEY_ESC },
830
831     { VK_LEFT, KEY_LEFT },
832     { VK_RIGHT, KEY_RIGHT },
833     { VK_UP, KEY_UP },
834     { VK_DOWN, KEY_DOWN },
835
836     { VK_HOME, KEY_HOME },
837     { VK_END, KEY_END },
838     { VK_PRIOR, KEY_PAGEUP },
839     { VK_NEXT, KEY_PAGEDOWN },
840
841     { VK_INSERT, KEY_INSERT },
842     { VK_DELETE, KEY_DELETE },
843
844     { VK_CONTROL, 0 },
845     { VK_SHIFT, 0 },
846     { VK_MENU, 0 },
847
848     { VK_BROWSER_BACK, KEY_BROWSER_BACK },
849     { VK_BROWSER_FORWARD, KEY_BROWSER_FORWARD },
850     { VK_BROWSER_REFRESH, KEY_BROWSER_REFRESH },
851     { VK_BROWSER_STOP, KEY_BROWSER_STOP },
852     { VK_BROWSER_SEARCH, KEY_BROWSER_SEARCH },
853     { VK_BROWSER_FAVORITES, KEY_BROWSER_FAVORITES },
854     { VK_BROWSER_HOME, KEY_BROWSER_HOME },
855     { VK_VOLUME_MUTE, KEY_VOLUME_MUTE },
856     { VK_VOLUME_DOWN, KEY_VOLUME_DOWN },
857     { VK_VOLUME_UP, KEY_VOLUME_UP },
858     { VK_MEDIA_NEXT_TRACK, KEY_MEDIA_NEXT_TRACK },
859     { VK_MEDIA_PREV_TRACK, KEY_MEDIA_PREV_TRACK },
860     { VK_MEDIA_STOP, KEY_MEDIA_STOP },
861     { VK_MEDIA_PLAY_PAUSE, KEY_MEDIA_PLAY_PAUSE },
862
863     { 0, 0 }
864 };
865
866 static int DirectXConvertKey( int i_key )
867 {
868     int i;
869
870     for( i = 0; dxkeys_to_vlckeys[i].i_dxkey != 0; i++ )
871     {
872         if( dxkeys_to_vlckeys[i].i_dxkey == i_key )
873         {
874             return dxkeys_to_vlckeys[i].i_vlckey;
875         }
876     }
877
878     return 0;
879 }
880
881 int CreateEventThread( vout_thread_t *p_vout )
882 {
883     /* Create the Vout EventThread, this thread is created by us to isolate
884      * the Win32 PeekMessage function calls. We want to do this because
885      * Windows can stay blocked inside this call for a long time, and when
886      * this happens it thus blocks vlc's video_output thread.
887      * Vout EventThread will take care of the creation of the video
888      * window (because PeekMessage has to be called from the same thread which
889      * created the window). */
890     msg_Dbg( p_vout, "creating Vout EventThread" );
891     event_thread_t *p_event = p_vout->p_sys->p_event =
892         vlc_object_create( p_vout, sizeof(event_thread_t) );
893     p_event->p_vout = p_vout;
894     p_event->window_ready = CreateEvent( NULL, TRUE, FALSE, NULL );
895     if( vlc_thread_create( p_event, "Vout Events Thread",
896                            EventThread, 0 ) )
897     {
898         msg_Err( p_vout, "cannot create Vout EventThread" );
899         CloseHandle( p_event->window_ready );
900         vlc_object_release( p_event );
901         p_vout->p_sys->p_event = NULL;
902         return 0;
903     }
904     WaitForSingleObject( p_event->window_ready, INFINITE );
905     CloseHandle( p_event->window_ready );
906
907     if( p_event->b_error )
908     {
909         msg_Err( p_vout, "Vout EventThread failed" );
910         return 0;
911     }
912
913     msg_Dbg( p_vout, "Vout EventThread running" );
914     return 1;
915 }
916
917 void StopEventThread( vout_thread_t *p_vout )
918 {
919     if( p_vout->b_fullscreen )
920     {
921         msg_Dbg( p_vout, "Quitting fullscreen" );
922         Win32ToggleFullscreen( p_vout );
923         /* Force fullscreen in the core for the next video */
924         var_SetBool( p_vout, "fullscreen", true );
925     }
926
927     if( p_vout->p_sys->p_event )
928     {
929         event_thread_t *p_event = p_vout->p_sys->p_event;
930
931         /* Kill Vout EventThread */
932         vlc_object_kill( p_event );
933
934         /* we need to be sure Vout EventThread won't stay stuck in
935          * GetMessage, so we send a fake message */
936         if( p_vout->p_sys->hwnd )
937         {
938             PostMessage( p_vout->p_sys->hwnd, WM_NULL, 0, 0);
939         }
940
941         vlc_thread_join( p_event );
942         vlc_object_release( p_event );
943     }
944 }
945