]> git.sesse.net Git - vlc/blob - modules/video_output/msw/events.c
MSW vout: remove tabs
[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 <vlc_common.h>
34 #include <vlc_vout_display.h>
35
36 #include <windows.h>
37 #include <windowsx.h>                                        /* GET_X_LPARAM */
38 #include <shellapi.h>                                         /* ExtractIcon */
39
40 #include "common.h"
41
42 /*****************************************************************************
43  * Local prototypes.
44  *****************************************************************************/
45 #define WM_VLC_HIDE_MOUSE   (WM_APP + 0)
46 #define WM_VLC_CHANGE_TEXT  (WM_APP + 1)
47
48 struct event_thread_t
49 {
50     vout_display_t *vd;
51
52     /* */
53     vlc_thread_t thread;
54     vlc_mutex_t  lock;
55     vlc_cond_t   wait;
56     bool         b_ready;
57     bool         b_done;
58     bool         b_error;
59
60     /* */
61     bool use_desktop;
62     bool use_overlay;
63
64     /* Mouse */
65     bool is_cursor_hidden;
66     HCURSOR cursor_arrow;
67     HCURSOR cursor_empty;
68     unsigned button_pressed;
69
70     /* Title */
71     char *psz_title;
72
73     int               i_window_style;
74     vout_window_cfg_t wnd_cfg;
75
76     /* */
77     vout_window_t *parent_window;
78     TCHAR class_main[256];
79     TCHAR class_video[256];
80     HWND hparent;
81     HWND hwnd;
82     HWND hvideownd;
83     HWND hfswnd;
84     video_format_t       source;
85     vout_display_place_t place;
86
87     HICON vlc_icon;
88
89     bool has_moved;
90 };
91
92 /***************************
93  * Local Prototypes        *
94  ***************************/
95 /* Window Creation */
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 );
100
101 /* Display/Hide Cursor */
102 static void UpdateCursor( event_thread_t *p_event, bool b_show );
103 static HCURSOR EmptyCursor( HINSTANCE instance );
104
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 );
108
109 /* Local helpers */
110 static inline bool isMouseEvent( WPARAM type )
111 {
112     return type >= WM_MOUSEFIRST &&
113            type <= WM_MOUSELAST;
114 }
115
116 static inline bool isKeyEvent( WPARAM type )
117 {
118     return type >= WM_KEYFIRST &&
119            type <= WM_KEYLAST;
120 }
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 )
130 {
131     event_thread_t *p_event = (event_thread_t *)p_this;
132     vout_display_t *vd = p_event->vd;
133     MSG msg;
134     POINT old_mouse_pos = {0,0}, mouse_pos;
135     int canc = vlc_savecancel ();
136
137     bool b_mouse_support = var_InheritBool( p_event->vd, "mouse-events" );
138     bool b_key_support = var_InheritBool( p_event->vd, "keyboard-events" );
139
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
143      * message queue */
144     if( Win32VoutCreateWindow( p_event ) )
145         p_event->b_error = true;
146
147     p_event->b_ready = true;
148     vlc_cond_signal( &p_event->wait );
149
150     const bool b_error = p_event->b_error;
151     vlc_mutex_unlock( &p_event->lock );
152
153     if( b_error )
154     {
155         vlc_restorecancel( canc );
156         return NULL;
157     }
158
159     /* Prevent monitor from powering off */
160     if (var_GetBool(vd, "disable-screensaver"))
161         SetThreadExecutionState( ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED | ES_CONTINUOUS );
162
163     /* Main loop */
164     /* GetMessage will sleep if there's no message in the queue */
165     for( ;; )
166     {
167         vout_display_place_t place;
168         video_format_t       source;
169
170         if( !GetMessage( &msg, 0, 0, 0 ) )
171         {
172             vlc_mutex_lock( &p_event->lock );
173             p_event->b_done = true;
174             vlc_mutex_unlock( &p_event->lock );
175             break;
176         }
177
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 );
182         if( b_done )
183             break;
184
185         if( !b_mouse_support && isMouseEvent( msg.message ) )
186             continue;
187
188         if( !b_key_support && isKeyEvent( msg.message ) )
189             continue;
190
191         /* Handle mouse state */
192         if( msg.message == WM_MOUSEMOVE ||
193             msg.message == WM_NCMOUSEMOVE )
194         {
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 ) )
199             {
200                 old_mouse_pos = mouse_pos;
201                 UpdateCursor( p_event, true );
202             }
203         }
204         else if( isMouseEvent( msg.message ) )
205         {
206             UpdateCursor( p_event, true );
207         }
208         else if( msg.message == WM_VLC_HIDE_MOUSE )
209         {
210             UpdateCursor( p_event, false );
211         }
212
213         /* */
214         switch( msg.message )
215         {
216         case WM_MOUSEMOVE:
217             vlc_mutex_lock( &p_event->lock );
218             place  = p_event->place;
219             source = p_event->source;
220             vlc_mutex_unlock( &p_event->lock );
221
222             if( place.width > 0 && place.height > 0 )
223             {
224                 if( msg.hwnd == p_event->hvideownd )
225                 {
226                     /* Child window */
227                     place.x = 0;
228                     place.y = 0;
229                 }
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);
235             }
236             break;
237         case WM_NCMOUSEMOVE:
238             break;
239
240         case WM_VLC_HIDE_MOUSE:
241             break;
242
243         case WM_LBUTTONDOWN:
244             MousePressed( p_event, msg.hwnd, MOUSE_BUTTON_LEFT );
245             break;
246         case WM_LBUTTONUP:
247             MouseReleased( p_event, MOUSE_BUTTON_LEFT );
248             break;
249         case WM_LBUTTONDBLCLK:
250             vout_display_SendEventMouseDoubleClick(vd);
251             break;
252
253         case WM_MBUTTONDOWN:
254             MousePressed( p_event, msg.hwnd, MOUSE_BUTTON_CENTER );
255             break;
256         case WM_MBUTTONUP:
257             MouseReleased( p_event, MOUSE_BUTTON_CENTER );
258             break;
259
260         case WM_RBUTTONDOWN:
261             MousePressed( p_event, msg.hwnd, MOUSE_BUTTON_RIGHT );
262             break;
263         case WM_RBUTTONUP:
264             MouseReleased( p_event, MOUSE_BUTTON_RIGHT );
265             break;
266
267         case WM_KEYDOWN:
268         case WM_SYSKEYDOWN:
269         {
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
272              * modifier keys. */
273             int i_key = Win32VoutConvertKey( msg.wParam );
274             if( !i_key )
275             {
276                 /* This appears to be a "normal" (ascii) key */
277                 i_key = tolower( (unsigned char)MapVirtualKey( msg.wParam, 2 ) );
278             }
279
280             if( i_key )
281             {
282                 if( GetKeyState(VK_CONTROL) & 0x8000 )
283                 {
284                     i_key |= KEY_MODIFIER_CTRL;
285                 }
286                 if( GetKeyState(VK_SHIFT) & 0x8000 )
287                 {
288                     i_key |= KEY_MODIFIER_SHIFT;
289                 }
290                 if( GetKeyState(VK_MENU) & 0x8000 )
291                 {
292                     i_key |= KEY_MODIFIER_ALT;
293                 }
294
295                 vout_display_SendEventKey(vd, i_key);
296             }
297             break;
298         }
299
300         case WM_MOUSEWHEEL:
301         {
302             int i_key;
303             if( GET_WHEEL_DELTA_WPARAM( msg.wParam ) > 0 )
304             {
305                 i_key = KEY_MOUSEWHEELUP;
306             }
307             else
308             {
309                 i_key = KEY_MOUSEWHEELDOWN;
310             }
311             if( i_key )
312             {
313                 if( GetKeyState(VK_CONTROL) & 0x8000 )
314                 {
315                     i_key |= KEY_MODIFIER_CTRL;
316                 }
317                 if( GetKeyState(VK_SHIFT) & 0x8000 )
318                 {
319                     i_key |= KEY_MODIFIER_SHIFT;
320                 }
321                 if( GetKeyState(VK_MENU) & 0x8000 )
322                 {
323                     i_key |= KEY_MODIFIER_ALT;
324                 }
325                 vout_display_SendEventKey(vd, i_key);
326             }
327             break;
328         }
329
330         case WM_VLC_CHANGE_TEXT:
331         {
332             vlc_mutex_lock( &p_event->lock );
333             wchar_t *pwz_title = NULL;
334             if( p_event->psz_title )
335             {
336                 const size_t i_length = strlen(p_event->psz_title);
337                 pwz_title = malloc( 2 * (i_length + 1) );
338                 if( pwz_title )
339                 {
340                     mbstowcs( pwz_title, p_event->psz_title, 2 * i_length );
341                     pwz_title[i_length] = 0;
342                 }
343             }
344             vlc_mutex_unlock( &p_event->lock );
345
346             if( pwz_title )
347             {
348                 SetWindowTextW( p_event->hwnd, pwz_title );
349                 if( p_event->hfswnd )
350                     SetWindowTextW( p_event->hfswnd, pwz_title );
351                 free( pwz_title );
352             }
353             break;
354         }
355
356         default:
357             /* Messages we don't handle directly are dispatched to the
358              * window procedure */
359             TranslateMessage(&msg);
360             DispatchMessage(&msg);
361             break;
362
363         } /* End Switch */
364
365     } /* End Main loop */
366
367     /* Check for WM_QUIT if we created the window */
368     if( !p_event->hparent && msg.message == WM_QUIT )
369     {
370         msg_Warn( vd, "WM_QUIT... should not happen!!" );
371         p_event->hwnd = NULL; /* Window already destroyed */
372     }
373
374     msg_Dbg( vd, "Win32 Vout EventThread terminating" );
375
376     Win32VoutCloseWindow( p_event );
377     vlc_restorecancel(canc);
378     return NULL;
379 }
380
381 void EventThreadMouseHide( event_thread_t *p_event )
382 {
383     PostMessage( p_event->hwnd, WM_VLC_HIDE_MOUSE, 0, 0 );
384 }
385
386 void EventThreadUpdateTitle( event_thread_t *p_event, const char *psz_fallback )
387 {
388     char *psz_title = var_InheritString( p_event->vd, "video-title" );
389     if( !psz_title )
390         psz_title = strdup( psz_fallback );
391     if( !psz_title )
392         return;
393
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 );
398
399     PostMessage( p_event->hwnd, WM_VLC_CHANGE_TEXT, 0, 0 );
400 }
401 int EventThreadGetWindowStyle( event_thread_t *p_event )
402 {
403     /* No need to lock, it is serialized by EventThreadStart */
404     return p_event->i_window_style;
405 }
406
407 void EventThreadUpdateWindowPosition( event_thread_t *p_event,
408                                       bool *pb_moved, bool *pb_resized,
409                                       int x, int y, unsigned w, unsigned h )
410 {
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;
416
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 );
422 }
423
424 void EventThreadUpdateSourceAndPlace( event_thread_t *p_event,
425                                       const video_format_t *p_source,
426                                       const vout_display_place_t *p_place )
427 {
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 );
432 }
433
434 void EventThreadUseOverlay( event_thread_t *p_event, bool b_used )
435 {
436     vlc_mutex_lock( &p_event->lock );
437     p_event->use_overlay = b_used;
438     vlc_mutex_unlock( &p_event->lock );
439 }
440 bool EventThreadGetAndResetHasMoved( event_thread_t *p_event )
441 {
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 );
446
447     return has_moved;
448 }
449
450 event_thread_t *EventThreadCreate( vout_display_t *vd)
451 {
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) );
461     if( !p_event )
462         return NULL;
463
464     p_event->vd = vd;
465     vlc_mutex_init( &p_event->lock );
466     vlc_cond_init( &p_event->wait );
467
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);
473
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 );
478     return p_event;
479 }
480
481 void EventThreadDestroy( event_thread_t *p_event )
482 {
483     free( p_event->psz_title );
484     vlc_cond_destroy( &p_event->wait );
485     vlc_mutex_destroy( &p_event->lock );
486     free( p_event );
487 }
488
489 int EventThreadStart( event_thread_t *p_event, event_hwnd_t *p_hwnd, const event_cfg_t *p_cfg )
490 {
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;
494
495     p_event->has_moved = false;
496
497     p_event->b_ready = false;
498     p_event->b_done  = false;
499     p_event->b_error = false;
500
501     if( vlc_clone( &p_event->thread, EventThread, p_event,
502                    VLC_THREAD_PRIORITY_LOW ) )
503     {
504         msg_Err( p_event->vd, "cannot create Vout EventThread" );
505         return VLC_EGENERIC;
506     }
507
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 );
513
514     if( b_error )
515     {
516         vlc_join( p_event->thread, NULL );
517         p_event->b_ready = false;
518         return VLC_EGENERIC;
519     }
520     msg_Dbg( p_event->vd, "Vout EventThread running" );
521
522     /* */
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;
528     return VLC_SUCCESS;
529 }
530
531 void EventThreadStop( event_thread_t *p_event )
532 {
533     if( !p_event->b_ready )
534         return;
535
536     vlc_mutex_lock( &p_event->lock );
537     p_event->b_done = true;
538     vlc_mutex_unlock( &p_event->lock );
539
540     /* we need to be sure Vout EventThread won't stay stuck in
541      * GetMessage, so we send a fake message */
542     if( p_event->hwnd )
543         PostMessage( p_event->hwnd, WM_NULL, 0, 0);
544
545     vlc_join( p_event->thread, NULL );
546     p_event->b_ready = false;
547 }
548
549
550 /***********************************
551  * Local functions implementations *
552  ***********************************/
553 static void UpdateCursor( event_thread_t *p_event, bool b_show )
554 {
555     if( p_event->is_cursor_hidden == !b_show )
556         return;
557     p_event->is_cursor_hidden = !b_show;
558
559 #if 1
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 );
563     if( p_event->hwnd )
564         SetClassLongPtr( p_event->hwnd, GCLP_HCURSOR, (LONG_PTR)cursor );
565 #endif
566
567     /* FIXME I failed to find a cleaner way to force a redraw of the cursor */
568     POINT p;
569     GetCursorPos(&p);
570     HWND hwnd = WindowFromPoint(p);
571     if( hwnd == p_event->hvideownd || hwnd == p_event->hwnd )
572     {
573         if( b_show )
574             SetCursor( cursor );
575         else
576             SetCursorPos( p.x, p.y );
577     }
578 }
579
580 static HCURSOR EmptyCursor( HINSTANCE instance )
581 {
582     const int cw = GetSystemMetrics(SM_CXCURSOR);
583     const int ch = GetSystemMetrics(SM_CYCURSOR);
584
585     HCURSOR cursor = NULL;
586     uint8_t *and = malloc(cw * ch);
587     uint8_t *xor = malloc(cw * ch);
588     if( and && xor )
589     {
590         memset(and, 0xff, cw * ch );
591         memset(xor, 0x00, cw * ch );
592         cursor = CreateCursor( instance, 0, 0, cw, ch, and, xor);
593     }
594     free( and );
595     free( xor );
596
597     return cursor;
598 }
599
600 static void MousePressed( event_thread_t *p_event, HWND hwnd, unsigned button )
601 {
602     if( !p_event->button_pressed )
603         SetCapture( hwnd );
604     p_event->button_pressed |= 1 << button;
605     vout_display_SendEventMousePressed( p_event->vd, button );
606 }
607
608 static void MouseReleased( event_thread_t *p_event, unsigned button )
609 {
610     p_event->button_pressed &= ~(1 << button);
611     if( !p_event->button_pressed )
612         ReleaseCapture();
613     vout_display_SendEventMouseReleased( p_event->vd, button );
614 }
615
616 #ifdef MODULE_NAME_IS_direct3d
617 static int CALLBACK
618 enumWindowsProc(HWND hwnd, LPARAM lParam)
619 {
620     HWND *wnd = (HWND *)lParam;
621
622     char name[128];
623     name[0] = '\0';
624     GetClassNameA( hwnd, name, 128 );
625
626     if( !strcasecmp( name, "WorkerW" ) )
627     {
628         hwnd = FindWindowEx( hwnd, NULL, _T("SHELLDLL_DefView"), NULL );
629         if( hwnd ) hwnd = FindWindowEx( hwnd, NULL, _T("SysListView32"), NULL );
630         if( hwnd )
631         {
632             *wnd = hwnd;
633             return false;
634         }
635     }
636     return true;
637 }
638
639 static HWND GetDesktopHandle(vout_display_t *vd)
640 {
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 );
645     if( hwnd )
646         return hwnd;
647
648     msg_Dbg( vd, "Couldn't find desktop icon window,. Trying the hard way." );
649
650     EnumWindows( enumWindowsProc, (LPARAM)&hwnd );
651     return hwnd;
652 }
653 #endif
654
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
660  * events.
661  *****************************************************************************/
662 static int Win32VoutCreateWindow( event_thread_t *p_event )
663 {
664     vout_display_t *vd = p_event->vd;
665     HINSTANCE  hInstance;
666     HMENU      hMenu;
667     RECT       rect_window;
668     WNDCLASS   wc;                            /* window class components */
669     TCHAR      vlc_path[MAX_PATH+1];
670     int        i_style, i_stylex;
671
672     msg_Dbg( vd, "Win32VoutCreateWindow" );
673
674     /* Get this module's instance */
675     hInstance = GetModuleHandle(NULL);
676
677     #ifdef MODULE_NAME_IS_direct3d
678     if( !p_event->use_desktop )
679     {
680     #endif
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;
685         else
686             p_event->hparent = NULL;
687     #ifdef MODULE_NAME_IS_direct3d
688     }
689     else
690     {
691         p_event->parent_window = NULL;
692         p_event->hparent = GetDesktopHandle(vd);
693     }
694     #endif
695     p_event->cursor_arrow = LoadCursor(NULL, IDC_ARROW);
696     p_event->cursor_empty = EmptyCursor(hInstance);
697
698     /* Get the Icon from the main app */
699     p_event->vlc_icon = NULL;
700     if( GetModuleFileName( NULL, vlc_path, MAX_PATH ) )
701     {
702         p_event->vlc_icon = ExtractIcon( hInstance, vlc_path, 0 );
703     }
704
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 */
717
718     /* Register the window class */
719     if( !RegisterClass(&wc) )
720     {
721         if( p_event->vlc_icon )
722             DestroyIcon( p_event->vlc_icon );
723
724         msg_Err( vd, "Win32VoutCreateWindow RegisterClass FAILED (err=%lu)", GetLastError() );
725         return VLC_EGENERIC;
726     }
727
728     /* Register the video sub-window class */
729     wc.lpszClassName = p_event->class_video;
730     wc.hIcon = 0;
731     wc.hbrBackground = NULL; /* no background color */
732     if( !RegisterClass(&wc) )
733     {
734         msg_Err( vd, "Win32VoutCreateWindow RegisterClass FAILED (err=%lu)", GetLastError() );
735         return VLC_EGENERIC;
736     }
737
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;
746
747     if( var_GetBool( vd, "video-deco" ) )
748     {
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;
752         i_stylex = 0;
753     }
754     else
755     {
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.
761     }
762
763     if( p_event->hparent )
764     {
765         i_style = WS_VISIBLE|WS_CLIPCHILDREN|WS_CHILD;
766         i_stylex = 0;
767
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;
773     }
774
775     p_event->i_window_style = i_style;
776
777     /* Create the window */
778     p_event->hwnd =
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 */
793
794     if( !p_event->hwnd )
795     {
796         msg_Warn( vd, "Win32VoutCreateWindow create window FAILED (err=%lu)", GetLastError() );
797         return VLC_EGENERIC;
798     }
799
800     if( p_event->hparent )
801     {
802         LONG i_style;
803
804         /* We don't want the window owner to overwrite our client area */
805         i_style = GetWindowLong( p_event->hparent, GWL_STYLE );
806
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 );
811
812         /* Create our fullscreen window */
813         p_event->hfswnd =
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 );
820     }
821     else
822     {
823         p_event->hfswnd = NULL;
824     }
825
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") );
831
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 */
836     p_event->hvideownd =
837     CreateWindow( p_event->class_video, _T(""),   /* window class */
838         WS_CHILD,                   /* window style, not visible initially */
839         0, 0,
840         vd->source.i_width,          /* default width */
841         vd->source.i_height,        /* default height */
842         p_event->hwnd,               /* parent window */
843         NULL, hInstance,
844         (LPVOID)p_event );    /* send vd to WM_CREATE */
845
846     if( !p_event->hvideownd )
847         msg_Warn( vd, "can't create video sub-window" );
848     else
849         msg_Dbg( vd, "created video sub-window" );
850
851     /* Now display the window */
852     ShowWindow( p_event->hwnd, SW_SHOW );
853
854     return VLC_SUCCESS;
855 }
856
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 )
863 {
864     vout_display_t *vd = p_event->vd;
865     msg_Dbg( vd, "Win32VoutCloseWindow" );
866
867     DestroyWindow( p_event->hwnd );
868     if( p_event->hfswnd )
869         DestroyWindow( p_event->hfswnd );
870
871     #ifdef MODULE_NAME_IS_direct3d
872     if( !p_event->use_desktop )
873     #endif
874         vout_display_DeleteWindow( vd, p_event->parent_window );
875     p_event->hwnd = NULL;
876
877     HINSTANCE hInstance = GetModuleHandle(NULL);
878     UnregisterClass( p_event->class_video, hInstance );
879     UnregisterClass( p_event->class_main, hInstance );
880
881     if( p_event->vlc_icon )
882         DestroyIcon( p_event->vlc_icon );
883
884     DestroyCursor( p_event->cursor_empty );
885 }
886
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 )
900 {
901     event_thread_t *p_event;
902
903     if( message == WM_CREATE )
904     {
905         /* Store vd for future use */
906         p_event = (event_thread_t *)((CREATESTRUCT *)lParam)->lpCreateParams;
907         SetWindowLongPtr( hwnd, GWLP_USERDATA, (LONG_PTR)p_event );
908         return TRUE;
909     }
910     else
911     {
912         LONG_PTR p_user_data = GetWindowLongPtr( hwnd, GWLP_USERDATA );
913         p_event = (event_thread_t *)p_user_data;
914         if( !p_event )
915         {
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);
919         }
920     }
921     vout_display_t *vd = p_event->vd;
922
923 #if 0
924     if( message == WM_SETCURSOR )
925     {
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 );
928         return 1;
929     }
930 #endif
931     if( message == WM_CAPTURECHANGED )
932     {
933         for( int button = 0; p_event->button_pressed; button++ )
934         {
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;
939         }
940         p_event->button_pressed = 0;
941         return 0;
942     }
943
944     if( hwnd == p_event->hvideownd )
945     {
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 );
950 #endif
951
952         switch( message )
953         {
954 #ifdef MODULE_NAME_IS_directdraw
955         case WM_ERASEBKGND:
956         /* For overlay, we need to erase background */
957             return !use_overlay ? 1 : DefWindowProc(hwnd, message, wParam, lParam);
958         case WM_PAINT:
959         /*
960         ** For overlay, DefWindowProc() will erase dirty regions
961         ** with colorkey.
962         ** For non-overlay, vout will paint the whole window at
963         ** regular interval, therefore dirty regions can be ignored
964         ** to minimize repaint.
965         */
966             if( !use_overlay )
967             {
968                 ValidateRect(hwnd, NULL);
969             }
970             // fall through to default
971 #else
972         /*
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.
976         */
977         case WM_ERASEBKGND:
978             /* nothing to erase */
979             return 1;
980         case WM_PAINT:
981             /* nothing to repaint */
982             ValidateRect(hwnd, NULL);
983             // fall through
984 #endif
985         default:
986             return DefWindowProc(hwnd, message, wParam, lParam);
987         }
988     }
989
990     switch( message )
991     {
992
993     case WM_WINDOWPOSCHANGED:
994         vlc_mutex_lock( &p_event->lock );
995         p_event->has_moved = true;
996         vlc_mutex_unlock( &p_event->lock );
997         return 0;
998
999     /* the user wants to close the window */
1000     case WM_CLOSE:
1001         vout_display_SendEventClose(vd);
1002         return 0;
1003
1004     /* the window has been closed so shut down everything now */
1005     case WM_DESTROY:
1006         msg_Dbg( vd, "WinProc WM_DESTROY" );
1007         /* just destroy the window */
1008         PostQuitMessage( 0 );
1009         return 0;
1010
1011     case WM_SYSCOMMAND:
1012         switch (wParam)
1013         {
1014         case IDM_TOGGLE_ON_TOP:            /* toggle the "on top" status */
1015         {
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);
1020             return 0;
1021         }
1022         default:
1023             break;
1024         }
1025         break;
1026
1027     case WM_PAINT:
1028     case WM_NCPAINT:
1029     case WM_ERASEBKGND:
1030         return DefWindowProc(hwnd, message, wParam, lParam);
1031
1032     case WM_KILLFOCUS:
1033         return 0;
1034
1035     case WM_SETFOCUS:
1036         return 0;
1037
1038     default:
1039         //msg_Dbg( vd, "WinProc WM Default %i", message );
1040         break;
1041     }
1042
1043     /* Let windows handle the message */
1044     return DefWindowProc(hwnd, message, wParam, lParam);
1045 }
1046
1047 static struct
1048 {
1049     int i_dxkey;
1050     int i_vlckey;
1051
1052 } dxkeys_to_vlckeys[] =
1053 {
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 },
1058
1059     { VK_RETURN, KEY_ENTER },
1060     { VK_SPACE, ' ' },
1061     { VK_ESCAPE, KEY_ESC },
1062
1063     { VK_LEFT, KEY_LEFT },
1064     { VK_RIGHT, KEY_RIGHT },
1065     { VK_UP, KEY_UP },
1066     { VK_DOWN, KEY_DOWN },
1067
1068     { VK_HOME, KEY_HOME },
1069     { VK_END, KEY_END },
1070     { VK_PRIOR, KEY_PAGEUP },
1071     { VK_NEXT, KEY_PAGEDOWN },
1072
1073     { VK_INSERT, KEY_INSERT },
1074     { VK_DELETE, KEY_DELETE },
1075
1076     { VK_CONTROL, 0 },
1077     { VK_SHIFT, 0 },
1078     { VK_MENU, 0 },
1079
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 },
1094
1095     { 0, 0 }
1096 };
1097
1098 static int Win32VoutConvertKey( int i_key )
1099 {
1100     for( int i = 0; dxkeys_to_vlckeys[i].i_dxkey != 0; i++ )
1101     {
1102         if( dxkeys_to_vlckeys[i].i_dxkey == i_key )
1103         {
1104             return dxkeys_to_vlckeys[i].i_vlckey;
1105         }
1106     }
1107
1108     return 0;
1109 }
1110