events.c 33.4 KB
Newer Older
1 2 3
/*****************************************************************************
 * events.c: Windows DirectX video output events handler
 *****************************************************************************
gbazin's avatar
 
gbazin committed
4
 * Copyright (C) 2001-2004 VideoLAN
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 34 35
#include <string.h>                                            /* strerror() */

#include <vlc/vlc.h>
#include <vlc/intf.h>
36
#include <vlc/input.h>
37 38 39 40 41 42 43 44
#include <vlc/vout.h>

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

#include <ddraw.h>

45
#include "vlc_keys.h"
46 47 48 49 50 51 52
#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
53 54
static long FAR PASCAL DirectXEventProc( HWND, UINT, WPARAM, LPARAM );
static long FAR PASCAL DirectXVideoEventProc( HWND, UINT, WPARAM, LPARAM );
55

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

58 59 60 61 62 63 64 65 66 67 68 69 70
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 );
    }
}

71 72
static int DirectXConvertKey( int i_key );

73 74 75 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.
 *****************************************************************************/
void DirectXEventThread( event_thread_t *p_event )
{
    MSG msg;
gbazin's avatar
gbazin committed
84
    POINT old_mouse_pos = {0,0}, mouse_pos;
85 86
    vlc_value_t val;
    int i_width, i_height, i_x, i_y;
87
    HMODULE hkernel32;
88 89

    /* Initialisation */
gbazin's avatar
gbazin committed
90
    p_event->p_vout->pf_control = Control;
91 92 93

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

101
    /* Signal the creation of the window */
102 103
    vlc_thread_ready( p_event );

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

        OurSetThreadExecutionState =
            GetProcAddress( hkernel32, "SetThreadExecutionState" );

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

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

        switch( msg.message )
        {

        case WM_MOUSEMOVE:
132 133 134 135 136
            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 );

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

gbazin's avatar
gbazin committed
143 144 145 146 147 148 149 150 151 152 153 154
            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 );
            }
155

156
        case WM_NCMOUSEMOVE:
gbazin's avatar
gbazin committed
157 158 159
            GetCursorPos( &mouse_pos );
            if( (abs(mouse_pos.x - old_mouse_pos.x) > 2 ||
                (abs(mouse_pos.y - old_mouse_pos.y)) > 2 ) )
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
            {
                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:
            GetCursorPos( &old_mouse_pos );
            ShowCursor( FALSE );
            break;

gbazin's avatar
 
gbazin committed
177 178 179 180
        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 );
181
            DirectXPopupMenu( p_event, VLC_FALSE );
gbazin's avatar
 
gbazin committed
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
            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 );
201
            DirectXPopupMenu( p_event, VLC_FALSE );
gbazin's avatar
 
gbazin committed
202 203 204 205 206 207 208 209 210 211 212 213
            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 );
214
            DirectXPopupMenu( p_event, VLC_FALSE );
gbazin's avatar
 
gbazin committed
215 216
            break;

217
        case WM_RBUTTONUP:
gbazin's avatar
 
gbazin committed
218 219 220
            var_Get( p_event->p_vout, "mouse-button-down", &val );
            val.i_int &= ~4;
            var_Set( p_event->p_vout, "mouse-button-down", val );
221
            DirectXPopupMenu( p_event, VLC_TRUE );
222 223 224
            break;

        case WM_KEYDOWN:
gbazin's avatar
 
gbazin committed
225
        case WM_SYSKEYDOWN:
226 227 228 229 230
            /* 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 )
231
            {
232 233
                /* This appears to be a "normal" (ascii) key */
                val.i_int = tolower( MapVirtualKey( msg.wParam, 2 ) );
234
            }
235

