1 /*****************************************************************************
2  * events.c: Windows video output events handler
3  *****************************************************************************
4  * Copyright (C) 2001-2009 VLC authors and VideoLAN
5  * $Id: cea69d95e8dfece2651ac1dfb31bec48e8f5f537 $
6  *
7  * Authors: Gildas Bazin <gbazin@videolan.org>
8  *          Martell Malone <martellmalone@gmail.com>
9  *
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU Lesser General Public License as published by
12  * the Free Software Foundation; either version 2.1 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this program; if not, write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24 
25 /*****************************************************************************
26  * Preamble: This file contains the functions related to the creation of
27  *             a window and the handling of its messages (events).
28  *****************************************************************************/
29 
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33 
34 #include "win32touch.h"
35 
36 #include <vlc_common.h>
37 #include <vlc_vout_display.h>
38 #include <vlc_atomic.h>
39 
40 #include <windows.h>
41 #include <windowsx.h>                                        /* GET_X_LPARAM */
42 #include <shellapi.h>                                         /* ExtractIcon */
43 
44 #define vout_display_sys_win32_t vout_display_sys_t
45 
46 #include "common.h"
47 
48 /*****************************************************************************
49  * Local prototypes.
50  *****************************************************************************/
51 #define WM_VLC_CHANGE_TEXT  (WM_APP + 1)
52 
53 struct event_thread_t
54 {
55     vout_display_t *vd;
56 
57     /* */
58     vlc_thread_t thread;
59     vlc_mutex_t  lock;
60     vlc_cond_t   wait;
61     bool         b_ready;
62     bool         b_done;
63     bool         b_error;
64 
65     /* */
66     bool use_desktop;
67     bool use_overlay;
68 
69     /* Mouse */
70     bool is_cursor_hidden;
71     HCURSOR cursor_arrow;
72     HCURSOR cursor_empty;
73     unsigned button_pressed;
74     mtime_t hide_timeout;
75     mtime_t last_moved;
76 
77     /* Gestures */
78     win32_gesture_sys_t *p_gesture;
79 
80     /* Sensors */
81     void *p_sensors;
82 
83     /* Title */
84     char *psz_title;
85 
86     int i_window_style;
87     int x, y;
88     unsigned width, height;
89 
90     /* */
91     vout_window_t *parent_window;
92     TCHAR class_main[256];
93     TCHAR class_video[256];
94     HWND hparent;
95     HWND hwnd;
96     HWND hvideownd;
97     HWND hfswnd;
98     video_format_t       source;
99     vout_display_place_t place;
100 
101     HICON vlc_icon;
102 
103     atomic_bool has_moved;
104 };
105 
106 /***************************
107  * Local Prototypes        *
108  ***************************/
109 /* Window Creation */
110 static int  Win32VoutCreateWindow( event_thread_t * );
111 static void Win32VoutCloseWindow ( event_thread_t * );
112 static long FAR PASCAL WinVoutEventProc( HWND, UINT, WPARAM, LPARAM );
113 static int  Win32VoutConvertKey( int i_key );
114 
115 /* Display/Hide Cursor */
116 static void UpdateCursor( event_thread_t *p_event, bool b_show );
117 static HCURSOR EmptyCursor( HINSTANCE instance );
118 
119 /* Mouse events sending functions */
120 static void MouseReleased( event_thread_t *p_event, unsigned button );
121 static void MousePressed( event_thread_t *p_event, HWND hwnd, unsigned button );
122 
HideMouse(HWND hwnd,UINT uMsg,UINT_PTR idEvent,DWORD dwTime)123 static void CALLBACK HideMouse(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
124 {
125     VLC_UNUSED(uMsg); VLC_UNUSED(dwTime);
126     if (hwnd)
127     {
128         event_thread_t *p_event = (event_thread_t *)idEvent;
129         UpdateCursor( p_event, false );
130     }
131 }
132 
UpdateCursorMoved(event_thread_t * p_event)133 static void UpdateCursorMoved( event_thread_t *p_event )
134 {
135     UpdateCursor( p_event, true );
136     p_event->last_moved = mdate();
137     if( p_event->hwnd )
138         SetTimer( p_event->hwnd, (UINT_PTR)p_event, p_event->hide_timeout, HideMouse );
139 }
140 
141 /* Local helpers */
isMouseEvent(WPARAM type)142 static inline bool isMouseEvent( WPARAM type )
143 {
144     return type >= WM_MOUSEFIRST &&
145            type <= WM_MOUSELAST;
146 }
147 
isKeyEvent(WPARAM type)148 static inline bool isKeyEvent( WPARAM type )
149 {
150     return type >= WM_KEYFIRST &&
151            type <= WM_KEYLAST;
152 }
153 /*****************************************************************************
154  * EventThread: Create video window & handle its messages
155  *****************************************************************************
156  * This function creates a video window and then enters an infinite loop
157  * that handles the messages sent to that window.
158  * The main goal of this thread is to isolate the Win32 PeekMessage function
159  * because this one can block for a long time.
160  *****************************************************************************/
EventThread(void * p_this)161 static void *EventThread( void *p_this )
162 {
163     event_thread_t *p_event = (event_thread_t *)p_this;
164     vout_display_t *vd = p_event->vd;
165     MSG msg;
166     POINT old_mouse_pos = {0,0}, mouse_pos;
167     int canc = vlc_savecancel ();
168 
169     bool b_mouse_support = var_InheritBool( p_event->vd, "mouse-events" );
170     bool b_key_support = var_InheritBool( p_event->vd, "keyboard-events" );
171 
172     vlc_mutex_lock( &p_event->lock );
173     /* Create a window for the video */
174     /* Creating a window under Windows also initializes the thread's event
175      * message queue */
176     if( Win32VoutCreateWindow( p_event ) )
177         p_event->b_error = true;
178 
179     p_event->b_ready = true;
180     vlc_cond_signal( &p_event->wait );
181 
182     const bool b_error = p_event->b_error;
183     vlc_mutex_unlock( &p_event->lock );
184 
185     if( b_error )
186     {
187         vlc_restorecancel( canc );
188         return NULL;
189     }
190 
191     /* Main loop */
192     /* GetMessage will sleep if there's no message in the queue */
193     for( ;; )
194     {
195         vout_display_place_t place;
196         video_format_t       source;
197 
198         if( !GetMessage( &msg, 0, 0, 0 ) )
199         {
200             vlc_mutex_lock( &p_event->lock );
201             p_event->b_done = true;
202             vlc_mutex_unlock( &p_event->lock );
203             break;
204         }
205 
206         /* Check if we are asked to exit */
207         vlc_mutex_lock( &p_event->lock );
208         const bool b_done = p_event->b_done;
209         vlc_mutex_unlock( &p_event->lock );
210         if( b_done )
211             break;
212 
213         if( !b_mouse_support && isMouseEvent( msg.message ) )
214             continue;
215 
216         if( !b_key_support && isKeyEvent( msg.message ) )
217             continue;
218 
219         /* Handle mouse state */
220         if( msg.message == WM_MOUSEMOVE ||
221             msg.message == WM_NCMOUSEMOVE )
222         {
223             GetCursorPos( &mouse_pos );
224             /* FIXME, why this >2 limits ? */
225             if( (abs(mouse_pos.x - old_mouse_pos.x) > 2 ||
226                 (abs(mouse_pos.y - old_mouse_pos.y)) > 2 ) )
227             {
228                 old_mouse_pos = mouse_pos;
229                 UpdateCursorMoved( p_event );
230             }
231         }
232         else if( isMouseEvent( msg.message ) )
233         {
234             UpdateCursorMoved( p_event );
235         }
236 
237         /* */
238         switch( msg.message )
239         {
240         case WM_MOUSEMOVE:
241             vlc_mutex_lock( &p_event->lock );
242             place  = p_event->place;
243             source = p_event->source;
244             vlc_mutex_unlock( &p_event->lock );
245 
246             if( place.width > 0 && place.height > 0 )
247             {
248                 if( msg.hwnd == p_event->hvideownd )
249                 {
250                     /* Child window */
251                     place.x = 0;
252                     place.y = 0;
253                 }
254                 const int x = source.i_x_offset +
255                     (int64_t)(GET_X_LPARAM(msg.lParam) - place.x) * source.i_width  / place.width;
256                 const int y = source.i_y_offset +
257                     (int64_t)(GET_Y_LPARAM(msg.lParam) - place.y) * source.i_height / place.height;
258                 vout_display_SendEventMouseMoved(vd, x, y);
259             }
260             break;
261         case WM_NCMOUSEMOVE:
262             break;
263 
264         case WM_LBUTTONDOWN:
265             MousePressed( p_event, msg.hwnd, MOUSE_BUTTON_LEFT );
266             break;
267         case WM_LBUTTONUP:
268             MouseReleased( p_event, MOUSE_BUTTON_LEFT );
269             break;
270         case WM_LBUTTONDBLCLK:
271             vout_display_SendEventMouseDoubleClick(vd);
272             break;
273 
274         case WM_MBUTTONDOWN:
275             MousePressed( p_event, msg.hwnd, MOUSE_BUTTON_CENTER );
276             break;
277         case WM_MBUTTONUP:
278             MouseReleased( p_event, MOUSE_BUTTON_CENTER );
279             break;
280 
281         case WM_RBUTTONDOWN:
282             MousePressed( p_event, msg.hwnd, MOUSE_BUTTON_RIGHT );
283             break;
284         case WM_RBUTTONUP:
285             MouseReleased( p_event, MOUSE_BUTTON_RIGHT );
286             break;
287 
288         case WM_KEYDOWN:
289         case WM_SYSKEYDOWN:
290         {
291             /* The key events are first processed here and not translated
292              * into WM_CHAR events because we need to know the status of the
293              * modifier keys. */
294             int i_key = Win32VoutConvertKey( msg.wParam );
295             if( !i_key )
296             {
297                 /* This appears to be a "normal" (ascii) key */
298                 i_key = tolower( (unsigned char)MapVirtualKey( msg.wParam, 2 ) );
299             }
300 
301             if( i_key )
302             {
303                 if( GetKeyState(VK_CONTROL) & 0x8000 )
304                 {
305                     i_key |= KEY_MODIFIER_CTRL;
306                 }
307                 if( GetKeyState(VK_SHIFT) & 0x8000 )
308                 {
309                     i_key |= KEY_MODIFIER_SHIFT;
310                 }
311                 if( GetKeyState(VK_MENU) & 0x8000 )
312                 {
313                     i_key |= KEY_MODIFIER_ALT;
314                 }
315 
316                 vout_display_SendEventKey(vd, i_key);
317             }
318             break;
319         }
320 
321         case WM_MOUSEWHEEL:
322         {
323             int i_key;
324             if( GET_WHEEL_DELTA_WPARAM( msg.wParam ) > 0 )
325             {
326                 i_key = KEY_MOUSEWHEELUP;
327             }
328             else
329             {
330                 i_key = KEY_MOUSEWHEELDOWN;
331             }
332             if( i_key )
333             {
334                 if( GetKeyState(VK_CONTROL) & 0x8000 )
335                 {
336                     i_key |= KEY_MODIFIER_CTRL;
337                 }
338                 if( GetKeyState(VK_SHIFT) & 0x8000 )
339                 {
340                     i_key |= KEY_MODIFIER_SHIFT;
341                 }
342                 if( GetKeyState(VK_MENU) & 0x8000 )
343                 {
344                     i_key |= KEY_MODIFIER_ALT;
345                 }
346                 vout_display_SendEventKey(vd, i_key);
347             }
348             break;
349         }
350 
351         case WM_VLC_CHANGE_TEXT:
352         {
353             vlc_mutex_lock( &p_event->lock );
354             wchar_t *pwz_title = NULL;
355             if( p_event->psz_title )
356             {
357                 const size_t i_length = strlen(p_event->psz_title);
358                 pwz_title = vlc_alloc( i_length + 1, 2 );
359                 if( pwz_title )
360                 {
361                     mbstowcs( pwz_title, p_event->psz_title, 2 * i_length );
362                     pwz_title[i_length] = 0;
363                 }
364             }
365             vlc_mutex_unlock( &p_event->lock );
366 
367             if( pwz_title )
368             {
369                 SetWindowTextW( p_event->hwnd, pwz_title );
370                 if( p_event->hfswnd )
371                     SetWindowTextW( p_event->hfswnd, pwz_title );
372                 free( pwz_title );
373             }
374             break;
375         }
376 
377         default:
378             /* Messages we don't handle directly are dispatched to the
379              * window procedure */
380             TranslateMessage(&msg);
381             DispatchMessage(&msg);
382             break;
383 
384         } /* End Switch */
385 
386     } /* End Main loop */
387 
388     /* Check for WM_QUIT if we created the window */
389     if( !p_event->hparent && msg.message == WM_QUIT )
390     {
391         msg_Warn( vd, "WM_QUIT... should not happen!!" );
392         p_event->hwnd = NULL; /* Window already destroyed */
393     }
394 
395     msg_Dbg( vd, "Win32 Vout EventThread terminating" );
396 
397     Win32VoutCloseWindow( p_event );
398     vlc_restorecancel(canc);
399     return NULL;
400 }
401 
EventThreadUpdateTitle(event_thread_t * p_event,const char * psz_fallback)402 void EventThreadUpdateTitle( event_thread_t *p_event, const char *psz_fallback )
403 {
404     char *psz_title = var_InheritString( p_event->vd, "video-title" );
405     if( !psz_title )
406         psz_title = strdup( psz_fallback );
407     if( !psz_title )
408         return;
409 
410     vlc_mutex_lock( &p_event->lock );
411     free( p_event->psz_title );
412     p_event->psz_title = psz_title;
413     vlc_mutex_unlock( &p_event->lock );
414 
415     PostMessage( p_event->hwnd, WM_VLC_CHANGE_TEXT, 0, 0 );
416 }
EventThreadGetWindowStyle(event_thread_t * p_event)417 int EventThreadGetWindowStyle( event_thread_t *p_event )
418 {
419     /* No need to lock, it is serialized by EventThreadStart */
420     return p_event->i_window_style;
421 }
422 
EventThreadUpdateWindowPosition(event_thread_t * p_event,bool * pb_moved,bool * pb_resized,int x,int y,unsigned w,unsigned h)423 void EventThreadUpdateWindowPosition( event_thread_t *p_event,
424                                       bool *pb_moved, bool *pb_resized,
425                                       int x, int y, unsigned w, unsigned h )
426 {
427     vlc_mutex_lock( &p_event->lock );
428     *pb_moved   = x != p_event->x || y != p_event->y;
429     *pb_resized = w != p_event->width || h != p_event->height;
430 
431     p_event->x      = x;
432     p_event->y      = y;
433     p_event->width  = w;
434     p_event->height = h;
435     vlc_mutex_unlock( &p_event->lock );
436 }
437 
EventThreadUpdateSourceAndPlace(event_thread_t * p_event,const video_format_t * p_source,const vout_display_place_t * p_place)438 void EventThreadUpdateSourceAndPlace( event_thread_t *p_event,
439                                       const video_format_t *p_source,
440                                       const vout_display_place_t *p_place )
441 {
442     vlc_mutex_lock( &p_event->lock );
443     p_event->source = *p_source;
444     p_event->place  = *p_place;
445     vlc_mutex_unlock( &p_event->lock );
446 }
447 
EventThreadUseOverlay(event_thread_t * p_event,bool b_used)448 void EventThreadUseOverlay( event_thread_t *p_event, bool b_used )
449 {
450     vlc_mutex_lock( &p_event->lock );
451     p_event->use_overlay = b_used;
452     vlc_mutex_unlock( &p_event->lock );
453 }
EventThreadGetAndResetHasMoved(event_thread_t * p_event)454 bool EventThreadGetAndResetHasMoved( event_thread_t *p_event )
455 {
456     return atomic_exchange(&p_event->has_moved, false);
457 }
458 
EventThreadCreate(vout_display_t * vd)459 event_thread_t *EventThreadCreate( vout_display_t *vd)
460 {
461      /* Create the Vout EventThread, this thread is created by us to isolate
462      * the Win32 PeekMessage function calls. We want to do this because
463      * Windows can stay blocked inside this call for a long time, and when
464      * this happens it thus blocks vlc's video_output thread.
465      * Vout EventThread will take care of the creation of the video
466      * window (because PeekMessage has to be called from the same thread which
467      * created the window). */
468     msg_Dbg( vd, "creating Vout EventThread" );
469     event_thread_t *p_event = malloc( sizeof(*p_event) );
470     if( !p_event )
471         return NULL;
472 
473     p_event->vd = vd;
474     vlc_mutex_init( &p_event->lock );
475     vlc_cond_init( &p_event->wait );
476 
477     p_event->is_cursor_hidden = false;
478     p_event->button_pressed = 0;
479     p_event->psz_title = NULL;
480     p_event->source = vd->source;
481     p_event->hwnd = NULL;
482     atomic_init(&p_event->has_moved, false);
483     vout_display_PlacePicture(&p_event->place, &vd->source, vd->cfg, false);
484 
485     _sntprintf( p_event->class_main, sizeof(p_event->class_main)/sizeof(*p_event->class_main),
486                _T("VLC video main %p"), (void *)p_event );
487     _sntprintf( p_event->class_video, sizeof(p_event->class_video)/sizeof(*p_event->class_video),
488                _T("VLC video output %p"), (void *)p_event );
489     return p_event;
490 }
491 
EventThreadDestroy(event_thread_t * p_event)492 void EventThreadDestroy( event_thread_t *p_event )
493 {
494     free( p_event->psz_title );
495     vlc_cond_destroy( &p_event->wait );
496     vlc_mutex_destroy( &p_event->lock );
497     free( p_event );
498 }
499 
EventThreadStart(event_thread_t * p_event,event_hwnd_t * p_hwnd,const event_cfg_t * p_cfg)500 int EventThreadStart( event_thread_t *p_event, event_hwnd_t *p_hwnd, const event_cfg_t *p_cfg )
501 {
502     p_event->use_desktop = p_cfg->use_desktop;
503     p_event->use_overlay = p_cfg->use_overlay;
504     p_event->x           = p_cfg->x;
505     p_event->y           = p_cfg->y;
506     p_event->width       = p_cfg->width;
507     p_event->height      = p_cfg->height;
508 
509     atomic_store(&p_event->has_moved, false);
510 
511     p_event->b_ready = false;
512     p_event->b_done  = false;
513     p_event->b_error = false;
514 
515     if( vlc_clone( &p_event->thread, EventThread, p_event,
516                    VLC_THREAD_PRIORITY_LOW ) )
517     {
518         msg_Err( p_event->vd, "cannot create Vout EventThread" );
519         return VLC_EGENERIC;
520     }
521 
522     vlc_mutex_lock( &p_event->lock );
523     while( !p_event->b_ready )
524         vlc_cond_wait( &p_event->wait, &p_event->lock );
525     const bool b_error = p_event->b_error;
526     vlc_mutex_unlock( &p_event->lock );
527 
528     if( b_error )
529     {
530         vlc_join( p_event->thread, NULL );
531         p_event->b_ready = false;
532         return VLC_EGENERIC;
533     }
534     msg_Dbg( p_event->vd, "Vout EventThread running" );
535 
536     /* */
537     p_hwnd->parent_window = p_event->parent_window;
538     p_hwnd->hparent       = p_event->hparent;
539     p_hwnd->hwnd          = p_event->hwnd;
540     p_hwnd->hvideownd     = p_event->hvideownd;
541     p_hwnd->hfswnd        = p_event->hfswnd;
542     return VLC_SUCCESS;
543 }
544 
EventThreadStop(event_thread_t * p_event)545 void EventThreadStop( event_thread_t *p_event )
546 {
547     if( !p_event->b_ready )
548         return;
549 
550     vlc_mutex_lock( &p_event->lock );
551     p_event->b_done = true;
552     vlc_mutex_unlock( &p_event->lock );
553 
554     /* we need to be sure Vout EventThread won't stay stuck in
555      * GetMessage, so we send a fake message */
556     if( p_event->hwnd )
557         PostMessage( p_event->hwnd, WM_NULL, 0, 0);
558 
559     vlc_join( p_event->thread, NULL );
560     p_event->b_ready = false;
561 }
562 
563 
564 /***********************************
565  * Local functions implementations *
566  ***********************************/
UpdateCursor(event_thread_t * p_event,bool b_show)567 static void UpdateCursor( event_thread_t *p_event, bool b_show )
568 {
569     if( p_event->is_cursor_hidden == !b_show )
570         return;
571     p_event->is_cursor_hidden = !b_show;
572 
573 #if 1
574     HCURSOR cursor = b_show ? p_event->cursor_arrow : p_event->cursor_empty;
575     if( p_event->hvideownd )
576         SetClassLongPtr( p_event->hvideownd, GCLP_HCURSOR, (LONG_PTR)cursor );
577     if( p_event->hwnd )
578         SetClassLongPtr( p_event->hwnd, GCLP_HCURSOR, (LONG_PTR)cursor );
579 #endif
580 
581     /* FIXME I failed to find a cleaner way to force a redraw of the cursor */
582     POINT p;
583     GetCursorPos(&p);
584     HWND hwnd = WindowFromPoint(p);
585     if( hwnd == p_event->hvideownd || hwnd == p_event->hwnd )
586     {
587         SetCursor( cursor );
588     }
589 }
590 
EmptyCursor(HINSTANCE instance)591 static HCURSOR EmptyCursor( HINSTANCE instance )
592 {
593     const int cw = GetSystemMetrics(SM_CXCURSOR);
594     const int ch = GetSystemMetrics(SM_CYCURSOR);
595 
596     HCURSOR cursor = NULL;
597     uint8_t *and = malloc(cw * ch);
598     uint8_t *xor = malloc(cw * ch);
599     if( and && xor )
600     {
601         memset(and, 0xff, cw * ch );
602         memset(xor, 0x00, cw * ch );
603         cursor = CreateCursor( instance, 0, 0, cw, ch, and, xor);
604     }
605     free( and );
606     free( xor );
607 
608     return cursor;
609 }
610 
MousePressed(event_thread_t * p_event,HWND hwnd,unsigned button)611 static void MousePressed( event_thread_t *p_event, HWND hwnd, unsigned button )
612 {
613     if( !p_event->button_pressed )
614         SetCapture( hwnd );
615     p_event->button_pressed |= 1 << button;
616     vout_display_SendEventMousePressed( p_event->vd, button );
617 }
618 
MouseReleased(event_thread_t * p_event,unsigned button)619 static void MouseReleased( event_thread_t *p_event, unsigned button )
620 {
621     p_event->button_pressed &= ~(1 << button);
622     if( !p_event->button_pressed )
623         ReleaseCapture();
624     vout_display_SendEventMouseReleased( p_event->vd, button );
625 }
626 
627 #if defined(MODULE_NAME_IS_direct3d9) || defined(MODULE_NAME_IS_direct3d11)
628 static int CALLBACK
enumWindowsProc(HWND hwnd,LPARAM lParam)629 enumWindowsProc(HWND hwnd, LPARAM lParam)
630 {
631     HWND *wnd = (HWND *)lParam;
632 
633     char name[128];
634     name[0] = '\0';
635     GetClassNameA( hwnd, name, 128 );
636 
637     if( !strcasecmp( name, "WorkerW" ) )
638     {
639         hwnd = FindWindowEx( hwnd, NULL, _T("SHELLDLL_DefView"), NULL );
640         if( hwnd ) hwnd = FindWindowEx( hwnd, NULL, _T("SysListView32"), NULL );
641         if( hwnd )
642         {
643             *wnd = hwnd;
644             return false;
645         }
646     }
647     return true;
648 }
649 
GetDesktopHandle(vout_display_t * vd)650 static HWND GetDesktopHandle(vout_display_t *vd)
651 {
652     /* Find Program Manager */
653     HWND hwnd = FindWindow( _T("Progman"), NULL );
654     if( hwnd ) hwnd = FindWindowEx( hwnd, NULL, _T("SHELLDLL_DefView"), NULL );
655     if( hwnd ) hwnd = FindWindowEx( hwnd, NULL, _T("SysListView32"), NULL );
656     if( hwnd )
657         return hwnd;
658 
659     msg_Dbg( vd, "Couldn't find desktop icon window,. Trying the hard way." );
660 
661     EnumWindows( enumWindowsProc, (LPARAM)&hwnd );
662     return hwnd;
663 }
664 #endif
665 
666 /*****************************************************************************
667  * Win32VoutCreateWindow: create a window for the video.
668  *****************************************************************************
669  * Before creating a direct draw surface, we need to create a window in which
670  * the video will be displayed. This window will also allow us to capture the
671  * events.
672  *****************************************************************************/
Win32VoutCreateWindow(event_thread_t * p_event)673 static int Win32VoutCreateWindow( event_thread_t *p_event )
674 {
675     vout_display_t *vd = p_event->vd;
676     HINSTANCE  hInstance;
677     HMENU      hMenu;
678     RECT       rect_window;
679     WNDCLASS   wc;                            /* window class components */
680     TCHAR      vlc_path[MAX_PATH+1];
681     int        i_style;
682 
683     msg_Dbg( vd, "Win32VoutCreateWindow" );
684 
685     /* Get this module's instance */
686     hInstance = GetModuleHandle(NULL);
687 
688     #if defined(MODULE_NAME_IS_direct3d9) || defined(MODULE_NAME_IS_direct3d11)
689     if( !p_event->use_desktop )
690     #endif
691     {
692         /* If an external window was specified, we'll draw in it. */
693         p_event->parent_window = vout_display_NewWindow(vd, VOUT_WINDOW_TYPE_HWND);
694         if( p_event->parent_window )
695             p_event->hparent = p_event->parent_window->handle.hwnd;
696         else
697             p_event->hparent = NULL;
698     }
699     #if defined(MODULE_NAME_IS_direct3d9) || defined(MODULE_NAME_IS_direct3d11)
700     else
701     {
702         vout_display_DeleteWindow(vd, NULL);
703         p_event->parent_window = NULL;
704         p_event->hparent = GetDesktopHandle(vd);
705     }
706     #endif
707     p_event->cursor_arrow = LoadCursor(NULL, IDC_ARROW);
708     p_event->cursor_empty = EmptyCursor(hInstance);
709 
710     /* Get the Icon from the main app */
711     p_event->vlc_icon = NULL;
712     if( GetModuleFileName( NULL, vlc_path, MAX_PATH ) )
713     {
714         p_event->vlc_icon = ExtractIcon( hInstance, vlc_path, 0 );
715     }
716     p_event->hide_timeout = var_InheritInteger( p_event->vd, "mouse-hide-timeout" );
717     UpdateCursorMoved( p_event );
718 
719     /* Fill in the window class structure */
720     wc.style         = CS_OWNDC|CS_DBLCLKS;          /* style: dbl click */
721     wc.lpfnWndProc   = (WNDPROC)WinVoutEventProc;       /* event handler */
722     wc.cbClsExtra    = 0;                         /* no extra class data */
723     wc.cbWndExtra    = 0;                        /* no extra window data */
724     wc.hInstance     = hInstance;                            /* instance */
725     wc.hIcon         = p_event->vlc_icon;       /* load the vlc big icon */
726     wc.hCursor       = p_event->is_cursor_hidden ? p_event->cursor_empty :
727                                                    p_event->cursor_arrow;
728 #if !VLC_WINSTORE_APP
729     wc.hbrBackground = GetStockObject(BLACK_BRUSH);  /* background color */
730 #else
731     wc.hbrBackground = NULL;
732 #endif
733     wc.lpszMenuName  = NULL;                                  /* no menu */
734     wc.lpszClassName = p_event->class_main;       /* use a special class */
735 
736     /* Register the window class */
737     if( !RegisterClass(&wc) )
738     {
739         if( p_event->vlc_icon )
740             DestroyIcon( p_event->vlc_icon );
741 
742         msg_Err( vd, "Win32VoutCreateWindow RegisterClass FAILED (err=%lu)", GetLastError() );
743         return VLC_EGENERIC;
744     }
745 
746     /* Register the video sub-window class */
747     wc.lpszClassName = p_event->class_video;
748     wc.hIcon = 0;
749     wc.hbrBackground = NULL; /* no background color */
750     if( !RegisterClass(&wc) )
751     {
752         msg_Err( vd, "Win32VoutCreateWindow RegisterClass FAILED (err=%lu)", GetLastError() );
753         return VLC_EGENERIC;
754     }
755 
756     /* When you create a window you give the dimensions you wish it to
757      * have. Unfortunatly these dimensions will include the borders and
758      * titlebar. We use the following function to find out the size of
759      * the window corresponding to the useable surface we want */
760     rect_window.left   = 10;
761     rect_window.top    = 10;
762     rect_window.right  = rect_window.left + p_event->width;
763     rect_window.bottom = rect_window.top  + p_event->height;
764 
765     i_style = var_GetBool( vd, "video-deco" )
766         /* Open with window decoration */
767         ? WS_OVERLAPPEDWINDOW|WS_SIZEBOX
768         /* No window decoration */
769         : WS_POPUP;
770     AdjustWindowRect( &rect_window, i_style, 0 );
771     i_style |= WS_VISIBLE|WS_CLIPCHILDREN;
772 
773     if( p_event->hparent )
774     {
775         i_style = WS_VISIBLE|WS_CLIPCHILDREN|WS_CHILD;
776 
777         /* allow user to regain control over input events if requested */
778         bool b_mouse_support = var_InheritBool( vd, "mouse-events" );
779         bool b_key_support = var_InheritBool( vd, "keyboard-events" );
780         if( !b_mouse_support && !b_key_support )
781             i_style |= WS_DISABLED;
782     }
783 
784     p_event->i_window_style = i_style;
785 
786     /* Create the window */
787     p_event->hwnd =
788         CreateWindowEx( WS_EX_NOPARENTNOTIFY,
789                     p_event->class_main,             /* name of window class */
790                     _T(VOUT_TITLE) _T(" (VLC Video Output)"),/* window title */
791                     i_style,                                 /* window style */
792                     (!p_event->x) ? (UINT)CW_USEDEFAULT :
793                         (UINT)p_event->x,            /* default X coordinate */
794                     (!p_event->y) ? (UINT)CW_USEDEFAULT :
795                         (UINT)p_event->y,            /* default Y coordinate */
796                     rect_window.right - rect_window.left,    /* window width */
797                     rect_window.bottom - rect_window.top,   /* window height */
798                     p_event->hparent,                       /* parent window */
799                     NULL,                          /* no menu in this window */
800                     hInstance,            /* handle of this program instance */
801                     (LPVOID)p_event );           /* send vd to WM_CREATE */
802 
803     if( !p_event->hwnd )
804     {
805         msg_Warn( vd, "Win32VoutCreateWindow create window FAILED (err=%lu)", GetLastError() );
806         return VLC_EGENERIC;
807     }
808 
809     bool b_isProjected  = (vd->fmt.projection_mode != PROJECTION_MODE_RECTANGULAR);
810     InitGestures( p_event->hwnd, &p_event->p_gesture, b_isProjected );
811 
812     p_event->p_sensors = HookWindowsSensors(vd, p_event->hwnd);
813 
814     if( p_event->hparent )
815     {
816         LONG i_style;
817 
818         /* We don't want the window owner to overwrite our client area */
819         i_style = GetWindowLong( p_event->hparent, GWL_STYLE );
820 
821         if( !(i_style & WS_CLIPCHILDREN) )
822             /* Hmmm, apparently this is a blocking call... */
823             SetWindowLong( p_event->hparent, GWL_STYLE,
824                            i_style | WS_CLIPCHILDREN );
825 
826         /* Create our fullscreen window */
827         p_event->hfswnd =
828             CreateWindowEx( WS_EX_APPWINDOW, p_event->class_main,
829                             _T(VOUT_TITLE) _T(" (VLC Fullscreen Video Output)"),
830                             WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN|WS_SIZEBOX,
831                             CW_USEDEFAULT, CW_USEDEFAULT,
832                             CW_USEDEFAULT, CW_USEDEFAULT,
833                             NULL, NULL, hInstance, NULL );
834     }
835     else
836     {
837         p_event->hfswnd = NULL;
838     }
839 
840     /* Append a "Always On Top" entry in the system menu */
841     hMenu = GetSystemMenu( p_event->hwnd, FALSE );
842     AppendMenu( hMenu, MF_SEPARATOR, 0, _T("") );
843     AppendMenu( hMenu, MF_STRING | MF_UNCHECKED,
844                        IDM_TOGGLE_ON_TOP, _T("Always on &Top") );
845 
846     /* Create video sub-window. This sub window will always exactly match
847      * the size of the video, which allows us to use crazy overlay colorkeys
848      * without having them shown outside of the video area. */
849     /* FIXME vd->source.i_width/i_height seems wrong */
850     p_event->hvideownd =
851     CreateWindow( p_event->class_video, _T(""),   /* window class */
852         WS_CHILD,                   /* window style, not visible initially */
853         0, 0,
854         vd->source.i_width,          /* default width */
855         vd->source.i_height,        /* default height */
856         p_event->hwnd,               /* parent window */
857         NULL, hInstance,
858         (LPVOID)p_event );    /* send vd to WM_CREATE */
859 
860     if( !p_event->hvideownd )
861         msg_Warn( vd, "can't create video sub-window" );
862     else
863         msg_Dbg( vd, "created video sub-window" );
864 
865     /* Now display the window */
866     ShowWindow( p_event->hwnd, SW_SHOW );
867 
868     return VLC_SUCCESS;
869 }
870 
871 /*****************************************************************************
872  * Win32VoutCloseWindow: close the window created by Win32VoutCreateWindow
873  *****************************************************************************
874  * This function returns all resources allocated by Win32VoutCreateWindow.
875  *****************************************************************************/
Win32VoutCloseWindow(event_thread_t * p_event)876 static void Win32VoutCloseWindow( event_thread_t *p_event )
877 {
878     vout_display_t *vd = p_event->vd;
879     msg_Dbg( vd, "Win32VoutCloseWindow" );
880 
881     #if defined(MODULE_NAME_IS_direct3d9) || defined(MODULE_NAME_IS_direct3d11)
882     DestroyWindow( p_event->hvideownd );
883     #endif
884     DestroyWindow( p_event->hwnd );
885     if( p_event->hfswnd )
886         DestroyWindow( p_event->hfswnd );
887 
888     #if defined(MODULE_NAME_IS_direct3d9) || defined(MODULE_NAME_IS_direct3d11)
889     if( !p_event->use_desktop )
890     #endif
891         vout_display_DeleteWindow( vd, p_event->parent_window );
892     p_event->hwnd = NULL;
893 
894     HINSTANCE hInstance = GetModuleHandle(NULL);
895     UnregisterClass( p_event->class_video, hInstance );
896     UnregisterClass( p_event->class_main, hInstance );
897 
898     if( p_event->vlc_icon )
899         DestroyIcon( p_event->vlc_icon );
900 
901     DestroyCursor( p_event->cursor_empty );
902 
903     UnhookWindowsSensors(p_event->p_sensors);
904 
905     CloseGestures( p_event->p_gesture);
906 }
907 
908 /*****************************************************************************
909  * WinVoutEventProc: This is the window event processing function.
910  *****************************************************************************
911  * On Windows, when you create a window you have to attach an event processing
912  * function to it. The aim of this function is to manage "Queued Messages" and
913  * "Nonqueued Messages".
914  * Queued Messages are those picked up and retransmitted by vout_Manage
915  * (using the GetMessage and DispatchMessage functions).
916  * Nonqueued Messages are those that Windows will send directly to this
917  * procedure (like WM_DESTROY, WM_WINDOWPOSCHANGED...)
918  *****************************************************************************/
WinVoutEventProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)919 static long FAR PASCAL WinVoutEventProc( HWND hwnd, UINT message,
920                                          WPARAM wParam, LPARAM lParam )
921 {
922     event_thread_t *p_event;
923 
924     if( message == WM_CREATE )
925     {
926         /* Store vd for future use */
927         p_event = (event_thread_t *)((CREATESTRUCT *)lParam)->lpCreateParams;
928         SetWindowLongPtr( hwnd, GWLP_USERDATA, (LONG_PTR)p_event );
929         return TRUE;
930     }
931     else
932     {
933         LONG_PTR p_user_data = GetWindowLongPtr( hwnd, GWLP_USERDATA );
934         p_event = (event_thread_t *)p_user_data;
935         if( !p_event )
936         {
937             /* Hmmm mozilla does manage somehow to save the pointer to our
938              * windowproc and still calls it after the vout has been closed. */
939             return DefWindowProc(hwnd, message, wParam, lParam);
940         }
941     }
942     vout_display_t *vd = p_event->vd;
943 
944 #if 0
945     if( message == WM_SETCURSOR )
946     {
947         msg_Err(vd, "WM_SETCURSOR: %d (t2)", p_event->is_cursor_hidden);
948         SetCursor( p_event->is_cursor_hidden ? p_event->cursor_empty : p_event->cursor_arrow );
949         return 1;
950     }
951 #endif
952     if( message == WM_CAPTURECHANGED )
953     {
954         for( int button = 0; p_event->button_pressed; button++ )
955         {
956             unsigned m = 1 << button;
957             if( p_event->button_pressed & m )
958                 vout_display_SendEventMouseReleased( p_event->vd, button );
959             p_event->button_pressed &= ~m;
960         }
961         p_event->button_pressed = 0;
962         return 0;
963     }
964 
965     if( hwnd == p_event->hvideownd )
966     {
967 #ifdef MODULE_NAME_IS_directdraw
968         vlc_mutex_lock( &p_event->lock );
969         const bool use_overlay = p_event->use_overlay;
970         vlc_mutex_unlock( &p_event->lock );
971 #endif
972 
973         switch( message )
974         {
975 #ifdef MODULE_NAME_IS_directdraw
976         case WM_ERASEBKGND:
977         /* For overlay, we need to erase background */
978             return !use_overlay ? 1 : DefWindowProc(hwnd, message, wParam, lParam);
979         case WM_PAINT:
980         /*
981         ** For overlay, DefWindowProc() will erase dirty regions
982         ** with colorkey.
983         ** For non-overlay, vout will paint the whole window at
984         ** regular interval, therefore dirty regions can be ignored
985         ** to minimize repaint.
986         */
987             if( !use_overlay )
988             {
989                 ValidateRect(hwnd, NULL);
990             }
991             // fall through to default
992 #else
993         /*
994         ** For OpenGL and Direct3D, vout will update the whole
995         ** window at regular interval, therefore dirty region
996         ** can be ignored to minimize repaint.
997         */
998         case WM_ERASEBKGND:
999             /* nothing to erase */
1000             return 1;
1001         case WM_PAINT:
1002             /* nothing to repaint */
1003             ValidateRect(hwnd, NULL);
1004             // fall through
1005 #endif
1006         default:
1007             return DefWindowProc(hwnd, message, wParam, lParam);
1008         }
1009     }
1010 
1011     switch( message )
1012     {
1013 
1014     case WM_WINDOWPOSCHANGED:
1015         atomic_store(&p_event->has_moved, true);
1016         return 0;
1017 
1018     /* the user wants to close the window */
1019     case WM_CLOSE:
1020         vout_display_SendEventClose(vd);
1021         return 0;
1022 
1023     /* the window has been closed so shut down everything now */
1024     case WM_DESTROY:
1025         msg_Dbg( vd, "WinProc WM_DESTROY" );
1026         /* just destroy the window */
1027         PostQuitMessage( 0 );
1028         return 0;
1029 
1030     case WM_SYSCOMMAND:
1031         switch (wParam)
1032         {
1033         case IDM_TOGGLE_ON_TOP:            /* toggle the "on top" status */
1034         {
1035             msg_Dbg(vd, "WinProc WM_SYSCOMMAND: IDM_TOGGLE_ON_TOP");
1036             HMENU hMenu = GetSystemMenu(vd->sys->hwnd, FALSE);
1037             vout_display_SendWindowState(vd, (GetMenuState(hMenu, IDM_TOGGLE_ON_TOP, MF_BYCOMMAND) & MF_CHECKED) ?
1038                     VOUT_WINDOW_STATE_NORMAL : VOUT_WINDOW_STATE_ABOVE);
1039             return 0;
1040         }
1041         default:
1042             break;
1043         }
1044         break;
1045 
1046     case WM_PAINT:
1047     case WM_NCPAINT:
1048     case WM_ERASEBKGND:
1049         return DefWindowProc(hwnd, message, wParam, lParam);
1050 
1051     case WM_KILLFOCUS:
1052         return 0;
1053 
1054     case WM_SETFOCUS:
1055         return 0;
1056 
1057     case WM_GESTURE:
1058         return DecodeGesture( VLC_OBJECT(vd), p_event->p_gesture, hwnd, message, wParam, lParam );
1059 
1060     default:
1061         //msg_Dbg( vd, "WinProc WM Default %i", message );
1062         break;
1063     }
1064 
1065     /* Let windows handle the message */
1066     return DefWindowProc(hwnd, message, wParam, lParam);
1067 }
1068 
1069 static struct
1070 {
1071     int i_dxkey;
1072     int i_vlckey;
1073 
1074 } dxkeys_to_vlckeys[] =
1075 {
1076     { VK_F1, KEY_F1 }, { VK_F2, KEY_F2 }, { VK_F3, KEY_F3 }, { VK_F4, KEY_F4 },
1077     { VK_F5, KEY_F5 }, { VK_F6, KEY_F6 }, { VK_F7, KEY_F7 }, { VK_F8, KEY_F8 },
1078     { VK_F9, KEY_F9 }, { VK_F10, KEY_F10 }, { VK_F11, KEY_F11 },
1079     { VK_F12, KEY_F12 },
1080 
1081     { VK_RETURN, KEY_ENTER },
1082     { VK_SPACE, ' ' },
1083     { VK_ESCAPE, KEY_ESC },
1084 
1085     { VK_LEFT, KEY_LEFT },
1086     { VK_RIGHT, KEY_RIGHT },
1087     { VK_UP, KEY_UP },
1088     { VK_DOWN, KEY_DOWN },
1089 
1090     { VK_HOME, KEY_HOME },
1091     { VK_END, KEY_END },
1092     { VK_PRIOR, KEY_PAGEUP },
1093     { VK_NEXT, KEY_PAGEDOWN },
1094 
1095     { VK_INSERT, KEY_INSERT },
1096     { VK_DELETE, KEY_DELETE },
1097 
1098     { VK_CONTROL, 0 },
1099     { VK_SHIFT, 0 },
1100     { VK_MENU, 0 },
1101 
1102     { VK_BROWSER_BACK, KEY_BROWSER_BACK },
1103     { VK_BROWSER_FORWARD, KEY_BROWSER_FORWARD },
1104     { VK_BROWSER_REFRESH, KEY_BROWSER_REFRESH },
1105     { VK_BROWSER_STOP, KEY_BROWSER_STOP },
1106     { VK_BROWSER_SEARCH, KEY_BROWSER_SEARCH },
1107     { VK_BROWSER_FAVORITES, KEY_BROWSER_FAVORITES },
1108     { VK_BROWSER_HOME, KEY_BROWSER_HOME },
1109     { VK_VOLUME_MUTE, KEY_VOLUME_MUTE },
1110     { VK_VOLUME_DOWN, KEY_VOLUME_DOWN },
1111     { VK_VOLUME_UP, KEY_VOLUME_UP },
1112     { VK_MEDIA_NEXT_TRACK, KEY_MEDIA_NEXT_TRACK },
1113     { VK_MEDIA_PREV_TRACK, KEY_MEDIA_PREV_TRACK },
1114     { VK_MEDIA_STOP, KEY_MEDIA_STOP },
1115     { VK_MEDIA_PLAY_PAUSE, KEY_MEDIA_PLAY_PAUSE },
1116 
1117     { 0, 0 }
1118 };
1119 
Win32VoutConvertKey(int i_key)1120 static int Win32VoutConvertKey( int i_key )
1121 {
1122     for( int i = 0; dxkeys_to_vlckeys[i].i_dxkey != 0; i++ )
1123     {
1124         if( dxkeys_to_vlckeys[i].i_dxkey == i_key )
1125         {
1126             return dxkeys_to_vlckeys[i].i_vlckey;
1127         }
1128     }
1129 
1130     return 0;
1131 }
1132 
1133