events.c 33.8 KB
Newer Older
1
2
3
/*****************************************************************************
 * events.c: Windows DirectX video output events handler
 *****************************************************************************
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
4
 * Copyright (C) 2001-2004 VideoLAN (Centrale Réseaux) and its contributors
5
 * $Id$
6
 *
gbazin's avatar
gbazin committed
7
 * Authors: Gildas Bazin <gbazin@videolan.org>
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/


/*****************************************************************************
 * Preamble: This file contains the functions related to the creation of
 *             a window and the handling of its messages (events).
 *****************************************************************************/
#include <errno.h>                                                 /* ENOMEM */
#include <stdlib.h>                                                /* free() */
31
#include <ctype.h>                                              /* tolower() */
32
33
#include <string.h>                                            /* strerror() */

34
35
36
37
#ifndef _WIN32_WINNT
#   define _WIN32_WINNT 0x0400
#endif

38
39
#include <vlc/vlc.h>
#include <vlc/intf.h>
40
#include <vlc/input.h>
41
42
43
44
45
46
47
48
#include <vlc/vout.h>

#include <windows.h>
#include <windowsx.h>
#include <shellapi.h>

#include <ddraw.h>

49
#include "vlc_keys.h"
50
51
52
53
54
55
56
#include "vout.h"

/*****************************************************************************
 * Local prototypes.
 *****************************************************************************/
static int  DirectXCreateWindow( vout_thread_t *p_vout );
static void DirectXCloseWindow ( vout_thread_t *p_vout );
gbazin's avatar
   
gbazin committed
57
static long FAR PASCAL DirectXEventProc( HWND, UINT, WPARAM, LPARAM );
58

gbazin's avatar
gbazin committed
59
60
static int Control( vout_thread_t *p_vout, int i_query, va_list args );

