]> git.sesse.net Git - vlc/blobdiff - modules/video_output/directx/events.c
* src/video_output/vout_intf.c: new vout_ControlWindow() function.
[vlc] / modules / video_output / directx / events.c
index 24985ddcaf5d8ee67488f9096a091404d8d0f9c4..730e158105616f7a395a9b417899b56a4006b417 100644 (file)
@@ -1,10 +1,10 @@
 /*****************************************************************************
  * events.c: Windows DirectX video output events handler
  *****************************************************************************
- * Copyright (C) 2001 VideoLAN
- * $Id: events.c,v 1.20 2003/07/29 21:14:10 gbazin Exp $
+ * Copyright (C) 2001-2004 VideoLAN
+ * $Id$
  *
- * Authors: Gildas Bazin <gbazin@netcourrier.com>
+ * Authors: Gildas Bazin <gbazin@videolan.org>
  *
  * 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
@@ -28,6 +28,7 @@
  *****************************************************************************/
 #include <errno.h>                                                 /* ENOMEM */
 #include <stdlib.h>                                                /* free() */
+#include <ctype.h>                                              /* tolower() */
 #include <string.h>                                            /* strerror() */
 
 #include <vlc/vlc.h>
@@ -41,6 +42,7 @@
 
 #include <ddraw.h>
 
+#include "vlc_keys.h"
 #include "vout.h"
 
 /*****************************************************************************
  *****************************************************************************/
 static int  DirectXCreateWindow( vout_thread_t *p_vout );
 static void DirectXCloseWindow ( vout_thread_t *p_vout );
-static long FAR PASCAL DirectXEventProc ( HWND hwnd, UINT message,
-                                          WPARAM wParam, LPARAM lParam );
+static long FAR PASCAL DirectXEventProc( HWND, UINT, WPARAM, LPARAM );
+static long FAR PASCAL DirectXVideoEventProc( HWND, UINT, WPARAM, LPARAM );
+
+static int Control( vout_thread_t *p_vout, int i_query, va_list args );
+
+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 );
+    }
+}
+
+static int DirectXConvertKey( int i_key );
 
 /*****************************************************************************
  * DirectXEventThread: Create video window & handle its messages
@@ -67,6 +86,7 @@ void DirectXEventThread( event_thread_t *p_event )
     int i_width, i_height, i_x, i_y;
 
     /* Initialisation */
+    p_event->p_vout->pf_control = Control;
 
     /* Create a window for the video */
     /* Creating a window under Windows also initializes the thread's event
@@ -77,38 +97,55 @@ void DirectXEventThread( event_thread_t *p_event )
         p_event->b_dead = VLC_TRUE;
     }
 
-    /* signal the creation of the window */
+    /* Signal the creation of the window */
     vlc_thread_ready( p_event );
 
     /* Main loop */
     /* GetMessage will sleep if there's no message in the queue */
-    while( !p_event->b_die
-           && GetMessage( &msg, p_event->p_vout->p_sys->hwnd, 0, 0 ) )
+    while( !p_event->b_die && ( p_event->p_vout->p_sys->hparent ||
+           GetMessage( &msg, p_event->p_vout->p_sys->hwnd, 0, 0 ) ) )
     {
         /* Check if we are asked to exit */
         if( p_event->b_die )
             break;
 
+        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;
+        }
+
         switch( msg.message )
         {
 
-        case WM_NCMOUSEMOVE:
         case WM_MOUSEMOVE:
             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 );
 
-            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 );
+            if( msg.hwnd != p_event->p_vout->p_sys->hwnd )
+            {
+                /* Child window */
+                i_x = i_y = 0;
+            }
 
-            val.b_bool = VLC_TRUE;
-            var_Set( p_event->p_vout, "mouse-moved", val );
+            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 );
+            }
 
