platform: new fbdev bakend, move in_evdev to bakends

This commit is contained in:
mittorn 2019-10-22 01:41:44 +07:00
parent 0933261898
commit 803c396fa2
9 changed files with 639 additions and 33 deletions

View File

@ -21,6 +21,7 @@ GNU General Public License for more details.
#define VIDEO_NULL 0
#define VIDEO_SDL 1
#define VIDEO_ANDROID 2
#define VIDEO_FBDEV 3
// audio backends (XASH_SOUND)
#define SOUND_NULL 0
@ -37,6 +38,7 @@ GNU General Public License for more details.
#define INPUT_NULL 0
#define INPUT_SDL 1
#define INPUT_ANDROID 2
#define INPUT_EVDEV 3
// timer (XASH_TIMER)
#define TIMER_NULL 0 // not used

View File

@ -67,6 +67,25 @@ SETUP BACKENDS DEFINITIONS
#endif
#endif // android case
#ifdef XASH_FBDEV
#ifndef XASH_VIDEO
#define XASH_VIDEO VIDEO_FBDEV
#endif
#ifndef XASH_TIMER
#define XASH_TIMER TIMER_LINUX
#endif
#ifndef XASH_INPUT
#define XASH_INPUT INPUT_EVDEV
#endif
#ifndef XASH_SOUND
#define XASH_SOUND SOUND_NULL
#endif
#define XASH_USE_EVDEV
#endif // android case
#endif // XASH_DEDICATED
// select crashhandler based on defines

View File

@ -222,7 +222,7 @@ void IN_ToggleClientMouse( int newstate, int oldstate )
#ifdef __ANDROID__
Android_ShowMouse( true );
#endif
#ifdef USE_EVDEV
#ifdef XASH_USE_EVDEV
Evdev_SetGrab( false );
#endif
}
@ -231,7 +231,7 @@ void IN_ToggleClientMouse( int newstate, int oldstate )
#ifdef __ANDROID__
Android_ShowMouse( false );
#endif
#ifdef USE_EVDEV
#ifdef XASH_USE_EVDEV
Evdev_SetGrab( true );
#endif
}

View File

@ -45,6 +45,8 @@ uint IN_CollectInputDevices( void );
void IN_LockInputDevices( qboolean lock );
void IN_EngineAppendMove( float frametime, void *cmd, qboolean active );
extern convar_t *m_yaw;
extern convar_t *m_pitch;
//
// in_touch.c
//
@ -103,13 +105,5 @@ void Joy_Init( void );
void Joy_Shutdown( void );
void Joy_EnableTextInput(qboolean enable, qboolean force);
//
// in_evdev.c
//
#ifdef XASH_USE_EVDEV
void Evdev_SetGrab( qboolean grab );
void Evdev_Shutdown( void );
void Evdev_Init( void );
#endif // XASH_USE_EVDEV
#endif//INPUT_H

View File