61
62
63
64
65
66
67
68
69
70
71
72
73
static void DirectXPopupMenu( event_thread_t *p_event, vlc_bool_t b_open )
{
    playlist_t *p_playlist =
        vlc_object_find( p_event, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
    if( p_playlist != NULL )
    {
        vlc_value_t val;
        val.b_bool = b_open;
        var_Set( p_playlist, "intf-popupmenu", val );
        vlc_object_release( p_playlist );
    }
}

74
75
static int DirectXConvertKey( int i_key );

76
77
78
79
80
81
82
83
/*****************************************************************************
 * DirectXEventThread: Create video window & handle its messages
 *****************************************************************************
 * This function creates a video window and then enters an infinite loop
 * that handles the messages sent to that window.
 * The main goal of this thread is to isolate the Win32 PeekMessage function
 * because this one can block for a long time.
 *****************************************************************************/
84
void E_(DirectXEventThread)( event_thread_t *p_event )
85
86
{
    MSG msg;
gbazin's avatar
gbazin committed
87
    POINT old_mouse_pos = {0,0}, mouse_pos;
88
89
    vlc_value_t val;
    int i_width, i_height, i_x, i_y;
90
    HMODULE hkernel32;
91
92

    /* Initialisation */
gbazin's avatar
gbazin committed
93
    p_event->p_vout->pf_control = Control;
94
95
96

    /* Create a window for the video */
    /* Creating a window under Windows also initializes the thread's event
97
     * message queue */
98
99
100
    if( DirectXCreateWindow( p_event->p_vout ) )
    {
        msg_Err( p_event, "out of memory" );
101
        p_event->b_dead = VLC_TRUE;
102
103
    }

104
    /* Signal the creation of the window */
105
106
    vlc_thread_ready( p_event );

107
    /* Set power management stuff */
108
    if( (hkernel32 = GetModuleHandle( _T("KERNEL32") ) ) )
109
110
111
    {
        ULONG (WINAPI* OurSetThreadExecutionState)( ULONG );

112
        OurSetThreadExecutionState = (ULONG (WINAPI*)( ULONG ))
113
            GetProcAddress( hkernel32, _T("SetThreadExecutionState") );
114
115
116

        if( OurSetThreadExecutionState )
            /* Prevent monitor from powering off */
117
            OurSetThreadExecutionState( ES_DISPLAY_REQUIRED | ES_CONTINUOUS );
118
119
120
121
        else
            msg_Dbg( p_event, "no support for SetThreadExecutionState()" );
    }

122
123
    /* Main loop */
    /* GetMessage will sleep if there's no message in the queue */
124
    while( !p_event->b_die && GetMessage( &msg, 0, 0, 0 ) )
125
126
127
128
129
130
131
132
133
    {
        /* Check if we are asked to exit */
        if( p_event->b_die )
            break;

        switch( msg.message )
        {

        case WM_MOUSEMOVE:
134
135
136
137
138
            vout_PlacePicture( p_event->p_vout,
                               p_event->p_vout->p_sys->i_window_width,
                               p_event->p_vout->p_sys->i_window_height,
                               &i_x, &i_y, &i_width, &i_height );

139
            if( msg.hwnd == p_event->p_vout->p_sys->hvideownd )
140
141
142
143
144
            {
                /* Child window */
                i_x = i_y = 0;
            }

gbazin's avatar
gbazin committed
145
146
147
148
149
150
151
152
153
154
155
156
            if( i_width && i_height )
            {
                val.i_int = ( GET_X_LPARAM(msg.lParam) - i_x )
                             * p_event->p_vout->render.i_width / i_width;
                var_Set( p_event->p_vout, "mouse-x", val );
                val.i_int = ( GET_Y_LPARAM(msg.lParam) - i_y )
                             * p_event->p_vout->render.i_height / i_height;
                var_Set( p_event->p_vout, "mouse-y", val );

                val.b_bool = VLC_TRUE;
                var_Set( p_event->p_vout, "mouse-moved", val );
            }
157

158
        case WM_NCMOUSEMOVE:
gbazin's avatar
gbazin committed
159
160
161
            GetCursorPos( &mouse_pos );
            if( (abs(mouse_pos.x - old_mouse_pos.x) > 2 ||
                (abs(mouse_pos.y - old_mouse_pos.y)) > 2 ) )
162
163
164
165
166
167
168
169
170
171
172
173
174
            {
                GetCursorPos( &old_mouse_pos );
                p_event->p_vout->p_sys->i_lastmoved = mdate();

                if( p_event->p_vout->p_sys->b_cursor_hidden )
                {
                    p_event->p_vout->p_sys->b_cursor_hidden = 0;
                    ShowCursor( TRUE );
                }
            }
            break;

        case WM_VLC_HIDE_MOUSE:
175
176
            if( p_event->p_vout->p_sys->b_cursor_hidden ) break;
            p_event->p_vout->p_sys->b_cursor_hidden = VLC_TRUE;
177
178
            GetCursorPos( &old_mouse_pos );
            ShowCursor( FALSE );
179
180
181
182
183
184
185
            break;

        case WM_VLC_SHOW_MOUSE:
            if( !p_event->p_vout->p_sys->b_cursor_hidden ) break;
            p_event->p_vout->p_sys->b_cursor_hidden = VLC_FALSE;
            GetCursorPos( &old_mouse_pos );
            ShowCursor( TRUE );
186
187
            break;

gbazin's avatar
   
gbazin committed
188
189
190
191
        case WM_LBUTTONDOWN:
            var_Get( p_event->p_vout, "mouse-button-down", &val );
            val.i_int |= 1;
            var_Set( p_event->p_vout, "mouse-button-down", val );
192
            DirectXPopupMenu( p_event, VLC_FALSE );
gbazin's avatar
   
gbazin committed
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
            break;

        case WM_LBUTTONUP:
            var_Get( p_event->p_vout, "mouse-button-down", &val );
            val.i_int &= ~1;
            var_Set( p_event->p_vout, "mouse-button-down", val );

            val.b_bool = VLC_TRUE;
            var_Set( p_event->p_vout, "mouse-clicked", val );
            break;

        case WM_LBUTTONDBLCLK:
            p_event->p_vout->p_sys->i_changes |= VOUT_FULLSCREEN_CHANGE;
            break;

        case WM_MBUTTONDOWN:
            var_Get( p_event->p_vout, "mouse-button-down", &val );
            val.i_int |= 2;
            var_Set( p_event->p_vout, "mouse-button-down", val );
212
            DirectXPopupMenu( p_event, VLC_FALSE );
gbazin's avatar
   
gbazin committed
213
214
215
216
217
218
219
220
221
222
223
224
            break;

        case WM_MBUTTONUP:
            var_Get( p_event->p_vout, "mouse-button-down", &val );
            val.i_int &= ~2;
            var_Set( p_event->p_vout, "mouse-button-down", val );
            break;

        case WM_RBUTTONDOWN:
            var_Get( p_event->p_vout, "mouse-button-down", &val );
            val.i_int |= 4;
            var_Set( p_event->p_vout, "mouse-button-down", val );
225
            DirectXPopupMenu( p_event, VLC_FALSE );
gbazin's avatar
   
gbazin committed
226
227
            break;

228
        case WM_RBUTTONUP:
gbazin's avatar
   
gbazin committed
229
230
231
            var_Get( p_event->p_vout, "mouse-button-down", &val );
            val.i_int &= ~4;
            var_Set( p_event->p_vout, "mouse-button-down", val );
232
            DirectXPopupMenu( p_event, VLC_TRUE );
233
234
235
            break;

        case WM_KEYDOWN:
gbazin's avatar
   
gbazin committed
236
        case WM_SYSKEYDOWN:
237
238
239
240
241
            /* The key events are first processed here and not translated
             * into WM_CHAR events because we need to know the status of the
             * modifier keys. */
            val.i_int = DirectXConvertKey( msg.wParam );
            if( !val.i_int )
242
            {
243
244
                /* This appears to be a "normal" (ascii) key */
                val.i_int = tolower( MapVirtualKey( msg.wParam, 2 ) );
245
            }
246

247
248
249
            if( val.i_int )
            {
                if( GetKeyState(VK_CONTROL) & 0x8000 )
250
                {
251
                    val.i_int |= KEY_MODIFIER_CTRL;
252
                }
gbazin's avatar
   
gbazin committed
253
                if( GetKeyState(VK_SHIFT) & 0x8000 )
gbazin's avatar
   
gbazin committed
254
                {
255
                    val.i_int |= KEY_MODIFIER_SHIFT;
gbazin's avatar
   
gbazin committed
256
                }
gbazin's avatar
   
gbazin committed
257
                if( GetKeyState(VK_MENU) & 0x8000 )
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
                {
                    val.i_int |= KEY_MODIFIER_ALT;
                }

                var_Set( p_event->p_vlc, "key-pressed", val );
            }
            break;

        case WM_MOUSEWHEEL:
            if( GET_WHEEL_DELTA_WPARAM( msg.wParam ) > 0 )
            {
                val.i_int = KEY_MOUSEWHEELUP;
            }
            else
            {
                val.i_int = KEY_MOUSEWHEELDOWN;
            }
            if( val.i_int )
            {
                if( GetKeyState(VK_CONTROL) & 0x8000 )
                {
                    val.i_int |= KEY_MODIFIER_CTRL;
                }
                if( GetKeyState(VK_SHIFT) & 0x8000 )
                {
                    val.i_int |= KEY_MODIFIER_SHIFT;
                }
                if( GetKeyState(VK_MENU) & 0x8000 )
gbazin's avatar
   
gbazin committed
286
                {
287
                    val.i_int |= KEY_MODIFIER_ALT;
gbazin's avatar
   
gbazin committed
288
                }
289

gbazin's avatar
   
gbazin committed
290
                var_Set( p_event->p_vlc, "key-pressed", val );
291
292
293
            }
            break;

294
        case WM_VLC_CHANGE_TEXT:
295
296
297
298
            var_Get( p_event->p_vout, "video-title", &val );

            if( !val.psz_string || !*val.psz_string ) /* Default video title */
            {
299
300
#ifdef MODULE_NAME_IS_glwin32
                SetWindowText( p_event->p_vout->p_sys->hwnd,
301
                    _T(VOUT_TITLE) _T(" (OpenGL output)") );
302
#else
303
                if( p_event->p_vout->p_sys->b_using_overlay )
304
305
                    SetWindowText( p_event->p_vout->p_sys->hwnd, _T(VOUT_TITLE)
                        _T(" (hardware YUV overlay DirectX output)") );
306
                else if( p_event->p_vout->p_sys->b_hw_yuv )
307
308
309
310
311
                    SetWindowText( p_event->p_vout->p_sys->hwnd, _T(VOUT_TITLE)
                        _T(" (hardware YUV DirectX output)") );
                else
                    SetWindowText( p_event->p_vout->p_sys->hwnd, _T(VOUT_TITLE)
                        _T(" (software RGB DirectX output)") );
312
#endif
313
314
315
316
317
            }
            else
            {
                SetWindowText( p_event->p_vout->p_sys->hwnd, val.psz_string );
            }
318
319
            break;

320
321
322
323
324
325
326
327
328
329
330
        default:
            /* Messages we don't handle directly are dispatched to the
             * window procedure */
            TranslateMessage(&msg);
            DispatchMessage(&msg);
            break;

        } /* End Switch */

    } /* End Main loop */

331
332
    /* Check for WM_QUIT if we created the window */
    if( !p_event->p_vout->p_sys->hparent && msg.message == WM_QUIT )
333
334
335
336
337
    {
        msg_Warn( p_event, "WM_QUIT... should not happen!!" );
        p_event->p_vout->p_sys->hwnd = NULL; /* Window already destroyed */
    }

338
    msg_Dbg( p_event, "DirectXEventThread terminating" );
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358

    /* clear the changes formerly signaled */
    p_event->p_vout->p_sys->i_changes = 0;

    DirectXCloseWindow( p_event->p_vout );
}


/* following functions are local */

/*****************************************************************************
 * DirectXCreateWindow: create a window for the video.
 *****************************************************************************
 * Before creating a direct draw surface, we need to create a window in which
 * the video will be displayed. This window will also allow us to capture the
 * events.
 *****************************************************************************/
static int DirectXCreateWindow( vout_thread_t *p_vout )
{
    HINSTANCE  hInstance;
359
    HMENU      hMenu;
360
    RECT       rect_window;
361
    WNDCLASS   wc;                            /* window class components */
gbazin's avatar
gbazin committed
362
363
    HICON      vlc_icon = NULL;
    char       vlc_path[MAX_PATH+1];
364
    int        i_style, i_stylex;
365
366
367

    msg_Dbg( p_vout, "DirectXCreateWindow" );

368
    /* Get this module's instance */
369
370
    hInstance = GetModuleHandle(NULL);

371
    /* If an external window was specified, we'll draw in it. */
gbazin's avatar
gbazin committed
372
    p_vout->p_sys->hparent =
gbazin's avatar
gbazin committed
373
374
375
376
        vout_RequestWindow( p_vout, &p_vout->p_sys->i_window_x,
                            &p_vout->p_sys->i_window_y,
                            &p_vout->p_sys->i_window_width,
                            &p_vout->p_sys->i_window_height );
377

gbazin's avatar
gbazin committed
378
379
380
381
382
    /* We create the window ourself, there is no previous window proc. */
    p_vout->p_sys->pf_wndproc = NULL;

    /* Get the Icon from the main app */
    vlc_icon = NULL;
383
#ifndef UNDER_CE
gbazin's avatar
gbazin committed
384
    if( GetModuleFileName( NULL, vlc_path, MAX_PATH ) )
385
    {
gbazin's avatar
gbazin committed
386
        vlc_icon = ExtractIcon( hInstance, vlc_path, 0 );
387
    }
388
#endif
gbazin's avatar
gbazin committed
389
390

    /* Fill in the window class structure */
391
    wc.style         = CS_OWNDC|CS_DBLCLKS;          /* style: dbl click */
gbazin's avatar
gbazin committed
392
393
394
395
396
397
398
399
    wc.lpfnWndProc   = (WNDPROC)DirectXEventProc;       /* event handler */
    wc.cbClsExtra    = 0;                         /* no extra class data */
    wc.cbWndExtra    = 0;                        /* no extra window data */
    wc.hInstance     = hInstance;                            /* instance */
    wc.hIcon         = vlc_icon;                /* load the vlc big icon */
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);    /* default cursor */
    wc.hbrBackground = GetStockObject(BLACK_BRUSH);  /* background color */
    wc.lpszMenuName  = NULL;                                  /* no menu */
400
    wc.lpszClassName = _T("VLC DirectX");         /* use a special class */
gbazin's avatar
gbazin committed
401
402

    /* Register the window class */
403
    if( !RegisterClass(&wc) )
404
    {
gbazin's avatar
gbazin committed
405
        WNDCLASS wndclass;
406

gbazin's avatar
gbazin committed
407
        if( vlc_icon ) DestroyIcon( vlc_icon );
408

gbazin's avatar
gbazin committed
409
410
        /* Check why it failed. If it's because one already exists
         * then fine, otherwise return with an error. */
411
        if( !GetClassInfo( hInstance, _T("VLC DirectX"), &wndclass ) )
412
        {
gbazin's avatar
gbazin committed
413
414
            msg_Err( p_vout, "DirectXCreateWindow RegisterClass FAILED" );
            return VLC_EGENERIC;
415
        }
gbazin's avatar
gbazin committed
416
    }
417

418
    /* Register the video sub-window class */
419
420
    wc.lpszClassName = _T("VLC DirectX video"); wc.hIcon = 0;
    if( !RegisterClass(&wc) )
421
422
423
424
425
    {
        WNDCLASS wndclass;

        /* Check why it failed. If it's because one already exists
         * then fine, otherwise return with an error. */
426
        if( !GetClassInfo( hInstance, _T("VLC DirectX video"), &wndclass ) )
427
428
429
430
431
432
        {
            msg_Err( p_vout, "DirectXCreateWindow RegisterClass FAILED" );
            return VLC_EGENERIC;
        }
    }

gbazin's avatar
gbazin committed
433
434
435
436
437
438
439
440
    /* When you create a window you give the dimensions you wish it to
     * have. Unfortunatly these dimensions will include the borders and
     * titlebar. We use the following function to find out the size of
     * the window corresponding to the useable surface we want */
    rect_window.top    = 10;
    rect_window.left   = 10;
    rect_window.right  = rect_window.left + p_vout->p_sys->i_window_width;
    rect_window.bottom = rect_window.top + p_vout->p_sys->i_window_height;
441

442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
    if( var_GetBool( p_vout, "video-deco" ) )
    {
        /* Open with window decoration */
        AdjustWindowRect( &rect_window, WS_OVERLAPPEDWINDOW|WS_SIZEBOX, 0 );
        i_style = WS_OVERLAPPEDWINDOW|WS_SIZEBOX|WS_VISIBLE|WS_CLIPCHILDREN;
        i_stylex = 0;
    }
    else
    {
        /* No window decoration */
        AdjustWindowRect( &rect_window, WS_POPUP, 0 );
        i_style = WS_POPUP|WS_VISIBLE|WS_CLIPCHILDREN;
        i_stylex = 0; // WS_EX_TOOLWINDOW; Is TOOLWINDOW really needed ?
                      // It messes up the fullscreen window.
    }
457

gbazin's avatar
gbazin committed
458
    if( p_vout->p_sys->hparent )
459
    {
gbazin's avatar
gbazin committed
460
        i_style = WS_VISIBLE|WS_CLIPCHILDREN|WS_CHILD;
461
        i_stylex = 0;
462
    }
463

464
465
    p_vout->p_sys->i_window_style = i_style;

gbazin's avatar
gbazin committed
466
467
    /* Create the window */
    p_vout->p_sys->hwnd =
468
        CreateWindowEx( WS_EX_NOPARENTNOTIFY | i_stylex,
469
470
                    _T("VLC DirectX"),               /* name of window class */
                    _T(VOUT_TITLE) _T(" (DirectX Output)"),  /* window title */
gbazin's avatar
gbazin committed
471
                    i_style,                                 /* window style */
gbazin's avatar
gbazin committed
472
473
474
475
                    (p_vout->p_sys->i_window_x < 0) ? CW_USEDEFAULT :
                        p_vout->p_sys->i_window_x,   /* default X coordinate */
                    (p_vout->p_sys->i_window_y < 0) ? CW_USEDEFAULT :
                        p_vout->p_sys->i_window_y,   /* default Y coordinate */
476
477
                    rect_window.right - rect_window.left,    /* window width */
                    rect_window.bottom - rect_window.top,   /* window height */
gbazin's avatar
gbazin committed
478
                    p_vout->p_sys->hparent,                 /* parent window */
479
480
                    NULL,                          /* no menu in this window */
                    hInstance,            /* handle of this program instance */
481
                    (LPVOID)p_vout );            /* send p_vout to WM_CREATE */
482

gbazin's avatar
gbazin committed
483
484
485
486
487
488
489
490
491
492
493
494
    if( !p_vout->p_sys->hwnd )
    {
        msg_Warn( p_vout, "DirectXCreateWindow create window FAILED" );
        return VLC_EGENERIC;
    }

    if( p_vout->p_sys->hparent )
    {
        LONG i_style;

        /* We don't want the window owner to overwrite our client area */
        i_style = GetWindowLong( p_vout->p_sys->hparent, GWL_STYLE );
495
496
497
498
499

        if( !(i_style & WS_CLIPCHILDREN) )
            /* Hmmm, apparently this is a blocking call... */
            SetWindowLong( p_vout->p_sys->hparent, GWL_STYLE,
                           i_style | WS_CLIPCHILDREN );
500
501
502

        /* Create our fullscreen window */
        p_vout->p_sys->hfswnd =
503
504
            CreateWindowEx( WS_EX_APPWINDOW, _T("VLC DirectX"),
                            _T(VOUT_TITLE) _T(" (DirectX Output)"),
505
506
507
508
                            WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN|WS_SIZEBOX,
                            CW_USEDEFAULT, CW_USEDEFAULT,
                            CW_USEDEFAULT, CW_USEDEFAULT,
                            NULL, NULL, hInstance, NULL );
509
510
    }

511
512
513
514
515
516
517
518
    /* Now display the window */
    ShowWindow( p_vout->p_sys->hwnd, SW_SHOW );

    /* Create video sub-window. This sub window will always exactly match
     * the size of the video, which allows us to use crazy overlay colorkeys
     * without having them shown outside of the video area. */
    SendMessage( p_vout->p_sys->hwnd, WM_VLC_CREATE_VIDEO_WIN, 0, 0 );

519
    /* Append a "Always On Top" entry in the system menu */
520
    hMenu = GetSystemMenu( p_vout->p_sys->hwnd, FALSE );
521
    AppendMenu( hMenu, MF_SEPARATOR, 0, _T("") );
522
    AppendMenu( hMenu, MF_STRING | MF_UNCHECKED,
523
                       IDM_TOGGLE_ON_TOP, _T("Always on &Top") );
524

525
    return VLC_SUCCESS;
526
527
528
529
530
531
532
533
534
535
536
}