236 237 238
            if( val.i_int )
            {
                if( GetKeyState(VK_CONTROL) & 0x8000 )
239
                {
240
                    val.i_int |= KEY_MODIFIER_CTRL;
241
                }
gbazin's avatar
 
gbazin committed
242
                if( GetKeyState(VK_SHIFT) & 0x8000 )
gbazin's avatar
 
gbazin committed
243
                {
244
                    val.i_int |= KEY_MODIFIER_SHIFT;
gbazin's avatar
 
gbazin committed
245
                }
gbazin's avatar
 
gbazin committed
246
                if( GetKeyState(VK_MENU) & 0x8000 )
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
                {
                    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
275
                {
276
                    val.i_int |= KEY_MODIFIER_ALT;
gbazin's avatar
 
gbazin committed
277
                }
278

gbazin's avatar
 
gbazin committed
279
                var_Set( p_event->p_vlc, "key-pressed", val );
280 281 282
            }
            break;

283
        case WM_VLC_CHANGE_TEXT:
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
            var_Get( p_event->p_vout, "video-title", &val );

            if( !val.psz_string || !*val.psz_string ) /* Default video title */
            {
                if( p_event->p_vout->p_sys->b_using_overlay )
                    SetWindowText( p_event->p_vout->p_sys->hwnd,
                        VOUT_TITLE " (hardware YUV overlay DirectX output)" );
                else if( p_event->p_vout->p_sys->b_hw_yuv )
                    SetWindowText( p_event->p_vout->p_sys->hwnd,
                        VOUT_TITLE " (hardware YUV DirectX output)" );
                else SetWindowText( p_event->p_vout->p_sys->hwnd,
                        VOUT_TITLE " (software RGB DirectX output)" );
            }
            else
            {
                SetWindowText( p_event->p_vout->p_sys->hwnd, val.psz_string );
            }
301 302
            break;

303 304 305 306 307 308 309 310 311 312 313
        default:
            /* Messages we don't handle directly are dispatched to the
             * window procedure */
            TranslateMessage(&msg);
            DispatchMessage(&msg);
            break;

        } /* End Switch */

    } /* End Main loop */

314 315
    /* Check for WM_QUIT if we created the window */
    if( !p_event->p_vout->p_sys->hparent && msg.message == WM_QUIT )
316 317 318 319 320
    {
        msg_Warn( p_event, "WM_QUIT... should not happen!!" );
        p_event->p_vout->p_sys->hwnd = NULL; /* Window already destroyed */
    }

321
    msg_Dbg( p_event, "DirectXEventThread terminating" );
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341

    /* 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;
342
    HMENU      hMenu;
343
    RECT       rect_window;
gbazin's avatar
gbazin committed
344 345 346 347
    WNDCLASSEX wc;                            /* window class components */
    HICON      vlc_icon = NULL;
    char       vlc_path[MAX_PATH+1];
    int        i_style;
348 349 350

    msg_Dbg( p_vout, "DirectXCreateWindow" );

351
    /* Get this module's instance */
352 353
    hInstance = GetModuleHandle(NULL);

354
    /* If an external window was specified, we'll draw in it. */
gbazin's avatar
gbazin committed
355
    p_vout->p_sys->hparent =
gbazin's avatar
gbazin committed
356 357 358 359
        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 );
360

gbazin's avatar
gbazin committed
361 362 363 364 365 366
    /* 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;
    if( GetModuleFileName( NULL, vlc_path, MAX_PATH ) )
367
    {
gbazin's avatar
gbazin committed
368
        vlc_icon = ExtractIcon( hInstance, vlc_path, 0 );
369
    }
gbazin's avatar
gbazin committed
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386

    /* Fill in the window class structure */
    wc.cbSize        = sizeof(WNDCLASSEX);
    wc.style         = CS_DBLCLKS;                   /* style: dbl click */
    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 */
    wc.lpszClassName = "VLC DirectX";             /* use a special class */
    wc.hIconSm       = vlc_icon;              /* load the vlc small icon */

    /* Register the window class */
    if( !RegisterClassEx(&wc) )
387
    {
gbazin's avatar
gbazin committed
388
        WNDCLASS wndclass;
389

gbazin's avatar
gbazin committed
390
        if( vlc_icon ) DestroyIcon( vlc_icon );
391

gbazin's avatar
gbazin committed
392 393 394
        /* Check why it failed. If it's because one already exists
         * then fine, otherwise return with an error. */
        if( !GetClassInfo( hInstance, "VLC DirectX", &wndclass ) )
395
        {
gbazin's avatar
gbazin committed
396 397
            msg_Err( p_vout, "DirectXCreateWindow RegisterClass FAILED" );
            return VLC_EGENERIC;
398
        }
gbazin's avatar
gbazin committed
399
    }
400

