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