+        case WM_NCMOUSEMOVE:
             if( (abs(GET_X_LPARAM(msg.lParam) - old_mouse_pos.x) > 2 ||
                 (abs(GET_Y_LPARAM(msg.lParam) - old_mouse_pos.y)) > 2 ) )
             {
@@ -132,6 +169,7 @@ void DirectXEventThread( event_thread_t *p_event )
             var_Get( p_event->p_vout, "mouse-button-down", &val );
             val.i_int |= 1;
             var_Set( p_event->p_vout, "mouse-button-down", val );
+            DirectXPopupMenu( p_event, VLC_FALSE );
             break;
 
         case WM_LBUTTONUP:
@@ -151,6 +189,7 @@ void DirectXEventThread( event_thread_t *p_event )
             var_Get( p_event->p_vout, "mouse-button-down", &val );
             val.i_int |= 2;
             var_Set( p_event->p_vout, "mouse-button-down", val );
+            DirectXPopupMenu( p_event, VLC_FALSE );
             break;
 
         case WM_MBUTTONUP:
@@ -163,127 +202,94 @@ void DirectXEventThread( event_thread_t *p_event )
             var_Get( p_event->p_vout, "mouse-button-down", &val );
             val.i_int |= 4;
             var_Set( p_event->p_vout, "mouse-button-down", val );
+            DirectXPopupMenu( p_event, VLC_FALSE );
             break;
 
         case WM_RBUTTONUP:
             var_Get( p_event->p_vout, "mouse-button-down", &val );
             val.i_int &= ~4;
             var_Set( p_event->p_vout, "mouse-button-down", val );
+            DirectXPopupMenu( p_event, VLC_TRUE );
+            break;
+
+        case WM_KEYDOWN:
+        case WM_SYSKEYDOWN:
+            /* 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 )
             {
-                playlist_t *p_playlist =
-                    vlc_object_find( p_event, VLC_OBJECT_PLAYLIST,
-                                     FIND_ANYWHERE );
-                if( p_playlist != NULL )
+                /* This appears to be a "normal" (ascii) key */
+                val.i_int = tolower( MapVirtualKey( msg.wParam, 2 ) );
+            }
+
+            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 )
                 {
-                    vlc_value_t val;
-                    val.b_bool = VLC_TRUE; /* make compiler happy */
-                    var_Set( p_playlist, "intf-popupmenu", val );
-                    vlc_object_release( p_playlist );
+                    val.i_int |= KEY_MODIFIER_ALT;
                 }
+
+                var_Set( p_event->p_vlc, "key-pressed", val );
             }
             break;
 
