events.c 33.9 KB
Newer Older
1
2
3
/*****************************************************************************
 * events.c: Windows DirectX video output events handler
 *****************************************************************************
4
 * Copyright (C) 2001-2004 the VideoLAN team
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

#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 */
652
653
654
655
656
657
658
659
660
661
662
663
664
665
    rect_src_clipped.left = p_vout->fmt_out.i_x_offset +
      (rect_dest_clipped.left - rect_dest.left) *
      p_vout->fmt_out.i_visible_width / (rect_dest.right - rect_dest.left);
    rect_src_clipped.right = p_vout->fmt_out.i_x_offset +
      p_vout->fmt_out.i_visible_width -
      (rect_dest.right - rect_dest_clipped.right) *
      p_vout->fmt_out.i_visible_width / (rect_dest.right - rect_dest.left);
    rect_src_clipped.top = p_vout->fmt_out.i_y_offset +
      (rect_dest_clipped.top - rect_dest.top) *
      p_vout->fmt_out.i_visible_height / (rect_dest.bottom - rect_dest.top);
    rect_src_clipped.bottom = p_vout->fmt_out.i_y_offset +
      p_vout->fmt_out.i_visible_height -
      (rect_dest.bottom - rect_dest_clipped.bottom) *
      p_vout->fmt_out.i_visible_height / (rect_dest.bottom - rect_dest.top);
666

667
668
669
670
671
672
673
674
675
676
677
678
679
680
    /* 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
681

682
683
684
685
686
687
688
#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

689
690
691
692
693
694
695
    /* 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
696
    if( p_vout->p_sys->b_using_overlay )
697
        E_(DirectXUpdateOverlay)( p_vout );
gbazin's avatar
gbazin committed
698

699
700
    /* Signal the change in size/position */
    p_vout->p_sys->i_changes |= DX_POSITION_CHANGE;
701

702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
#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 )
{
722
    vout_thread_t *p_vout;
723

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

735
736
737
738
739
740
741
742
    /* 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
743
744
745
746
747
748
749
    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);
    }

750
751
752
    if( hwnd == p_vout->p_sys->hvideownd )
        return DefWindowProc(hwnd, message, wParam, lParam);

753
754
755
756
    switch( message )
    {

    case WM_WINDOWPOSCHANGED:
757
        E_(DirectXUpdateRects)( p_vout, VLC_TRUE );
758
759
760
761
        return 0;

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

        playlist_Stop( p_playlist );
        vlc_object_release( p_playlist );
773
        return 0;
774
    }
775
776
777
778
779
780
781
782
783
784
785

    /* 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)
        {
786
787
            case IDM_TOGGLE_ON_TOP:            /* toggle the "on top" status */
            {
788
                vlc_value_t val;
789
790
                msg_Dbg( p_vout, "WinProc WM_SYSCOMMAND: IDM_TOGGLE_ON_TOP");

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

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

        if( !p_vout->p_sys->hvideownd )
gbazin's avatar
   
gbazin committed
812
            msg_Warn( p_vout, "Can't create video sub-window" );
813
        else
gbazin's avatar
   
gbazin committed
814
            msg_Dbg( p_vout, "Created video sub-window" );
815
816
        break;

gbazin's avatar
gbazin committed
817
818
819
820
821
822
823
824
    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;

825
826
827
828
829
    default:
        //msg_Dbg( p_vout, "WinProc WM Default %i", message );
        break;
    }

gbazin's avatar
gbazin committed
830
831
    /* Let windows handle the message */
    return DefWindowProc(hwnd, message, wParam, lParam);
gbazin's avatar
   
gbazin committed
832
}
833

834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
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
859

860
861
862
    { VK_INSERT, KEY_INSERT },
    { VK_DELETE, KEY_DELETE },

gbazin's avatar
   
gbazin committed
863
864
865
866
    { VK_CONTROL, 0 },
    { VK_SHIFT, 0 },
    { VK_MENU, 0 },

867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
    { 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
884
885
886
887
888
889
890
891

/*****************************************************************************
 * 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;
892
    POINT point;
gbazin's avatar
gbazin committed
893
894
895
896

    switch( i_query )
    {
    case VOUT_SET_ZOOM:
897
        if( p_vout->p_sys->hparent )
898
899
900
            return vout_ControlWindow( p_vout,
                    (void *)p_vout->p_sys->hparent, i_query, args );

gbazin's avatar
gbazin committed
901
902
903
904
905
906
        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;
907
        AdjustWindowRect( &rect_window, p_vout->p_sys->i_window_style, 0 );
gbazin's avatar
gbazin committed
908
909
910
911
912
913
914

        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;

915
916
    case VOUT_CLOSE:
        ShowWindow( p_vout->p_sys->hwnd, SW_HIDE );
gbazin's avatar
gbazin committed
917
918
919
920
921
922
    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 );

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

927
928
929
        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
930
        SetWindowLong( p_vout->p_sys->hwnd, GWL_STYLE,
931
932
933
                       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 );
934
935
        SetWindowPos( p_vout->p_sys->hwnd, 0, point.x, point.y, 0, 0,
                      SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED );
gbazin's avatar
gbazin committed
936

937
        return vout_vaControlDefault( p_vout, i_query, args );
gbazin's avatar
gbazin committed
938

939
940
941
942
943
944
945
946
    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
947
    default:
948
        return vout_vaControlDefault( p_vout, i_query, args );
gbazin's avatar
gbazin committed
949
950
    }
}