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