-        case WM_KEYDOWN:
-            /* the key events are first processed here. The next
-             * message processed by this main message loop will be the
-             * char translation of the key event */
-            msg_Dbg( p_event, "WM_KEYDOWN" );
-            switch( msg.wParam )
+        case WM_MOUSEWHEEL:
+            if( GET_WHEEL_DELTA_WPARAM( msg.wParam ) > 0 )
             {
-            case VK_ESCAPE:
-                if( p_event->p_vout->b_fullscreen )
+                val.i_int = KEY_MOUSEWHEELUP;
+            }
+            else
+            {
+                val.i_int = KEY_MOUSEWHEELDOWN;
+            }
+            if( val.i_int )
+            {
+                if( GetKeyState(VK_CONTROL) & 0x8000 )
                 {
-                    p_event->p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
+                    val.i_int |= KEY_MODIFIER_CTRL;
                 }
-                else
+                if( GetKeyState(VK_SHIFT) & 0x8000 )
                 {
-                    /* close video window */
-                    PostMessage( msg.hwnd, WM_CLOSE, 0, 0 );
+                    val.i_int |= KEY_MODIFIER_SHIFT;
                 }
-                break;
-
-            case VK_MENU:
+                if( GetKeyState(VK_MENU) & 0x8000 )
                 {
-                    playlist_t *p_playlist =
-                        vlc_object_find( p_event, VLC_OBJECT_PLAYLIST,
-                                         FIND_ANYWHERE );
-                    if( p_playlist != NULL )
-                    {
-                        vlc_value_t val;
-                        var_Set( p_playlist, "intf-popupmenu", val );
-                        vlc_object_release( p_playlist );
-                    }
+                    val.i_int |= KEY_MODIFIER_ALT;
                 }
-                break;
 
-            case VK_LEFT:
-                /* input_Seek( p_event->p_vout, -5,
-                   INPUT_SEEK_SECONDS | INPUT_SEEK_CUR ); */
-                val.psz_string = "LEFT";
-                var_Set( p_vout, "key-pressed", val );
-                break;
-            case VK_RIGHT:
-                /* input_Seek( p_event->p_vout, 5,
-                   INPUT_SEEK_SECONDS | INPUT_SEEK_CUR ); */
-                val.psz_string = "RIGHT";
-                var_Set( p_vout, "key-pressed", val );
-                break;
-            case VK_UP:
-                /* input_Seek( p_event->p_vout, 60,
-                   INPUT_SEEK_SECONDS | INPUT_SEEK_CUR ); */
-                val.psz_string = "UP";
-                var_Set( p_vout, "key-pressed", val );
-                break;
-            case VK_DOWN:
-                /* input_Seek( p_event->p_vout, -60,
-                   INPUT_SEEK_SECONDS | INPUT_SEEK_CUR ); */
-                val.psz_string = "DOWN";
-                var_Set( p_vout, "key-pressed", val );
-                break;
-            case VK_RETURN:
-                val.psz_string = "ENTER";
-                var_Set( p_vout, "key-pressed", val );
-                break;
-            case VK_HOME:
-                input_Seek( p_event->p_vout, 0,
-                            INPUT_SEEK_BYTES | INPUT_SEEK_SET );
-                break;
-            case VK_END:
-                input_Seek( p_event->p_vout, 0,
-                            INPUT_SEEK_BYTES | INPUT_SEEK_END );
-                break;
-            case VK_PRIOR:
-                input_Seek( p_event->p_vout, 10,
-                            INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
-                break;
-            case VK_NEXT:
-                input_Seek( p_event->p_vout, -10,
-                            INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
-                break;
-            case VK_SPACE:
-                input_SetStatus( p_event->p_vout, INPUT_STATUS_PAUSE );
-                break;
+                var_Set( p_event->p_vlc, "key-pressed", val );
             }
-            TranslateMessage(&msg);
             break;
 
-        case WM_CHAR:
-            switch( msg.wParam )
-            {
-            case 'q':
-            case 'Q':
-                /* exit application */
-                p_event->p_vlc->b_die = VLC_TRUE;
-                break;
-
-            case 'f':                            /* switch to fullscreen */
-            case 'F':
-                p_event->p_vout->p_sys->i_changes |= VOUT_FULLSCREEN_CHANGE;
-                break;
+        case WM_VLC_CHANGE_TEXT:
+            var_Get( p_event->p_vout, "video-title", &val );
 
-            default:
-                break;
+            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 );
+            }
+            break;
 
         default:
             /* Messages we don't handle directly are dispatched to the
@@ -296,7 +302,8 @@ void DirectXEventThread( event_thread_t *p_event )
 
     } /* End Main loop */
 
-    if( msg.message == WM_QUIT )
+    /* Check for WM_QUIT if we created the window */
+    if( !p_event->p_vout->p_sys->hparent && msg.message == WM_QUIT )
     {
         msg_Warn( p_event, "WM_QUIT... should not happen!!" );
         p_event->p_vout->p_sys->hwnd = NULL; /* Window already destroyed */
@@ -323,59 +330,20 @@ void DirectXEventThread( event_thread_t *p_event )
 static int DirectXCreateWindow( vout_thread_t *p_vout )
 {
     HINSTANCE  hInstance;
-    COLORREF   colorkey;
-    HDC        hdc;
     HMENU      hMenu;
     RECT       rect_window;
 
-    vlc_value_t val;
-
     msg_Dbg( p_vout, "DirectXCreateWindow" );
 
     /* Get this module's instance */
     hInstance = GetModuleHandle(NULL);
 
-    /* Create a BRUSH that will be used by Windows to paint the window
-     * background.
-     * This window background is important for us as it will be used by the
-     * graphics card to display the overlay.
-     * This is why we carefully choose the color for this background, the goal
-     * being to choose a color which isn't complete black but nearly. We
-     * obviously don't want to use black as a colorkey for the overlay because
-     * black is one of the most used color and thus would give us undesirable
-     * effects */
-    /* the first step is to find the colorkey we want to use. The difficulty
-     * comes from the potential dithering (depends on the display depth)
-     * because we need to know the real RGB value of the chosen colorkey */
-    hdc = GetDC( NULL );
-    for( colorkey = 0x0a; colorkey < 0xff /* all shades of red */; colorkey++ )
-    {
-        if( colorkey == GetNearestColor( hdc, colorkey ) )
-        {
-            break;
-        }
-    }
-    msg_Dbg( p_vout, "background color: %i", colorkey );
-
-    /* Create the actual brush */
-    p_vout->p_sys->hbrush = CreateSolidBrush(colorkey);
-    p_vout->p_sys->i_rgb_colorkey = (int)colorkey;
-
-    /* Get the current size of the display and its colour depth */
-    p_vout->p_sys->rect_display.right = GetDeviceCaps( hdc, HORZRES );
-    p_vout->p_sys->rect_display.bottom = GetDeviceCaps( hdc, VERTRES );
-    p_vout->p_sys->i_display_depth = GetDeviceCaps( hdc, BITSPIXEL );
-    msg_Dbg( p_vout, "screen dimensions %ix%i colour depth %i",
-                      p_vout->p_sys->rect_display.right,
-                      p_vout->p_sys->rect_display.bottom,
-                      p_vout->p_sys->i_display_depth );
-
-    ReleaseDC( NULL, hdc );
-
     /* If an external window was specified, we'll draw in it. */
-    var_Get( p_vout->p_vlc, "drawable", &val );
     p_vout->p_sys->hparent = p_vout->p_sys->hwnd =
-             val.i_int ?  (void*)(ptrdiff_t) val.i_int : NULL;
+        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 );
 
     if( p_vout->p_sys->hparent )
     {
@@ -386,20 +354,19 @@ static int DirectXCreateWindow( vout_thread_t *p_vout )
         SetClassLong( p_vout->p_sys->hwnd,
                       GCL_STYLE, CS_DBLCLKS );
         SetClassLong( p_vout->p_sys->hwnd,
-                      GCL_HBRBACKGROUND, (LONG)p_vout->p_sys->hbrush );
+                      GCL_HBRBACKGROUND, (LONG)GetStockObject(BLACK_BRUSH) );
         SetClassLong( p_vout->p_sys->hwnd,
                       GCL_HCURSOR, (LONG)LoadCursor(NULL, IDC_ARROW) );
         /* 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 );
+        SetWindowLongPtr( p_vout->p_sys->hwnd, GWLP_USERDATA, (LONG_PTR)p_vout );
 
         p_vout->p_sys->pf_wndproc =
-               (WNDPROC)SetWindowLong( p_vout->p_sys->hwnd,
-                                       GWL_WNDPROC, (LONG)DirectXEventProc );
+            (WNDPROC)SetWindowLong( p_vout->p_sys->hwnd, GWLP_WNDPROC,
+                                    (LONG_PTR)DirectXEventProc );
 
         /* Blam! Erase everything that might have been there. */
-        RedrawWindow( p_vout->p_sys->hwnd, NULL, NULL,
-                      RDW_INVALIDATE | RDW_ERASE );
+        InvalidateRect( p_vout->p_sys->hwnd, NULL, TRUE );
     }
     else
     {
@@ -407,6 +374,9 @@ static int DirectXCreateWindow( vout_thread_t *p_vout )
         HICON      vlc_icon = NULL;
         char       vlc_path[MAX_PATH+1];
 
+        /* 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 ) )
@@ -423,7 +393,7 @@ static int DirectXCreateWindow( vout_thread_t *p_vout )
         wc.hInstance     = hInstance;                            /* instance */
         wc.hIcon         = vlc_icon;                /* load the vlc big icon */
         wc.hCursor       = LoadCursor(NULL, IDC_ARROW);    /* default cursor */
-        wc.hbrBackground = p_vout->p_sys->hbrush;        /* background color */
+        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 */
@@ -433,13 +403,6 @@ static int DirectXCreateWindow( vout_thread_t *p_vout )
         {
             WNDCLASS wndclass;
 
-            /* Free window background brush */
-            if( p_vout->p_sys->hbrush )
-            {
-                DeleteObject( p_vout->p_sys->hbrush );
-                p_vout->p_sys->hbrush = NULL;
-            }
-
             if( vlc_icon )
             {
                 DestroyIcon( vlc_icon );
@@ -468,9 +431,12 @@ static int DirectXCreateWindow( vout_thread_t *p_vout )
         p_vout->p_sys->hwnd =
             CreateWindow( "VLC DirectX",             /* name of window class */
                     VOUT_TITLE " (DirectX Output)", /* window title bar text */
-                    WS_OVERLAPPEDWINDOW | WS_SIZEBOX,        /* window style */
-                    CW_USEDEFAULT,                   /* default X coordinate */
-                    0,                               /* default Y coordinate */
+                    WS_OVERLAPPEDWINDOW | WS_SIZEBOX | WS_VISIBLE |
+                    WS_CLIPCHILDREN,                         /* window style */
+                    (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 */
                     rect_window.right - rect_window.left,    /* window width */
                     rect_window.bottom - rect_window.top,   /* window height */
                     NULL,                                /* no parent window */
@@ -485,15 +451,20 @@ static int DirectXCreateWindow( vout_thread_t *p_vout )
         }
     }
 
+    /* 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 );
+
     /* Append a "Always On Top" entry in the system menu */
     hMenu = GetSystemMenu( p_vout->p_sys->hwnd, FALSE );
     AppendMenu( hMenu, MF_SEPARATOR, 0, "" );
     AppendMenu( hMenu, MF_STRING | MF_UNCHECKED,
                        IDM_TOGGLE_ON_TOP, "Always on &Top" );
 
-    /* Now display the window */
-    ShowWindow( p_vout->p_sys->hwnd, SW_SHOW );
-
     return VLC_SUCCESS;
 }
 
@@ -510,6 +481,21 @@ static void DirectXCloseWindow( vout_thread_t *p_vout )
     {
         DestroyWindow( p_vout->p_sys->hwnd );
     }
+    else if( p_vout->p_sys->hparent )
+    {
+        /* Get rid of the video sub-window */
+        PostMessage( p_vout->p_sys->hvideownd, WM_VLC_DESTROY_VIDEO_WIN, 0, 0);
+
+        /* We don't want our windowproc to be called anymore */
+        SetWindowLongPtr( p_vout->p_sys->hwnd,
+                          GWLP_WNDPROC, (LONG_PTR)p_vout->p_sys->pf_wndproc );
+        SetWindowLongPtr( p_vout->p_sys->hwnd, GWLP_USERDATA, 0 );
+
+        /* Blam! Erase everything that might have been there. */
+        InvalidateRect( p_vout->p_sys->hwnd, NULL, TRUE );
+
+        vout_ReleaseWindow( p_vout, (void *)p_vout->p_sys->hparent );
+    }
 
     p_vout->p_sys->hwnd = NULL;
 
@@ -563,20 +549,40 @@ void DirectXUpdateRects( vout_thread_t *p_vout, vlc_bool_t b_force )
     vout_PlacePicture( p_vout, rect.right, rect.bottom,
                        &i_x, &i_y, &i_width, &i_height );
 
+    if( p_vout->p_sys->hvideownd )
+        SetWindowPos( p_vout->p_sys->hvideownd, HWND_TOP,
+                      i_x, i_y, i_width, i_height, 0 );
+
     /* Destination image position and dimensions */
     rect_dest.left = point.x + i_x;
     rect_dest.right = rect_dest.left + i_width;
     rect_dest.top = point.y + i_y;
     rect_dest.bottom = rect_dest.top + i_height;
 
+    /* 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;
+    }
 
     /* UpdateOverlay directdraw function doesn't automatically clip to the
      * display size so we need to do it otherwise it will fail */
 
     /* Clip the destination window */
-    IntersectRect( &rect_dest_clipped,
-                   &rect_dest,
-                   &p_vout->p_sys->rect_display );
+    if( !IntersectRect( &rect_dest_clipped, &rect_dest,
+                        &p_vout->p_sys->rect_display ) )
+    {
+        SetRectEmpty( &rect_src_clipped );
+        return;
+    }
 
 #if 0
     msg_Dbg( p_vout, "DirectXUpdateRects image_dst_clipped coords:"
@@ -611,6 +617,21 @@ void DirectXUpdateRects( vout_thread_t *p_vout, vlc_bool_t b_force )
       (rect_dest.bottom - rect_dest_clipped.bottom) * p_vout->render.i_height /
       (rect_dest.bottom - rect_dest.top);
 
+    /* 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;
+    }
+
 #if 0
     msg_Dbg( p_vout, "DirectXUpdateRects image_src_clipped"
                      " coords: %i,%i,%i,%i",
@@ -618,18 +639,15 @@ void DirectXUpdateRects( vout_thread_t *p_vout, vlc_bool_t b_force )
                      rect_src_clipped.right, rect_src_clipped.bottom );
 #endif
 
-    /* Signal the size change */
-    if( !p_vout->p_sys->p_event->b_die )
-    {
-        if( p_vout->p_sys->b_using_overlay )
-        {
-            DirectXUpdateOverlay( p_vout );
-        }
-        else
-        {
-            p_vout->p_sys->i_changes |= VOUT_SIZE_CHANGE;
-        }
-    }
+    /* 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;
+
+    /* Signal the change in size/position */
+    p_vout->p_sys->i_changes |= DX_POSITION_CHANGE;
 
 #undef rect_src
 #undef rect_src_clipped
@@ -657,11 +675,18 @@ static long FAR PASCAL DirectXEventProc( HWND hwnd, UINT message,
     {
         /* Store p_vout for future use */
         p_vout = (vout_thread_t *)((CREATESTRUCT *)lParam)->lpCreateParams;
-        SetWindowLong( hwnd, GWL_USERDATA, (LONG)p_vout );
+        SetWindowLongPtr( hwnd, GWLP_USERDATA, (LONG_PTR)p_vout );
     }
     else
     {
-        p_vout = (vout_thread_t *)GetWindowLong( hwnd, GWL_USERDATA );
+        p_vout = (vout_thread_t *)GetWindowLongPtr( hwnd, GWLP_USERDATA );
+    }
+
+    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);
     }
 
     switch( message )
@@ -707,42 +732,224 @@ static long FAR PASCAL DirectXEventProc( HWND hwnd, UINT message,
                 msg_Dbg( p_vout, "WinProc WM_SYSCOMMAND: IDM_TOGGLE_ON_TOP");
 
                 /* Get the current value... */
-                if( var_Get( p_vout, "directx-on-top", &val ) < 0 )
+                if( var_Get( p_vout, "video-on-top", &val ) < 0 )
                     return 0;
                 /* ...and change it */
                 val.b_bool = !val.b_bool;
-                var_Set( p_vout, "directx-on-top", val );
+                var_Set( p_vout, "video-on-top", val );
                 return 0;
                 break;
             }
         }
         break;
 
-    case WM_ERASEBKGND:
-        if( !p_vout->p_sys->b_using_overlay )
+    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 )
         {
-            /* We want to eliminate unnecessary background redraws which create
-             * an annoying flickering */
-            int i_width, i_height, i_x, i_y;
-            RECT rect_temp;
-            GetClipBox( (HDC)wParam, &rect_temp );
-#if 0
-            msg_Dbg( p_vout, "WinProc WM_ERASEBKGND %i,%i,%i,%i",
-                          rect_temp.left, rect_temp.top,
-                          rect_temp.right, rect_temp.bottom );
-#endif
-            vout_PlacePicture( p_vout, p_vout->p_sys->i_window_width,
-                               p_vout->p_sys->i_window_height,
-                               &i_x, &i_y, &i_width, &i_height );
-            ExcludeClipRect( (HDC)wParam, i_x, i_y,
-                             i_x + i_width, i_y + i_height );
+            msg_Warn( p_vout, "Can't create video sub-window" );
+        }
+        else
+        {
+            msg_Dbg( p_vout, "Created video sub-window" );
+            SetWindowLongPtr( p_vout->p_sys->hvideownd,
+                              GWLP_WNDPROC, (LONG_PTR)DirectXVideoEventProc );
+            /* 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 */
+            SetWindowLongPtr( p_vout->p_sys->hvideownd, GWLP_USERDATA,
+                              (LONG_PTR)p_vout->p_sys->pf_wndproc );
         }
         break;
 
+    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;
+
     default:
         //msg_Dbg( p_vout, "WinProc WM Default %i", message );
         break;
     }
 