/*****************************************************************************
 * DirectXCloseWindow: close the window created by DirectXCreateWindow
 *****************************************************************************
 * This function returns all resources allocated by DirectXCreateWindow.
 *****************************************************************************/
static void DirectXCloseWindow( vout_thread_t *p_vout )
{
    msg_Dbg( p_vout, "DirectXCloseWindow" );

gbazin's avatar
gbazin committed
537
    DestroyWindow( p_vout->p_sys->hwnd );
538
    if( p_vout->p_sys->hfswnd ) DestroyWindow( p_vout->p_sys->hfswnd );
gbazin's avatar
gbazin committed
539

gbazin's avatar
gbazin committed
540
    if( p_vout->p_sys->hparent )
gbazin's avatar
gbazin committed
541
        vout_ReleaseWindow( p_vout, (void *)p_vout->p_sys->hparent );
542

543
544
    p_vout->p_sys->hwnd = NULL;

545
546
547
548
549
550
    /* We don't unregister the Window Class because it could lead to race
     * conditions and it will be done anyway by the system when the app will
     * exit */
}

/*****************************************************************************
551
 * DirectXUpdateRects: update clipping rectangles
552
 *****************************************************************************
553
 * This function is called when the window position or size are changed, and
554
555
556
 * its job is to update the source and destination RECTs used to display the
 * picture.
 *****************************************************************************/
