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