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