557
void E_(DirectXUpdateRects)( vout_thread_t *p_vout, vlc_bool_t b_force )
558
559
560
561
562
563
{
#define rect_src p_vout->p_sys->rect_src
#define rect_src_clipped p_vout->p_sys->rect_src_clipped
#define rect_dest p_vout->p_sys->rect_dest
#define rect_dest_clipped p_vout->p_sys->rect_dest_clipped

564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
    int i_width, i_height, i_x, i_y;

    RECT  rect;
    POINT point;

    /* Retrieve the window size */
    GetClientRect( p_vout->p_sys->hwnd, &rect );

    /* Retrieve the window position */
    point.x = point.y = 0;
    ClientToScreen( p_vout->p_sys->hwnd, &point );

    /* If nothing changed, we can return */
    if( !b_force
         && p_vout->p_sys->i_window_width == rect.right
         && p_vout->p_sys->i_window_height == rect.bottom
         && p_vout->p_sys->i_window_x == point.x
         && p_vout->p_sys->i_window_y == point.y )
    {
        return;
    }

    /* Update the window position and size */
    p_vout->p_sys->i_window_x = point.x;
    p_vout->p_sys->i_window_y = point.y;
    p_vout->p_sys->i_window_width = rect.right;
    p_vout->p_sys->i_window_height = rect.bottom;

    vout_PlacePicture( p_vout, rect.right, rect.bottom,
593
594
                       &i_x, &i_y, &i_width, &i_height );

595
596
597
    if( p_vout->p_sys->hvideownd )
        SetWindowPos( p_vout->p_sys->hvideownd, HWND_TOP,
                      i_x, i_y, i_width, i_height, 0 );
598

599
    /* Destination image position and dimensions */
600
    rect_dest.left = point.x + i_x;
601
    rect_dest.right = rect_dest.left + i_width;
602
    rect_dest.top = point.y + i_y;
603
604
    rect_dest.bottom = rect_dest.top + i_height;

605
606
607
608
609
610
611
612
613
614
615
616
617
618
    /* Apply overlay hardware constraints */
    if( p_vout->p_sys->b_using_overlay )
    {
        if( p_vout->p_sys->i_align_dest_boundary )
            rect_dest.left = ( rect_dest.left +
                p_vout->p_sys->i_align_dest_boundary / 2 ) & 
                ~p_vout->p_sys->i_align_dest_boundary;

        if( p_vout->p_sys->i_align_dest_size )
            rect_dest.right = (( rect_dest.right - rect_dest.left +
                p_vout->p_sys->i_align_dest_size / 2 ) & 
                ~p_vout->p_sys->i_align_dest_size) + rect_dest.left;
    }

619
    /* UpdateOverlay directdraw function doesn't automatically clip to the
620
     * display size so we need to do it otherwise it will fail */
621
622

    /* Clip the destination window */
623
624
625
626
627
628
    if( !IntersectRect( &rect_dest_clipped, &rect_dest,
                        &p_vout->p_sys->rect_display ) )
    {
        SetRectEmpty( &rect_src_clipped );
        return;
    }
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653

#if 0
    msg_Dbg( p_vout, "DirectXUpdateRects image_dst_clipped coords:"
                     " %i,%i,%i,%i",
                     rect_dest_clipped.left, rect_dest_clipped.top,
                     rect_dest_clipped.right, rect_dest_clipped.bottom );
#endif

    /* the 2 following lines are to fix a bug when clicking on the desktop */
    if( (rect_dest_clipped.right - rect_dest_clipped.left)==0 ||
        (rect_dest_clipped.bottom - rect_dest_clipped.top)==0 )
    {
        SetRectEmpty( &rect_src_clipped );
        return;
    }

    /* src image dimensions */
    rect_src.left = 0;
    rect_src.top = 0;
    rect_src.right = p_vout->render.i_width;
    rect_src.bottom = p_vout->render.i_height;

    /* Clip the source image */
    rect_src_clipped.left = (rect_dest_clipped.left - rect_dest.left) *
      p_vout->render.i_width / (rect_dest.right - rect_dest.left);
654
    rect_src_clipped.right = p_vout->render.i_width -
655
656
657
658
659
660
661
662
      (rect_dest.right - rect_dest_clipped.right) * p_vout->render.i_width /
      (rect_dest.right - rect_dest.left);
    rect_src_clipped.top = (rect_dest_clipped.top - rect_dest.top) *
      p_vout->render.i_height / (rect_dest.bottom - rect_dest.top);
    rect_src_clipped.bottom = p_vout->render.i_height -
      (rect_dest.bottom - rect_dest_clipped.bottom) * p_vout->render.i_height /
      (rect_dest.bottom - rect_dest.top);

663
664
665
666
667
668
669
670
671
672
673
674
675
676
    /* Apply overlay hardware constraints */
    if( p_vout->p_sys->b_using_overlay )
    {
        if( p_vout->p_sys->i_align_src_boundary )
            rect_src_clipped.left = ( rect_src_clipped.left +
                p_vout->p_sys->i_align_src_boundary / 2 ) & 
                ~p_vout->p_sys->i_align_src_boundary;

        if( p_vout->p_sys->i_align_src_size )
            rect_src_clipped.right = (( rect_src_clipped.right -
                rect_src_clipped.left +
                p_vout->p_sys->i_align_src_size / 2 ) & 
                ~p_vout->p_sys->i_align_src_size) + rect_src_clipped.left;
    }
gbazin's avatar
   
gbazin committed
677

678
679
680
681
682
683
684
#if 0
    msg_Dbg( p_vout, "DirectXUpdateRects image_src_clipped"
                     " coords: %i,%i,%i,%i",
                     rect_src_clipped.left, rect_src_clipped.top,
                     rect_src_clipped.right, rect_src_clipped.bottom );
#endif

685
686
687
688
689
690
691
    /* The destination coordinates need to be relative to the current
     * directdraw primary surface (display) */
    rect_dest_clipped.left -= p_vout->p_sys->rect_display.left;
    rect_dest_clipped.right -= p_vout->p_sys->rect_display.left;
    rect_dest_clipped.top -= p_vout->p_sys->rect_display.top;
    rect_dest_clipped.bottom -= p_vout->p_sys->rect_display.top;

gbazin's avatar
gbazin committed
692
    if( p_vout->p_sys->b_using_overlay )
693
        E_(DirectXUpdateOverlay)( p_vout );
gbazin's avatar
gbazin committed
694

695
696
    /* Signal the change in size/position */
    p_vout->p_sys->i_changes |= DX_POSITION_CHANGE;
697

698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
#undef rect_src
#undef rect_src_clipped
#undef rect_dest
#undef rect_dest_clipped
}

