1 /*****************************************************************************
2 * events.c: Windows DirectX video output events handler
3 *****************************************************************************
4 * Copyright (C) 2001-2009 the VideoLAN team
7 * Authors: Gildas Bazin <gbazin@videolan.org>
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.
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.
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 *****************************************************************************/
25 /*****************************************************************************
26 * Preamble: This file contains the functions related to the creation of
27 * a window and the handling of its messages (events).
28 *****************************************************************************/
33 #include <vlc_common.h>
34 #include <vlc_vout_display.h>
35 #include <vlc_vout_window.h>
43 #ifdef MODULE_NAME_IS_directx
46 #ifdef MODULE_NAME_IS_direct3d
49 #ifdef MODULE_NAME_IS_glwin32
50 #include "../opengl.h"
58 //WINSHELLAPI BOOL WINAPI SHFullScreen(HWND hwndRequester, DWORD dwState);
60 UINT GetMenuState(HMENU hMenu, UINT id, UINT flags)
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))
68 /* XXX Submenu handling is missing... */
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);
80 /*****************************************************************************
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)
104 volatile bool b_cursor_hidden;
105 volatile mtime_t i_lastmoved;
106 mtime_t i_mouse_hide_timeout;
112 vout_window_cfg_t wnd_cfg;
115 vout_window_t *parent_window;
120 video_format_t source;
121 vout_display_place_t place;
126 static int DirectXCreateWindow( event_thread_t * );
127 static void DirectXCloseWindow ( event_thread_t * );
128 static long FAR PASCAL DirectXEventProc( HWND, UINT, WPARAM, LPARAM );
130 static int DirectXConvertKey( int i_key );
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 )
142 event_thread_t *p_event = (event_thread_t *)p_this;
143 vout_display_t *vd = p_event->vd;
145 POINT old_mouse_pos = {0,0}, mouse_pos;
147 int canc = vlc_savecancel ();
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
153 if( DirectXCreateWindow( p_event ) )
154 p_event->b_error = true;
156 p_event->b_ready = true;
157 vlc_cond_signal( &p_event->wait );
159 const bool b_error = p_event->b_error;
160 vlc_mutex_unlock( &p_event->lock );
164 vlc_restorecancel( canc );
169 /* Set power management stuff */
170 if( (hkernel32 = GetModuleHandle( _T("KERNEL32") ) ) )
172 ULONG (WINAPI* OurSetThreadExecutionState)( ULONG );
174 OurSetThreadExecutionState = (ULONG (WINAPI*)( ULONG ))
175 GetProcAddress( hkernel32, _T("SetThreadExecutionState") );
177 if( OurSetThreadExecutionState )
178 /* Prevent monitor from powering off */
179 OurSetThreadExecutionState( ES_DISPLAY_REQUIRED | ES_CONTINUOUS );
181 msg_Dbg( vd, "no support for SetThreadExecutionState()" );
186 /* GetMessage will sleep if there's no message in the queue */
189 vout_display_place_t place;
190 video_format_t source;
192 if( !GetMessage( &msg, 0, 0, 0 ) )
194 vlc_mutex_lock( &p_event->lock );
195 p_event->b_done = true;
196 vlc_mutex_unlock( &p_event->lock );
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 );
208 switch( msg.message )
211 vlc_mutex_lock( &p_event->lock );
212 place = p_event->place;
213 source = p_event->source;
214 vlc_mutex_unlock( &p_event->lock );
216 if( place.width > 0 && place.height > 0 )
218 if( msg.hwnd == p_event->hvideownd )
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);
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 ) )
237 GetCursorPos( &old_mouse_pos );
238 p_event->i_lastmoved = mdate();
240 if( p_event->b_cursor_hidden )
242 p_event->b_cursor_hidden = false;
248 case WM_VLC_HIDE_MOUSE:
249 if( p_event->b_cursor_hidden )
251 p_event->b_cursor_hidden = true;
252 GetCursorPos( &old_mouse_pos );
256 case WM_VLC_SHOW_MOUSE:
257 if( !p_event->b_cursor_hidden )
259 p_event->b_cursor_hidden = false;
260 GetCursorPos( &old_mouse_pos );
265 vout_display_SendEventMousePressed(vd, MOUSE_BUTTON_LEFT);
268 vout_display_SendEventMouseReleased(vd, MOUSE_BUTTON_LEFT);
270 case WM_LBUTTONDBLCLK:
271 vout_display_SendEventMouseDoubleClick(vd);
275 vout_display_SendEventMousePressed(vd, MOUSE_BUTTON_CENTER);
278 vout_display_SendEventMouseReleased(vd, MOUSE_BUTTON_CENTER);
282 vout_display_SendEventMousePressed(vd, MOUSE_BUTTON_RIGHT);
285 vout_display_SendEventMouseReleased(vd, MOUSE_BUTTON_RIGHT);
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
294 int i_key = DirectXConvertKey( msg.wParam );
297 /* This appears to be a "normal" (ascii) key */
298 i_key = tolower( MapVirtualKey( msg.wParam, 2 ) );
303 if( GetKeyState(VK_CONTROL) & 0x8000 )
305 i_key |= KEY_MODIFIER_CTRL;
307 if( GetKeyState(VK_SHIFT) & 0x8000 )
309 i_key |= KEY_MODIFIER_SHIFT;
311 if( GetKeyState(VK_MENU) & 0x8000 )
313 i_key |= KEY_MODIFIER_ALT;
316 vout_display_SendEventKey(vd, i_key);
324 if( GET_WHEEL_DELTA_WPARAM( msg.wParam ) > 0 )
326 i_key = KEY_MOUSEWHEELUP;
330 i_key = KEY_MOUSEWHEELDOWN;
334 if( GetKeyState(VK_CONTROL) & 0x8000 )
336 i_key |= KEY_MODIFIER_CTRL;
338 if( GetKeyState(VK_SHIFT) & 0x8000 )
340 i_key |= KEY_MODIFIER_SHIFT;
342 if( GetKeyState(VK_MENU) & 0x8000 )
344 i_key |= KEY_MODIFIER_ALT;
346 vout_display_SendEventKey(vd, i_key);
351 case WM_VLC_CHANGE_TEXT:
353 vlc_mutex_lock( &p_event->lock );
354 wchar_t *pwz_title = NULL;
355 if( p_event->psz_title )
357 const size_t i_length = strlen(p_event->psz_title);
358 pwz_title = malloc( 2 * (i_length + 1) );
361 mbstowcs( pwz_title, p_event->psz_title, 2 * i_length );
362 pwz_title[i_length] = 0;
365 vlc_mutex_unlock( &p_event->lock );
369 SetWindowTextW( p_event->hwnd, (LPCTSTR)pwz_title );
370 if( p_event->hfswnd )
371 SetWindowTextW( p_event->hfswnd, (LPCTSTR)pwz_title );
378 /* Messages we don't handle directly are dispatched to the
379 * window procedure */
380 TranslateMessage(&msg);
381 DispatchMessage(&msg);
386 } /* End Main loop */
388 /* Check for WM_QUIT if we created the window */
389 if( !p_event->hparent && msg.message == WM_QUIT )
391 msg_Warn( vd, "WM_QUIT... should not happen!!" );
392 p_event->hwnd = NULL; /* Window already destroyed */
395 msg_Dbg( vd, "DirectXEventThread terminating" );
397 DirectXCloseWindow( p_event );
398 vlc_restorecancel(canc);
403 /* following functions are local */
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
411 *****************************************************************************/
412 static int DirectXCreateWindow( event_thread_t *p_event )
414 vout_display_t *vd = p_event->vd;
418 WNDCLASS wc; /* window class components */
420 char vlc_path[MAX_PATH+1];
421 int i_style, i_stylex;
423 msg_Dbg( vd, "DirectXCreateWindow" );
425 /* Get this module's instance */
426 hInstance = GetModuleHandle(NULL);
428 #ifdef MODULE_NAME_IS_direct3d
429 if( !p_event->use_desktop )
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;
437 p_event->hparent = NULL;
438 #ifdef MODULE_NAME_IS_direct3d
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 );
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;
453 /* Get the Icon from the main app */
456 if( GetModuleFileName( NULL, vlc_path, MAX_PATH ) )
458 vlc_icon = ExtractIcon( hInstance, vlc_path, 0 );
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 */
474 /* Register the window class */
475 if( !RegisterClass(&wc) )
479 if( vlc_icon ) DestroyIcon( vlc_icon );
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 ) )
485 msg_Err( vd, "DirectXCreateWindow RegisterClass FAILED (err=%lu)", GetLastError() );
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) )
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 ) )
501 msg_Err( vd, "DirectXCreateWindow RegisterClass FAILED (err=%lu)", GetLastError() );
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.top = 10;
511 rect_window.left = 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;
515 if( var_GetBool( vd, "video-deco" ) )
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;
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.
531 if( p_event->hparent )
533 i_style = WS_VISIBLE|WS_CLIPCHILDREN|WS_CHILD;
537 p_event->i_window_style = i_style;
539 /* Create the window */
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 */
558 msg_Warn( vd, "DirectXCreateWindow create window FAILED (err=%lu)", GetLastError() );
562 if( p_event->hparent )
566 /* We don't want the window owner to overwrite our client area */
567 i_style = GetWindowLong( p_event->hparent, GWL_STYLE );
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 );
574 /* Create our fullscreen window */
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 );
585 p_event->hfswnd = NULL;
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") );
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 */
599 CreateWindow( _T("VLC DirectX video"), _T(""), /* window class */
600 WS_CHILD, /* window style, not visible initially */
602 vd->source.i_width, /* default width */
603 vd->source.i_height, /* default height */
604 p_event->hwnd, /* parent window */
606 (LPVOID)p_event ); /* send vd to WM_CREATE */
608 if( !p_event->hvideownd )
609 msg_Warn( vd, "can't create video sub-window" );
611 msg_Dbg( vd, "created video sub-window" );
613 /* Now display the window */
614 ShowWindow( p_event->hwnd, SW_SHOW );
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 )
626 vout_display_t *vd = p_event->vd;
627 msg_Dbg( vd, "DirectXCloseWindow" );
629 DestroyWindow( p_event->hwnd );
630 if( p_event->hfswnd )
631 DestroyWindow( p_event->hfswnd );
633 #ifdef MODULE_NAME_IS_direct3d
634 if( !p_event->use_desktop )
636 vout_display_DeleteWindow( vd, p_event->parent_window );
637 p_event->hwnd = NULL;
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
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 )
658 event_thread_t *p_event;
660 if( message == WM_CREATE )
662 /* Store vd for future use */
663 p_event = (event_thread_t *)((CREATESTRUCT *)lParam)->lpCreateParams;
664 SetWindowLongPtr( hwnd, GWLP_USERDATA, (LONG_PTR)p_event );
669 LONG_PTR p_user_data = GetWindowLongPtr( hwnd, GWLP_USERDATA );
670 p_event = (event_thread_t *)p_user_data;
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);
678 vout_display_t *vd = p_event->vd;
681 /* Catch the screensaver and the monitor turn-off */
682 if( message == WM_SYSCOMMAND &&
683 ( (wParam & 0xFFF0) == SC_SCREENSAVE || (wParam & 0xFFF0) == SC_MONITORPOWER ) )
685 //if( vd ) msg_Dbg( vd, "WinProc WM_SYSCOMMAND screensaver" );
686 return 0; /* this stops them from happening */
690 if( hwnd == p_event->hvideownd )
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 );
700 #ifdef MODULE_NAME_IS_directx
702 /* For overlay, we need to erase background */
703 return !use_overlay ? 1 : DefWindowProc(hwnd, message, wParam, lParam);
706 ** For overlay, DefWindowProc() will erase dirty regions
708 ** For non-overlay, vout will paint the whole window at
709 ** regular interval, therefore dirty regions can be ignored
710 ** to minimize repaint.
714 ValidateRect(hwnd, NULL);
716 // fall through to default
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.
724 /* nothing to erase */
727 /* nothing to repaint */
728 ValidateRect(hwnd, NULL);
732 return DefWindowProc(hwnd, message, wParam, lParam);
739 case WM_WINDOWPOSCHANGED:
740 vlc_mutex_lock( &p_event->lock );
741 p_event->has_moved = true;
742 vlc_mutex_unlock( &p_event->lock );
745 /* the user wants to close the window */
747 vout_display_SendEventClose(vd);
750 /* the window has been closed so shut down everything now */
752 msg_Dbg( vd, "WinProc WM_DESTROY" );
753 /* just destroy the window */
754 PostQuitMessage( 0 );
760 case IDM_TOGGLE_ON_TOP: /* toggle the "on top" status */
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);
776 return DefWindowProc(hwnd, message, wParam, lParam);
779 #ifdef MODULE_NAME_IS_wingapi
783 if( hwnd == p_event->hfswnd )
785 HWND htbar = FindWindow( _T("HHTaskbar"), NULL );
786 ShowWindow( htbar, SW_SHOW );
789 if( !p_event->hparent ||
790 hwnd == p_event->hfswnd )
792 SHFullScreen( hwnd, SHFS_SHOWSIPBUTTON );
798 #ifdef MODULE_NAME_IS_wingapi
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);
808 if( hwnd == p_event->hfswnd )
810 HWND htbar = FindWindow( _T("HHTaskbar"), NULL );
811 ShowWindow( htbar, SW_HIDE );
814 if( !p_event->hparent ||
815 hwnd == p_event->hfswnd )
817 SHFullScreen( hwnd, SHFS_HIDESIPBUTTON );
823 //msg_Dbg( vd, "WinProc WM Default %i", message );
827 /* Let windows handle the message */
828 return DefWindowProc(hwnd, message, wParam, lParam);
836 } dxkeys_to_vlckeys[] =
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 },
843 { VK_RETURN, KEY_ENTER },
845 { VK_ESCAPE, KEY_ESC },
847 { VK_LEFT, KEY_LEFT },
848 { VK_RIGHT, KEY_RIGHT },
850 { VK_DOWN, KEY_DOWN },
852 { VK_HOME, KEY_HOME },
854 { VK_PRIOR, KEY_PAGEUP },
855 { VK_NEXT, KEY_PAGEDOWN },
857 { VK_INSERT, KEY_INSERT },
858 { VK_DELETE, KEY_DELETE },
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 },
882 static int DirectXConvertKey( int i_key )
886 for( i = 0; dxkeys_to_vlckeys[i].i_dxkey != 0; i++ )
888 if( dxkeys_to_vlckeys[i].i_dxkey == i_key )
890 return dxkeys_to_vlckeys[i].i_vlckey;
897 void EventThreadMouseAutoHide( event_thread_t *p_event )
899 if (!p_event->b_cursor_hidden &&
900 (mdate() - p_event->i_lastmoved) > p_event->i_mouse_hide_timeout )
902 /* Hide the cursor only if it is inside our window */
904 GetCursorPos( &point );
906 HWND hwnd = WindowFromPoint(point);
907 if( hwnd == p_event->hwnd || hwnd == p_event->hvideownd )
909 PostMessage( p_event->hwnd, WM_VLC_HIDE_MOUSE, 0, 0 );
913 p_event->i_lastmoved = mdate();
917 void EventThreadMouseShow( event_thread_t *p_event )
919 PostMessage( p_event->hwnd, WM_VLC_SHOW_MOUSE, 0, 0 );
921 void EventThreadUpdateTitle( event_thread_t *p_event, const char *psz_fallback )
923 char *psz_title = var_GetNonEmptyString( p_event->vd, "video-title" );
925 psz_title = strdup( psz_fallback );
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 );
934 PostMessage( p_event->hwnd, WM_VLC_CHANGE_TEXT, 0, 0 );
936 int EventThreadGetWindowStyle( event_thread_t *p_event )
938 /* No need to lock, it is serialized by EventThreadStart */
939 return p_event->i_window_style;
942 void EventThreadUpdateWindowPosition( event_thread_t *p_event,
943 bool *pb_moved, bool *pb_resized,
944 int x, int y, int w, int h )
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;
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 );
959 void EventThreadUpdateSourceAndPlace( event_thread_t *p_event,
960 const video_format_t *p_source,
961 const vout_display_place_t *p_place )
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 );
969 void EventThreadUseOverlay( event_thread_t *p_event, bool b_used )
971 vlc_mutex_lock( &p_event->lock );
972 p_event->use_overlay = b_used;
973 vlc_mutex_unlock( &p_event->lock );
975 bool EventThreadGetAndResetHasMoved( event_thread_t *p_event )
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 );
985 event_thread_t *EventThreadCreate( vout_display_t *vd)
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) );
1000 vlc_mutex_init( &p_event->lock );
1001 vlc_cond_init( &p_event->wait );
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);
1014 void EventThreadDestroy( event_thread_t *p_event )
1016 free( p_event->psz_title );
1017 vlc_cond_destroy( &p_event->wait );
1018 vlc_mutex_destroy( &p_event->lock );
1022 int EventThreadStart( event_thread_t *p_event, event_hwnd_t *p_hwnd, const event_cfg_t *p_cfg )
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;
1028 p_event->has_moved = false;
1030 p_event->b_ready = false;
1031 p_event->b_done = false;
1032 p_event->b_error = false;
1034 if( vlc_clone( &p_event->thread, EventThread, p_event,
1035 VLC_THREAD_PRIORITY_LOW ) )
1037 msg_Err( p_event->vd, "cannot create Vout EventThread" );
1038 return VLC_EGENERIC;
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 );
1049 vlc_join( p_event->thread, NULL );
1050 p_event->b_ready = false;
1051 return VLC_EGENERIC;
1053 msg_Dbg( p_event->vd, "Vout EventThread running" );
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;
1064 void EventThreadStop( event_thread_t *p_event )
1066 if( !p_event->b_ready )
1069 vlc_mutex_lock( &p_event->lock );
1070 p_event->b_done = true;
1071 vlc_mutex_unlock( &p_event->lock );
1073 /* we need to be sure Vout EventThread won't stay stuck in
1074 * GetMessage, so we send a fake message */
1076 PostMessage( p_event->hwnd, WM_NULL, 0, 0);
1078 vlc_join( p_event->thread, NULL );
1079 p_event->b_ready = false;