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);
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);
68 /*****************************************************************************
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)
92 volatile bool b_cursor_hidden;
93 volatile mtime_t i_lastmoved;
94 mtime_t i_mouse_hide_timeout;
100 vout_window_cfg_t wnd_cfg;
103 vout_window_t *parent_window;
108 video_format_t source;
109 vout_display_place_t place;
114 static int DirectXCreateWindow( event_thread_t * );
115 static void DirectXCloseWindow ( event_thread_t * );
116 static long FAR PASCAL DirectXEventProc( HWND, UINT, WPARAM, LPARAM );
118 static int DirectXConvertKey( int i_key );
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 )
130 event_thread_t *p_event = (event_thread_t *)p_this;
131 vout_display_t *vd = p_event->vd;
133 POINT old_mouse_pos = {0,0}, mouse_pos;
135 int canc = vlc_savecancel ();
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
141 if( DirectXCreateWindow( p_event ) )
142 p_event->b_error = true;
144 p_event->b_ready = true;
145 vlc_cond_signal( &p_event->wait );
147 const bool b_error = p_event->b_error;
148 vlc_mutex_unlock( &p_event->lock );
152 vlc_restorecancel( canc );
157 /* Set power management stuff */
158 if( (hkernel32 = GetModuleHandle( _T("KERNEL32") ) ) )
160 ULONG (WINAPI* OurSetThreadExecutionState)( ULONG );
162 OurSetThreadExecutionState = (ULONG (WINAPI*)( ULONG ))
163 GetProcAddress( hkernel32, _T("SetThreadExecutionState") );
165 if( OurSetThreadExecutionState )
166 /* Prevent monitor from powering off */
167 OurSetThreadExecutionState( ES_DISPLAY_REQUIRED | ES_CONTINUOUS );
169 msg_Dbg( vd, "no support for SetThreadExecutionState()" );
174 /* GetMessage will sleep if there's no message in the queue */
177 vout_display_place_t place;
178 video_format_t source;
180 if( !GetMessage( &msg, 0, 0, 0 ) )
182 vlc_mutex_lock( &p_event->lock );
183 p_event->b_done = true;
184 vlc_mutex_unlock( &p_event->lock );
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 );
196 switch( msg.message )
199 vlc_mutex_lock( &p_event->lock );
200 place = p_event->place;
201 source = p_event->source;
202 vlc_mutex_unlock( &p_event->lock );
204 if( place.width > 0 && place.height > 0 )
206 if( msg.hwnd == p_event->hvideownd )
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);
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 ) )
225 GetCursorPos( &old_mouse_pos );
226 p_event->i_lastmoved = mdate();
228 if( p_event->b_cursor_hidden )
230 p_event->b_cursor_hidden = false;
236 case WM_VLC_HIDE_MOUSE:
237 if( p_event->b_cursor_hidden )
239 p_event->b_cursor_hidden = true;
240 GetCursorPos( &old_mouse_pos );
244 case WM_VLC_SHOW_MOUSE:
245 if( !p_event->b_cursor_hidden )
247 p_event->b_cursor_hidden = false;
248 GetCursorPos( &old_mouse_pos );
253 vout_display_SendEventMousePressed(vd, MOUSE_BUTTON_LEFT);
256 vout_display_SendEventMouseReleased(vd, MOUSE_BUTTON_LEFT);
258 case WM_LBUTTONDBLCLK:
259 vout_display_SendEventMouseDoubleClick(vd);
263 vout_display_SendEventMousePressed(vd, MOUSE_BUTTON_CENTER);
266 vout_display_SendEventMouseReleased(vd, MOUSE_BUTTON_CENTER);
270 vout_display_SendEventMousePressed(vd, MOUSE_BUTTON_RIGHT);
273 vout_display_SendEventMouseReleased(vd, MOUSE_BUTTON_RIGHT);
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
282 int i_key = DirectXConvertKey( msg.wParam );
285 /* This appears to be a "normal" (ascii) key */
286 i_key = tolower( MapVirtualKey( msg.wParam, 2 ) );
291 if( GetKeyState(VK_CONTROL) & 0x8000 )
293 i_key |= KEY_MODIFIER_CTRL;
295 if( GetKeyState(VK_SHIFT) & 0x8000 )
297 i_key |= KEY_MODIFIER_SHIFT;
299 if( GetKeyState(VK_MENU) & 0x8000 )
301 i_key |= KEY_MODIFIER_ALT;
304 vout_display_SendEventKey(vd, i_key);
312 if( GET_WHEEL_DELTA_WPARAM( msg.wParam ) > 0 )
314 i_key = KEY_MOUSEWHEELUP;
318 i_key = KEY_MOUSEWHEELDOWN;
322 if( GetKeyState(VK_CONTROL) & 0x8000 )
324 i_key |= KEY_MODIFIER_CTRL;
326 if( GetKeyState(VK_SHIFT) & 0x8000 )
328 i_key |= KEY_MODIFIER_SHIFT;
330 if( GetKeyState(VK_MENU) & 0x8000 )
332 i_key |= KEY_MODIFIER_ALT;
334 vout_display_SendEventKey(vd, i_key);
339 case WM_VLC_CHANGE_TEXT:
341 vlc_mutex_lock( &p_event->lock );
342 wchar_t *pwz_title = NULL;
343 if( p_event->psz_title )
345 const size_t i_length = strlen(p_event->psz_title);
346 pwz_title = malloc( 2 * (i_length + 1) );
349 mbstowcs( pwz_title, p_event->psz_title, 2 * i_length );
350 pwz_title[i_length] = 0;
353 vlc_mutex_unlock( &p_event->lock );
357 SetWindowText( p_event->hwnd, (LPCTSTR)pwz_title );
358 if( p_event->hfswnd )
359 SetWindowText( p_event->hfswnd, (LPCTSTR)pwz_title );
366 /* Messages we don't handle directly are dispatched to the
367 * window procedure */
368 TranslateMessage(&msg);
369 DispatchMessage(&msg);
374 } /* End Main loop */
376 /* Check for WM_QUIT if we created the window */
377 if( !p_event->hparent && msg.message == WM_QUIT )
379 msg_Warn( vd, "WM_QUIT... should not happen!!" );
380 p_event->hwnd = NULL; /* Window already destroyed */
383 msg_Dbg( vd, "DirectXEventThread terminating" );
385 DirectXCloseWindow( p_event );
386 vlc_restorecancel(canc);
391 /* following functions are local */
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
399 *****************************************************************************/
400 static int DirectXCreateWindow( event_thread_t *p_event )
402 vout_display_t *vd = p_event->vd;
406 WNDCLASS wc; /* window class components */
408 char vlc_path[MAX_PATH+1];
409 int i_style, i_stylex;
411 msg_Dbg( vd, "DirectXCreateWindow" );
413 /* Get this module's instance */
414 hInstance = GetModuleHandle(NULL);
416 #ifdef MODULE_NAME_IS_direct3d
417 if( !p_event->use_desktop )
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->handle.hwnd;
425 p_event->hparent = NULL;
426 #ifdef MODULE_NAME_IS_direct3d
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 );
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;
441 /* Get the Icon from the main app */
444 if( GetModuleFileName( NULL, vlc_path, MAX_PATH ) )
446 vlc_icon = ExtractIcon( hInstance, vlc_path, 0 );
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 */
462 /* Register the window class */
463 if( !RegisterClass(&wc) )
467 if( vlc_icon ) DestroyIcon( vlc_icon );
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 ) )
473 msg_Err( vd, "DirectXCreateWindow RegisterClass FAILED (err=%lu)", GetLastError() );
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) )
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 ) )
489 msg_Err( vd, "DirectXCreateWindow RegisterClass FAILED (err=%lu)", GetLastError() );
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;
503 if( var_GetBool( vd, "video-deco" ) )
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;
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.
519 if( p_event->hparent )
521 i_style = WS_VISIBLE|WS_CLIPCHILDREN|WS_CHILD;
525 p_event->i_window_style = i_style;
527 /* Create the window */
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 */
546 msg_Warn( vd, "DirectXCreateWindow create window FAILED (err=%lu)", GetLastError() );
550 if( p_event->hparent )
554 /* We don't want the window owner to overwrite our client area */
555 i_style = GetWindowLong( p_event->hparent, GWL_STYLE );
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 );
562 /* Create our fullscreen window */
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 );
573 p_event->hfswnd = NULL;
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") );
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 */
587 CreateWindow( _T("VLC DirectX video"), _T(""), /* window class */
588 WS_CHILD, /* window style, not visible initially */
590 vd->source.i_width, /* default width */
591 vd->source.i_height, /* default height */
592 p_event->hwnd, /* parent window */
594 (LPVOID)p_event ); /* send vd to WM_CREATE */
596 if( !p_event->hvideownd )
597 msg_Warn( vd, "can't create video sub-window" );
599 msg_Dbg( vd, "created video sub-window" );
601 /* Now display the window */
602 ShowWindow( p_event->hwnd, SW_SHOW );
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 )
614 vout_display_t *vd = p_event->vd;
615 msg_Dbg( vd, "DirectXCloseWindow" );
617 DestroyWindow( p_event->hwnd );
618 if( p_event->hfswnd )
619 DestroyWindow( p_event->hfswnd );
621 #ifdef MODULE_NAME_IS_direct3d
622 if( !p_event->use_desktop )
624 vout_display_DeleteWindow( vd, p_event->parent_window );
625 p_event->hwnd = NULL;
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
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 )
646 event_thread_t *p_event;
648 if( message == WM_CREATE )
650 /* Store vd for future use */
651 p_event = (event_thread_t *)((CREATESTRUCT *)lParam)->lpCreateParams;
652 SetWindowLongPtr( hwnd, GWLP_USERDATA, (LONG_PTR)p_event );
657 LONG_PTR p_user_data = GetWindowLongPtr( hwnd, GWLP_USERDATA );
658 p_event = (event_thread_t *)p_user_data;
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);
666 vout_display_t *vd = p_event->vd;
669 /* Catch the screensaver and the monitor turn-off */
670 if( message == WM_SYSCOMMAND &&
671 ( (wParam & 0xFFF0) == SC_SCREENSAVE || (wParam & 0xFFF0) == SC_MONITORPOWER ) )
673 //if( vd ) msg_Dbg( vd, "WinProc WM_SYSCOMMAND screensaver" );
674 return 0; /* this stops them from happening */
678 if( hwnd == p_event->hvideownd )
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 );
688 #ifdef MODULE_NAME_IS_directx
690 /* For overlay, we need to erase background */
691 return !use_overlay ? 1 : DefWindowProc(hwnd, message, wParam, lParam);
694 ** For overlay, DefWindowProc() will erase dirty regions
696 ** For non-overlay, vout will paint the whole window at
697 ** regular interval, therefore dirty regions can be ignored
698 ** to minimize repaint.
702 ValidateRect(hwnd, NULL);
704 // fall through to default
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.
712 /* nothing to erase */
715 /* nothing to repaint */
716 ValidateRect(hwnd, NULL);
720 return DefWindowProc(hwnd, message, wParam, lParam);
727 case WM_WINDOWPOSCHANGED:
728 vlc_mutex_lock( &p_event->lock );
729 p_event->has_moved = true;
730 vlc_mutex_unlock( &p_event->lock );
733 /* the user wants to close the window */
735 vout_display_SendEventClose(vd);
738 /* the window has been closed so shut down everything now */
740 msg_Dbg( vd, "WinProc WM_DESTROY" );
741 /* just destroy the window */
742 PostQuitMessage( 0 );
748 case IDM_TOGGLE_ON_TOP: /* toggle the "on top" status */
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);
763 return DefWindowProc(hwnd, message, wParam, lParam);
766 #ifdef MODULE_NAME_IS_wingapi
770 if( hwnd == p_event->hfswnd )
772 HWND htbar = FindWindow( _T("HHTaskbar"), NULL );
773 ShowWindow( htbar, SW_SHOW );
776 if( !p_event->hparent ||
777 hwnd == p_event->hfswnd )
779 SHFullScreen( hwnd, SHFS_SHOWSIPBUTTON );
785 #ifdef MODULE_NAME_IS_wingapi
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);
795 if( hwnd == p_event->hfswnd )
797 HWND htbar = FindWindow( _T("HHTaskbar"), NULL );
798 ShowWindow( htbar, SW_HIDE );
801 if( !p_event->hparent ||
802 hwnd == p_event->hfswnd )
804 SHFullScreen( hwnd, SHFS_HIDESIPBUTTON );
810 //msg_Dbg( vd, "WinProc WM Default %i", message );
814 /* Let windows handle the message */
815 return DefWindowProc(hwnd, message, wParam, lParam);
823 } dxkeys_to_vlckeys[] =
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 },
830 { VK_RETURN, KEY_ENTER },
832 { VK_ESCAPE, KEY_ESC },
834 { VK_LEFT, KEY_LEFT },
835 { VK_RIGHT, KEY_RIGHT },
837 { VK_DOWN, KEY_DOWN },
839 { VK_HOME, KEY_HOME },
841 { VK_PRIOR, KEY_PAGEUP },
842 { VK_NEXT, KEY_PAGEDOWN },
844 { VK_INSERT, KEY_INSERT },
845 { VK_DELETE, KEY_DELETE },
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 },
869 static int DirectXConvertKey( int i_key )
873 for( i = 0; dxkeys_to_vlckeys[i].i_dxkey != 0; i++ )
875 if( dxkeys_to_vlckeys[i].i_dxkey == i_key )
877 return dxkeys_to_vlckeys[i].i_vlckey;
884 void EventThreadMouseAutoHide( event_thread_t *p_event )
886 if (!p_event->b_cursor_hidden &&
887 (mdate() - p_event->i_lastmoved) > p_event->i_mouse_hide_timeout )
889 /* Hide the cursor only if it is inside our window */
891 GetCursorPos( &point );
893 HWND hwnd = WindowFromPoint(point);
894 if( hwnd == p_event->hwnd || hwnd == p_event->hvideownd )
896 PostMessage( p_event->hwnd, WM_VLC_HIDE_MOUSE, 0, 0 );
900 p_event->i_lastmoved = mdate();
904 void EventThreadMouseShow( event_thread_t *p_event )
906 PostMessage( p_event->hwnd, WM_VLC_SHOW_MOUSE, 0, 0 );
908 void EventThreadUpdateTitle( event_thread_t *p_event, const char *psz_fallback )
910 char *psz_title = var_GetNonEmptyString( p_event->vd, "video-title" );
912 psz_title = strdup( psz_fallback );
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 );
921 PostMessage( p_event->hwnd, WM_VLC_CHANGE_TEXT, 0, 0 );
923 int EventThreadGetWindowStyle( event_thread_t *p_event )
925 /* No need to lock, it is serialized by EventThreadStart */
926 return p_event->i_window_style;
929 void EventThreadUpdateWindowPosition( event_thread_t *p_event, bool *pb_changed,
930 int x, int y, int w, int h )
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;
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 );
945 void EventThreadUpdateSourceAndPlace( event_thread_t *p_event,
946 const video_format_t *p_source,
947 const vout_display_place_t *p_place )
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 );
955 void EventThreadUseOverlay( event_thread_t *p_event, bool b_used )
957 vlc_mutex_lock( &p_event->lock );
958 p_event->use_overlay = b_used;
959 vlc_mutex_unlock( &p_event->lock );
961 bool EventThreadGetAndResetHasMoved( event_thread_t *p_event )
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 );
971 event_thread_t *EventThreadCreate( vout_display_t *vd)
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) );
986 vlc_mutex_init( &p_event->lock );
987 vlc_cond_init( &p_event->wait );
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);
1000 void EventThreadDestroy( event_thread_t *p_event )
1002 free( p_event->psz_title );
1003 vlc_cond_destroy( &p_event->wait );
1004 vlc_mutex_destroy( &p_event->lock );
1008 int EventThreadStart( event_thread_t *p_event, event_hwnd_t *p_hwnd, const event_cfg_t *p_cfg )
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;
1014 p_event->has_moved = false;
1016 p_event->b_ready = false;
1017 p_event->b_done = false;
1018 p_event->b_error = false;
1020 if( vlc_clone( &p_event->thread, EventThread, p_event,
1021 VLC_THREAD_PRIORITY_LOW ) )
1023 msg_Err( p_event->vd, "cannot create Vout EventThread" );
1024 return VLC_EGENERIC;
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 );
1035 vlc_join( p_event->thread, NULL );
1036 p_event->b_ready = false;
1037 return VLC_EGENERIC;
1039 msg_Dbg( p_event->vd, "Vout EventThread running" );
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;
1050 void EventThreadStop( event_thread_t *p_event )
1052 if( !p_event->b_ready )
1055 vlc_mutex_lock( &p_event->lock );
1056 p_event->b_done = true;
1057 vlc_mutex_unlock( &p_event->lock );
1059 /* we need to be sure Vout EventThread won't stay stuck in
1060 * GetMessage, so we send a fake message */
1062 PostMessage( p_event->hwnd, WM_NULL, 0, 0);
1064 vlc_join( p_event->thread, NULL );
1065 p_event->b_ready = false;