/*****************************************************************************
 * DirectXEventProc: This is the window event processing function.
 *****************************************************************************
 * On Windows, when you create a window you have to attach an event processing
 * function to it. The aim of this function is to manage "Queued Messages" and
 * "Nonqueued Messages".
 * Queued Messages are those picked up and retransmitted by vout_Manage
 * (using the GetMessage and DispatchMessage functions).
 * Nonqueued Messages are those that Windows will send directly to this
 * procedure (like WM_DESTROY, WM_WINDOWPOSCHANGED...)
 *****************************************************************************/
static long FAR PASCAL DirectXEventProc( HWND hwnd, UINT message,
                                         WPARAM wParam, LPARAM lParam )
{
718
    vout_thread_t *p_vout;
719

720
721
722
723
    if( message == WM_CREATE )
    {
        /* Store p_vout for future use */
        p_vout = (vout_thread_t *)((CREATESTRUCT *)lParam)->lpCreateParams;
724
        SetWindowLongPtr( hwnd, GWLP_USERDATA, (LONG_PTR)p_vout );
725
726
    }
    else
727
    {
728
        p_vout = (vout_thread_t *)GetWindowLongPtr( hwnd, GWLP_USERDATA );
729
730
    }

731
732
733
734
735
736
737
738
    /* Catch the screensaver and the monitor turn-off */
    if( message == WM_SYSCOMMAND &&
        ( wParam == SC_SCREENSAVE || wParam == SC_MONITORPOWER ) )
    {
        //if( p_vout ) msg_Dbg( p_vout, "WinProc WM_SYSCOMMAND screensaver" );
        return 0; /* this stops them from happening */
    }

gbazin's avatar
   
gbazin committed
739
740
741
742
743
744
745
    if( !p_vout )
    {
        /* Hmmm mozilla does manage somehow to save the pointer to our
         * windowproc and still calls it after the vout has been closed. */
        return DefWindowProc(hwnd, message, wParam, lParam);
    }

746
747
748
    if( hwnd == p_vout->p_sys->hvideownd )
        return DefWindowProc(hwnd, message, wParam, lParam);

749
750
751
752
    switch( message )
    {

    case WM_WINDOWPOSCHANGED:
753
        E_(DirectXUpdateRects)( p_vout, VLC_TRUE );
754
755
756
757
        return 0;

    /* the user wants to close the window */
    case WM_CLOSE:
758
759
760
761
762
763
    {
        playlist_t * p_playlist =
            (playlist_t *)vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
                                           FIND_ANYWHERE );
        if( p_playlist == NULL )
        {
764
            return 0;
765
766
767
768
        }

        playlist_Stop( p_playlist );
        vlc_object_release( p_playlist );
769
        return 0;
770
    }
771
772
773
774
775
776
777
778
779
780
781

    /* the window has been closed so shut down everything now */
    case WM_DESTROY:
        msg_Dbg( p_vout, "WinProc WM_DESTROY" );
        /* just destroy the window */
        PostQuitMessage( 0 );
        return 0;

    case WM_SYSCOMMAND:
        switch (wParam)
        {
782
783
            case IDM_TOGGLE_ON_TOP:            /* toggle the "on top" status */
            {
784
                vlc_value_t val;
785
786
                msg_Dbg( p_vout, "WinProc WM_SYSCOMMAND: IDM_TOGGLE_ON_TOP");

787
788
                /* Change the current value */
                var_Get( p_vout, "video-on-top", &val );
789
                val.b_bool = !val.b_bool;
gbazin's avatar
   
gbazin committed
790
                var_Set( p_vout, "video-on-top", val );
791
792
                return 0;
            }
793
794
795
        }
        break;

796
797
798
    case WM_VLC_CREATE_VIDEO_WIN:
        /* Create video sub-window */
        p_vout->p_sys->hvideownd =
799
            CreateWindow( _T("VLC DirectX video"), _T(""),   /* window class */
800
801
802
803
                    WS_CHILD | WS_VISIBLE,                   /* window style */
                    CW_USEDEFAULT, CW_USEDEFAULT,     /* default coordinates */
                    CW_USEDEFAULT, CW_USEDEFAULT,
                    hwnd,                                   /* parent window */
804
805
                    NULL, GetModuleHandle(NULL),
                    (LPVOID)p_vout );            /* send p_vout to WM_CREATE */
806
807

        if( !p_vout->p_sys->hvideownd )
gbazin's avatar
   
gbazin committed
808
            msg_Warn( p_vout, "Can't create video sub-window" );
809
        else
gbazin's avatar
   
gbazin committed
810
            msg_Dbg( p_vout, "Created video sub-window" );
811
812
        break;

gbazin's avatar
gbazin committed
813
814
815
816
817
818
819
820
    case WM_PAINT:
    case WM_NCPAINT:
    case WM_ERASEBKGND:
        /* We do not want to relay these messages to the parent window
         * because we rely on the background color for the overlay. */
        return DefWindowProc(hwnd, message, wParam, lParam);
        break;

821
822
823
824
825
    default:
        //msg_Dbg( p_vout, "WinProc WM Default %i", message );
        break;
    }

