events.c 29.2 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 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
 *
 * Authors: Gildas Bazin <gbazin@netcourrier.com>
 *
 * 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

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

69 70
static int DirectXConvertKey( int i_key );

71 72 73 74 75 76 77 78 79 80 81
/*****************************************************************************
 * 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
82
    POINT old_mouse_pos = {0,0};
83 84
    vlc_value_t val;
    int i_width, i_height, i_x, i_y;
85 86 87 88 89

    /* Initialisation */

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

97
    /* Signal the creation of the window */
98 99 100 101
    vlc_thread_ready( p_event );

    /* Main loop */
    /* GetMessage will sleep if there's no message in the queue */
102 103
    while( !p_event->b_die && ( p_event->p_vout->p_sys->hparent ||
           GetMessage( &msg, p_event->p_vout->p_sys->hwnd, 0, 0 ) ) )
104 105 106 107 108
    {
        /* Check if we are asked to exit */
        if( p_event->b_die )
            break;

gbazin's avatar
 
gbazin committed
109 110 111 112 113 114 115
        if( p_event->p_vout->p_sys->hparent )
        {
            /* Parent window was created in another thread so we can't
             * access the window messages. */
            msleep( INTF_IDLE_SLEEP );
            continue;
        }
116

117 118 119 120
        switch( msg.message )
        {

        case WM_MOUSEMOVE:
121 122 123 124 125
            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 );

126 127 128 129 130 131
            if( msg.hwnd != p_event->p_vout->p_sys->hwnd )
            {
                /* Child window */
                i_x = i_y = 0;
            }

132 133 134 135 136 137 138 139 140 141
            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 );

142
        case WM_NCMOUSEMOVE:
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
            if( (abs(GET_X_LPARAM(msg.lParam) - old_mouse_pos.x) > 2 ||
                (abs(GET_Y_LPARAM(msg.lParam) - old_mouse_pos.y)) > 2 ) )
            {
                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
162 163 164 165
        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 );
166
            DirectXPopupMenu( p_event, VLC_FALSE );
gbazin's avatar
 
gbazin committed
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
            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 );
186
            DirectXPopupMenu( p_event, VLC_FALSE );
gbazin's avatar
 
gbazin committed
187 188 189 190 191 192 193 194 195 196 197 198
            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 );
199
            DirectXPopupMenu( p_event, VLC_FALSE );
gbazin's avatar
 
gbazin committed
200 201
            break;

202
        case WM_RBUTTONUP:
gbazin's avatar
 
gbazin committed
203 204 205
            var_Get( p_event->p_vout, "mouse-button-down", &val );
            val.i_int &= ~4;
            var_Set( p_event->p_vout, "mouse-button-down", val );
206
            DirectXPopupMenu( p_event, VLC_TRUE );
207 208 209
            break;

        case WM_KEYDOWN:
gbazin's avatar
 
gbazin committed
210
        case WM_SYSKEYDOWN:
211 212 213 214 215
            /* 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 )
216
            {
217 218
                /* This appears to be a "normal" (ascii) key */
                val.i_int = tolower( MapVirtualKey( msg.wParam, 2 ) );
219
            }
220

221 222 223
            if( val.i_int )
            {
                if( GetKeyState(VK_CONTROL) & 0x8000 )
224
                {
225
                    val.i_int |= KEY_MODIFIER_CTRL;
226
                }
gbazin's avatar
 
gbazin committed
227
                if( GetKeyState(VK_SHIFT) & 0x8000 )
gbazin's avatar
 
gbazin committed
228
                {
229
                    val.i_int |= KEY_MODIFIER_SHIFT;
gbazin's avatar
 
gbazin committed
230
                }
gbazin's avatar
 
gbazin committed
231
                if( GetKeyState(VK_MENU) & 0x8000 )
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
                {
                    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
260
                {
261
                    val.i_int |= KEY_MODIFIER_ALT;
gbazin's avatar
 
gbazin committed
262
                }
263

gbazin's avatar
 
gbazin committed
264
                var_Set( p_event->p_vlc, "key-pressed", val );
265 266 267
            }
            break;

268 269 270 271 272 273 274 275 276 277 278
        case WM_VLC_CHANGE_TEXT:
            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)" );
            break;