@ -12,17 +12,17 @@ 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.
*/
#include "platform/platform.h"
#ifdef XASH_USE_EVDEV
#include "common.h"
#include "input.h"
#include "client.h"
#include <fcntl.h>
#include <errno.h>
#include <linux/input.h>
#include <dirent.h>
#include "common.h"
#include "input.h"
#include "client.h"
#include "vgui_draw.h"
#define MAX_EVDEV_DEVICES 5
struct evdev_s
@ -31,12 +31,100 @@ struct evdev_s
int fds[MAX_EVDEV_DEVICES];
string paths[MAX_EVDEV_DEVICES];
qboolean grab;
float grabtime;
double grabtime;
float x, y;
qboolean chars;
qboolean shift;
} evdev;
int KeycodeFromEvdev(int keycode, int value);
static int KeycodeFromEvdev(int keycode, int value)
{
switch (keycode) {
case KEY_0: return '0';
case KEY_1: return '1';
case KEY_2: return '2';
case KEY_3: return '3';
case KEY_4: return '4';
case KEY_5: return '5';
case KEY_6: return '6';
case KEY_7: return '7';
case KEY_8: return '8';
case KEY_9: return '9';
case KEY_BACKSPACE: return K_BACKSPACE;
case KEY_ENTER: return K_ENTER;
case KEY_ESC: return K_ESCAPE;
case KEY_KP0: return K_KP_INS;
case KEY_KP1: return K_KP_END;
case KEY_KP2: return K_KP_DOWNARROW;
case KEY_KP3: return K_KP_PGDN;
case KEY_KP4: return K_KP_LEFTARROW;
case KEY_KP5: return K_KP_5;
case KEY_KP6: return K_KP_RIGHTARROW;
case KEY_KP7: return K_KP_HOME;
case KEY_KP8: return K_KP_UPARROW;
case KEY_KP9: return K_KP_PGUP;
case KEY_KPDOT: return K_KP_DEL;
case KEY_KPENTER: return K_KP_ENTER;
case KEY_Q: return 'q';
case KEY_W: return 'w';
case KEY_E: return 'e';
case KEY_R: return 'r';
case KEY_T: return 't';
case KEY_Y: return 'y';
case KEY_U: return 'u';
case KEY_I: return 'i';
case KEY_O: return 'o';
case KEY_P: return 'p';
case KEY_A: return 'a';
case KEY_S: return 's';
case KEY_D: return 'd';
case KEY_F: return 'f';
case KEY_G: return 'g';
case KEY_H: return 'h';
case KEY_J: return 'j';
case KEY_K: return 'k';
case KEY_L: return 'l';
case KEY_Z: return 'z';
case KEY_X: return 'x';
case KEY_C: return 'c';
case KEY_V: return 'v';
case KEY_B: return 'b';
case KEY_N: return 'n';
case KEY_M: return 'm';
case KEY_LEFTBRACE: return '[';
case KEY_RIGHTBRACE: return ']';
case KEY_MINUS: return '-';
case KEY_EQUAL: return '=';
case KEY_TAB: return K_TAB;
case KEY_SEMICOLON: return ';';
case KEY_APOSTROPHE: return '\'';
case KEY_GRAVE: return '`';
case KEY_BACKSLASH: return '\\';
case KEY_COMMA: return ',';
case KEY_DOT: return '.';
case KEY_SLASH: return '/';
case KEY_SPACE: return K_SPACE;
case KEY_KPASTERISK: return '*';
case KEY_RIGHTCTRL:
case KEY_LEFTCTRL:
return K_CTRL;
case KEY_RIGHTSHIFT:
case KEY_LEFTSHIFT:
return K_SHIFT;
case KEY_LEFT: return K_LEFTARROW;
case KEY_RIGHT: return K_RIGHTARROW;
case KEY_UP: return K_UPARROW;
case KEY_DOWN: return K_DOWNARROW;
case BTN_LEFT: return K_MOUSE1;
case BTN_RIGHT: return K_MOUSE2;
case BTN_MIDDLE: return K_MOUSE3;
default:
break;
}
return 0;
}
static void Evdev_CheckPermissions( void )
{
#ifdef __ANDROID__
@ -110,7 +198,7 @@ void Evdev_Autodetect_f( void )
if( EV_HASBIT( types, EV_REL ) )
{
Q_memset( codes, 0, sizeof( codes ) );
memset( codes, 0, sizeof( codes ) );
ioctl( fd, EVIOCGBIT( EV_REL, KEY_MAX ), codes );
if( !EV_HASBIT( codes, REL_X ) )
@ -129,6 +217,10 @@ open:
Q_strncpy( evdev.paths[evdev.devices], path, MAX_STRING );
evdev.fds[evdev.devices++] = fd;
Msg( "Opened device %s\n", path );
#if XASH_INPUT == INPUT_EVDEV
if( Sys_CheckParm( "-grab" ) )
ioctl( evdev.fds[i], EVIOCGRAB, (void*) 1 );
#endif
goto next;
close:
close( fd );
@ -178,6 +270,11 @@ void Evdev_OpenDevice ( const char *path )
Msg( "Input device #%d: %s opened sucessfully\n", evdev.devices, path );
evdev.fds[evdev.devices] = ret;
Q_strncpy( evdev.paths[evdev.devices++], path, MAX_STRING );
#if XASH_INPUT == INPUT_EVDEV
if( Sys_CheckParm( "-grab" ) )
ioctl( evdev.fds[i], EVIOCGRAB, (void*) 1 );
#endif
}
void Evdev_OpenDevice_f( void )
@ -196,7 +293,7 @@ Evdev_CloseDevice_f
void Evdev_CloseDevice_f ( void )
{
uint i;
char *arg;
const char *arg;
if( Cmd_Argc() < 2 )
return;
@ -260,22 +357,28 @@ void IN_EvdevFrame ( void )
break;
}
}
else if ( ( ev.type == EV_KEY ) && cls.key_dest == key_game )
else if ( ( ev.type == EV_KEY ) && (cls.key_dest == key_game || XASH_INPUT == INPUT_EVDEV ) )
{
switch( ev.code )
int key = KeycodeFromEvdev( ev.code, ev.value );
Key_Event ( key , ev.value );
if( evdev.chars && ev.value )
{
case BTN_LEFT:
Key_Event( K_MOUSE1, ev.value );
break;
case BTN_MIDDLE:
Key_Event( K_MOUSE3, ev.value );
break;
case BTN_RIGHT:
Key_Event( K_MOUSE2, ev.value );
break;
default:
Key_Event ( KeycodeFromEvdev( ev.code, ev.value ) , ev.value);
if( key >= 32 && key < 127 )
{
if( evdev.shift )
{
key = Q_toupper( key );
if( key == '-' )
key = '_';
if( key == '=' )
key = '+';
}
CL_CharEvent( key );
}
}
if( key == K_SHIFT )
evdev.shift = ev.value;
}
}
@ -285,7 +388,7 @@ void IN_EvdevFrame ( void )
Key_ClearStates();
}
if( m_ignore->integer )
if( CVAR_TO_BOOL(m_ignore) )
continue;
evdev.x += -dx * m_yaw->value;
@ -297,6 +400,8 @@ void IN_EvdevFrame ( void )
void Evdev_SetGrab( qboolean grab )
{
// grab only if evdev is secondary input source
#if XASH_INPUT != INPUT_EVDEV
int i;
if( grab )
@ -311,6 +416,7 @@ void Evdev_SetGrab( qboolean grab )
ioctl( evdev.fds[i], EVIOCGRAB, (void*) 0 );
}
evdev.grab = grab;
#endif
}
void IN_EvdevMove( float *yaw, float *pitch )
@ -320,18 +426,40 @@ void IN_EvdevMove( float *yaw, float *pitch )
evdev.x = evdev.y = 0.0f;
}
#if XASH_INPUT == INPUT_EVDEV
void Platform_EnableTextInput( qboolean enable )
{
evdev.chars = enable;
evdev.shift = false;
}
#endif
void Evdev_Init( void )
{
Cmd_AddCommand ("evdev_open", Evdev_OpenDevice_f, "Open event device");
Cmd_AddCommand ("evdev_close", Evdev_CloseDevice_f, "Close event device");
Cmd_AddCommand ("evdev_autodetect", Evdev_Autodetect_f, "Automaticly open mouses and keyboards");
#if XASH_INPUT == INPUT_EVDEV
Evdev_Autodetect_f();
#endif
}
void Evdev_Shutdown( void )
{
int i;
Cmd_RemoveCommand( "evdev_open" );
Cmd_RemoveCommand( "evdev_close" );
Cmd_RemoveCommand( "evdev_autodetect" );
for( i = 0; i < evdev.devices; i++ )
{
ioctl( evdev.fds[i], EVIOCGRAB, (void*) 0 );
close( evdev.fds[i] );
evdev.fds[i] = -1;
}
evdev.devices = 0;
}
#endif // XASH_USE_EVDEV