gbazin's avatar
gbazin committed
826
827
    /* Let windows handle the message */
    return DefWindowProc(hwnd, message, wParam, lParam);
gbazin's avatar
   
gbazin committed
828
}
829

830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
static struct
{
    int i_dxkey;
    int i_vlckey;

} dxkeys_to_vlckeys[] =
{
    { VK_F1, KEY_F1 }, { VK_F2, KEY_F2 }, { VK_F3, KEY_F3 }, { VK_F4, KEY_F4 },
    { VK_F5, KEY_F5 }, { VK_F6, KEY_F6 }, { VK_F7, KEY_F7 }, { VK_F8, KEY_F8 },
    { VK_F9, KEY_F9 }, { VK_F10, KEY_F10 }, { VK_F11, KEY_F11 },
    { VK_F12, KEY_F12 },

    { VK_RETURN, KEY_ENTER },
    { VK_SPACE, KEY_SPACE },
    { VK_ESCAPE, KEY_ESC },

    { VK_LEFT, KEY_LEFT },
    { VK_RIGHT, KEY_RIGHT },
    { VK_UP, KEY_UP },
    { VK_DOWN, KEY_DOWN },

    { VK_HOME, KEY_HOME },
    { VK_END, KEY_END },
    { VK_PRIOR, KEY_PAGEUP },
    { VK_NEXT, KEY_PAGEDOWN },
gbazin's avatar
   
gbazin committed
855

856
857
858
    { VK_INSERT, KEY_INSERT },
    { VK_DELETE, KEY_DELETE },

gbazin's avatar
   
gbazin committed
859
860
861
862
    { VK_CONTROL, 0 },
    { VK_SHIFT, 0 },
    { VK_MENU, 0 },

863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
    { 0, 0 }
};

