1
mirror of https://code.videolan.org/videolan/vlc synced 2024-09-04 09:11:33 +02:00
vlc/modules/video_output/window_os2.c
2022-05-16 16:51:03 +00:00

679 lines
20 KiB
C

/*****************************************************************************
* window_os2.c: OS/2 non-embedded video window provider
*****************************************************************************
* Copyright (C) 2020 VLC authors and VideoLAN
*
* Authors: KO Myung-Hun <komh@chollian.net>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
#include <vlc_charset.h>
#include <vlc_plugin.h>
#include <vlc_window.h>
#include <vlc_mouse.h>
#include <vlc_actions.h>
#include <float.h>
#include <ctype.h>
/*****************************************************************************
* Module descriptor
*****************************************************************************/
static int Open( vlc_window_t *wnd );
vlc_module_begin ()
set_shortname( N_("OS/2 window"))
set_description( N_("OS/2 non-embeded window video"))
set_subcategory( SUBCAT_VIDEO_VOUT )
set_capability ("vout window", 30 )
set_callback( Open )
vlc_module_end ()
typedef struct vout_window_sys_t
{
TID tid;
HEV ack_event;
int i_result;
HAB hab;
HMQ hmq;
HWND frame;
HWND client;
LONG i_screen_width;
LONG i_screen_height;
RECTL client_rect;
unsigned button_pressed;
bool is_mouse_hidden;
ULONG cursor_timeout;
} vout_window_sys_t;
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static int ConvertKey( USHORT i_pmkey );
static void MousePressed( vlc_window_t *wnd, HWND hwnd, unsigned button );
static void MouseReleased( vlc_window_t *wnd, unsigned button );
static MRESULT EXPENTRY WndProc( HWND hwnd, ULONG msg,
MPARAM mp1, MPARAM mp2 );
static void MorphToPM( void );
static void PMThread( void *arg );
static int Enable( vlc_window_t *wnd, const vlc_window_cfg_t *cfg );
static void Disable( struct vlc_window *wnd );
static void Resize( vlc_window_t *wnd, unsigned width, unsigned height );
static void Close(vlc_window_t *wnd );
static void SetState( vlc_window_t *wnd, unsigned state );
static void UnsetFullscreen( vlc_window_t *wnd );
static void SetFullscreen( vlc_window_t *wnd, const char *id );
static void SetTitle( vlc_window_t *wnd, const char *title );
#define WC_VLC_WINDOW_OS2 "WC_VLC_WINDOW_OS2"
#define WM_VLC_ENABLE ( WM_USER + 1 )
#define WM_VLC_RESIZE ( WM_USER + 2 )
#define WM_VLC_SETSTATE ( WM_USER + 3 )
#define WM_VLC_FULLSCREEN ( WM_USER + 4 )
#define WM_VLC_SETTITLE ( WM_USER + 5 )
#define TID_HIDE_MOUSE 0x1010
static const struct
{
USHORT i_pmkey;
int i_vlckey;
} pmkeys_to_vlckeys[] =
{
{ VK_LEFT, KEY_LEFT },
{ VK_RIGHT, KEY_RIGHT },
{ VK_UP, KEY_UP },
{ VK_DOWN, KEY_DOWN },
{ VK_SPACE, ' ' },
{ VK_NEWLINE, KEY_ENTER },
{ VK_ENTER, KEY_ENTER },
{ 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_HOME, KEY_HOME },
{ VK_END, KEY_END },
{ VK_INSERT, KEY_INSERT },
{ VK_DELETE, KEY_DELETE },
/*
Not supported
{, KEY_MENU },
*/
{ VK_ESC, KEY_ESC },
{ VK_PAGEUP, KEY_PAGEUP },
{ VK_PAGEDOWN, KEY_PAGEDOWN },
{ VK_TAB, KEY_TAB },
{ VK_BACKSPACE, KEY_BACKSPACE },
/*
Not supported
{, KEY_MOUSEWHEELUP },
{, KEY_MOUSEWHEELDOWN },
{, KEY_MOUSEWHEELLEFT },
{, KEY_MOUSEWHEELRIGHT },
{, KEY_BROWSER_BACK },
{, KEY_BROWSER_FORWARD },
{, KEY_BROWSER_REFRESH },
{, KEY_BROWSER_STOP },
{, KEY_BROWSER_SEARCH },
{, KEY_BROWSER_FAVORITES },
{, KEY_BROWSER_HOME },
{, KEY_VOLUME_MUTE },
{, KEY_VOLUME_DOWN },
{, KEY_VOLUME_UP },
{, KEY_MEDIA_NEXT_TRACK },
{, KEY_MEDIA_PREV_TRACK },
{, KEY_MEDIA_STOP },
{, KEY_MEDIA_PLAY_PAUSE },
*/
{ 0, 0 }
};
static int ConvertKey( USHORT i_pmkey )
{
int i;
for( i = 0; pmkeys_to_vlckeys[ i ].i_pmkey != 0; i++ )
{
if( pmkeys_to_vlckeys[ i ].i_pmkey == i_pmkey )
return pmkeys_to_vlckeys[ i ].i_vlckey;
}
return 0;
}
static void MousePressed( vlc_window_t *wnd, HWND hwnd, unsigned button )
{
vout_window_sys_t *sys = wnd->sys;
if( WinQueryFocus( HWND_DESKTOP ) != hwnd )
WinSetFocus( HWND_DESKTOP, hwnd );
if( !sys->button_pressed )
WinSetCapture( HWND_DESKTOP, hwnd );
sys->button_pressed |= 1 << button;
vlc_window_ReportMousePressed( wnd, button );
}
static void MouseReleased( vlc_window_t *wnd, unsigned button )
{
vout_window_sys_t *sys = wnd->sys;
sys->button_pressed &= ~( 1 << button );
if( !sys->button_pressed )
WinSetCapture( HWND_DESKTOP, NULLHANDLE );
vlc_window_ReportMouseReleased(wnd, button);
}
#define WM_MOUSELEAVE 0x41F
static MRESULT EXPENTRY WndProc( HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2 )
{
vlc_window_t *wnd = WinQueryWindowPtr( hwnd, 0 );
MRESULT result = ( MRESULT )TRUE;
if ( !wnd )
return WinDefWindowProc( hwnd, msg, mp1, mp2 );
vout_window_sys_t *sys = wnd->sys;
RECTL rcl;
SWP swp;
if ( sys->is_mouse_hidden &&
(( msg >= WM_MOUSEFIRST && msg <= WM_MOUSELAST ) ||
( msg >= WM_EXTMOUSEFIRST && msg <= WM_EXTMOUSELAST ) ||
msg == WM_MOUSELEAVE ))
{
WinShowPointer( HWND_DESKTOP, TRUE );
sys->is_mouse_hidden = false;
WinStartTimer( sys->hab, sys->client, TID_HIDE_MOUSE,
sys->cursor_timeout );
}
switch( msg )
{
/* the user wants to close the window */
case WM_CLOSE:
vlc_window_ReportClose( wnd );
WinPostMsg( hwnd, WM_QUIT, 0, 0 );
result = 0;
break;
case WM_SIZE:
vlc_window_ReportSize( wnd,
SHORT1FROMMP( mp2 ), SHORT2FROMMP( mp2 ));
result = 0;
break;
case WM_MOUSEMOVE:
{
SHORT i_mouse_x = SHORT1FROMMP( mp1 );
SHORT i_mouse_y = SHORT2FROMMP( mp1 );
WinQueryWindowRect( hwnd, &rcl );
// Invert Y
int h = rcl.yTop - rcl.yBottom;
i_mouse_y = ( h - i_mouse_y ) - 1;
vlc_window_ReportMouseMoved( wnd, i_mouse_x, i_mouse_y );
result = WinDefWindowProc( hwnd, msg, mp1, mp2 );
break;
}
case WM_BUTTON1DOWN :
MousePressed( wnd, hwnd, MOUSE_BUTTON_LEFT );
break;
case WM_BUTTON2DOWN :
MousePressed( wnd, hwnd, MOUSE_BUTTON_RIGHT );
break;
case WM_BUTTON3DOWN :
MousePressed( wnd, hwnd, MOUSE_BUTTON_CENTER );
break;
case WM_BUTTON1UP :
MouseReleased( wnd, MOUSE_BUTTON_LEFT );
break;
case WM_BUTTON2UP :
MouseReleased( wnd, MOUSE_BUTTON_RIGHT );
break;
case WM_BUTTON3UP :
MouseReleased( wnd, MOUSE_BUTTON_CENTER );
break;
case WM_BUTTON1DBLCLK :
vlc_window_ReportMouseDoubleClick( wnd, MOUSE_BUTTON_LEFT );
break;
case WM_BUTTON2DBLCLK :
vlc_window_ReportMouseDoubleClick( wnd, MOUSE_BUTTON_RIGHT );
break;
case WM_BUTTON3DBLCLK :
vlc_window_ReportMouseDoubleClick( wnd, MOUSE_BUTTON_CENTER );
break;
case WM_TRANSLATEACCEL :
/* We have no accelerator table at all */
result = ( MRESULT )FALSE;
break;
case WM_CHAR :
{
USHORT i_flags = SHORT1FROMMP( mp1 );
USHORT i_ch = SHORT1FROMMP( mp2 );
USHORT i_vk = SHORT2FROMMP( mp2 );
int i_key = 0;
if( !( i_flags & KC_KEYUP ))
{
if( i_flags & KC_VIRTUALKEY )
/* convert the key if possible */
i_key = ConvertKey( i_vk );
else if(( i_flags & KC_CHAR ) && !HIBYTE( i_ch ))
i_key = tolower( i_ch );
if( i_key )
{
if( i_flags & KC_SHIFT )
i_key |= KEY_MODIFIER_SHIFT;
if( i_flags & KC_CTRL )
i_key |= KEY_MODIFIER_CTRL;
if( i_flags & KC_ALT )
i_key |= KEY_MODIFIER_ALT;
vlc_window_ReportKeyPress( wnd, i_key );
}
}
break;
}
case WM_TIMER :
if( !sys->is_mouse_hidden &&
SHORT1FROMMP( mp1 ) == TID_HIDE_MOUSE )
{
POINTL ptl;
WinQueryPointerPos( HWND_DESKTOP, &ptl );
if( WinWindowFromPoint( HWND_DESKTOP, &ptl, TRUE )
== sys->client )
{
WinShowPointer( HWND_DESKTOP, FALSE );
sys->is_mouse_hidden = true;
WinStopTimer( sys->hab, sys->client, TID_HIDE_MOUSE );
}
}
break;
case WM_VLC_ENABLE :
{
if( !LONGFROMMP( mp1 ))
{
/* Disable */
WinShowWindow( sys->frame, FALSE );
break;
}
/* Enable */
vlc_window_cfg_t *cfg = PVOIDFROMMP( mp2 );
if( cfg->is_decorated )
{
WinSetParent( WinWindowFromID( sys->frame, FID_SYSMENU ),
sys->frame, FALSE );
WinSetParent( WinWindowFromID( sys->frame, FID_TITLEBAR ),
sys->frame, FALSE );
WinSetParent( WinWindowFromID( sys->frame, FID_MINMAX ),
sys->frame, FALSE );
WinSetWindowBits( sys->frame, QWL_STYLE,
FS_SIZEBORDER, FS_SIZEBORDER );
}
else
{
WinSetParent( WinWindowFromID( sys->frame, FID_SYSMENU ),
HWND_OBJECT, FALSE );
WinSetParent( WinWindowFromID( sys->frame, FID_TITLEBAR ),
HWND_OBJECT, FALSE );
WinSetParent( WinWindowFromID( sys->frame, FID_MINMAX ),
HWND_OBJECT, FALSE );
WinSetWindowBits( sys->frame, QWL_STYLE, 0, FS_SIZEBORDER );
}
/* allow user to regain control over input events if requested */
bool b_mouse_support = var_InheritBool( wnd, "mouse-events");
bool b_key_support = var_InheritBool( wnd, "keyboard-events");
WinEnableWindow( hwnd, b_mouse_support || b_key_support );
sys->client_rect.xLeft = ( sys->i_screen_width - cfg->width )
/ 2;
sys->client_rect.yBottom = ( sys->i_screen_height - cfg->height )
/ 2;
sys->client_rect.xRight = sys->client_rect.xLeft + cfg->width;
sys->client_rect.yTop = sys->client_rect.yBottom + cfg->height;
if( cfg->is_fullscreen )
WinQueryWindowRect( HWND_DESKTOP, &rcl );
else
rcl = sys->client_rect;
WinCalcFrameRect( sys->frame, &rcl, FALSE );
WinSetWindowPos( sys->frame, HWND_TOP,
rcl.xLeft, rcl.yBottom,
rcl.xRight - rcl.xLeft, rcl.yTop - rcl.yBottom,
SWP_ZORDER | SWP_MOVE | SWP_SIZE |
SWP_SHOW | SWP_ACTIVATE );
free( cfg );
break;
}
case WM_VLC_RESIZE :
rcl.xLeft = 0;
rcl.yBottom = 0;
rcl.xRight = LONGFROMMP( mp1 );
rcl.yTop = LONGFROMMP( mp2 );
WinCalcFrameRect( sys->frame, &rcl, FALSE );
WinSetWindowPos( sys->frame, NULLHANDLE,
0, 0,
rcl.xRight - rcl.xLeft, rcl.yTop - rcl.yBottom,
SWP_SIZE );
WinQueryWindowPos( sys->frame, &swp );
sys->client_rect.xLeft = swp.x;
sys->client_rect.yBottom = swp.y;
sys->client_rect.xRight = sys->client_rect.xLeft + swp.cx;
sys->client_rect.yTop = sys->client_rect.yBottom + swp.cy;
WinCalcFrameRect( sys->frame, &sys->client_rect, TRUE );
break;
case WM_VLC_SETSTATE:
/* TODO */
break;
case WM_VLC_FULLSCREEN:
if( LONGFROMMP( mp1 ))
{
/* Fullscreen */
WinQueryWindowPos( sys->frame, &swp );
sys->client_rect.xLeft = swp.x;
sys->client_rect.yBottom = swp.y;
sys->client_rect.xRight = sys->client_rect.xLeft + swp.cx;
sys->client_rect.yTop = sys->client_rect.yBottom + swp.cy;
WinCalcFrameRect( sys->frame, &sys->client_rect, TRUE );
WinQueryWindowRect( HWND_DESKTOP, &rcl );
}
else
{
/* Windowed */
rcl = sys->client_rect;
}
WinCalcFrameRect( sys->frame, &rcl, FALSE );
WinSetWindowPos( sys->frame, HWND_TOP,
rcl.xLeft, rcl.yBottom,
rcl.xRight - rcl.xLeft, rcl.yTop - rcl.yBottom,
SWP_MOVE | SWP_SIZE | SWP_ZORDER | SWP_SHOW |
SWP_ACTIVATE );
break;
case WM_VLC_SETTITLE :
{
char *title = PVOIDFROMMP( mp1 );
WinSetWindowText( hwnd, title );
free( title );
break;
}
default :
return WinDefWindowProc( hwnd, msg, mp1, mp2 );
}
return result;
}
static void MorphToPM( void )
{
PPIB pib;
DosGetInfoBlocks(NULL, &pib);
/* Change flag from VIO to PM */
if (pib->pib_ultype == 2)
pib->pib_ultype = 3;
}
static void PMThread( void *arg )
{
vlc_window_t *wnd = arg;
vout_window_sys_t *sys = wnd->sys;
ULONG i_frame_flags;
QMSG qm;
/* */
MorphToPM();
sys->hab = WinInitialize( 0 );
sys->hmq = WinCreateMsgQueue( sys->hab, 0);
WinRegisterClass( sys->hab,
WC_VLC_WINDOW_OS2,
WndProc,
CS_SIZEREDRAW | CS_MOVENOTIFY,
sizeof( PVOID ));
i_frame_flags = FCF_SYSMENU | FCF_TITLEBAR | FCF_MINMAX |
FCF_SIZEBORDER | FCF_TASKLIST;
sys->frame =
WinCreateStdWindow( HWND_DESKTOP, /* parent window handle */
WS_VISIBLE, /* frame window style */
&i_frame_flags, /* window style */
WC_VLC_WINDOW_OS2, /* class name */
"VLC Video Window", /* window title */
0L, /* default client style */
NULLHANDLE, /* resource in exe file */
1, /* frame window id */
&sys->client ); /* client window handle */
if( sys->frame == NULLHANDLE )
{
msg_Err( wnd, "cannot create a frame window");
goto exit_error;
}
WinSetWindowPtr( sys->client, 0, wnd );
sys->cursor_timeout =
( ULONG )var_InheritInteger( wnd, "mouse-hide-timeout" );
WinStartTimer( sys->hab, sys->client, TID_HIDE_MOUSE,
sys->cursor_timeout );
sys->i_screen_width = WinQuerySysValue( HWND_DESKTOP, SV_CXSCREEN );
sys->i_screen_height = WinQuerySysValue( HWND_DESKTOP, SV_CYSCREEN );
/* Prevent SIG_FPE */
_control87( MCW_EM, MCW_EM );
sys->i_result = VLC_SUCCESS;
DosPostEventSem( sys->ack_event );
while( WinGetMsg( sys->hab, &qm, NULLHANDLE, 0, 0 ))
WinDispatchMsg( sys->hab, &qm );
WinDestroyWindow( sys->frame );
if( sys->is_mouse_hidden )
WinShowPointer( HWND_DESKTOP, TRUE );
exit_error:
WinDestroyMsgQueue( sys->hmq );
WinTerminate( sys->hab );
sys->i_result = VLC_EGENERIC;
DosPostEventSem( sys->ack_event );
}
static const struct vlc_window_operations ops = {
.enable = Enable,
.disable = Disable,
.resize = Resize,
.destroy = Close,
.set_state = SetState,
.unset_fullscreen = UnsetFullscreen,
.set_fullscreen = SetFullscreen,
.set_title = SetTitle,
};
static int Open( vlc_window_t *wnd )
{
vout_window_sys_t *sys;
wnd->sys = sys = calloc( 1, sizeof( *sys ));
if( !sys )
return VLC_ENOMEM;
DosCreateEventSem( NULL, &sys->ack_event, 0, FALSE );
sys->tid = _beginthread( PMThread, NULL, 1024 * 1024, wnd );
DosWaitEventSem( sys->ack_event, SEM_INDEFINITE_WAIT );
if( sys->i_result != VLC_SUCCESS )
{
DosCloseEventSem( sys->ack_event );
free( sys );
return VLC_EGENERIC;
}
wnd->type = VLC_WINDOW_TYPE_HWND;
wnd->handle.hwnd = ( void * )sys->client;
wnd->ops = &ops;
wnd->info.has_double_click = true;
return VLC_SUCCESS;
}
static int Enable( vlc_window_t *wnd, const vlc_window_cfg_t *cfg)
{
vout_window_sys_t *sys = wnd->sys;
vlc_window_cfg_t *config = malloc( sizeof( *config ));
if( !config )
return VLC_ENOMEM;
memcpy( config, cfg, sizeof( *config ));
WinPostMsg( sys->client, WM_VLC_ENABLE,
MPFROMLONG( TRUE ), MPFROMP( config ) );
return VLC_SUCCESS;
}
static void Disable( struct vlc_window *wnd )
{
vout_window_sys_t *sys = wnd->sys;
WinPostMsg( sys->client, WM_VLC_ENABLE, MPFROMLONG( FALSE ), 0 );
}
static void Resize( vlc_window_t *wnd, unsigned width, unsigned height )
{
vout_window_sys_t *sys = wnd->sys;
WinPostMsg( sys->client, WM_VLC_RESIZE,
MPFROMLONG( width ), MPFROMLONG( height ));
}
static void Close( vlc_window_t *wnd )
{
vout_window_sys_t *sys = wnd->sys;
WinPostQueueMsg( sys->hmq, WM_QUIT, 0, 0 );
DosWaitThread( &sys->tid, DCWW_WAIT );
DosCloseEventSem( sys->ack_event );
free( sys );
}
static void SetState( vlc_window_t *wnd, unsigned state )
{
vout_window_sys_t *sys = wnd->sys;
WinPostMsg( sys->client, WM_VLC_SETSTATE, MPFROMLONG( state ), 0 );
}
static void UnsetFullscreen(vlc_window_t *wnd)
{
vout_window_sys_t *sys = wnd->sys;
WinPostMsg( sys->client, WM_VLC_FULLSCREEN, MPFROMLONG( FALSE ), 0 );
}
static void SetFullscreen(vlc_window_t *wnd, const char *id)
{
VLC_UNUSED( id );
vout_window_sys_t *sys = wnd->sys;
WinPostMsg( sys->client, WM_VLC_FULLSCREEN, MPFROMLONG( TRUE ), 0 );
}
static void SetTitle(vlc_window_t *wnd, const char *title)
{
vout_window_sys_t *sys = wnd->sys;
WinPostMsg( sys->client, WM_VLC_SETTITLE, MPFROMP( strdup( title )), 0 );
}