events.c 28.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
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 )
gbazin's avatar
   
gbazin committed
232
                {
233
                    val.i_int |= KEY_MODIFIER_ALT;
gbazin's avatar
   
gbazin committed
234
                }
235

gbazin's avatar
   
gbazin committed
236
                var_Set( p_event->p_vlc, "key-pressed", val );
237
238
239
            }
            break;

240
241
242
243
244
245
246
247
248
249
250
        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;

251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
        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 */
    }

268
    msg_Dbg( p_event, "DirectXEventThread terminating" );
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288

    /* 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;
289
    HMENU      hMenu;
290
    RECT       rect_window;
291

292
293
    vlc_value_t val;

294
295
    msg_Dbg( p_vout, "DirectXCreateWindow" );

296
    /* Get this module's instance */
297
298
    hInstance = GetModuleHandle(NULL);

299
    /* If an external window was specified, we'll draw in it. */
300
    var_Get( p_vout->p_vlc, "drawable", &val );
301
    p_vout->p_sys->hparent = p_vout->p_sys->hwnd =
302
             val.i_int ?  (void*)(ptrdiff_t) val.i_int : NULL;
303
304

    if( p_vout->p_sys->hparent )
305
    {
306
307
308
309
310
311
312
        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,
313
                      GCL_HBRBACKGROUND, (LONG)GetStockObject(BLACK_BRUSH) );
314
315
        SetClassLong( p_vout->p_sys->hwnd,
                      GCL_HCURSOR, (LONG)LoadCursor(NULL, IDC_ARROW) );
316
317
318
319
        /* 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 );

320
321
322
323
324
        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
325
        InvalidateRect( p_vout->p_sys->hwnd, NULL, TRUE );
326
    }
327
    else
328
    {
329
330
331
        WNDCLASSEX wc;                            /* window class components */
        HICON      vlc_icon = NULL;
        char       vlc_path[MAX_PATH+1];
332

333
334
335
        /* Get the Icon from the main app */
        vlc_icon = NULL;
        if( GetModuleFileName( NULL, vlc_path, MAX_PATH ) )
336
        {
337
            vlc_icon = ExtractIcon( hInstance, vlc_path, 0 );
338
339
        }

340
341
342
343
344
345
346
347
348
        /* 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 */
349
        wc.hbrBackground = GetStockObject(BLACK_BRUSH);  /* background color */
350
351
352
353
354
355
        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) )
356
        {
357
358
359
360
361
362
363
364
365
366
367
368
369
370
            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;
            }
371
372
        }

373
374
375
376
377
378
379
380
381
382
383
384
385
        /* 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 */
386
                    VOUT_TITLE " (DirectX Output)", /* window title bar text */
387
388
                    WS_OVERLAPPEDWINDOW | WS_SIZEBOX | WS_VISIBLE |
                    WS_CLIPCHILDREN,                         /* window style */
389
390
391
392
393
394
395
                    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 */
396
                    (LPVOID)p_vout );            /* send p_vout to WM_CREATE */
397

398
399
400
401
402
        if( !p_vout->p_sys->hwnd )
        {
            msg_Warn( p_vout, "DirectXCreateWindow create window FAILED" );
            return VLC_EGENERIC;
        }
403
404
    }

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

413
    /* Append a "Always On Top" entry in the system menu */
414
415
    hMenu = GetSystemMenu( p_vout->p_sys->hwnd, FALSE );
    AppendMenu( hMenu, MF_SEPARATOR, 0, "" );
416
417
    AppendMenu( hMenu, MF_STRING | MF_UNCHECKED,
                       IDM_TOGGLE_ON_TOP, "Always on &Top" );
418

419
    return VLC_SUCCESS;
420
421
422
423
424
425
426
427
428
429
430
}

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

431
    if( p_vout->p_sys->hwnd && !p_vout->p_sys->hparent )
432
433
434
    {
        DestroyWindow( p_vout->p_sys->hwnd );
    }
435
436
    else if( p_vout->p_sys->hparent )
    {
gbazin's avatar
   
gbazin committed
437
438
439
        /* Get rid of the video sub-window */
        PostMessage( p_vout->p_sys->hvideownd, WM_VLC_DESTROY_VIDEO_WIN, 0, 0);

440
441
442
        /* 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
443
        SetWindowLong( p_vout->p_sys->hwnd, GWL_USERDATA, (LONG)NULL );
444
445
446
447

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

449
450
    p_vout->p_sys->hwnd = NULL;

451
452
453
454
455
456
    /* 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 */
}

/*****************************************************************************
457
 * DirectXUpdateRects: update clipping rectangles
458
 *****************************************************************************
459
 * This function is called when the window position or size are changed, and
460
461
462
 * its job is to update the source and destination RECTs used to display the
 * picture.
 *****************************************************************************/