279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
        default:
            /* Messages we don't handle directly are dispatched to the
             * window procedure */
            TranslateMessage(&msg);
            DispatchMessage(&msg);
            break;

        } /* End Switch */

    } /* End Main loop */

    if( msg.message == WM_QUIT )
    {
        msg_Warn( p_event, "WM_QUIT... should not happen!!" );
        p_event->p_vout->p_sys->hwnd = NULL; /* Window already destroyed */
    }

296
    msg_Dbg( p_event, "DirectXEventThread terminating" );
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316

    /* 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;
317
    HMENU      hMenu;
318
    RECT       rect_window;
319

320 321
    vlc_value_t val;

322 323
    msg_Dbg( p_vout, "DirectXCreateWindow" );

324
    /* Get this module's instance */
325 326
    hInstance = GetModuleHandle(NULL);

327
    /* If an external window was specified, we'll draw in it. */
328
    var_Get( p_vout->p_vlc, "drawable", &val );
329
    p_vout->p_sys->hparent = p_vout->p_sys->hwnd =
330
             val.i_int ?  (void*)(ptrdiff_t) val.i_int : NULL;
331 332

    if( p_vout->p_sys->hparent )
333
    {
334 335 336 337 338 339 340
        msg_Dbg( p_vout, "using external window %p\n", p_vout->p_sys->hwnd );

        /* Set stuff in the window that we can not put directly in
         * a class (see below). */
        SetClassLong( p_vout->p_sys->hwnd,
                      GCL_STYLE, CS_DBLCLKS );
        SetClassLong( p_vout->p_sys->hwnd,
341
                      GCL_HBRBACKGROUND, (LONG)GetStockObject(BLACK_BRUSH) );
342 343
        SetClassLong( p_vout->p_sys->hwnd,
                      GCL_HCURSOR, (LONG)LoadCursor(NULL, IDC_ARROW) );
344 345 346 347
        /* Store a p_vout pointer into the window local storage (for later
         * use in DirectXEventProc). */
        SetWindowLong( p_vout->p_sys->hwnd, GWL_USERDATA, (LONG)p_vout );

348 349 350 351 352
        p_vout->p_sys->pf_wndproc =
               (WNDPROC)SetWindowLong( p_vout->p_sys->hwnd,
                                       GWL_WNDPROC, (LONG)DirectXEventProc );

        /* Blam! Erase everything that might have been there. */
gbazin's avatar
 
gbazin committed
353
        InvalidateRect( p_vout->p_sys->hwnd, NULL, TRUE );
354
    }
355
    else
356
    {
357 358 359
        WNDCLASSEX wc;                            /* window class components */
        HICON      vlc_icon = NULL;
        char       vlc_path[MAX_PATH+1];
360

361 362 363
        /* Get the Icon from the main app */
        vlc_icon = NULL;
        if( GetModuleFileName( NULL, vlc_path, MAX_PATH ) )
364
        {
365
            vlc_icon = ExtractIcon( hInstance, vlc_path, 0 );
366 367
        }

368 369 370 371 372 373 374 375 376
        /* 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 */
377
        wc.hbrBackground = GetStockObject(BLACK_BRUSH);  /* background color */
378 379 380 381 382 383
        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) )
