events.c 33.7 KB
Newer Older
1 2 3
/*****************************************************************************
 * events.c: Windows DirectX video output events handler
 *****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin committed
4
 * Copyright (C) 2001-2004 VideoLAN
5
 * $Id$
6
 *
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 );
Gildas Bazin's avatar
 
Gildas Bazin committed
57
static long FAR PASCAL DirectXEventProc( HWND, UINT, WPARAM, LPARAM );
58

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;
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 */
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;
            }

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:
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;

Gildas Bazin's avatar
 
Gildas Bazin 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 );
Gildas Bazin's avatar
 
Gildas Bazin 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 );
Gildas Bazin's avatar
 
Gildas Bazin 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 );
Gildas Bazin's avatar
 
Gildas Bazin committed
226 227
            break;

228
        case WM_RBUTTONUP:
Gildas Bazin's avatar
 
Gildas Bazin 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:
Gildas Bazin's avatar
 
Gildas Bazin 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
                }
Gildas Bazin's avatar
 
Gildas Bazin committed
253
                if( GetKeyState(VK_SHIFT) & 0x8000 )
Gildas Bazin's avatar
 
Gildas Bazin committed
254
                {
255
                    val.i_int |= KEY_MODIFIER_SHIFT;
Gildas Bazin's avatar
 
Gildas Bazin committed
256
                }
Gildas Bazin's avatar
 
Gildas Bazin 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 )
Gildas Bazin's avatar
 
Gildas Bazin committed
286
                {
287
                    val.i_int |= KEY_MODIFIER_ALT;
Gildas Bazin's avatar
 
Gildas Bazin committed
288
                }
289

Gildas Bazin's avatar
 
Gildas Bazin 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 */
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. */
372
    p_vout->p_sys->hparent =
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

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
384
    if( GetModuleFileName( NULL, vlc_path, MAX_PATH ) )
385
    {
386
        vlc_icon = ExtractIcon( hInstance, vlc_path, 0 );
387
    }
388
#endif
389 390

    /* Fill in the window class structure */
391
    wc.style         = CS_OWNDC|CS_DBLCLKS;          /* style: dbl click */
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 */
401 402

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

407
        if( vlc_icon ) DestroyIcon( vlc_icon );
408

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
        {
413 414
            msg_Err( p_vout, "DirectXCreateWindow RegisterClass FAILED" );
            return VLC_EGENERIC;
415
        }
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;
        }
    }

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

458
    if( p_vout->p_sys->hparent )
459
    {
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;

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 */
471
                    i_style,                                 /* window style */
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 */
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

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" );

537
    DestroyWindow( p_vout->p_sys->hwnd );
538
    if( p_vout->p_sys->hfswnd ) DestroyWindow( p_vout->p_sys->hfswnd );
539

540
    if( p_vout->p_sys->hparent )
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;
    }
Gildas Bazin's avatar
 
Gildas Bazin 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;

692
    if( p_vout->p_sys->b_using_overlay )
693
        E_(DirectXUpdateOverlay)( p_vout );
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 */
    }

Gildas Bazin's avatar
 
Gildas Bazin 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;
Gildas Bazin's avatar
 
Gildas Bazin 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 )
Gildas Bazin's avatar
 
Gildas Bazin committed
808
            msg_Warn( p_vout, "Can't create video sub-window" );
809
        else
Gildas Bazin's avatar
 
Gildas Bazin committed
810
            msg_Dbg( p_vout, "Created video sub-window" );
811 812
        break;

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;
    }

826 827
    /* Let windows handle the message */
    return DefWindowProc(hwnd, message, wParam, lParam);
Gildas Bazin's avatar
 
Gildas Bazin 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 },
Gildas Bazin's avatar
 
Gildas Bazin committed
855 856 857 858 859

    { VK_CONTROL, 0 },
    { VK_SHIFT, 0 },
    { VK_MENU, 0 },

860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876
    { 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;
}
877 878 879 880 881 882 883 884

/*****************************************************************************
 * 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;
885
    POINT point;
886 887 888 889

    switch( i_query )
    {
    case VOUT_SET_ZOOM:
890
        if( p_vout->p_sys->hparent )
891 892 893
            return vout_ControlWindow( p_vout,
                    (void *)p_vout->p_sys->hparent, i_query, args );

894 895 896 897 898 899
        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;
900
        AdjustWindowRect( &rect_window, p_vout->p_sys->i_window_style, 0 );
901 902 903 904 905 906 907

        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;

908 909
    case VOUT_CLOSE:
        ShowWindow( p_vout->p_sys->hwnd, SW_HIDE );
910 911 912 913 914 915
    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 );

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

920 921 922
        SetParent( p_vout->p_sys->hwnd, 0 );
        p_vout->p_sys->i_window_style =
            WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW | WS_SIZEBOX;
923
        SetWindowLong( p_vout->p_sys->hwnd, GWL_STYLE,
924 925 926
                       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 );
927 928
        SetWindowPos( p_vout->p_sys->hwnd, 0, point.x, point.y, 0, 0,
                      SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED );
929

930
        return vout_vaControlDefault( p_vout, i_query, args );
931

932 933 934 935 936 937 938 939
    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;

940
    default:
941
        return vout_vaControlDefault( p_vout, i_query, args );
942 943
    }
}