+    if( p_vout->p_sys->pf_wndproc )
+    {
+        LRESULT i_ret;
+
+        /* Hmmm mozilla does manage somehow to save the pointer to our
+         * windowproc and will call us again whereby creating an
+         * infinite loop.
+         * We can detect this by resetting GWL_USERDATA before calling
+         * the parent's windowproc. */
+        SetWindowLongPtr( p_vout->p_sys->hwnd, GWLP_USERDATA, 0 );
+
+        /* Call next window proc in chain */
+        i_ret = CallWindowProc( p_vout->p_sys->pf_wndproc, hwnd, message,
+                                wParam, lParam );
+
+        SetWindowLongPtr( p_vout->p_sys->hwnd, GWLP_USERDATA,
+                          (LONG_PTR)p_vout );
+        return i_ret;
+    }
+    else
+    {
+        /* Let windows handle the message */
+        return DefWindowProc(hwnd, message, wParam, lParam);
+    }
+
+}
+
+static long FAR PASCAL DirectXVideoEventProc( HWND hwnd, UINT message,
+                                              WPARAM wParam, LPARAM lParam )
+{
+    WNDPROC pf_parentwndproc;
+    POINT pt;
+
+    switch( message )
+    {
+    case WM_VLC_DESTROY_VIDEO_WIN:
+        /* Destroy video sub-window */
+        DestroyWindow( hwnd );
+        break;
+    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
+         * confusion in the creator's window proc. */
+        pf_parentwndproc = (WNDPROC)GetWindowLongPtr( hwnd, GWLP_USERDATA );
+
+        if( pf_parentwndproc )
+        {
+            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;
+        }
+        break;
+    }
+
     return DefWindowProc(hwnd, message, wParam, lParam);
 }
+
+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 },
+
+    { VK_CONTROL, 0 },
+    { VK_SHIFT, 0 },
+    { VK_MENU, 0 },
+
+    { 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;
+}
+
+/*****************************************************************************
+ * 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:
+        if( p_vout->p_sys->p_win->owner_window )
+            return vout_ControlWindow( p_vout,
+                    (void *)p_vout->p_sys->hparent, i_query, args );
+
+        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;
+
+    case VOUT_CLOSE:
+        return VLC_SUCCESS;
+
+    default:
+        msg_Dbg( p_vout, "control query not supported" );
+        return VLC_EGENERIC;
+    }
+}