View File

@ -0,0 +1,296 @@
#include "platform/platform.h"
#if XASH_VIDEO == VIDEO_FBDEV
#include "input.h"
#include "client.h"
#include "filesystem.h"
#include "vid_common.h"
#include <fcntl.h>
#include <errno.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#ifdef __ANDROID__
#include <linux/kd.h>
#else
#include <sys/kd.h>
#endif
struct fb_s
{
int fd, tty_fd;
void *map;
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;
qboolean vsync;
int doublebuffer;
} fb;
#define DEFAULT_FBDEV "/dev/fb0"
/*
========================
Android_SwapBuffers
Update screen. Use native EGL if possible
========================
*/
void GL_SwapBuffers( void )
{
}
void FB_GetScreenRes(int *x, int *y)
{
*x = fb.vinfo.xres;
*y = fb.vinfo.yres;
}
qboolean R_Init_Video( const int type )
{
qboolean retval;
string fbdev = DEFAULT_FBDEV;
fb.fd = -1;
VID_StartupGamma();
if( type != REF_SOFTWARE )
return false;
Sys_GetParmFromCmdLine( "-fbdev", fbdev );
fb.fd = open( fbdev, O_RDWR );
if( fb.fd < 0 )
{
Con_Printf( S_ERROR, "failed to open framebuffer device: %s\n", strerror(errno));
}
if( Sys_CheckParm( "-ttygfx" ) )
fb.tty_fd = open( "/dev/tty", O_RDWR ); // only need this to set graphics mode, optional
ioctl(fb.fd, FBIOGET_FSCREENINFO, &fb.finfo);
ioctl(fb.fd, FBIOGET_VSCREENINFO, &fb.vinfo);
if( !(retval = VID_SetMode()) )
{
return retval;
}
host.renderinfo_changed = false;
return true;
}
void R_Free_Video( void )
{
// VID_DestroyWindow ();
// R_FreeVideoModes();
if( fb.doublebuffer )
{
fb.vinfo.yoffset = 0;
fb.vinfo.yres_virtual >>= 1;
ioctl( fb.fd, FBIOPAN_DISPLAY, &fb.vinfo );
}
if( fb.map )
munmap( fb.map, fb.finfo.smem_len );
close( fb.fd );
fb.fd = -1;
fb.map = NULL;
if( fb.tty_fd >= 0 )
{
ioctl( fb.tty_fd, KDSETMODE, KD_TEXT );
close( fb.tty_fd );
fb.tty_fd = -1;
}
ref.dllFuncs.GL_ClearExtensions();
}
qboolean VID_SetMode( void )
{
if( fb.tty_fd > 0 )
ioctl( fb.tty_fd, KDSETMODE, KD_GRAPHICS );
R_ChangeDisplaySettings( 0, 0, false ); // width and height are ignored anyway
return true;
}
rserr_t R_ChangeDisplaySettings( int width, int height, qboolean fullscreen )
{
FB_GetScreenRes( &width, &height );
Con_Reportf( "R_ChangeDisplaySettings: forced resolution to %dx%d)\n", width, height);
R_SaveVideoMode( width, height );
host.window_center_x = width / 2;
host.window_center_y = height / 2;
refState.wideScreen = true; // V_AdjustFov will check for widescreen
return rserr_ok;
}
int GL_SetAttribute( int attr, int val )
{
return 0;
}
int GL_GetAttribute( int attr, int *val )
{
return 0;
}
int R_MaxVideoModes( void )
{
return 0;
}
vidmode_t* R_GetVideoMode( int num )
{
return NULL;
}
void* GL_GetProcAddress( const char *name ) // RenderAPI requirement
{
return NULL;
}
void GL_UpdateSwapInterval( void )
{
// disable VSync while level is loading
if( cls.state < ca_active )
{
// setup fb vsync here
fb.vsync = false;
SetBits( gl_vsync->flags, FCVAR_CHANGED );
}
else if( FBitSet( gl_vsync->flags, FCVAR_CHANGED ))
{
ClearBits( gl_vsync->flags, FCVAR_CHANGED );
fb.vsync = true;
}
}
void *SW_LockBuffer( void )
{
if( fb.vsync )
{
int stub = 0;
ioctl(fb.fd, FBIO_WAITFORVSYNC, &stub);
}
if( fb.doublebuffer )
{
static int page = 0;
page = !page;
fb.vinfo.yoffset = page * fb.vinfo.yres;
return fb.map + page * fb.doublebuffer;
}
else
return fb.map;
}
void SW_UnlockBuffer( void )
{
// some single-buffer fb devices need this too
ioctl(fb.fd, FBIOPAN_DISPLAY, &fb.vinfo);
}
#define FB_BF_TO_MASK(x) (((1 << x.length) - 1) << (x.offset))
qboolean SW_CreateBuffer( int width, int height, uint *stride, uint *bpp, uint *r, uint *g, uint *b )
{
*bpp = fb.vinfo.bits_per_pixel >> 3;
*stride = fb.vinfo.xres_virtual;
*r = FB_BF_TO_MASK(fb.vinfo.red);
*g = FB_BF_TO_MASK(fb.vinfo.green);
*b = FB_BF_TO_MASK(fb.vinfo.blue);
if( Sys_CheckParm("-doublebuffer") )
{
fb.doublebuffer = *bpp * *stride * fb.vinfo.yres;
fb.vinfo.yres_virtual = fb.vinfo.yres * 2;
if(ioctl (fb.fd, FBIOPUT_VSCREENINFO, &fb.vinfo ))
{
fb.vinfo.transp.length = fb.vinfo.transp.offset = 0;
if( ioctl (fb.fd, FBIOPUT_VSCREENINFO, &fb.vinfo ) )
{
Con_Printf( S_ERROR "failed to enable double buffering!\n" );
}
}
ioctl( fb.fd, FBIOGET_FSCREENINFO, &fb.finfo );
ioctl( fb.fd, FBIOGET_VSCREENINFO, &fb.vinfo );
ioctl( fb.fd, FBIOPAN_DISPLAY, &fb.vinfo );
if( fb.finfo.smem_len < fb.doublebuffer * 2 )
{
Con_Printf( S_ERROR "not enough memory for double buffering, disabling!\n" );
fb.doublebuffer = 0;
}
}
if( fb.map )
munmap(fb.map, fb.finfo.smem_len);
fb.map = mmap(0, fb.finfo.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fb.fd, 0);
if( !fb.map )
return false;
return true;
}
// unrelated stubs
void Platform_MessageBox( const char *title, const char *message, qboolean parentMainWindow )
{
}
void Platform_GetClipboardText( char *buffer, size_t size )
{
}
void Platform_SetClipboardText( const char *buffer, size_t size )
{
}
void Platform_PreCreateMove( void )
{
}
// will be implemented later
void Platform_RunEvents( void )
{
}
void *Platform_GetNativeObject( const char *name )
{
return NULL;
}
void Platform_GetMousePos( int *x, int *y )
{
*x = *y = 0;
}
void Platform_SetMousePos(int x, int y)
{
}
void Platform_Vibrate(float life, char flags)
{
}
int Platform_JoyInit( int numjoy )
{
return 0;
}
#endif

