1 /*****************************************************************************
2 * events.c: Windows video output events handler
3 *****************************************************************************
4 * Copyright (C) 2001-2009 VLC authors and VideoLAN
7 * Authors: Gildas Bazin <gbazin@videolan.org>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
25 * Preamble: This file contains the functions related to the creation of
26 * a window and the handling of its messages (events).
27 *****************************************************************************/
33 #include <vlc_common.h>
34 #include <vlc_vout_display.h>
37 #include <windowsx.h> /* GET_X_LPARAM */
38 #include <shellapi.h> /* ExtractIcon */
42 /*****************************************************************************
44 *****************************************************************************/
45 #define WM_VLC_HIDE_MOUSE (WM_APP + 0)
46 #define WM_VLC_CHANGE_TEXT (WM_APP + 1)
65 bool is_cursor_hidden;
68 unsigned button_pressed;
74 vout_window_cfg_t wnd_cfg;
77 vout_window_t *parent_window;
78 TCHAR class_main[256];
79 TCHAR class_video[256];
84 video_format_t source;
85 vout_display_place_t place;
92 /***************************
94 ***************************/
96 static int Win32VoutCreateWindow( event_thread_t * );
97 static void Win32VoutCloseWindow ( event_thread_t * );
98 static long FAR PASCAL WinVoutEventProc( HWND, UINT, WPARAM, LPARAM );
99 static int Win32VoutConvertKey( int i_key );
101 /* Display/Hide Cursor */
102 static void UpdateCursor( event_thread_t *p_event, bool b_show );
103 static HCURSOR EmptyCursor( HINSTANCE instance );
105 /* Mouse events sending functions */
106 static void MouseReleased( event_thread_t *p_event, unsigned button );
107 static void MousePressed( event_thread_t *p_event, HWND hwnd, unsigned button );
110 static inline bool isMouseEvent( WPARAM type )
112 return type >= WM_MOUSEFIRST &&
113 type <= WM_MOUSELAST;
116 static inline bool isKeyEvent( WPARAM type )
118 return type >= WM_KEYFIRST &&
121 /*****************************************************************************
122 * EventThread: Create video window & handle its messages
123 *****************************************************************************
124 * This function creates a video window and then enters an infinite loop
125 * that handles the messages sent to that window.
126 * The main goal of this thread is to isolate the Win32 PeekMessage function
127 * because this one can block for a long time.
128 *****************************************************************************/
129 static void *EventThread( void *p_this )
131 event_thread_t *p_event = (event_thread_t *)p_this;
132 vout_display_t *vd = p_event->vd;
134 POINT old_mouse_pos = {0,0}, mouse_pos;
135 int canc = vlc_savecancel ();
137 bool b_mouse_support = var_InheritBool( p_event->vd, "mouse-events" );
138 bool b_key_support = var_InheritBool( p_event->vd, "keyboard-events" );
140 vlc_mutex_lock( &p_event->lock );
141 /* Create a window for the video */
142 /* Creating a window under Windows also initializes the thread's event
144 if( Win32VoutCreateWindow( p_event ) )
145 p_event->b_error = true;
147 p_event->b_ready = true;
148 vlc_cond_signal( &p_event->wait );
150 const bool b_error = p_event->b_error;
151 vlc_mutex_unlock( &p_event->lock );
155 vlc_restorecancel( canc );
159 /* Prevent monitor from powering off */
160 if (var_GetBool(vd, "disable-screensaver"))
161 SetThreadExecutionState( ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED | ES_CONTINUOUS );
164 /* GetMessage will sleep if there's no message in the queue */
167 vout_display_place_t place;
168 video_format_t source;
170 if( !GetMessage( &msg, 0, 0, 0 ) )
172 vlc_mutex_lock( &p_event->lock );
173 p_event->b_done = true;
174 vlc_mutex_unlock( &p_event->lock );
178 /* Check if we are asked to exit */
179 vlc_mutex_lock( &p_event->lock );
180 const bool b_done = p_event->b_done;
181 vlc_mutex_unlock( &p_event->lock );
185 if( !b_mouse_support && isMouseEvent( msg.message ) )
188 if( !b_key_support && isKeyEvent( msg.message ) )
191 /* Handle mouse state */
192 if( msg.message == WM_MOUSEMOVE ||
193 msg.message == WM_NCMOUSEMOVE )
195 GetCursorPos( &mouse_pos );
196 /* FIXME, why this >2 limits ? */
197 if( (abs(mouse_pos.x - old_mouse_pos.x) > 2 ||
198 (abs(mouse_pos.y - old_mouse_pos.y)) > 2 ) )
200 old_mouse_pos = mouse_pos;
201 UpdateCursor( p_event, true );
204 else if( isMouseEvent( msg.message ) )
206 UpdateCursor( p_event, true );
208 else if( msg.message == WM_VLC_HIDE_MOUSE )
210 UpdateCursor( p_event, false );
214 switch( msg.message )
217 vlc_mutex_lock( &p_event->lock );
218 place = p_event->place;
219 source = p_event->source;
220 vlc_mutex_unlock( &p_event->lock );
222 if( place.width > 0 && place.height > 0 )
224 if( msg.hwnd == p_event->hvideownd )
230 const int x = source.i_x_offset +
231 (int64_t)(GET_X_LPARAM(msg.lParam) - place.x) * source.i_width / place.width;
232 const int y = source.i_y_offset +
233 (int64_t)(GET_Y_LPARAM(msg.lParam) - place.y) * source.i_height / place.height;
234 vout_display_SendEventMouseMoved(vd, x, y);
240 case WM_VLC_HIDE_MOUSE:
244 MousePressed( p_event, msg.hwnd, MOUSE_BUTTON_LEFT );
247 MouseReleased( p_event, MOUSE_BUTTON_LEFT );
249 case WM_LBUTTONDBLCLK:
250 vout_display_SendEventMouseDoubleClick(vd);
254 MousePressed( p_event, msg.hwnd, MOUSE_BUTTON_CENTER );
257 MouseReleased( p_event, MOUSE_BUTTON_CENTER );
261 MousePressed( p_event, msg.hwnd, MOUSE_BUTTON_RIGHT );
264 MouseReleased( p_event, MOUSE_BUTTON_RIGHT );
270 /* The key events are first processed here and not translated
271 * into WM_CHAR events because we need to know the status of the
273 int i_key = Win32VoutConvertKey( msg.wParam );
276 /* This appears to be a "normal" (ascii) key */
277 i_key = tolower( (unsigned char)MapVirtualKey( msg.wParam, 2 ) );
282 if( GetKeyState(VK_CONTROL) & 0x8000 )
284 i_key |= KEY_MODIFIER_CTRL;
286 if( GetKeyState(VK_SHIFT) & 0x8000 )
288 i_key |= KEY_MODIFIER_SHIFT;
290 if( GetKeyState(VK_MENU) & 0x8000 )
292 i_key |= KEY_MODIFIER_ALT;
295 vout_display_SendEventKey(vd, i_key);
303 if( GET_WHEEL_DELTA_WPARAM( msg.wParam ) > 0 )
305 i_key = KEY_MOUSEWHEELUP;
309 i_key = KEY_MOUSEWHEELDOWN;
313 if( GetKeyState(VK_CONTROL) & 0x8000 )
315 i_key |= KEY_MODIFIER_CTRL;
317 if( GetKeyState(VK_SHIFT) & 0x8000 )
319 i_key |= KEY_MODIFIER_SHIFT;
321 if( GetKeyState(VK_MENU) & 0x8000 )
323 i_key |= KEY_MODIFIER_ALT;
325 vout_display_SendEventKey(vd, i_key);
330 case WM_VLC_CHANGE_TEXT:
332 vlc_mutex_lock( &p_event->lock );
333 wchar_t *pwz_title = NULL;
334 if( p_event->psz_title )
336 const size_t i_length = strlen(p_event->psz_title);
337 pwz_title = malloc( 2 * (i_length + 1) );
340 mbstowcs( pwz_title, p_event->psz_title, 2 * i_length );
341 pwz_title[i_length] = 0;
344 vlc_mutex_unlock( &p_event->lock );
348 SetWindowTextW( p_event->hwnd, pwz_title );
349 if( p_event->hfswnd )
350 SetWindowTextW( p_event->hfswnd, pwz_title );
357 /* Messages we don't handle directly are dispatched to the
358 * window procedure */
359 TranslateMessage(&msg);
360 DispatchMessage(&msg);
365 } /* End Main loop */
367 /* Check for WM_QUIT if we created the window */
368 if( !p_event->hparent && msg.message == WM_QUIT )
370 msg_Warn( vd, "WM_QUIT... should not happen!!" );
371 p_event->hwnd = NULL; /* Window already destroyed */
374 msg_Dbg( vd, "Win32 Vout EventThread terminating" );
376 Win32VoutCloseWindow( p_event );
377 vlc_restorecancel(canc);
381 void EventThreadMouseHide( event_thread_t *p_event )
383 PostMessage( p_event->hwnd, WM_VLC_HIDE_MOUSE, 0, 0 );
386 void EventThreadUpdateTitle( event_thread_t *p_event, const char *psz_fallback )
388 char *psz_title = var_InheritString( p_event->vd, "video-title" );
390 psz_title = strdup( psz_fallback );
394 vlc_mutex_lock( &p_event->lock );
395 free( p_event->psz_title );
396 p_event->psz_title = psz_title;
397 vlc_mutex_unlock( &p_event->lock );
399 PostMessage( p_event->hwnd, WM_VLC_CHANGE_TEXT, 0, 0 );
401 int EventThreadGetWindowStyle( event_thread_t *p_event )
403 /* No need to lock, it is serialized by EventThreadStart */
404 return p_event->i_window_style;
407 void EventThreadUpdateWindowPosition( event_thread_t *p_event,
408 bool *pb_moved, bool *pb_resized,
409 int x, int y, unsigned w, unsigned h )
411 vlc_mutex_lock( &p_event->lock );
412 *pb_moved = x != p_event->wnd_cfg.x ||
413 y != p_event->wnd_cfg.y;
414 *pb_resized = w != p_event->wnd_cfg.width ||
415 h != p_event->wnd_cfg.height;
417 p_event->wnd_cfg.x = x;
418 p_event->wnd_cfg.y = y;
419 p_event->wnd_cfg.width = w;
420 p_event->wnd_cfg.height = h;
421 vlc_mutex_unlock( &p_event->lock );
424 void EventThreadUpdateSourceAndPlace( event_thread_t *p_event,
425 const video_format_t *p_source,
426 const vout_display_place_t *p_place )
428 vlc_mutex_lock( &p_event->lock );
429 p_event->source = *p_source;
430 p_event->place = *p_place;
431 vlc_mutex_unlock( &p_event->lock );
434 void EventThreadUseOverlay( event_thread_t *p_event, bool b_used )
436 vlc_mutex_lock( &p_event->lock );
437 p_event->use_overlay = b_used;
438 vlc_mutex_unlock( &p_event->lock );
440 bool EventThreadGetAndResetHasMoved( event_thread_t *p_event )
442 vlc_mutex_lock( &p_event->lock );
443 const bool has_moved = p_event->has_moved;
444 p_event->has_moved = false;
445 vlc_mutex_unlock( &p_event->lock );
450 event_thread_t *EventThreadCreate( vout_display_t *vd)
452 /* Create the Vout EventThread, this thread is created by us to isolate
453 * the Win32 PeekMessage function calls. We want to do this because
454 * Windows can stay blocked inside this call for a long time, and when
455 * this happens it thus blocks vlc's video_output thread.
456 * Vout EventThread will take care of the creation of the video
457 * window (because PeekMessage has to be called from the same thread which
458 * created the window). */
459 msg_Dbg( vd, "creating Vout EventThread" );
460 event_thread_t *p_event = malloc( sizeof(*p_event) );
465 vlc_mutex_init( &p_event->lock );
466 vlc_cond_init( &p_event->wait );
468 p_event->is_cursor_hidden = false;
469 p_event->button_pressed = 0;
470 p_event->psz_title = NULL;
471 p_event->source = vd->source;
472 vout_display_PlacePicture(&p_event->place, &vd->source, vd->cfg, false);
474 _sntprintf( p_event->class_main, sizeof(p_event->class_main)/sizeof(*p_event->class_main),
475 _T("VLC video main %p"), p_event );
476 _sntprintf( p_event->class_video, sizeof(p_event->class_video)/sizeof(*p_event->class_video),
477 _T("VLC video output %p"), p_event );
481 void EventThreadDestroy( event_thread_t *p_event )
483 free( p_event->psz_title );
484 vlc_cond_destroy( &p_event->wait );
485 vlc_mutex_destroy( &p_event->lock );
489 int EventThreadStart( event_thread_t *p_event, event_hwnd_t *p_hwnd, const event_cfg_t *p_cfg )
491 p_event->use_desktop = p_cfg->use_desktop;
492 p_event->use_overlay = p_cfg->use_overlay;
493 p_event->wnd_cfg = p_cfg->win;
495 p_event->has_moved = false;
497 p_event->b_ready = false;
498 p_event->b_done = false;
499 p_event->b_error = false;
501 if( vlc_clone( &p_event->thread, EventThread, p_event,
502 VLC_THREAD_PRIORITY_LOW ) )
504 msg_Err( p_event->vd, "cannot create Vout EventThread" );
508 vlc_mutex_lock( &p_event->lock );
509 while( !p_event->b_ready )
510 vlc_cond_wait( &p_event->wait, &p_event->lock );
511 const bool b_error = p_event->b_error;
512 vlc_mutex_unlock( &p_event->lock );
516 vlc_join( p_event->thread, NULL );
517 p_event->b_ready = false;
520 msg_Dbg( p_event->vd, "Vout EventThread running" );
523 p_hwnd->parent_window = p_event->parent_window;
524 p_hwnd->hparent = p_event->hparent;
525 p_hwnd->hwnd = p_event->hwnd;
526 p_hwnd->hvideownd = p_event->hvideownd;
527 p_hwnd->hfswnd = p_event->hfswnd;
531 void EventThreadStop( event_thread_t *p_event )
533 if( !p_event->b_ready )
536 vlc_mutex_lock( &p_event->lock );
537 p_event->b_done = true;
538 vlc_mutex_unlock( &p_event->lock );
540 /* we need to be sure Vout EventThread won't stay stuck in
541 * GetMessage, so we send a fake message */
543 PostMessage( p_event->hwnd, WM_NULL, 0, 0);
545 vlc_join( p_event->thread, NULL );
546 p_event->b_ready = false;
550 /***********************************
551 * Local functions implementations *
552 ***********************************/
553 static void UpdateCursor( event_thread_t *p_event, bool b_show )
555 if( p_event->is_cursor_hidden == !b_show )
557 p_event->is_cursor_hidden = !b_show;
560 HCURSOR cursor = b_show ? p_event->cursor_arrow : p_event->cursor_empty;
561 if( p_event->hvideownd )
562 SetClassLongPtr( p_event->hvideownd, GCLP_HCURSOR, (LONG_PTR)cursor );
564 SetClassLongPtr( p_event->hwnd, GCLP_HCURSOR, (LONG_PTR)cursor );
567 /* FIXME I failed to find a cleaner way to force a redraw of the cursor */
570 HWND hwnd = WindowFromPoint(p);
571 if( hwnd == p_event->hvideownd || hwnd == p_event->hwnd )
576 SetCursorPos( p.x, p.y );
580 static HCURSOR EmptyCursor( HINSTANCE instance )
582 const int cw = GetSystemMetrics(SM_CXCURSOR);
583 const int ch = GetSystemMetrics(SM_CYCURSOR);
585 HCURSOR cursor = NULL;
586 uint8_t *and = malloc(cw * ch);
587 uint8_t *xor = malloc(cw * ch);
590 memset(and, 0xff, cw * ch );
591 memset(xor, 0x00, cw * ch );
592 cursor = CreateCursor( instance, 0, 0, cw, ch, and, xor);
600 static void MousePressed( event_thread_t *p_event, HWND hwnd, unsigned button )
602 if( !p_event->button_pressed )
604 p_event->button_pressed |= 1 << button;
605 vout_display_SendEventMousePressed( p_event->vd, button );
608 static void MouseReleased( event_thread_t *p_event, unsigned button )
610 p_event->button_pressed &= ~(1 << button);
611 if( !p_event->button_pressed )
613 vout_display_SendEventMouseReleased( p_event->vd, button );
616 #ifdef MODULE_NAME_IS_direct3d
618 enumWindowsProc(HWND hwnd, LPARAM lParam)
620 HWND *wnd = (HWND *)lParam;
624 GetClassNameA( hwnd, name, 128 );
626 if( !strcasecmp( name, "WorkerW" ) )
628 hwnd = FindWindowEx( hwnd, NULL, _T("SHELLDLL_DefView"), NULL );
629 if( hwnd ) hwnd = FindWindowEx( hwnd, NULL, _T("SysListView32"), NULL );
639 static HWND GetDesktopHandle(vout_display_t *vd)
641 /* Find Program Manager */
642 HWND hwnd = FindWindow( _T("Progman"), NULL );
643 if( hwnd ) hwnd = FindWindowEx( hwnd, NULL, _T("SHELLDLL_DefView"), NULL );
644 if( hwnd ) hwnd = FindWindowEx( hwnd, NULL, _T("SysListView32"), NULL );
648 msg_Dbg( vd, "Couldn't find desktop icon window,. Trying the hard way." );
650 EnumWindows( enumWindowsProc, (LPARAM)&hwnd );
655 /*****************************************************************************
656 * Win32VoutCreateWindow: create a window for the video.
657 *****************************************************************************
658 * Before creating a direct draw surface, we need to create a window in which
659 * the video will be displayed. This window will also allow us to capture the
661 *****************************************************************************/
662 static int Win32VoutCreateWindow( event_thread_t *p_event )
664 vout_display_t *vd = p_event->vd;
668 WNDCLASS wc; /* window class components */
669 TCHAR vlc_path[MAX_PATH+1];
670 int i_style, i_stylex;
672 msg_Dbg( vd, "Win32VoutCreateWindow" );
674 /* Get this module's instance */
675 hInstance = GetModuleHandle(NULL);
677 #ifdef MODULE_NAME_IS_direct3d
678 if( !p_event->use_desktop )
681 /* If an external window was specified, we'll draw in it. */
682 p_event->parent_window = vout_display_NewWindow(vd, &p_event->wnd_cfg );
683 if( p_event->parent_window )
684 p_event->hparent = p_event->parent_window->handle.hwnd;
686 p_event->hparent = NULL;
687 #ifdef MODULE_NAME_IS_direct3d
691 p_event->parent_window = NULL;
692 p_event->hparent = GetDesktopHandle(vd);
695 p_event->cursor_arrow = LoadCursor(NULL, IDC_ARROW);
696 p_event->cursor_empty = EmptyCursor(hInstance);
698 /* Get the Icon from the main app */
699 p_event->vlc_icon = NULL;
700 if( GetModuleFileName( NULL, vlc_path, MAX_PATH ) )
702 p_event->vlc_icon = ExtractIcon( hInstance, vlc_path, 0 );
705 /* Fill in the window class structure */
706 wc.style = CS_OWNDC|CS_DBLCLKS; /* style: dbl click */
707 wc.lpfnWndProc = (WNDPROC)WinVoutEventProc; /* event handler */
708 wc.cbClsExtra = 0; /* no extra class data */
709 wc.cbWndExtra = 0; /* no extra window data */
710 wc.hInstance = hInstance; /* instance */
711 wc.hIcon = p_event->vlc_icon; /* load the vlc big icon */
712 wc.hCursor = p_event->is_cursor_hidden ? p_event->cursor_empty :
713 p_event->cursor_arrow;
714 wc.hbrBackground = GetStockObject(BLACK_BRUSH); /* background color */
715 wc.lpszMenuName = NULL; /* no menu */
716 wc.lpszClassName = p_event->class_main; /* use a special class */
718 /* Register the window class */
719 if( !RegisterClass(&wc) )
721 if( p_event->vlc_icon )
722 DestroyIcon( p_event->vlc_icon );
724 msg_Err( vd, "Win32VoutCreateWindow RegisterClass FAILED (err=%lu)", GetLastError() );
728 /* Register the video sub-window class */
729 wc.lpszClassName = p_event->class_video;
731 wc.hbrBackground = NULL; /* no background color */
732 if( !RegisterClass(&wc) )
734 msg_Err( vd, "Win32VoutCreateWindow RegisterClass FAILED (err=%lu)", GetLastError() );
738 /* When you create a window you give the dimensions you wish it to
739 * have. Unfortunatly these dimensions will include the borders and
740 * titlebar. We use the following function to find out the size of
741 * the window corresponding to the useable surface we want */
742 rect_window.left = 10;
743 rect_window.top = 10;
744 rect_window.right = rect_window.left + p_event->wnd_cfg.width;
745 rect_window.bottom = rect_window.top + p_event->wnd_cfg.height;
747 if( var_GetBool( vd, "video-deco" ) )
749 /* Open with window decoration */
750 AdjustWindowRect( &rect_window, WS_OVERLAPPEDWINDOW|WS_SIZEBOX, 0 );
751 i_style = WS_OVERLAPPEDWINDOW|WS_SIZEBOX|WS_VISIBLE|WS_CLIPCHILDREN;
756 /* No window decoration */
757 AdjustWindowRect( &rect_window, WS_POPUP, 0 );
758 i_style = WS_POPUP|WS_VISIBLE|WS_CLIPCHILDREN;
759 i_stylex = 0; // WS_EX_TOOLWINDOW; Is TOOLWINDOW really needed ?
760 // It messes up the fullscreen window.
763 if( p_event->hparent )
765 i_style = WS_VISIBLE|WS_CLIPCHILDREN|WS_CHILD;
768 /* allow user to regain control over input events if requested */
769 bool b_mouse_support = var_InheritBool( vd, "mouse-events" );
770 bool b_key_support = var_InheritBool( vd, "keyboard-events" );
771 if( !b_mouse_support && !b_key_support )
772 i_style |= WS_DISABLED;
775 p_event->i_window_style = i_style;
777 /* Create the window */
779 CreateWindowEx( WS_EX_NOPARENTNOTIFY | i_stylex,
780 p_event->class_main, /* name of window class */
781 _T(VOUT_TITLE) _T(" (VLC Video Output)"), /* window title */
782 i_style, /* window style */
783 (!p_event->wnd_cfg.x) ? (UINT)CW_USEDEFAULT :
784 (UINT)p_event->wnd_cfg.x, /* default X coordinate */
785 (!p_event->wnd_cfg.y) ? (UINT)CW_USEDEFAULT :
786 (UINT)p_event->wnd_cfg.y, /* default Y coordinate */
787 rect_window.right - rect_window.left, /* window width */
788 rect_window.bottom - rect_window.top, /* window height */
789 p_event->hparent, /* parent window */
790 NULL, /* no menu in this window */
791 hInstance, /* handle of this program instance */
792 (LPVOID)p_event ); /* send vd to WM_CREATE */
796 msg_Warn( vd, "Win32VoutCreateWindow create window FAILED (err=%lu)", GetLastError() );
800 if( p_event->hparent )
804 /* We don't want the window owner to overwrite our client area */
805 i_style = GetWindowLong( p_event->hparent, GWL_STYLE );
807 if( !(i_style & WS_CLIPCHILDREN) )
808 /* Hmmm, apparently this is a blocking call... */
809 SetWindowLong( p_event->hparent, GWL_STYLE,
810 i_style | WS_CLIPCHILDREN );
812 /* Create our fullscreen window */
814 CreateWindowEx( WS_EX_APPWINDOW, p_event->class_main,
815 _T(VOUT_TITLE) _T(" (VLC Fullscreen Video Output)"),
816 WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN|WS_SIZEBOX,
817 CW_USEDEFAULT, CW_USEDEFAULT,
818 CW_USEDEFAULT, CW_USEDEFAULT,
819 NULL, NULL, hInstance, NULL );
823 p_event->hfswnd = NULL;
826 /* Append a "Always On Top" entry in the system menu */
827 hMenu = GetSystemMenu( p_event->hwnd, FALSE );
828 AppendMenu( hMenu, MF_SEPARATOR, 0, _T("") );
829 AppendMenu( hMenu, MF_STRING | MF_UNCHECKED,
830 IDM_TOGGLE_ON_TOP, _T("Always on &Top") );
832 /* Create video sub-window. This sub window will always exactly match
833 * the size of the video, which allows us to use crazy overlay colorkeys
834 * without having them shown outside of the video area. */
835 /* FIXME vd->source.i_width/i_height seems wrong */
837 CreateWindow( p_event->class_video, _T(""), /* window class */
838 WS_CHILD, /* window style, not visible initially */
840 vd->source.i_width, /* default width */
841 vd->source.i_height, /* default height */
842 p_event->hwnd, /* parent window */
844 (LPVOID)p_event ); /* send vd to WM_CREATE */
846 if( !p_event->hvideownd )
847 msg_Warn( vd, "can't create video sub-window" );
849 msg_Dbg( vd, "created video sub-window" );
851 /* Now display the window */
852 ShowWindow( p_event->hwnd, SW_SHOW );
857 /*****************************************************************************
858 * Win32VoutCloseWindow: close the window created by Win32VoutCreateWindow
859 *****************************************************************************
860 * This function returns all resources allocated by Win32VoutCreateWindow.
861 *****************************************************************************/
862 static void Win32VoutCloseWindow( event_thread_t *p_event )
864 vout_display_t *vd = p_event->vd;
865 msg_Dbg( vd, "Win32VoutCloseWindow" );
867 DestroyWindow( p_event->hwnd );
868 if( p_event->hfswnd )
869 DestroyWindow( p_event->hfswnd );
871 #ifdef MODULE_NAME_IS_direct3d
872 if( !p_event->use_desktop )
874 vout_display_DeleteWindow( vd, p_event->parent_window );
875 p_event->hwnd = NULL;
877 HINSTANCE hInstance = GetModuleHandle(NULL);
878 UnregisterClass( p_event->class_video, hInstance );
879 UnregisterClass( p_event->class_main, hInstance );
881 if( p_event->vlc_icon )
882 DestroyIcon( p_event->vlc_icon );
884 DestroyCursor( p_event->cursor_empty );
887 /*****************************************************************************
888 * WinVoutEventProc: This is the window event processing function.
889 *****************************************************************************
890 * On Windows, when you create a window you have to attach an event processing
891 * function to it. The aim of this function is to manage "Queued Messages" and
892 * "Nonqueued Messages".
893 * Queued Messages are those picked up and retransmitted by vout_Manage
894 * (using the GetMessage and DispatchMessage functions).
895 * Nonqueued Messages are those that Windows will send directly to this
896 * procedure (like WM_DESTROY, WM_WINDOWPOSCHANGED...)
897 *****************************************************************************/
898 static long FAR PASCAL WinVoutEventProc( HWND hwnd, UINT message,
899 WPARAM wParam, LPARAM lParam )
901 event_thread_t *p_event;
903 if( message == WM_CREATE )
905 /* Store vd for future use */
906 p_event = (event_thread_t *)((CREATESTRUCT *)lParam)->lpCreateParams;
907 SetWindowLongPtr( hwnd, GWLP_USERDATA, (LONG_PTR)p_event );
912 LONG_PTR p_user_data = GetWindowLongPtr( hwnd, GWLP_USERDATA );
913 p_event = (event_thread_t *)p_user_data;
916 /* Hmmm mozilla does manage somehow to save the pointer to our
917 * windowproc and still calls it after the vout has been closed. */
918 return DefWindowProc(hwnd, message, wParam, lParam);
921 vout_display_t *vd = p_event->vd;
924 if( message == WM_SETCURSOR )
926 msg_Err(vd, "WM_SETCURSOR: %d (t2)", p_event->is_cursor_hidden);
927 SetCursor( p_event->is_cursor_hidden ? p_event->cursor_empty : p_event->cursor_arrow );
931 if( message == WM_CAPTURECHANGED )
933 for( int button = 0; p_event->button_pressed; button++ )
935 unsigned m = 1 << button;
936 if( p_event->button_pressed & m )
937 vout_display_SendEventMouseReleased( p_event->vd, button );
938 p_event->button_pressed &= ~m;
940 p_event->button_pressed = 0;
944 if( hwnd == p_event->hvideownd )
946 #ifdef MODULE_NAME_IS_directdraw
947 vlc_mutex_lock( &p_event->lock );
948 const bool use_overlay = p_event->use_overlay;
949 vlc_mutex_unlock( &p_event->lock );
954 #ifdef MODULE_NAME_IS_directdraw
956 /* For overlay, we need to erase background */
957 return !use_overlay ? 1 : DefWindowProc(hwnd, message, wParam, lParam);
960 ** For overlay, DefWindowProc() will erase dirty regions
962 ** For non-overlay, vout will paint the whole window at
963 ** regular interval, therefore dirty regions can be ignored
964 ** to minimize repaint.
968 ValidateRect(hwnd, NULL);
970 // fall through to default
973 ** For OpenGL and Direct3D, vout will update the whole
974 ** window at regular interval, therefore dirty region
975 ** can be ignored to minimize repaint.
978 /* nothing to erase */
981 /* nothing to repaint */
982 ValidateRect(hwnd, NULL);
986 return DefWindowProc(hwnd, message, wParam, lParam);
993 case WM_WINDOWPOSCHANGED:
994 vlc_mutex_lock( &p_event->lock );
995 p_event->has_moved = true;
996 vlc_mutex_unlock( &p_event->lock );
999 /* the user wants to close the window */
1001 vout_display_SendEventClose(vd);
1004 /* the window has been closed so shut down everything now */
1006 msg_Dbg( vd, "WinProc WM_DESTROY" );
1007 /* just destroy the window */
1008 PostQuitMessage( 0 );
1014 case IDM_TOGGLE_ON_TOP: /* toggle the "on top" status */
1016 msg_Dbg(vd, "WinProc WM_SYSCOMMAND: IDM_TOGGLE_ON_TOP");
1017 HMENU hMenu = GetSystemMenu(vd->sys->hwnd, FALSE);
1018 vout_display_SendWindowState(vd, (GetMenuState(hMenu, IDM_TOGGLE_ON_TOP, MF_BYCOMMAND) & MF_CHECKED) ?
1019 VOUT_WINDOW_STATE_NORMAL : VOUT_WINDOW_STATE_ABOVE);
1030 return DefWindowProc(hwnd, message, wParam, lParam);
1039 //msg_Dbg( vd, "WinProc WM Default %i", message );
1043 /* Let windows handle the message */
1044 return DefWindowProc(hwnd, message, wParam, lParam);
1052 } dxkeys_to_vlckeys[] =
1054 { VK_F1, KEY_F1 }, { VK_F2, KEY_F2 }, { VK_F3, KEY_F3 }, { VK_F4, KEY_F4 },
1055 { VK_F5, KEY_F5 }, { VK_F6, KEY_F6 }, { VK_F7, KEY_F7 }, { VK_F8, KEY_F8 },
1056 { VK_F9, KEY_F9 }, { VK_F10, KEY_F10 }, { VK_F11, KEY_F11 },
1057 { VK_F12, KEY_F12 },
1059 { VK_RETURN, KEY_ENTER },
1061 { VK_ESCAPE, KEY_ESC },
1063 { VK_LEFT, KEY_LEFT },
1064 { VK_RIGHT, KEY_RIGHT },
1066 { VK_DOWN, KEY_DOWN },
1068 { VK_HOME, KEY_HOME },
1069 { VK_END, KEY_END },
1070 { VK_PRIOR, KEY_PAGEUP },
1071 { VK_NEXT, KEY_PAGEDOWN },
1073 { VK_INSERT, KEY_INSERT },
1074 { VK_DELETE, KEY_DELETE },
1080 { VK_BROWSER_BACK, KEY_BROWSER_BACK },
1081 { VK_BROWSER_FORWARD, KEY_BROWSER_FORWARD },
1082 { VK_BROWSER_REFRESH, KEY_BROWSER_REFRESH },
1083 { VK_BROWSER_STOP, KEY_BROWSER_STOP },
1084 { VK_BROWSER_SEARCH, KEY_BROWSER_SEARCH },
1085 { VK_BROWSER_FAVORITES, KEY_BROWSER_FAVORITES },
1086 { VK_BROWSER_HOME, KEY_BROWSER_HOME },
1087 { VK_VOLUME_MUTE, KEY_VOLUME_MUTE },
1088 { VK_VOLUME_DOWN, KEY_VOLUME_DOWN },
1089 { VK_VOLUME_UP, KEY_VOLUME_UP },
1090 { VK_MEDIA_NEXT_TRACK, KEY_MEDIA_NEXT_TRACK },
1091 { VK_MEDIA_PREV_TRACK, KEY_MEDIA_PREV_TRACK },
1092 { VK_MEDIA_STOP, KEY_MEDIA_STOP },
1093 { VK_MEDIA_PLAY_PAUSE, KEY_MEDIA_PLAY_PAUSE },
1098 static int Win32VoutConvertKey( int i_key )
1100 for( int i = 0; dxkeys_to_vlckeys[i].i_dxkey != 0; i++ )
1102 if( dxkeys_to_vlckeys[i].i_dxkey == i_key )
1104 return dxkeys_to_vlckeys[i].i_vlckey;