X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fvideo_output%2Fdirectx%2Fevents.c;h=e366b25cf2a866e7497123b8d2b496cca6a9ac1c;hb=a748c62527732a3fb44103c1b5baf593add82038;hp=a11a488578d7cbad93985f9c0eefc0993103f027;hpb=1692658e9310713ecfd29f6f4360419e1ccba966;p=vlc diff --git a/modules/video_output/directx/events.c b/modules/video_output/directx/events.c index a11a488578..e366b25cf2 100644 --- a/modules/video_output/directx/events.c +++ b/modules/video_output/directx/events.c @@ -1,10 +1,10 @@ /***************************************************************************** * events.c: Windows DirectX video output events handler ***************************************************************************** - * Copyright (C) 2001 VideoLAN - * $Id: events.c,v 1.23 2003/09/24 16:10:02 gbazin Exp $ + * Copyright (C) 2001-2004 VideoLAN + * $Id$ * - * Authors: Gildas Bazin + * Authors: Gildas Bazin * * 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,8 +28,13 @@ *****************************************************************************/ #include /* ENOMEM */ #include /* free() */ +#include /* tolower() */ #include /* strerror() */ +#ifndef _WIN32_WINNT +# define _WIN32_WINNT 0x0400 +#endif + #include #include #include @@ -41,6 +46,7 @@ #include +#include "vlc_keys.h" #include "vout.h" /***************************************************************************** @@ -48,8 +54,10 @@ *****************************************************************************/ 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 ) { @@ -64,6 +72,8 @@ static void DirectXPopupMenu( event_thread_t *p_event, vlc_bool_t b_open ) } } +static int DirectXConvertKey( int i_key ); + /***************************************************************************** * DirectXEventThread: Create video window & handle its messages ***************************************************************************** @@ -75,11 +85,13 @@ static void DirectXPopupMenu( event_thread_t *p_event, vlc_bool_t b_open ) void DirectXEventThread( event_thread_t *p_event ) { MSG msg; - POINT old_mouse_pos = {0,0}; + POINT old_mouse_pos = {0,0}, mouse_pos; vlc_value_t val; int i_width, i_height, i_x, i_y; + HMODULE hkernel32; /* 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 @@ -90,42 +102,65 @@ 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 ); + /* Set power management stuff */ + if( (hkernel32 = GetModuleHandle( "KERNEL32" ) ) ) + { + ULONG (WINAPI* OurSetThreadExecutionState)( ULONG ); + + OurSetThreadExecutionState = + GetProcAddress( hkernel32, "SetThreadExecutionState" ); + + if( OurSetThreadExecutionState ) + /* Prevent monitor from powering off */ + OurSetThreadExecutionState( ES_DISPLAY_REQUIRED | ES_CONTINUOUS ); + else + msg_Dbg( p_event, "no support for SetThreadExecutionState()" ); + } + /* Main loop */ /* GetMessage will sleep if there's no message in the queue */ - while( !p_event->b_die && ( p_event->p_vout->p_sys->hparent || - GetMessage( &msg, p_event->p_vout->p_sys->hwnd, 0, 0 ) ) ) + while( !p_event->b_die && + 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 ) msleep( INTF_IDLE_SLEEP ); - 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 ); + } - if( (abs(GET_X_LPARAM(msg.lParam) - old_mouse_pos.x) > 2 || - (abs(GET_Y_LPARAM(msg.lParam) - old_mouse_pos.y)) > 2 ) ) + case WM_NCMOUSEMOVE: + GetCursorPos( &mouse_pos ); + if( (abs(mouse_pos.x - old_mouse_pos.x) > 2 || + (abs(mouse_pos.y - old_mouse_pos.y)) > 2 ) ) { GetCursorPos( &old_mouse_pos ); p_event->p_vout->p_sys->i_lastmoved = mdate(); @@ -139,10 +174,19 @@ void DirectXEventThread( event_thread_t *p_event ) break; case WM_VLC_HIDE_MOUSE: + if( p_event->p_vout->p_sys->b_cursor_hidden ) break; + p_event->p_vout->p_sys->b_cursor_hidden = VLC_TRUE; GetCursorPos( &old_mouse_pos ); ShowCursor( FALSE ); break; + case WM_VLC_SHOW_MOUSE: + if( !p_event->p_vout->p_sys->b_cursor_hidden ) break; + p_event->p_vout->p_sys->b_cursor_hidden = VLC_FALSE; + GetCursorPos( &old_mouse_pos ); + ShowCursor( TRUE ); + break; + case WM_LBUTTONDOWN: var_Get( p_event->p_vout, "mouse-button-down", &val ); val.i_int |= 1; @@ -191,106 +235,83 @@ void DirectXEventThread( event_thread_t *p_event ) 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_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 ) + { + /* This appears to be a "normal" (ascii) key */ + val.i_int = tolower( MapVirtualKey( msg.wParam, 2 ) ); + } + + if( val.i_int ) { - case VK_ESCAPE: - if( p_event->p_vout->b_fullscreen ) + 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_event->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_event->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_event->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_event->p_vout, "key-pressed", val ); - break; - case VK_RETURN: - val.psz_string = "ENTER"; - var_Set( p_event->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 WM_MOUSEWHEEL: + if( GET_WHEEL_DELTA_WPARAM( msg.wParam ) > 0 ) { - case 'q': - case 'Q': - /* exit application */ - p_event->p_vlc->b_die = VLC_TRUE; - break; + 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 ) + { + val.i_int |= KEY_MODIFIER_ALT; + } - case 'f': /* switch to fullscreen */ - case 'F': - p_event->p_vout->p_sys->i_changes |= VOUT_FULLSCREEN_CHANGE; - break; + var_Set( p_event->p_vlc, "key-pressed", val ); + } + break; - default: - break; + case WM_VLC_CHANGE_TEXT: + var_Get( p_event->p_vout, "video-title", &val ); + + if( !val.psz_string || !*val.psz_string ) /* Default video title */ + { + if( p_event->p_vout->p_sys->b_using_overlay ) + SetWindowText( p_event->p_vout->p_sys->hwnd, + VOUT_TITLE " (hardware YUV overlay DirectX output)" ); + else if( p_event->p_vout->p_sys->b_hw_yuv ) + SetWindowText( p_event->p_vout->p_sys->hwnd, + VOUT_TITLE " (hardware YUV DirectX output)" ); + else SetWindowText( p_event->p_vout->p_sys->hwnd, + VOUT_TITLE " (software RGB DirectX output)" ); + } + else + { + SetWindowText( p_event->p_vout->p_sys->hwnd, val.psz_string ); } + break; default: /* Messages we don't handle directly are dispatched to the @@ -303,7 +324,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 */ @@ -330,177 +352,128 @@ 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; + WNDCLASSEX wc; /* window class components */ + HICON vlc_icon = NULL; + char vlc_path[MAX_PATH+1]; + int i_style; 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; - - if( p_vout->p_sys->hparent ) + p_vout->p_sys->hparent = + 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 ); + + /* 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 ) ) { - 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, - GCL_HBRBACKGROUND, (LONG)p_vout->p_sys->hbrush ); - 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 ); - - 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. */ - RedrawWindow( p_vout->p_sys->hwnd, NULL, NULL, - RDW_INVALIDATE | RDW_ERASE ); + vlc_icon = ExtractIcon( hInstance, vlc_path, 0 ); } - else + + /* Fill in the window class structure */ + wc.cbSize = sizeof(WNDCLASSEX); + wc.style = CS_DBLCLKS; /* style: dbl click */ + wc.lpfnWndProc = (WNDPROC)DirectXEventProc; /* event handler */ + wc.cbClsExtra = 0; /* no extra class data */ + wc.cbWndExtra = 0; /* no extra window data */ + wc.hInstance = hInstance; /* instance */ + wc.hIcon = vlc_icon; /* load the vlc big icon */ + wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* default cursor */ + wc.hbrBackground = GetStockObject(BLACK_BRUSH); /* background color */ + wc.lpszMenuName = NULL; /* no menu */ + wc.lpszClassName = "VLC DirectX"; /* use a special class */ + wc.hIconSm = vlc_icon; /* load the vlc small icon */ + + /* Register the window class */ + if( !RegisterClassEx(&wc) ) { - WNDCLASSEX wc; /* window class components */ - HICON vlc_icon = NULL; - char vlc_path[MAX_PATH+1]; + WNDCLASS wndclass; - /* Get the Icon from the main app */ - vlc_icon = NULL; - if( GetModuleFileName( NULL, vlc_path, MAX_PATH ) ) - { - vlc_icon = ExtractIcon( hInstance, vlc_path, 0 ); - } + if( vlc_icon ) DestroyIcon( vlc_icon ); - /* Fill in the window class structure */ - wc.cbSize = sizeof(WNDCLASSEX); - wc.style = CS_DBLCLKS; /* style: dbl click */ - wc.lpfnWndProc = (WNDPROC)DirectXEventProc; /* event handler */ - wc.cbClsExtra = 0; /* no extra class data */ - wc.cbWndExtra = 0; /* no extra window data */ - wc.hInstance = hInstance; /* instance */ - wc.hIcon = vlc_icon; /* load the vlc big icon */ - wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* default cursor */ - wc.hbrBackground = p_vout->p_sys->hbrush; /* background color */ - wc.lpszMenuName = NULL; /* no menu */ - wc.lpszClassName = "VLC DirectX"; /* use a special class */ - wc.hIconSm = vlc_icon; /* load the vlc small icon */ - - /* Register the window class */ - if( !RegisterClassEx(&wc) ) + /* Check why it failed. If it's because one already exists + * then fine, otherwise return with an error. */ + if( !GetClassInfo( hInstance, "VLC DirectX", &wndclass ) ) { - 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 ); - } - - /* 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; - } + msg_Err( p_vout, "DirectXCreateWindow RegisterClass FAILED" ); + return VLC_EGENERIC; } + } - /* 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 ); + /* 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 */ + if( p_vout->p_sys->hparent ) + i_style = WS_VISIBLE|WS_CLIPCHILDREN|WS_CHILD; + else + i_style = WS_OVERLAPPEDWINDOW|WS_SIZEBOX|WS_VISIBLE|WS_CLIPCHILDREN; + + /* Create the window */ + p_vout->p_sys->hwnd = + CreateWindowEx( WS_EX_NOPARENTNOTIFY, + "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 */ + i_style, /* 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 */ + p_vout->p_sys->hparent, /* parent window */ NULL, /* no menu in this window */ hInstance, /* handle of this program instance */ (LPVOID)p_vout ); /* send p_vout to WM_CREATE */ - if( !p_vout->p_sys->hwnd ) - { - msg_Warn( p_vout, "DirectXCreateWindow create window FAILED" ); - return VLC_EGENERIC; - } + if( !p_vout->p_sys->hwnd ) + { + msg_Warn( p_vout, "DirectXCreateWindow create window FAILED" ); + return VLC_EGENERIC; + } + + if( p_vout->p_sys->hparent ) + { + LONG i_style; + + /* We don't want the window owner to overwrite our client area */ + i_style = GetWindowLong( p_vout->p_sys->hparent, GWL_STYLE ); + SetWindowLong( p_vout->p_sys->hparent, GWL_STYLE, + i_style | WS_CLIPCHILDREN ); + } + /* 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; } @@ -513,19 +486,10 @@ static void DirectXCloseWindow( vout_thread_t *p_vout ) { msg_Dbg( p_vout, "DirectXCloseWindow" ); - if( p_vout->p_sys->hwnd && !p_vout->p_sys->hparent ) - { - DestroyWindow( p_vout->p_sys->hwnd ); - } - else if( p_vout->p_sys->hparent ) - { - /* 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 ); + DestroyWindow( p_vout->p_sys->hwnd ); - /* Blam! Erase everything that might have been there. */ - InvalidateRect( p_vout->p_sys->hwnd, NULL, TRUE ); - } + if( p_vout->p_sys->hparent ) + vout_ReleaseWindow( p_vout, (void *)p_vout->p_sys->hparent ); p_vout->p_sys->hwnd = NULL; @@ -579,20 +543,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:" @@ -627,6 +611,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", @@ -634,18 +633,18 @@ 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; + + if( p_vout->p_sys->b_using_overlay ) + DirectXUpdateOverlay( p_vout ); + + /* Signal the change in size/position */ + p_vout->p_sys->i_changes |= DX_POSITION_CHANGE; #undef rect_src #undef rect_src_clipped @@ -673,11 +672,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 ) @@ -723,36 +729,50 @@ 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: @@ -760,5 +780,173 @@ static long FAR PASCAL DirectXEventProc( HWND hwnd, UINT message, break; } + /* 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_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; + POINT point; + + switch( i_query ) + { + case VOUT_SET_ZOOM: + if( p_vout->p_sys->hparent ) + 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_REPARENT: + /* Change window style, borders and title bar */ + vlc_mutex_lock( &p_vout->p_sys->lock ); + p_vout->p_sys->hparent = 0; + vlc_mutex_unlock( &p_vout->p_sys->lock ); + + /* Retrieve the window position */ + point.x = point.y = 0; + ClientToScreen( p_vout->p_sys->hwnd, &point ); + + SetParent( p_vout->p_sys->hwnd, GetDesktopWindow() ); + SetWindowLong( p_vout->p_sys->hwnd, GWL_STYLE, + WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW | + WS_SIZEBOX | WS_VISIBLE ); + SetWindowPos( p_vout->p_sys->hwnd, 0, point.x, point.y, 0, 0, + SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED ); + + return vout_vaControlDefault( p_vout, i_query, args ); + + case VOUT_CLOSE: + return VLC_SUCCESS; + + case VOUT_SET_STAY_ON_TOP: + if( p_vout->p_sys->hparent ) + return vout_ControlWindow( p_vout, + (void *)p_vout->p_sys->hparent, i_query, args ); + + p_vout->p_sys->b_on_top_change = VLC_TRUE; + return VLC_SUCCESS; + + default: + return vout_vaControlDefault( p_vout, i_query, args ); + } +}