463
void DirectXUpdateRects( vout_thread_t *p_vout, vlc_bool_t b_force )
464
465
466
467
468
469
{
#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

470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
    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,
499
500
                       &i_x, &i_y, &i_width, &i_height );

501
502
503
    if( p_vout->p_sys->hvideownd )
        SetWindowPos( p_vout->p_sys->hvideownd, HWND_TOP,
                      i_x, i_y, i_width, i_height, 0 );
504

505
    /* Destination image position and dimensions */
506
    rect_dest.left = point.x + i_x;
507
    rect_dest.right = rect_dest.left + i_width;
508
    rect_dest.top = point.y + i_y;
509
510
    rect_dest.bottom = rect_dest.top + i_height;

511
512
513
514
515
516
517
518
519
520
521
522
523
524
    /* 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;
    }

525
    /* UpdateOverlay directdraw function doesn't automatically clip to the
526
     * display size so we need to do it otherwise it will fail */
527
528

    /* Clip the destination window */
529
530
531
532
533
534
    if( !IntersectRect( &rect_dest_clipped, &rect_dest,
                        &p_vout->p_sys->rect_display ) )
    {
        SetRectEmpty( &rect_src_clipped );
        return;
    }
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559

#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);
560
    rect_src_clipped.right = p_vout->render.i_width -
561
562
563
564
565
566
567
568
      (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);

569
570
571
572
573
574
575
576
577
578
579
580
581
582
    /* 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
583

584
585
586
587
588
589
590
#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

591
592
593
594
595
596
597
    /* 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;

598
599
    /* Signal the change in size/position */
    p_vout->p_sys->i_changes |= DX_POSITION_CHANGE;
600

601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
#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 )
{
621
    vout_thread_t *p_vout;
622

623
624
625
626
    if( message == WM_CREATE )
    {
        /* Store p_vout for future use */
        p_vout = (vout_thread_t *)((CREATESTRUCT *)lParam)->lpCreateParams;
627
        SetWindowLong( hwnd, GWL_USERDATA, (LONG)p_vout );
628
629
    }
    else
630
    {
631
        p_vout = (vout_thread_t *)GetWindowLong( hwnd, GWL_USERDATA );
632
633
    }

gbazin's avatar
   
gbazin committed
634
635
636
637
638
639
640
    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);
    }

641
642
643
644
    switch( message )
    {

    case WM_WINDOWPOSCHANGED:
645
        DirectXUpdateRects( p_vout, VLC_TRUE );
646
647
648
649
        return 0;

    /* the user wants to close the window */
    case WM_CLOSE:
650
651
652
653
654
655
    {
        playlist_t * p_playlist =
            (playlist_t *)vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
                                           FIND_ANYWHERE );
        if( p_playlist == NULL )
        {
656
            return 0;
657
658
659
660
        }

        playlist_Stop( p_playlist );
        vlc_object_release( p_playlist );
661
        return 0;
662
    }
663
664
665
666
667
668
669
670
671
672
673
674
675

    /* 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 */
676
677
678
679
                msg_Dbg( p_vout, "WinProc WM_SYSCOMMAND" );
                return 0;                  /* this stops them from happening */
            case IDM_TOGGLE_ON_TOP:            /* toggle the "on top" status */
            {
680
                vlc_value_t val;
681
682
                msg_Dbg( p_vout, "WinProc WM_SYSCOMMAND: IDM_TOGGLE_ON_TOP");

683
                /* Get the current value... */
gbazin's avatar
   
gbazin committed
684
                if( var_Get( p_vout, "video-on-top", &val ) < 0 )
685
686
687
                    return 0;
                /* ...and change it */
                val.b_bool = !val.b_bool;
gbazin's avatar
   
gbazin committed
688
                var_Set( p_vout, "video-on-top", val );
689
690
691
                return 0;
                break;
            }
692
693
694
        }
        break;

695
696
697
698
699
700
701
702
703
704
705
    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 )
706
        {
gbazin's avatar
   
gbazin committed
707
            msg_Warn( p_vout, "Can't create video sub-window" );
708
709
710
        }
        else
        {
gbazin's avatar
   
gbazin committed
711
            msg_Dbg( p_vout, "Created video sub-window" );
712
            SetWindowLong( p_vout->p_sys->hvideownd,
gbazin's avatar
   
gbazin committed
713
                           GWL_WNDPROC, (LONG)DirectXVideoEventProc );
714
715
716
717
718
719
720
721
        }
        break;

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

gbazin's avatar
   
gbazin committed
722
723
724
725
726
727
728
729
730
731
732
733
734
    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;
    }

735
736
    return DefWindowProc(hwnd, message, wParam, lParam);
}
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762

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
763
764
765
766
767

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

768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
    { 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;
}