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