View File

@ -111,6 +111,17 @@ void *SW_LockBuffer( void );
void SW_UnlockBuffer( void );
qboolean SW_CreateBuffer( int width, int height, uint *stride, uint *bpp, uint *r, uint *g, uint *b );
//
// in_evdev.c
//
#ifdef XASH_USE_EVDEV
void Evdev_SetGrab( qboolean grab );
void Evdev_Shutdown( void );
void Evdev_Init( void );
void IN_EvdevMove( float *yaw, float *pitch );
void IN_EvdevFrame ( void );
#endif // XASH_USE_EVDEV
/*
==============================================================================

View File

@ -0,0 +1,149 @@
/*
s_backend.c - sound hardware output
Copyright (C) 2009 Uncle Mike
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 3 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.
*/
#ifndef XASH_DEDICATED
#include "common.h"
#if XASH_SOUND == SOUND_NULL
#include "sound.h"
#define SAMPLE_16BIT_SHIFT 1
#define SECONDARY_BUFFER_SIZE 0x10000
/*
=======================================================================
Global variables. Must be visible to window-procedure function
so it can unlock and free the data block after it has been played.
=======================================================================
*/
dma_t dma;
void S_Activate( qboolean active )
{
}
/*
==================
SNDDMA_Init
Try to find a sound device to mix for.
Returns false if nothing is found.
==================
*/
qboolean SNDDMA_Init( void )
{
Msg( "Audio is not enabled\n" );
return false;
}
/*
==============
SNDDMA_GetDMAPos
return the current sample position (in mono samples read)
inside the recirculating dma buffer, so the mixing code will know
how many sample are required to fill it up.
===============
*/
int SNDDMA_GetDMAPos( void )
{
return dma.samplepos;
}
/*
==============
SNDDMA_GetSoundtime
update global soundtime
===============
*/
int SNDDMA_GetSoundtime( void )
{
static int buffers, oldsamplepos;
int samplepos, fullsamples;
fullsamples = dma.samples / 2;
// it is possible to miscount buffers
// if it has wrapped twice between
// calls to S_Update. Oh well.
samplepos = SNDDMA_GetDMAPos();
if( samplepos < oldsamplepos )
{
buffers++; // buffer wrapped
if( paintedtime > 0x40000000 )
{
// time to chop things off to avoid 32 bit limits
buffers = 0;
paintedtime = fullsamples;
S_StopAllSounds( true );
}
}
oldsamplepos = samplepos;
return (buffers * fullsamples + samplepos / 2);
}
/*
==============
SNDDMA_BeginPainting
Makes sure dma.buffer is valid
===============
*/
void SNDDMA_BeginPainting( void )
{
}
/*
==============
SNDDMA_Submit
Send sound to device if buffer isn't really the dma buffer
Also unlocks the dsound buffer
===============
*/
void SNDDMA_Submit( void )
{
}
/*
==============
SNDDMA_Shutdown
Reset the sound device for exiting
===============
*/
void SNDDMA_Shutdown( void )
{
Con_Printf("Shutting down audio.\n");
dma.initialized = false;
if (dma.buffer) {
Z_Free(dma.buffer);
dma.buffer = NULL;
}
}
#endif
#endif

View File

@ -12,6 +12,8 @@ def options(opt):
grp.add_option('--enable-stdin-input', action = 'store_true', dest = 'USE_SELECT', default = False,
help = 'enable console input from stdin (always enabled for dedicated) [default: %default]')
grp.add_option('--fbdev', action = 'store_true', dest = 'FBDEV_SW', default = False,
help = 'build fbdev-only software-only engine')
opt.load('sdl2')
@ -24,6 +26,9 @@ def configure(conf):
elif conf.env.DEST_OS == 'android': # Android doesn't need SDL2
for i in ['android', 'log', 'EGL']:
conf.check_cc(lib = i)
elif conf.options.FBDEV_SW:
conf.define('XASH_FBDEV', 1)
conf.env.FBDEV
else:
conf.load('sdl2')
if not conf.env.HAVE_SDL2:
@ -60,6 +65,8 @@ def build(bld):
if bld.env.DEST_OS == 'linux':
source += bld.path.ant_glob(['platform/linux/*.c'])
source += bld.path.ant_glob(['platform/stub/*.c'])
if bld.env.HAVE_SDL2:
libs.append('SDL2')
source += bld.path.ant_glob(['platform/sdl/*.c'])