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