gbazin's avatar
gbazin committed
401 402 403 404 405 406 407 408 409
    /* 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;
    AdjustWindowRect( &rect_window, WS_OVERLAPPEDWINDOW|WS_SIZEBOX, 0 );
410

gbazin's avatar
gbazin committed
411 412 413 414
    if( p_vout->p_sys->hparent )
        i_style = WS_VISIBLE|WS_CLIPCHILDREN|WS_CHILD;
    else
        i_style = WS_OVERLAPPEDWINDOW|WS_SIZEBOX|WS_VISIBLE|WS_CLIPCHILDREN;
415

gbazin's avatar
gbazin committed
416 417 418 419
    /* Create the window */
    p_vout->p_sys->hwnd =
        CreateWindowEx( WS_EX_NOPARENTNOTIFY,
                    "VLC DirectX",                   /* name of window class */
420
                    VOUT_TITLE " (DirectX Output)", /* window title bar text */
gbazin's avatar
gbazin committed
421
                    i_style,                                 /* window style */
gbazin's avatar
gbazin committed
422 423 424 425
                    (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 */
426 427
                    rect_window.right - rect_window.left,    /* window width */
                    rect_window.bottom - rect_window.top,   /* window height */
gbazin's avatar
gbazin committed
428
                    p_vout->p_sys->hparent,                 /* parent window */
429 430
                    NULL,                          /* no menu in this window */
                    hInstance,            /* handle of this program instance */
431
                    (LPVOID)p_vout );            /* send p_vout to WM_CREATE */
432

gbazin's avatar
gbazin committed
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
    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 );
        SetWindowLong( p_vout->p_sys->hparent, GWL_STYLE,
                       i_style | WS_CLIPCHILDREN );

448 449
    }

450 451 452 453 454 455 456 457
    /* 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 );

458
    /* Append a "Always On Top" entry in the system menu */
459 460
    hMenu = GetSystemMenu( p_vout->p_sys->hwnd, FALSE );
    AppendMenu( hMenu, MF_SEPARATOR, 0, "" );
461 462
    AppendMenu( hMenu, MF_STRING | MF_UNCHECKED,
                       IDM_TOGGLE_ON_TOP, "Always on &Top" );
463

464
    return VLC_SUCCESS;
465 466 467 468 469 470 471 472 473 474 475
}

/*****************************************************************************
 * 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
476
    DestroyWindow( p_vout->p_sys->hwnd );
gbazin's avatar
gbazin committed
477

gbazin's avatar
gbazin committed
478
    if( p_vout->p_sys->hparent )
gbazin's avatar
gbazin committed
479
        vout_ReleaseWindow( p_vout, (void *)p_vout->p_sys->hparent );
480

481 482
    p_vout->p_sys->hwnd = NULL;

483 484 485 486 487 488
    /* 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 */
}

/*****************************************************************************
489
 * DirectXUpdateRects: update clipping rectangles
490
 *****************************************************************************
491
 * This function is called when the window position or size are changed, and
492 493 494
 * its job is to update the source and destination RECTs used to display the
 * picture.
 *****************************************************************************/
495
void DirectXUpdateRects( vout_thread_t *p_vout, vlc_bool_t b_force )
496 497 498 499 500 501
{
#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

502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530
    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,
531 532
                       &i_x, &i_y, &i_width, &i_height );

533 534 535
    if( p_vout->p_sys->hvideownd )
        SetWindowPos( p_vout->p_sys->hvideownd, HWND_TOP,
                      i_x, i_y, i_width, i_height, 0 );
536

537
    /* Destination image position and dimensions */
538
    rect_dest.left = point.x + i_x;
539
    rect_dest.right = rect_dest.left + i_width;
540
    rect_dest.top = point.y + i_y;
541 542
    rect_dest.bottom = rect_dest.top + i_height;

543 544 545 546 547 548 549 550 551 552 553 554 555 556
    /* 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;
    }

557
    /* UpdateOverlay directdraw function doesn't automatically clip to the
558
     * display size so we need to do it otherwise it will fail */
559 560

    /* Clip the destination window */
561 562 563 564 565 566
    if( !IntersectRect( &rect_dest_clipped, &rect_dest,
                        &p_vout->p_sys->rect_display ) )
    {
        SetRectEmpty( &rect_src_clipped );
        return;
    }
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