384
        {
385 386 387 388 389 390 391 392 393 394 395 396 397 398
            WNDCLASS wndclass;

            if( vlc_icon )
            {
                DestroyIcon( vlc_icon );
            }

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

401 402 403 404 405 406 407 408 409 410 411 412 413
        /* 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 );

        /* Create the window */
        p_vout->p_sys->hwnd =
            CreateWindow( "VLC DirectX",             /* name of window class */
414
                    VOUT_TITLE " (DirectX Output)", /* window title bar text */
415 416
                    WS_OVERLAPPEDWINDOW | WS_SIZEBOX | WS_VISIBLE |
                    WS_CLIPCHILDREN,                         /* window style */
417 418 419 420 421 422 423
                    CW_USEDEFAULT,                   /* default X coordinate */
                    0,                               /* default Y coordinate */
                    rect_window.right - rect_window.left,    /* window width */
                    rect_window.bottom - rect_window.top,   /* window height */
                    NULL,                                /* no parent window */
                    NULL,                          /* no menu in this window */
                    hInstance,            /* handle of this program instance */
424
                    (LPVOID)p_vout );            /* send p_vout to WM_CREATE */
425

426 427 428 429 430
        if( !p_vout->p_sys->hwnd )
        {
            msg_Warn( p_vout, "DirectXCreateWindow create window FAILED" );
            return VLC_EGENERIC;
        }
431 432
    }

433 434 435 436 437 438 439 440
    /* 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 );

441
    /* Append a "Always On Top" entry in the system menu */
442 443
    hMenu = GetSystemMenu( p_vout->p_sys->hwnd, FALSE );
    AppendMenu( hMenu, MF_SEPARATOR, 0, "" );
444 445
    AppendMenu( hMenu, MF_STRING | MF_UNCHECKED,
                       IDM_TOGGLE_ON_TOP, "Always on &Top" );
446

447
    return VLC_SUCCESS;
448 449 450 451 452 453 454 455 456 457 458
}

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

459
    if( p_vout->p_sys->hwnd && !p_vout->p_sys->hparent )
460 461 462
    {
        DestroyWindow( p_vout->p_sys->hwnd );
    }
463 464
    else if( p_vout->p_sys->hparent )
    {
gbazin's avatar
 
gbazin committed
465 466 467
        /* Get rid of the video sub-window */
        PostMessage( p_vout->p_sys->hvideownd, WM_VLC_DESTROY_VIDEO_WIN, 0, 0);

468 469 470
        /* We don't want our windowproc to be called anymore */
        SetWindowLong( p_vout->p_sys->hwnd,
                       GWL_WNDPROC, (LONG)p_vout->p_sys->pf_wndproc );
gbazin's avatar
 
gbazin committed
471
        SetWindowLong( p_vout->p_sys->hwnd, GWL_USERDATA, (LONG)NULL );
472 473 474 475

        /* Blam! Erase everything that might have been there. */
        InvalidateRect( p_vout->p_sys->hwnd, NULL, TRUE );
    }
476

477 478
    p_vout->p_sys->hwnd = NULL;

479 480 481 482 483 484
    /* 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 */
}

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

498 499 500 501 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
    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,
527 528
                       &i_x, &i_y, &i_width, &i_height );

529 530 531
    if( p_vout->p_sys->hvideownd )
        SetWindowPos( p_vout->p_sys->hvideownd, HWND_TOP,
                      i_x, i_y, i_width, i_height, 0 );
532

533
    /* Destination image position and dimensions */
534
    rect_dest.left = point.x + i_x;
535
    rect_dest.right = rect_dest.left + i_width;
536
    rect_dest.top = point.y + i_y;
537 538
    rect_dest.bottom = rect_dest.top + i_height;

539 540 541 542 543 544 545 546 547 548 549 550 551 552
    /* 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;
    }

553
    /* UpdateOverlay directdraw function doesn't automatically clip to the
554
     * display size so we need to do it otherwise it will fail */
555 556

    /* Clip the destination window */
557 558 559 560 561 562
    if( !IntersectRect( &rect_dest_clipped, &rect_dest,
                        &p_vout->p_sys->rect_display ) )
    {
        SetRectEmpty( &rect_src_clipped );
        return;
    }
563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587

#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);
588
    rect_src_clipped.right = p_vout->render.i_width -
589 590 591 592 593 594 595 596
      (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);

