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