#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);
592
    rect_src_clipped.right = p_vout->render.i_width -
593 594 595 596 597 598 599 600
      (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);

601 602 603 604 605 606 607 608 609 610 611 612 613 614
    /* 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
615

616 617 618 619 620 621 622
#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

623 624 625 626 627 628 629
    /* 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
630 631 632
    if( p_vout->p_sys->b_using_overlay )
        DirectXUpdateOverlay( p_vout );

633 634
    /* Signal the change in size/position */
    p_vout->p_sys->i_changes |= DX_POSITION_CHANGE;
635

636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655
#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 )
{
656
    vout_thread_t *p_vout;
657

658 659 660 661
    if( message == WM_CREATE )
    {
        /* Store p_vout for future use */
        p_vout = (vout_thread_t *)((CREATESTRUCT *)lParam)->lpCreateParams;
662
        SetWindowLongPtr( hwnd, GWLP_USERDATA, (LONG_PTR)p_vout );
663 664
    }
    else
665
    {
666
        p_vout = (vout_thread_t *)GetWindowLongPtr( hwnd, GWLP_USERDATA );
667 668
    }

gbazin's avatar
 
gbazin committed
669 670 671 672 673 674 675
    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);
    }

676 677 678 679
    switch( message )
    {

    case WM_WINDOWPOSCHANGED:
680
        DirectXUpdateRects( p_vout, VLC_TRUE );
681 682 683 684
        return 0;

    /* the user wants to close the window */
    case WM_CLOSE:
685 686 687 688 689 690
    {
        playlist_t * p_playlist =
            (playlist_t *)vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
                                           FIND_ANYWHERE );
        if( p_playlist == NULL )
        {
691
            return 0;
692 693 694 695
        }

        playlist_Stop( p_playlist );
        vlc_object_release( p_playlist );
696
        return 0;
697
    }
698 699 700 701 702 703 704 705 706 707 708 709 710

    /* 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)
        {
            case SC_SCREENSAVE:                     /* catch the screensaver */
            case SC_MONITORPOWER:              /* catch the monitor turn-off */
711 712 713 714
                msg_Dbg( p_vout, "WinProc WM_SYSCOMMAND" );
                return 0;                  /* this stops them from happening */
            case IDM_TOGGLE_ON_TOP:            /* toggle the "on top" status */
            {
715
                vlc_value_t val;
716 717
                msg_Dbg( p_vout, "WinProc WM_SYSCOMMAND: IDM_TOGGLE_ON_TOP");

718
                /* Get the current value... */
gbazin's avatar
 
gbazin committed
719
                if( var_Get( p_vout, "video-on-top", &val ) < 0 )
720 721 722
                    return 0;
                /* ...and change it */
                val.b_bool = !val.b_bool;
gbazin's avatar
 
gbazin committed
723
                var_Set( p_vout, "video-on-top", val );
724 725 726
                return 0;
                break;
            }
727 728 729
        }
        break;

730 731 732 733 734 735 736 737 738 739 740
    case WM_VLC_CREATE_VIDEO_WIN:
        /* Create video sub-window */
        p_vout->p_sys->hvideownd =
            CreateWindow( "STATIC", "",   /* window class and title bar text */
                    WS_CHILD | WS_VISIBLE,                   /* window style */
                    CW_USEDEFAULT, CW_USEDEFAULT,     /* default coordinates */
                    CW_USEDEFAULT, CW_USEDEFAULT,
                    hwnd,                                   /* parent window */
                    NULL, GetModuleHandle(NULL), NULL );

        if( !p_vout->p_sys->hvideownd )
741
        {
gbazin's avatar
 
gbazin committed
742
            msg_Warn( p_vout, "Can't create video sub-window" );
743 744 745
        }
        else
        {
gbazin's avatar
 
gbazin committed
746
            msg_Dbg( p_vout, "Created video sub-window" );
747 748
            SetWindowLongPtr( p_vout->p_sys->hvideownd,
                              GWLP_WNDPROC, (LONG_PTR)DirectXVideoEventProc );
749 750 751
            /* Store the previous window proc of _this_ window with the video
             * window so we can use it in DirectXVideoEventProc to pass
             * messages to the creator of _this_ window */
752 753
            SetWindowLongPtr( p_vout->p_sys->hvideownd, GWLP_USERDATA,
                              (LONG_PTR)p_vout->p_sys->pf_wndproc );
754 755 756
        }
        break;

