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"
52 #ifdef MODULE_NAME_IS_direct2d
61 //WINSHELLAPI BOOL WINAPI SHFullScreen(HWND hwndRequester, DWORD dwState);
63 UINT GetMenuState(HMENU hMenu, UINT id, UINT flags)
66 memset(&info, 0, sizeof(info));
67 info.cbSize = sizeof(info);
68 info.fMask = MIIM_STATE;
69 if (!GetMenuItemInfo(hMenu, id, (flags & MF_BYPOSITION) != 0, &info))
71 /* XXX Submenu handling is missing... */
76 /*#if defined(UNDER_CE) && !defined(__PLUGIN__) --FIXME*/
77 /*# define SHFS_SHOWSIPBUTTON 0x0004
78 # define SHFS_HIDESIPBUTTON 0x0008
79 # define MENU_HEIGHT 26
80 BOOL SHFullScreen(HWND hwndRequester, DWORD dwState);
83 /*****************************************************************************
85 *****************************************************************************/
86 #define WM_VLC_HIDE_MOUSE (WM_APP + 0)
87 #define WM_VLC_CHANGE_TEXT (WM_APP + 1)
106 bool is_cursor_hidden;
107 HCURSOR cursor_arrow;
108 HCURSOR cursor_empty;
109 unsigned button_pressed;
115 vout_window_cfg_t wnd_cfg;
118 vout_window_t *parent_window;
119 TCHAR class_main[256];
120 TCHAR class_video[256];
125 video_format_t source;
126 vout_display_place_t place;
133 static int DirectXCreateWindow( event_thread_t * );
134 static void DirectXCloseWindow ( event_thread_t * );
135 static long FAR PASCAL DirectXEventProc( HWND, UINT, WPARAM, LPARAM );
137 static int DirectXConvertKey( int i_key );
139 static inline bool isMouseEvent( WPARAM type )
141 return type >= WM_MOUSEFIRST &&
142 type <= WM_MOUSELAST;
145 static inline bool isKeyEvent( WPARAM type )
147 return type >= WM_KEYFIRST &&
151 static void UpdateCursor( event_thread_t *p_event, bool b_show )
153 if( p_event->is_cursor_hidden == !b_show )
155 p_event->is_cursor_hidden = !b_show;
158 HCURSOR cursor = b_show ? p_event->cursor_arrow : p_event->cursor_empty;
159 if( p_event->hvideownd )
160 SetClassLongPtr( p_event->hvideownd, GCLP_HCURSOR, (LONG_PTR)cursor );
162 SetClassLongPtr( p_event->hwnd, GCLP_HCURSOR, (LONG_PTR)cursor );
165 /* FIXME I failed to find a cleaner way to force a redraw of the cursor */
168 HWND hwnd = WindowFromPoint(p);
169 if( hwnd == p_event->hvideownd || hwnd == p_event->hwnd )
174 SetCursorPos( p.x, p.y );
179 static HCURSOR EmptyCursor( HINSTANCE instance )
181 const int cw = GetSystemMetrics(SM_CXCURSOR);
182 const int ch = GetSystemMetrics(SM_CYCURSOR);
184 HCURSOR cursor = NULL;
185 uint8_t *and = malloc(cw * ch);
186 uint8_t *xor = malloc(cw * ch);
189 memset(and, 0xff, cw * ch );
190 memset(xor, 0x00, cw * ch );
191 cursor = CreateCursor( instance, 0, 0, cw, ch, and, xor);
200 static void MousePressed( event_thread_t *p_event, HWND hwnd, unsigned button )
202 if( !p_event->button_pressed )
204 p_event->button_pressed |= 1 << button;
205 vout_display_SendEventMousePressed( p_event->vd, button );
208 static void MouseReleased( event_thread_t *p_event, unsigned button )
210 p_event->button_pressed &= ~(1 << button);
211 if( !p_event->button_pressed )
213 vout_display_SendEventMouseReleased( p_event->vd, button );
215 /*****************************************************************************
216 * EventThread: Create video window & handle its messages
217 *****************************************************************************
218 * This function creates a video window and then enters an infinite loop
219 * that handles the messages sent to that window.
220 * The main goal of this thread is to isolate the Win32 PeekMessage function
221 * because this one can block for a long time.
222 *****************************************************************************/
223 static void *EventThread( void *p_this )
225 event_thread_t *p_event = (event_thread_t *)p_this;
226 vout_display_t *vd = p_event->vd;
228 POINT old_mouse_pos = {0,0}, mouse_pos;
229 int canc = vlc_savecancel ();
231 bool b_mouse_support = var_InheritBool( p_event->vd, "mouse-events" );
232 bool b_key_support = var_InheritBool( p_event->vd, "keyboard-events" );
234 vlc_mutex_lock( &p_event->lock );
235 /* Create a window for the video */
236 /* Creating a window under Windows also initializes the thread's event
238 if( DirectXCreateWindow( p_event ) )
239 p_event->b_error = true;
241 p_event->b_ready = true;
242 vlc_cond_signal( &p_event->wait );
244 const bool b_error = p_event->b_error;
245 vlc_mutex_unlock( &p_event->lock );
249 vlc_restorecancel( canc );
254 /* Prevent monitor from powering off */
255 SetThreadExecutionState( ES_DISPLAY_REQUIRED | ES_CONTINUOUS );
259 /* GetMessage will sleep if there's no message in the queue */
262 vout_display_place_t place;
263 video_format_t source;
265 if( !GetMessage( &msg, 0, 0, 0 ) )
267 vlc_mutex_lock( &p_event->lock );
268 p_event->b_done = true;
269 vlc_mutex_unlock( &p_event->lock );
273 /* Check if we are asked to exit */
274 vlc_mutex_lock( &p_event->lock );
275 const bool b_done = p_event->b_done;
276 vlc_mutex_unlock( &p_event->lock );
280 if( !b_mouse_support && isMouseEvent( msg.message ) )
283 if( !b_key_support && isKeyEvent( msg.message ) )
286 /* Handle mouse state */
287 if( msg.message == WM_MOUSEMOVE ||
288 msg.message == WM_NCMOUSEMOVE )
290 GetCursorPos( &mouse_pos );
291 /* FIXME, why this >2 limits ? */
292 if( (abs(mouse_pos.x - old_mouse_pos.x) > 2 ||
293 (abs(mouse_pos.y - old_mouse_pos.y)) > 2 ) )
295 old_mouse_pos = mouse_pos;
296 UpdateCursor( p_event, true );
299 else if( isMouseEvent( msg.message ) )
301 UpdateCursor( p_event, true );
303 else if( msg.message == WM_VLC_HIDE_MOUSE )
305 UpdateCursor( p_event, false );
309 switch( msg.message )
312 vlc_mutex_lock( &p_event->lock );
313 place = p_event->place;
314 source = p_event->source;
315 vlc_mutex_unlock( &p_event->lock );
317 if( place.width > 0 && place.height > 0 )
319 if( msg.hwnd == p_event->hvideownd )
325 const int x = source.i_x_offset +
326 (int64_t)(GET_X_LPARAM(msg.lParam) - place.x) * source.i_width / place.width;
327 const int y = source.i_y_offset +
328 (int64_t)(GET_Y_LPARAM(msg.lParam) - place.y) * source.i_height / place.height;
329 vout_display_SendEventMouseMoved(vd, x, y);
335 case WM_VLC_HIDE_MOUSE:
339 MousePressed( p_event, msg.hwnd, MOUSE_BUTTON_LEFT );
342 MouseReleased( p_event, MOUSE_BUTTON_LEFT );
344 case WM_LBUTTONDBLCLK:
345 vout_display_SendEventMouseDoubleClick(vd);
349 MousePressed( p_event, msg.hwnd, MOUSE_BUTTON_CENTER );
352 MouseReleased( p_event, MOUSE_BUTTON_CENTER );
356 MousePressed( p_event, msg.hwnd, MOUSE_BUTTON_RIGHT );
359 MouseReleased( p_event, MOUSE_BUTTON_RIGHT );
365 /* The key events are first processed here and not translated
366 * into WM_CHAR events because we need to know the status of the
368 int i_key = DirectXConvertKey( msg.wParam );
371 /* This appears to be a "normal" (ascii) key */
372 i_key = tolower( (unsigned char)MapVirtualKey( msg.wParam, 2 ) );
377 if( GetKeyState(VK_CONTROL) & 0x8000 )
379 i_key |= KEY_MODIFIER_CTRL;
381 if( GetKeyState(VK_SHIFT) & 0x8000 )
383 i_key |= KEY_MODIFIER_SHIFT;
385 if( GetKeyState(VK_MENU) & 0x8000 )
387 i_key |= KEY_MODIFIER_ALT;
390 vout_display_SendEventKey(vd, i_key);
398 if( GET_WHEEL_DELTA_WPARAM( msg.wParam ) > 0 )
400 i_key = KEY_MOUSEWHEELUP;
404 i_key = KEY_MOUSEWHEELDOWN;
408 if( GetKeyState(VK_CONTROL) & 0x8000 )
410 i_key |= KEY_MODIFIER_CTRL;
412 if( GetKeyState(VK_SHIFT) & 0x8000 )
414 i_key |= KEY_MODIFIER_SHIFT;
416 if( GetKeyState(VK_MENU) & 0x8000 )
418 i_key |= KEY_MODIFIER_ALT;
420 vout_display_SendEventKey(vd, i_key);
425 case WM_VLC_CHANGE_TEXT:
427 vlc_mutex_lock( &p_event->lock );
428 wchar_t *pwz_title = NULL;
429 if( p_event->psz_title )
431 const size_t i_length = strlen(p_event->psz_title);
432 pwz_title = malloc( 2 * (i_length + 1) );
435 mbstowcs( pwz_title, p_event->psz_title, 2 * i_length );
436 pwz_title[i_length] = 0;
439 vlc_mutex_unlock( &p_event->lock );
443 SetWindowTextW( p_event->hwnd, pwz_title );
444 if( p_event->hfswnd )
445 SetWindowTextW( p_event->hfswnd, pwz_title );
452 /* Messages we don't handle directly are dispatched to the
453 * window procedure */
454 TranslateMessage(&msg);
455 DispatchMessage(&msg);
460 } /* End Main loop */
462 /* Check for WM_QUIT if we created the window */
463 if( !p_event->hparent && msg.message == WM_QUIT )
465 msg_Warn( vd, "WM_QUIT... should not happen!!" );
466 p_event->hwnd = NULL; /* Window already destroyed */
469 msg_Dbg( vd, "DirectXEventThread terminating" );
471 DirectXCloseWindow( p_event );
472 vlc_restorecancel(canc);
477 /* following functions are local */
479 /*****************************************************************************
480 * DirectXCreateWindow: create a window for the video.
481 *****************************************************************************
482 * Before creating a direct draw surface, we need to create a window in which
483 * the video will be displayed. This window will also allow us to capture the
485 *****************************************************************************/
486 static int DirectXCreateWindow( event_thread_t *p_event )
488 vout_display_t *vd = p_event->vd;
492 WNDCLASS wc; /* window class components */
493 char vlc_path[MAX_PATH+1];
494 int i_style, i_stylex;
496 msg_Dbg( vd, "DirectXCreateWindow" );
498 /* Get this module's instance */
499 hInstance = GetModuleHandle(NULL);
501 #ifdef MODULE_NAME_IS_direct3d
502 if( !p_event->use_desktop )
505 /* If an external window was specified, we'll draw in it. */
506 p_event->parent_window = vout_display_NewWindow(vd, &p_event->wnd_cfg );
507 if( p_event->parent_window )
508 p_event->hparent = p_event->parent_window->handle.hwnd;
510 p_event->hparent = NULL;
511 #ifdef MODULE_NAME_IS_direct3d
515 /* Find Program Manager */
516 HWND hwnd = FindWindow( _T("Progman"), NULL );
517 if( hwnd ) hwnd = FindWindowEx( hwnd, NULL, _T("SHELLDLL_DefView"), NULL );
518 if( hwnd ) hwnd = FindWindowEx( hwnd, NULL, _T("SysListView32"), NULL );
520 msg_Err( vd, "Couldn't find desktop icon window. Desktop mode can't be established." );
521 p_event->parent_window = NULL;
522 p_event->hparent = hwnd;
525 p_event->cursor_arrow = LoadCursor(NULL, IDC_ARROW);
527 p_event->cursor_empty = EmptyCursor(hInstance);
530 /* Get the Icon from the main app */
531 p_event->vlc_icon = NULL;
533 if( GetModuleFileName( NULL, vlc_path, MAX_PATH ) )
535 p_event->vlc_icon = ExtractIcon( hInstance, vlc_path, 0 );
539 /* Fill in the window class structure */
540 wc.style = CS_OWNDC|CS_DBLCLKS; /* style: dbl click */
541 wc.lpfnWndProc = (WNDPROC)DirectXEventProc; /* event handler */
542 wc.cbClsExtra = 0; /* no extra class data */
543 wc.cbWndExtra = 0; /* no extra window data */
544 wc.hInstance = hInstance; /* instance */
545 wc.hIcon = p_event->vlc_icon; /* load the vlc big icon */
546 wc.hCursor = p_event->is_cursor_hidden ? p_event->cursor_empty :
547 p_event->cursor_arrow;
548 wc.hbrBackground = GetStockObject(BLACK_BRUSH); /* background color */
549 wc.lpszMenuName = NULL; /* no menu */
550 wc.lpszClassName = p_event->class_main; /* use a special class */
552 /* Register the window class */
553 if( !RegisterClass(&wc) )
555 if( p_event->vlc_icon )
556 DestroyIcon( p_event->vlc_icon );
558 msg_Err( vd, "DirectXCreateWindow RegisterClass FAILED (err=%lu)", GetLastError() );
562 /* Register the video sub-window class */
563 wc.lpszClassName = p_event->class_video;
565 wc.hbrBackground = NULL; /* no background color */
566 if( !RegisterClass(&wc) )
568 msg_Err( vd, "DirectXCreateWindow RegisterClass FAILED (err=%lu)", GetLastError() );
572 /* When you create a window you give the dimensions you wish it to
573 * have. Unfortunatly these dimensions will include the borders and
574 * titlebar. We use the following function to find out the size of
575 * the window corresponding to the useable surface we want */
576 rect_window.left = 10;
577 rect_window.top = 10;
578 rect_window.right = rect_window.left + p_event->wnd_cfg.width;
579 rect_window.bottom = rect_window.top + p_event->wnd_cfg.height;
581 if( var_GetBool( vd, "video-deco" ) )
583 /* Open with window decoration */
584 AdjustWindowRect( &rect_window, WS_OVERLAPPEDWINDOW|WS_SIZEBOX, 0 );
585 i_style = WS_OVERLAPPEDWINDOW|WS_SIZEBOX|WS_VISIBLE|WS_CLIPCHILDREN;
590 /* No window decoration */
591 AdjustWindowRect( &rect_window, WS_POPUP, 0 );
592 i_style = WS_POPUP|WS_VISIBLE|WS_CLIPCHILDREN;
593 i_stylex = 0; // WS_EX_TOOLWINDOW; Is TOOLWINDOW really needed ?
594 // It messes up the fullscreen window.
597 if( p_event->hparent )
599 i_style = WS_VISIBLE|WS_CLIPCHILDREN|WS_CHILD;
602 /* allow user to regain control over input events if requested */
603 bool b_mouse_support = var_InheritBool( vd, "mouse-events" );
604 bool b_key_support = var_InheritBool( vd, "keyboard-events" );
605 if( !b_mouse_support && !b_key_support )
606 i_style |= WS_DISABLED;
609 p_event->i_window_style = i_style;
611 /* Create the window */
613 CreateWindowEx( WS_EX_NOPARENTNOTIFY | i_stylex,
614 p_event->class_main, /* name of window class */
615 _T(VOUT_TITLE) _T(" (DirectX Output)"), /* window title */
616 i_style, /* window style */
617 (!p_event->wnd_cfg.x) ? CW_USEDEFAULT :
618 (UINT)p_event->wnd_cfg.x, /* default X coordinate */
619 (!p_event->wnd_cfg.y) ? CW_USEDEFAULT :
620 (UINT)p_event->wnd_cfg.y, /* default Y coordinate */
621 rect_window.right - rect_window.left, /* window width */
622 rect_window.bottom - rect_window.top, /* window height */
623 p_event->hparent, /* parent window */
624 NULL, /* no menu in this window */
625 hInstance, /* handle of this program instance */
626 (LPVOID)p_event ); /* send vd to WM_CREATE */
630 msg_Warn( vd, "DirectXCreateWindow create window FAILED (err=%lu)", GetLastError() );
634 if( p_event->hparent )
638 /* We don't want the window owner to overwrite our client area */
639 i_style = GetWindowLong( p_event->hparent, GWL_STYLE );
641 if( !(i_style & WS_CLIPCHILDREN) )
642 /* Hmmm, apparently this is a blocking call... */
643 SetWindowLong( p_event->hparent, GWL_STYLE,
644 i_style | WS_CLIPCHILDREN );
646 /* Create our fullscreen window */
648 CreateWindowEx( WS_EX_APPWINDOW, p_event->class_main,
649 _T(VOUT_TITLE) _T(" (DirectX Output)"),
650 WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN|WS_SIZEBOX,
651 CW_USEDEFAULT, CW_USEDEFAULT,
652 CW_USEDEFAULT, CW_USEDEFAULT,
653 NULL, NULL, hInstance, NULL );
657 p_event->hfswnd = NULL;
660 /* Append a "Always On Top" entry in the system menu */
661 hMenu = GetSystemMenu( p_event->hwnd, FALSE );
662 AppendMenu( hMenu, MF_SEPARATOR, 0, _T("") );
663 AppendMenu( hMenu, MF_STRING | MF_UNCHECKED,
664 IDM_TOGGLE_ON_TOP, _T("Always on &Top") );
666 /* Create video sub-window. This sub window will always exactly match
667 * the size of the video, which allows us to use crazy overlay colorkeys
668 * without having them shown outside of the video area. */
669 /* FIXME vd->source.i_width/i_height seems wrong */
671 CreateWindow( p_event->class_video, _T(""), /* window class */
672 WS_CHILD, /* window style, not visible initially */
674 vd->source.i_width, /* default width */
675 vd->source.i_height, /* default height */
676 p_event->hwnd, /* parent window */
678 (LPVOID)p_event ); /* send vd to WM_CREATE */
680 if( !p_event->hvideownd )
681 msg_Warn( vd, "can't create video sub-window" );
683 msg_Dbg( vd, "created video sub-window" );
685 /* Now display the window */
686 ShowWindow( p_event->hwnd, SW_SHOW );
691 /*****************************************************************************
692 * DirectXCloseWindow: close the window created by DirectXCreateWindow
693 *****************************************************************************
694 * This function returns all resources allocated by DirectXCreateWindow.
695 *****************************************************************************/
696 static void DirectXCloseWindow( event_thread_t *p_event )
698 vout_display_t *vd = p_event->vd;
699 msg_Dbg( vd, "DirectXCloseWindow" );
701 DestroyWindow( p_event->hwnd );
702 if( p_event->hfswnd )
703 DestroyWindow( p_event->hfswnd );
705 #ifdef MODULE_NAME_IS_direct3d
706 if( !p_event->use_desktop )
708 vout_display_DeleteWindow( vd, p_event->parent_window );
709 p_event->hwnd = NULL;
711 if( p_event->vlc_icon )
712 DestroyIcon( p_event->vlc_icon );
714 HINSTANCE hInstance = GetModuleHandle(NULL);
715 UnregisterClass( p_event->class_video, hInstance );
716 UnregisterClass( p_event->class_main, hInstance );
719 DestroyCursor( p_event->cursor_empty );
723 /*****************************************************************************
724 * DirectXEventProc: This is the window event processing function.
725 *****************************************************************************
726 * On Windows, when you create a window you have to attach an event processing
727 * function to it. The aim of this function is to manage "Queued Messages" and
728 * "Nonqueued Messages".
729 * Queued Messages are those picked up and retransmitted by vout_Manage
730 * (using the GetMessage and DispatchMessage functions).
731 * Nonqueued Messages are those that Windows will send directly to this
732 * procedure (like WM_DESTROY, WM_WINDOWPOSCHANGED...)
733 *****************************************************************************/
734 static long FAR PASCAL DirectXEventProc( HWND hwnd, UINT message,
735 WPARAM wParam, LPARAM lParam )
737 event_thread_t *p_event;
739 if( message == WM_CREATE )
741 /* Store vd for future use */
742 p_event = (event_thread_t *)((CREATESTRUCT *)lParam)->lpCreateParams;
743 SetWindowLongPtr( hwnd, GWLP_USERDATA, (LONG_PTR)p_event );
748 LONG_PTR p_user_data = GetWindowLongPtr( hwnd, GWLP_USERDATA );
749 p_event = (event_thread_t *)p_user_data;
752 /* Hmmm mozilla does manage somehow to save the pointer to our
753 * windowproc and still calls it after the vout has been closed. */
754 return DefWindowProc(hwnd, message, wParam, lParam);
757 vout_display_t *vd = p_event->vd;
760 /* Catch the screensaver and the monitor turn-off */
761 if( message == WM_SYSCOMMAND &&
762 ( (wParam & 0xFFF0) == SC_SCREENSAVE || (wParam & 0xFFF0) == SC_MONITORPOWER ) )
764 //if( vd ) msg_Dbg( vd, "WinProc WM_SYSCOMMAND screensaver" );
765 return 0; /* this stops them from happening */
769 if( message == WM_SETCURSOR )
771 msg_Err(vd, "WM_SETCURSOR: %d (t2)", p_event->is_cursor_hidden);
772 SetCursor( p_event->is_cursor_hidden ? p_event->cursor_empty : p_event->cursor_arrow );
776 if( message == WM_CAPTURECHANGED )
778 for( int button = 0; p_event->button_pressed; button++ )
780 unsigned m = 1 << button;
781 if( p_event->button_pressed & m )
782 vout_display_SendEventMouseReleased( p_event->vd, button );
783 p_event->button_pressed &= ~m;
785 p_event->button_pressed = 0;
789 if( hwnd == p_event->hvideownd )
791 #ifdef MODULE_NAME_IS_directx
792 vlc_mutex_lock( &p_event->lock );
793 const bool use_overlay = p_event->use_overlay;
794 vlc_mutex_unlock( &p_event->lock );
799 #ifdef MODULE_NAME_IS_directx
801 /* For overlay, we need to erase background */
802 return !use_overlay ? 1 : DefWindowProc(hwnd, message, wParam, lParam);
805 ** For overlay, DefWindowProc() will erase dirty regions
807 ** For non-overlay, vout will paint the whole window at
808 ** regular interval, therefore dirty regions can be ignored
809 ** to minimize repaint.
813 ValidateRect(hwnd, NULL);
815 // fall through to default
818 ** For OpenGL and Direct3D, vout will update the whole
819 ** window at regular interval, therefore dirty region
820 ** can be ignored to minimize repaint.
823 /* nothing to erase */
826 /* nothing to repaint */
827 ValidateRect(hwnd, NULL);
831 return DefWindowProc(hwnd, message, wParam, lParam);
838 case WM_WINDOWPOSCHANGED:
839 vlc_mutex_lock( &p_event->lock );
840 p_event->has_moved = true;
841 vlc_mutex_unlock( &p_event->lock );
844 /* the user wants to close the window */
846 vout_display_SendEventClose(vd);
849 /* the window has been closed so shut down everything now */
851 msg_Dbg( vd, "WinProc WM_DESTROY" );
852 /* just destroy the window */
853 PostQuitMessage( 0 );
859 case IDM_TOGGLE_ON_TOP: /* toggle the "on top" status */
861 msg_Dbg(vd, "WinProc WM_SYSCOMMAND: IDM_TOGGLE_ON_TOP");
862 HMENU hMenu = GetSystemMenu(vd->sys->hwnd, FALSE);
863 vout_display_SendWindowState(vd, (GetMenuState(hMenu, IDM_TOGGLE_ON_TOP, MF_BYCOMMAND) & MF_CHECKED) ?
864 VOUT_WINDOW_STATE_NORMAL : VOUT_WINDOW_STATE_ABOVE);
875 return DefWindowProc(hwnd, message, wParam, lParam);
878 #ifdef MODULE_NAME_IS_wingapi
882 if( hwnd == p_event->hfswnd )
884 HWND htbar = FindWindow( _T("HHTaskbar"), NULL );
885 ShowWindow( htbar, SW_SHOW );
888 if( !p_event->hparent ||
889 hwnd == p_event->hfswnd )
891 SHFullScreen( hwnd, SHFS_SHOWSIPBUTTON );
897 #ifdef MODULE_NAME_IS_wingapi
901 /* FIXME vd->cfg is not lock[ed/able] */
902 #warning "FIXME: race condition"
903 if( p_event->hparent &&
904 hwnd != p_event->hfswnd && vd->cfg->is_fullscreen )
905 vout_display_SendEventFullscreen(vd, false);
907 if( hwnd == p_event->hfswnd )
909 HWND htbar = FindWindow( _T("HHTaskbar"), NULL );
910 ShowWindow( htbar, SW_HIDE );
913 if( !p_event->hparent ||
914 hwnd == p_event->hfswnd )
916 SHFullScreen( hwnd, SHFS_HIDESIPBUTTON );
922 //msg_Dbg( vd, "WinProc WM Default %i", message );
926 /* Let windows handle the message */
927 return DefWindowProc(hwnd, message, wParam, lParam);
935 } dxkeys_to_vlckeys[] =
937 { VK_F1, KEY_F1 }, { VK_F2, KEY_F2 }, { VK_F3, KEY_F3 }, { VK_F4, KEY_F4 },
938 { VK_F5, KEY_F5 }, { VK_F6, KEY_F6 }, { VK_F7, KEY_F7 }, { VK_F8, KEY_F8 },
939 { VK_F9, KEY_F9 }, { VK_F10, KEY_F10 }, { VK_F11, KEY_F11 },
942 { VK_RETURN, KEY_ENTER },
944 { VK_ESCAPE, KEY_ESC },
946 { VK_LEFT, KEY_LEFT },
947 { VK_RIGHT, KEY_RIGHT },
949 { VK_DOWN, KEY_DOWN },
951 { VK_HOME, KEY_HOME },
953 { VK_PRIOR, KEY_PAGEUP },
954 { VK_NEXT, KEY_PAGEDOWN },
956 { VK_INSERT, KEY_INSERT },
957 { VK_DELETE, KEY_DELETE },
963 { VK_BROWSER_BACK, KEY_BROWSER_BACK },
964 { VK_BROWSER_FORWARD, KEY_BROWSER_FORWARD },
965 { VK_BROWSER_REFRESH, KEY_BROWSER_REFRESH },
966 { VK_BROWSER_STOP, KEY_BROWSER_STOP },
967 { VK_BROWSER_SEARCH, KEY_BROWSER_SEARCH },
968 { VK_BROWSER_FAVORITES, KEY_BROWSER_FAVORITES },
969 { VK_BROWSER_HOME, KEY_BROWSER_HOME },
970 { VK_VOLUME_MUTE, KEY_VOLUME_MUTE },
971 { VK_VOLUME_DOWN, KEY_VOLUME_DOWN },
972 { VK_VOLUME_UP, KEY_VOLUME_UP },
973 { VK_MEDIA_NEXT_TRACK, KEY_MEDIA_NEXT_TRACK },
974 { VK_MEDIA_PREV_TRACK, KEY_MEDIA_PREV_TRACK },
975 { VK_MEDIA_STOP, KEY_MEDIA_STOP },
976 { VK_MEDIA_PLAY_PAUSE, KEY_MEDIA_PLAY_PAUSE },
981 static int DirectXConvertKey( int i_key )
985 for( i = 0; dxkeys_to_vlckeys[i].i_dxkey != 0; i++ )
987 if( dxkeys_to_vlckeys[i].i_dxkey == i_key )
989 return dxkeys_to_vlckeys[i].i_vlckey;
996 void EventThreadMouseHide( event_thread_t *p_event )
998 PostMessage( p_event->hwnd, WM_VLC_HIDE_MOUSE, 0, 0 );
1001 void EventThreadUpdateTitle( event_thread_t *p_event, const char *psz_fallback )
1003 char *psz_title = var_GetNonEmptyString( p_event->vd, "video-title" );
1005 psz_title = strdup( psz_fallback );
1009 vlc_mutex_lock( &p_event->lock );
1010 free( p_event->psz_title );
1011 p_event->psz_title = psz_title;
1012 vlc_mutex_unlock( &p_event->lock );
1014 PostMessage( p_event->hwnd, WM_VLC_CHANGE_TEXT, 0, 0 );
1016 int EventThreadGetWindowStyle( event_thread_t *p_event )
1018 /* No need to lock, it is serialized by EventThreadStart */
1019 return p_event->i_window_style;
1022 void EventThreadUpdateWindowPosition( event_thread_t *p_event,
1023 bool *pb_moved, bool *pb_resized,
1024 int x, int y, unsigned w, unsigned h )
1026 vlc_mutex_lock( &p_event->lock );
1027 *pb_moved = x != p_event->wnd_cfg.x ||
1028 y != p_event->wnd_cfg.y;
1029 *pb_resized = w != p_event->wnd_cfg.width ||
1030 h != p_event->wnd_cfg.height;
1032 p_event->wnd_cfg.x = x;
1033 p_event->wnd_cfg.y = y;
1034 p_event->wnd_cfg.width = w;
1035 p_event->wnd_cfg.height = h;
1036 vlc_mutex_unlock( &p_event->lock );
1039 void EventThreadUpdateSourceAndPlace( event_thread_t *p_event,
1040 const video_format_t *p_source,
1041 const vout_display_place_t *p_place )
1043 vlc_mutex_lock( &p_event->lock );
1044 p_event->source = *p_source;
1045 p_event->place = *p_place;
1046 vlc_mutex_unlock( &p_event->lock );
1049 void EventThreadUseOverlay( event_thread_t *p_event, bool b_used )
1051 vlc_mutex_lock( &p_event->lock );
1052 p_event->use_overlay = b_used;
1053 vlc_mutex_unlock( &p_event->lock );
1055 bool EventThreadGetAndResetHasMoved( event_thread_t *p_event )
1057 vlc_mutex_lock( &p_event->lock );
1058 const bool has_moved = p_event->has_moved;
1059 p_event->has_moved = false;
1060 vlc_mutex_unlock( &p_event->lock );
1065 event_thread_t *EventThreadCreate( vout_display_t *vd)
1067 /* Create the Vout EventThread, this thread is created by us to isolate
1068 * the Win32 PeekMessage function calls. We want to do this because
1069 * Windows can stay blocked inside this call for a long time, and when
1070 * this happens it thus blocks vlc's video_output thread.
1071 * Vout EventThread will take care of the creation of the video
1072 * window (because PeekMessage has to be called from the same thread which
1073 * created the window). */
1074 msg_Dbg( vd, "creating Vout EventThread" );
1075 event_thread_t *p_event = malloc( sizeof(*p_event) );
1080 vlc_mutex_init( &p_event->lock );
1081 vlc_cond_init( &p_event->wait );
1083 p_event->is_cursor_hidden = false;
1084 p_event->button_pressed = 0;
1085 p_event->psz_title = NULL;
1086 p_event->source = vd->source;
1087 vout_display_PlacePicture(&p_event->place, &vd->source, vd->cfg, false);
1089 _snprintf( p_event->class_main, sizeof(p_event->class_main)/sizeof(*p_event->class_main),
1090 _T("VLC MSW %p"), p_event );
1091 _snprintf( p_event->class_video, sizeof(p_event->class_video)/sizeof(*p_event->class_video),
1092 _T("VLC MSW video %p"), p_event );
1096 void EventThreadDestroy( event_thread_t *p_event )
1098 free( p_event->psz_title );
1099 vlc_cond_destroy( &p_event->wait );
1100 vlc_mutex_destroy( &p_event->lock );
1104 int EventThreadStart( event_thread_t *p_event, event_hwnd_t *p_hwnd, const event_cfg_t *p_cfg )
1106 p_event->use_desktop = p_cfg->use_desktop;
1107 p_event->use_overlay = p_cfg->use_overlay;
1108 p_event->wnd_cfg = p_cfg->win;
1110 p_event->has_moved = false;
1112 p_event->b_ready = false;
1113 p_event->b_done = false;
1114 p_event->b_error = false;
1116 if( vlc_clone( &p_event->thread, EventThread, p_event,
1117 VLC_THREAD_PRIORITY_LOW ) )
1119 msg_Err( p_event->vd, "cannot create Vout EventThread" );
1120 return VLC_EGENERIC;
1123 vlc_mutex_lock( &p_event->lock );
1124 while( !p_event->b_ready )
1125 vlc_cond_wait( &p_event->wait, &p_event->lock );
1126 const bool b_error = p_event->b_error;
1127 vlc_mutex_unlock( &p_event->lock );
1131 vlc_join( p_event->thread, NULL );
1132 p_event->b_ready = false;
1133 return VLC_EGENERIC;
1135 msg_Dbg( p_event->vd, "Vout EventThread running" );
1138 p_hwnd->parent_window = p_event->parent_window;
1139 p_hwnd->hparent = p_event->hparent;
1140 p_hwnd->hwnd = p_event->hwnd;
1141 p_hwnd->hvideownd = p_event->hvideownd;
1142 p_hwnd->hfswnd = p_event->hfswnd;
1146 void EventThreadStop( event_thread_t *p_event )
1148 if( !p_event->b_ready )
1151 vlc_mutex_lock( &p_event->lock );
1152 p_event->b_done = true;
1153 vlc_mutex_unlock( &p_event->lock );
1155 /* we need to be sure Vout EventThread won't stay stuck in
1156 * GetMessage, so we send a fake message */
1158 PostMessage( p_event->hwnd, WM_NULL, 0, 0);
1160 vlc_join( p_event->thread, NULL );
1161 p_event->b_ready = false;