static int DirectXConvertKey( int i_key )
{
    int i;

    for( i = 0; dxkeys_to_vlckeys[i].i_dxkey != 0; i++ )
    {
        if( dxkeys_to_vlckeys[i].i_dxkey == i_key )
        {
            return dxkeys_to_vlckeys[i].i_vlckey;
        }
    }

    return 0;
}
gbazin's avatar
gbazin committed
880
881
882
883
884
885
886
887

/*****************************************************************************
 * Control: control facility for the vout
 *****************************************************************************/
static int Control( vout_thread_t *p_vout, int i_query, va_list args )
{
    double f_arg;
    RECT rect_window;
888
    POINT point;
gbazin's avatar
gbazin committed
889
890
891
892

    switch( i_query )
    {
    case VOUT_SET_ZOOM:
893
        if( p_vout->p_sys->hparent )
894
895
896
            return vout_ControlWindow( p_vout,
                    (void *)p_vout->p_sys->hparent, i_query, args );

gbazin's avatar
gbazin committed
897
898
899
900
901
902
        f_arg = va_arg( args, double );

        /* Update dimensions */
        rect_window.top = rect_window.left = 0;
        rect_window.right  = p_vout->i_window_width * f_arg;
        rect_window.bottom = p_vout->i_window_height * f_arg;
903
        AdjustWindowRect( &rect_window, p_vout->p_sys->i_window_style, 0 );
gbazin's avatar
gbazin committed
904
905
906
907
908
909
910

        SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0,
                      rect_window.right - rect_window.left,
                      rect_window.bottom - rect_window.top, SWP_NOMOVE );

        return VLC_SUCCESS;

911
912
    case VOUT_CLOSE:
        ShowWindow( p_vout->p_sys->hwnd, SW_HIDE );
gbazin's avatar
gbazin committed
913
914
915
916
917
918
    case VOUT_REPARENT:
        /* Change window style, borders and title bar */
        vlc_mutex_lock( &p_vout->p_sys->lock );
        p_vout->p_sys->hparent = 0;
        vlc_mutex_unlock( &p_vout->p_sys->lock );

919
920
921
922
        /* Retrieve the window position */
        point.x = point.y = 0;
        ClientToScreen( p_vout->p_sys->hwnd, &point );

923
924
925
        SetParent( p_vout->p_sys->hwnd, 0 );
        p_vout->p_sys->i_window_style =
            WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW | WS_SIZEBOX;
gbazin's avatar
gbazin committed
926
        SetWindowLong( p_vout->p_sys->hwnd, GWL_STYLE,
927
928
929
                       p_vout->p_sys->i_window_style |
                       (i_query == VOUT_CLOSE ? 0 : WS_VISIBLE) );
        SetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE, WS_EX_APPWINDOW );
930
931
        SetWindowPos( p_vout->p_sys->hwnd, 0, point.x, point.y, 0, 0,
                      SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED );
gbazin's avatar
gbazin committed
932

933
        return vout_vaControlDefault( p_vout, i_query, args );
gbazin's avatar
gbazin committed
934

935
936
937
938
939
940
941
942
    case VOUT_SET_STAY_ON_TOP:
        if( p_vout->p_sys->hparent )
            return vout_ControlWindow( p_vout,
                    (void *)p_vout->p_sys->hparent, i_query, args );

        p_vout->p_sys->b_on_top_change = VLC_TRUE;
        return VLC_SUCCESS;

gbazin's avatar
gbazin committed
943
    default:
944
        return vout_vaControlDefault( p_vout, i_query, args );
gbazin's avatar
gbazin committed
945
946
    }
}