diff --git a/common/backends.h b/common/backends.h index bd0d7769..560f9a59 100644 --- a/common/backends.h +++ b/common/backends.h @@ -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 diff --git a/common/defaults.h b/common/defaults.h index a036f51c..9e2a8631 100644 --- a/common/defaults.h +++ b/common/defaults.h @@ -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 diff --git a/engine/client/input.c b/engine/client/input.c index 538a36cd..683922a7 100644 --- a/engine/client/input.c +++ b/engine/client/input.c @@ -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 } diff --git a/engine/client/input.h b/engine/client/input.h index 761dd3a0..e68c5c36 100644 --- a/engine/client/input.h +++ b/engine/client/input.h @@ -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 diff --git a/engine/client/in_evdev.c b/engine/platform/linux/in_evdev.c similarity index 62% rename from engine/client/in_evdev.c rename to engine/platform/linux/in_evdev.c index b3093f21..98b81f57 100644 --- a/engine/client/in_evdev.c +++ b/engine/platform/linux/in_evdev.c @@ -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 #include #include #include -#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 diff --git a/engine/platform/linux/vid_fbdev.c b/engine/platform/linux/vid_fbdev.c new file mode 100644 index 00000000..49e1f0cb --- /dev/null +++ b/engine/platform/linux/vid_fbdev.c @@ -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 +#include + +#include +#include +#include +#ifdef __ANDROID__ +#include +#else +#include +#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 diff --git a/engine/platform/platform.h b/engine/platform/platform.h index 33e9b186..93d73afb 100644 --- a/engine/platform/platform.h +++ b/engine/platform/platform.h @@ -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 /* ============================================================================== diff --git a/engine/platform/stub/s_stub.c b/engine/platform/stub/s_stub.c new file mode 100644 index 00000000..f99c2b29 --- /dev/null +++ b/engine/platform/stub/s_stub.c @@ -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 diff --git a/engine/wscript b/engine/wscript index f7fb7461..6a64d981 100644 --- a/engine/wscript +++ b/engine/wscript @@ -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'])