gbazin's avatar
gbazin committed
757 758 759 760 761 762 763 764
    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;

765 766 767 768 769
    default:
        //msg_Dbg( p_vout, "WinProc WM Default %i", message );
        break;
    }

gbazin's avatar
gbazin committed
770 771
    /* Let windows handle the message */
    return DefWindowProc(hwnd, message, wParam, lParam);
gbazin's avatar
 
gbazin committed
772
}
773

gbazin's avatar
 
gbazin committed
774 775 776
static long FAR PASCAL DirectXVideoEventProc( HWND hwnd, UINT message,
                                              WPARAM wParam, LPARAM lParam )
{
777 778 779
    WNDPROC pf_parentwndproc;
    POINT pt;

gbazin's avatar
 
gbazin committed
780 781
    switch( message )
    {
782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804
    case WM_MOUSEMOVE:
    case WM_LBUTTONDOWN:
    case WM_LBUTTONUP:
    case WM_LBUTTONDBLCLK:
    case WM_MBUTTONDOWN:
    case WM_MBUTTONUP:
    case WM_RBUTTONDOWN:
    case WM_RBUTTONUP:
        /* Translate mouse cursor position to parent window coordinates. */
        pt.x = LOWORD( lParam );
        pt.y = HIWORD( lParam );
        MapWindowPoints( hwnd, GetParent( hwnd ), &pt, 1 );
        lParam = MAKELPARAM( pt.x, pt.y );
        /* Fall through. */
    case WM_KEYDOWN:
    case WM_KEYUP:
    case WM_SYSKEYDOWN:
    case WM_SYSKEYUP:
        /* Foward these to the original window proc of the parent so the
         * creator of the window gets a chance to process them. If we created
         * the parent window ourself DirectXEventThread will process these
         * and they will never make it here.
         * Note that we fake the hwnd to be our parent in order to prevent
805 806
         * confusion in the creator's window proc. */
        pf_parentwndproc = (WNDPROC)GetWindowLongPtr( hwnd, GWLP_USERDATA );
807 808 809

        if( pf_parentwndproc )
        {
810 811 812 813 814 815 816 817 818 819 820
            LRESULT i_ret;
            LONG_PTR p_backup;

            pf_parentwndproc = (WNDPROC)GetWindowLongPtr( hwnd, GWLP_USERDATA);

            p_backup = SetWindowLongPtr( GetParent( hwnd ), GWLP_USERDATA, 0 );
            i_ret = CallWindowProc( pf_parentwndproc, GetParent( hwnd ),
                                    message, wParam, lParam );
            SetWindowLongPtr( GetParent( hwnd ), GWLP_USERDATA, p_backup );

            return i_ret;
821 822
        }
        break;
gbazin's avatar
 
gbazin committed
823 824
    }

825 826
    return DefWindowProc(hwnd, message, wParam, lParam);
}
827 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

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
853 854 855 856 857

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

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

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

    switch( i_query )
    {
    case VOUT_SET_ZOOM:
887
        if( p_vout->p_sys->hparent )
888 889 890
            return vout_ControlWindow( p_vout,
                    (void *)p_vout->p_sys->hparent, i_query, args );

gbazin's avatar
gbazin committed
891 892 893 894 895 896 897 898 899 900 901 902 903 904
        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;
        AdjustWindowRect( &rect_window, WS_OVERLAPPEDWINDOW|WS_SIZEBOX, 0 );

        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;

gbazin's avatar
gbazin committed
905 906 907 908 909 910 911 912 913 914 915 916 917 918 919
    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 );

        SetParent( p_vout->p_sys->hwnd, GetDesktopWindow() );
        SetWindowLong( p_vout->p_sys->hwnd, GWL_STYLE,
                       WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW |
                       WS_SIZEBOX | WS_VISIBLE );
        SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0, 0, 0,
                      SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED );

      return VLC_SUCCESS;

920 921 922
    case VOUT_CLOSE:
        return VLC_SUCCESS;

923 924 925 926 927 928 929 930
    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
931 932 933 934 935
    default:
        msg_Dbg( p_vout, "control query not supported" );
        return VLC_EGENERIC;
    }
}