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