597 598 599 600 601 602 603 604 605 606 607 608 609 610
    /* 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
611

612 613 614 615 616 617 618
#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

619 620 621 622 623 624 625
    /* 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;

626 627
    /* Signal the change in size/position */
    p_vout->p_sys->i_changes |= DX_POSITION_CHANGE;
628

629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648
#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 )
{
649
    vout_thread_t *p_vout;
650

651 652 653 654
    if( message == WM_CREATE )
    {
        /* Store p_vout for future use */
        p_vout = (vout_thread_t *)((CREATESTRUCT *)lParam)->lpCreateParams;
ipkiss's avatar
ipkiss committed
655
        SetWindowLong( hwnd, GWL_USERDATA, (LONG)p_vout );
656 657
    }
    else
658
    {
ipkiss's avatar
ipkiss committed
659
        p_vout = (vout_thread_t *)GetWindowLong( hwnd, GWL_USERDATA );
660 661
    }

gbazin's avatar
 
gbazin committed
662 663 664 665 666 667 668
    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);
    }

669 670 671 672
    switch( message )
    {

    case WM_WINDOWPOSCHANGED:
673
        DirectXUpdateRects( p_vout, VLC_TRUE );
674 675 676 677
        return 0;

    /* the user wants to close the window */
    case WM_CLOSE:
678 679 680 681 682 683
    {
        playlist_t * p_playlist =
            (playlist_t *)vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
                                           FIND_ANYWHERE );
        if( p_playlist == NULL )
        {
684
            return 0;
685 686 687 688
        }

        playlist_Stop( p_playlist );
        vlc_object_release( p_playlist );
689
        return 0;
690
    }
691 692 693 694 695 696 697 698 699 700 701 702 703

    /* 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 */
704 705 706 707
                msg_Dbg( p_vout, "WinProc WM_SYSCOMMAND" );
                return 0;                  /* this stops them from happening */
            case IDM_TOGGLE_ON_TOP:            /* toggle the "on top" status */
            {
708
                vlc_value_t val;
709 710
                msg_Dbg( p_vout, "WinProc WM_SYSCOMMAND: IDM_TOGGLE_ON_TOP");

711
                /* Get the current value... */
gbazin's avatar
 
gbazin committed
712
                if( var_Get( p_vout, "video-on-top", &val ) < 0 )
713 714 715
                    return 0;
                /* ...and change it */
                val.b_bool = !val.b_bool;
gbazin's avatar
 
gbazin committed
716
                var_Set( p_vout, "video-on-top", val );
717 718 719
                return 0;
                break;
            }
720 721 722
        }
        break;

723 724 725 726 727 728 729 730 731 732 733
    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 )
734
        {
gbazin's avatar
 
gbazin committed
735
            msg_Warn( p_vout, "Can't create video sub-window" );
736 737 738
        }
        else
        {
gbazin's avatar
 
gbazin committed
739
            msg_Dbg( p_vout, "Created video sub-window" );
740
            SetWindowLong( p_vout->p_sys->hvideownd,
gbazin's avatar
 
gbazin committed
741
                           GWL_WNDPROC, (LONG)DirectXVideoEventProc );
742 743 744 745 746 747 748 749
        }
        break;

    default:
        //msg_Dbg( p_vout, "WinProc WM Default %i", message );
        break;
    }

gbazin's avatar
 
gbazin committed
750 751 752 753 754 755 756 757 758 759 760 761 762
    return DefWindowProc(hwnd, message, wParam, lParam);
}
static long FAR PASCAL DirectXVideoEventProc( HWND hwnd, UINT message,
                                              WPARAM wParam, LPARAM lParam )
{
    switch( message )
    {
    case WM_VLC_DESTROY_VIDEO_WIN:
        /* Destroy video sub-window */
        DestroyWindow( hwnd );
        break;
    }

763 764
    return DefWindowProc(hwnd, message, wParam, lParam);
}
765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790

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
791 792 793 794 795

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

796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812
    { 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;
}