diff --git a/build.md b/build.md index 0eb9fe4..45bcd7c 100644 --- a/build.md +++ b/build.md @@ -49,7 +49,10 @@ Building on Linux with GCC Requirements: -- NASM | On Debian and Ubuntu, run `sudo apt-get install nasm` to install it +| Name | Server | Client | Package name (apt-get) | +|-------|:------:|:------:|------------------------| +| NASM | X | X | nasm | +| SDL 2 | | X | libsdl2-dev | Options: @@ -79,7 +82,19 @@ There are 2 environment variables used for compiling and debugging: | QUAKE3DIR | required for building | optional | | CPMADIR | required for debugging | unused | -Build with other compilers --------------------------- +Building with other compilers +----------------------------- While it's not officially supported, you can modify the premake Lua script and run premake on it to generate new makefiles for your own needs. + +Bonus: Building SDL 2 from source on Linux +------------------------------------------ + +- Download the sources of the latest stable release +- Extract to sdl-src +- Create sdl-build next to sdl-src (it *has* to be out-of-tree) +- Navigate to the sdl-build directory with `cd` +- Run `../sdl-src/configure` +- Run `make` +- As superuser, run `make install` (or `sudo make install`) +- Run `ldconfig` to update the library paths diff --git a/changelog.txt b/changelog.txt index 5f4e543..c8c3841 100644 --- a/changelog.txt +++ b/changelog.txt @@ -141,6 +141,21 @@ fix: in raw mouse input mode, keeping any button pressed while focusing the wind Linux: +chg: the client requires SDL 2 - the following things are handled by it: + window, GL context, video modes, audio, kb and mouse input, monitor list, clipboard + +add: in_noGrab <0|1> (default: 0) disables input grabbing + +add: m_relative <0|1> (default: 1) enables SDL's relative mouse mode + you might want to set it to 0 if you have a messed up input driver + +add: s_autoMute selects when the audio output should be disabled + s_autoMute 0 = never mute + s_autoMute 1 (default) = mute when the window doesn't have input focus + s_autoMute 2 = mute when the window is minimized + +add: minimize command to minimize the client's window + add: automatic dedicated server process restarts for crashes and timed reboots (sv_minRebootDelayMins) this means 2 CNQ3 processes run per server: a parent (fixed pid) and a child (new pid after each restart) this behavior can be disabled by passing "nohardreboot" as a command-line argument @@ -156,14 +171,6 @@ fix: the crash handler will reset tty input mode correctly chg: tty input behavior matches in-game behavior when cgame is running and fixed the truncated tty input -fix: clear modifier keys on focus change to avoid ctrl/alt/shift being "stuck" - -chg: new Linux mouse input code, support for raw input with XInput 2 - in_mouse 0 = no input - in_mouse 1 = raw input (XInput 2) - in_mouse 2 = normal input (XWindows) - m_device to select a device explicity when using raw input - 17 Sep 11 - 1.47 diff --git a/code/unix/linux_local.h b/code/linux/linux_local.h similarity index 55% rename from code/unix/linux_local.h rename to code/linux/linux_local.h index b609ee4..55e6def 100644 --- a/code/unix/linux_local.h +++ b/code/linux/linux_local.h @@ -21,26 +21,37 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ // linux_local.h: Linux-specific Quake3 header file -void Sys_QueEvent( int time, sysEventType_t type, int value, int value2, int ptrLength, void *ptr ); -void Sys_SendKeyEvents (void); -void Sys_ConsoleInputShutdown(void); -// Input subsystem -void IN_Init (void); -void IN_Frame (void); -void IN_Shutdown (void); -void IN_JoyMove( void ); -void IN_StartupJoystick( void ); +#include "../qcommon/q_shared.h" +#include "../qcommon/qcommon.h" +#ifndef DEDICATED +#include "../client/client.h" +#endif -// GL subsystem -qboolean QGL_Init( const char *dllname ); -void QGL_EnableLogging( qboolean enable ); -void QGL_Shutdown( void ); -// signals.c -void SIG_InitChild(); -void SIG_InitParent(); -void SIG_Frame(); +void Lin_HardRebootHandler( int argc, char** argv ); +void Lin_TrackParentProcess(); +void Lin_QueEvent( int time, sysEventType_t type, int value, int value2, int ptrLength, void *ptr ); +void Lin_ConsoleInputInit(); +void Lin_ConsoleInputShutdown(); +const char* Lin_ConsoleInput(); -extern int q_argc; -extern char** q_argv; +qbool tty_Enabled(); +void tty_Hide(); +history_t* tty_GetHistory(); + +#ifndef DEDICATED +qbool sdl_Init(); +void sdl_InitCvarsAndCmds(); +void sdl_PollEvents(); +void sdl_Frame(); // polling AND dealing with state changes (e.g. input grab) +void sdl_UpdateMonitorIndexFromWindow(); +void sdl_MuteAudio( qbool mute ); +#endif + +void SIG_InitChild(); +void SIG_InitParent(); +void SIG_Frame(); + +extern int q_argc; +extern char** q_argv; diff --git a/code/linux/linux_main.cpp b/code/linux/linux_main.cpp new file mode 100644 index 0000000..d218534 --- /dev/null +++ b/code/linux/linux_main.cpp @@ -0,0 +1,57 @@ +#include "linux_local.h" + + +int q_argc = 0; +char** q_argv = NULL; + + +int main( int argc, char** argv ) +{ + q_argc = argc; + q_argv = argv; + +#ifdef DEDICATED + Lin_HardRebootHandler(argc, argv); +#endif + + SIG_InitChild(); + +#ifndef DEDICATED + if (!sdl_Init()) + return 1; +#endif + + // merge the command line: we need it in a single chunk + int len = 1, i; + for (i = 1; i < argc; ++i) + len += strlen(argv[i]) + 1; + char* cmdline = (char*)malloc(len); + *cmdline = 0; + for (i = 1; i < argc; ++i) { + if (i > 1) + strcat( cmdline, " " ); + strcat( cmdline, argv[i] ); + } + Com_Init( cmdline ); + + NET_Init(); + + Com_Printf( "Working directory: %s\n", Sys_Cwd() ); + + Lin_ConsoleInputInit(); + Lin_TrackParentProcess(); + +#ifndef DEDICATED + sdl_InitCvarsAndCmds(); +#endif + + for (;;) { + SIG_Frame(); +#ifndef DEDICATED + sdl_Frame(); +#endif + Com_Frame(); + } + + return 0; +} diff --git a/code/unix/linux_qgl.c b/code/linux/linux_qgl.c similarity index 97% rename from code/unix/linux_qgl.c rename to code/linux/linux_qgl.c index 54d1cda..459a945 100644 --- a/code/unix/linux_qgl.c +++ b/code/linux/linux_qgl.c @@ -30,12 +30,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ** QGL_Shutdown() - unloads libraries, NULLs function pointers */ -#include "../qcommon/q_shared.h" -#include -#include -#include -#include "unix_glw.h" -#include + +#include + #include "../renderer/tr_local.h" @@ -748,30 +745,6 @@ static void ( APIENTRY * dllViewport )(GLint x, GLint y, GLsizei width, GLsizei */ void QGL_Shutdown( void ) { - if ( glw_state.OpenGLLib ) - { - // 25/09/05 Tim Angus - // Certain combinations of hardware and software, specifically - // Linux/SMP/Nvidia/agpgart (OK, OK. MY combination of hardware and - // software), seem to cause a catastrophic (hard reboot required) crash - // when libGL is dynamically unloaded. I'm unsure of the precise cause, - // suffice to say I don't see anything in the Q3 code that could cause it. - // I suspect it's an Nvidia driver bug, but without the source or means to - // debug I obviously can't prove (or disprove) this. Interestingly (though - // perhaps not suprisingly), Enemy Territory and Doom 3 both exhibit the - // same problem. - // - // After many, many reboots and prodding here and there, it seems that a - // placing a short delay before libGL is unloaded works around the problem. - // This delay is changable via the r_GLlibCoolDownMsec cvar (nice name - // huh?), and it defaults to 0. For me, 500 seems to work. - //if( r_GLlibCoolDownMsec->integer ) - usleep( 500 * 1000 ); - - dlclose ( glw_state.OpenGLLib ); - glw_state.OpenGLLib = NULL; - } - qglAccum = NULL; qglAlphaFunc = NULL; qglAreTexturesResident = NULL; @@ -1130,23 +1103,7 @@ void QGL_Shutdown( void ) ** */ -static void *QGL_GetProcAddress( const char *symbol ) -{ - void *sym; - - if ( qglXGetProcAddress ) - { - sym = qglXGetProcAddress( symbol ); - if ( sym ) - { - return sym; - } - } - - return dlsym( glw_state.OpenGLLib, symbol ); -} - -#define GPA( a ) QGL_GetProcAddress( a ) +#define GPA( a ) SDL_GL_GetProcAddress( a ) /* @@ -1160,20 +1117,9 @@ static void *QGL_GetProcAddress( const char *symbol ) ** */ -qboolean QGL_Init( const char *dllname ) +qbool QGL_Init( const char * ) { - if (glw_state.OpenGLLib == 0) - { - glw_state.OpenGLLib = dlopen( dllname, RTLD_LAZY|RTLD_GLOBAL ); - } - - if ( glw_state.OpenGLLib == 0 ) { - ri.Printf( PRINT_ALL, "QGL_Init: Failed to load %s: %s\n", dllname, dlerror() ); - return qfalse; - } - qglXGetProcAddress = (void* (*)( const char *symbol ))GPA( "glXGetProcAddress" ); - qglAccum = dllAccum =(void (*)(GLenum, GLfloat))GPA( "glAccum" ); qglAlphaFunc = dllAlphaFunc =(void (*)(GLenum, GLclampf))GPA( "glAlphaFunc" ); qglAreTexturesResident = dllAreTexturesResident =(GLboolean (*)(GLsizei, const GLuint*, GLboolean*))GPA( "glAreTexturesResident" ); @@ -1592,7 +1538,6 @@ qbool GLW_InitGL2() { if (atof((const char*)qglGetString(GL_VERSION)) < 2.0f) { - Com_Error( ERR_FATAL, "OpenGL 2 is the required minimum" ); return qfalse; } @@ -1651,43 +1596,25 @@ qbool GLW_InitGL2() typedef void ( APIENTRY * PFNGLTEXIMAGE2DMULTISAMPLE )(GLenum, GLsizei, GLenum, GLsizei, GLsizei, GLboolean); +// 3.0 PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC qglRenderbufferStorageMultisample; -PFNGLTEXIMAGE2DMULTISAMPLE qglTexImage2DMultisample; +// 3.2 +PFNGLTEXIMAGE2DMULTISAMPLE qglTexImage2DMultisample; qbool GLW_InitGL3() { - if (atof((const char*)qglGetString(GL_VERSION)) < 3.2f) + if (atof((const char*)qglGetString(GL_VERSION)) < 3.0f) { return qfalse; } - + + // 3.0 QGL_EXT( glRenderbufferStorageMultisample, PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC ); + + // 3.2 QGL_EXT( glTexImage2DMultisample, PFNGLTEXIMAGE2DMULTISAMPLE ); return qtrue; } - - -void QGL_SwapInterval( Display *dpy, Window win, int interval ) -{ - if( qglSwapIntervalEXT ) - { - qglSwapIntervalEXT( interval ); - } - else if ( qglXSwapIntervalEXT ) - { - qglXSwapIntervalEXT( dpy, win, interval ); - } - else if ( qglXSwapIntervalMESA ) - { - qglXSwapIntervalMESA( interval ); - } - else if ( qglXSwapIntervalSGI ) - { - qglXSwapIntervalSGI( (unsigned int)interval ); - } -} - -// END linux_qgl.c diff --git a/code/linux/linux_shared.cpp b/code/linux/linux_shared.cpp new file mode 100644 index 0000000..9ddb300 --- /dev/null +++ b/code/linux/linux_shared.cpp @@ -0,0 +1,656 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code 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 2 of the License, +or (at your option) any later version. + +Quake III Arena source code 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. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef DEDICATED +#include +#endif + +#include "linux_local.h" + + +#define MEM_THRESHOLD 96*1024*1024 + +qboolean Sys_LowPhysicalMemory() +{ + return qfalse; // FIXME +} + + +void Sys_Error( const char *error, ... ) +{ + va_list argptr; + char string[1024]; + + if (tty_Enabled()) + tty_Hide(); + +#ifndef DEDICATED + CL_Shutdown(); +#endif + + va_start (argptr,error); + vsprintf (string,error,argptr); + va_end (argptr); + fprintf(stderr, "Sys_Error: %s\n", string); + + Lin_ConsoleInputShutdown(); + exit(1); +} + + +void Sys_Quit( int status ) +{ + Lin_ConsoleInputShutdown(); + exit( status ); +} + + +/////////////////////////////////////////////////////////////// + + +const char* Sys_DllError() +{ + return dlerror(); +} + + +void Sys_UnloadDll( void* dllHandle ) +{ + if ( !dllHandle ) + return; + + dlclose( dllHandle ); + + const char* err = Sys_DllError(); + if ( err ) { + Com_Error( ERR_FATAL, "Sys_UnloadDll failed: %s\n", err ); + } +} + + +static void* try_dlopen( const char* base, const char* gamedir, const char* filename ) +{ + const char* fn = FS_BuildOSPath( base, gamedir, filename ); + void* libHandle = dlopen( fn, RTLD_NOW ); + + if (!libHandle) { + Com_Printf( "Sys_LoadDll(%s) failed: %s\n", fn, Sys_DllError() ); + return NULL; + } + + Com_Printf( "Sys_LoadDll(%s) succeeded\n", fn ); + return libHandle; +} + + +// used to load a development dll instead of a virtual machine +// in release builds, the load procedure matches the VFS logic (fs_homepath, then fs_basepath) +// in debug builds, the current working directory is tried first + +void* QDECL Sys_LoadDll( const char* name, dllSyscall_t *entryPoint, dllSyscall_t systemcalls ) +{ + char filename[MAX_QPATH]; + Com_sprintf( filename, sizeof( filename ), "%s" ARCH_STRING DLL_EXT, name ); + + void* libHandle = 0; + // FIXME: use fs_searchpaths from files.c + const char* homepath = Cvar_VariableString( "fs_homepath" ); + const char* basepath = Cvar_VariableString( "fs_basepath" ); + const char* gamedir = Cvar_VariableString( "fs_game" ); + +#ifndef NDEBUG + libHandle = try_dlopen( Sys_Cwd(), gamedir, filename ); +#endif + + if (!libHandle && homepath) + libHandle = try_dlopen( homepath, gamedir, filename ); + + if (!libHandle && basepath) + libHandle = try_dlopen( basepath, gamedir, filename ); + + if ( !libHandle ) + return NULL; + + dllEntry_t dllEntry = (dllEntry_t)dlsym( libHandle, "dllEntry" ); + *entryPoint = (dllSyscall_t)dlsym( libHandle, "vmMain" ); + + if ( !*entryPoint || !dllEntry ) { + const char* err = Sys_DllError(); + Com_Printf( "Sys_LoadDll(%s) failed dlsym: %s\n", name, err ); + Sys_UnloadDll( libHandle ); + return NULL; + } + + dllEntry( systemcalls ); + return libHandle; +} + +#ifdef DEDICATED +char *Sys_GetClipboardData(void) +{ + return NULL; +} +#endif + +void Sys_Init() +{ + Cvar_Set( "arch", OS_STRING " " ARCH_STRING ); +} + + +qbool Sys_HardReboot() +{ +#ifdef DEDICATED + return qtrue; +#else + return qfalse; +#endif +} + + +#ifdef DEDICATED + + +static int Lin_RunProcess( char** argv ) +{ + const pid_t pid = fork(); + if (pid == 0) { + if (execve(argv[0], argv , NULL) == -1) { + fprintf(stderr, "failed to launch child process: %s\n", strerror(errno)); + _exit(1); // quit without calling atexit handlers + return 0; + } + } + + int status; + while (waitpid(pid, &status, WNOHANG) == 0) + sleep(1); // in seconds + + return WEXITSTATUS(status); +} + + +void Lin_HardRebootHandler( int argc, char** argv ) +{ + for (int i = 0; i < argc; ++i) { + if (!Q_stricmp(argv[i], "nohardreboot")) { + return; + } + } + + static char* args[256]; + if (argc + 2 >= sizeof(args) / sizeof(args[0])) { + fprintf(stderr, "too many arguments: %d\n", argc); + _exit(1); // quit without calling atexit handlers + return; + } + + for (int i = 0; i < argc; ++i) + args[i] = argv[i]; + args[argc + 0] = (char*)"nohardreboot"; + args[argc + 1] = NULL; + + SIG_InitParent(); + + for (;;) { + if (Lin_RunProcess(args) == 0) + _exit(0); // quit without calling atexit handlers + } +} + + +#endif + + +static qbool lin_hasParent = qfalse; +static pid_t lix_parentPid; + + +static const char* Lin_GetExeName(const char* path) +{ + const char* lastSlash = strrchr(path, '/'); + if (lastSlash == NULL) + return path; + + return lastSlash + 1; +} + + +void Lin_TrackParentProcess() +{ + static char cmdLine[1024]; + + char fileName[128]; + Com_sprintf(fileName, sizeof(fileName), "/proc/%d/cmdline", (int)getppid()); + + const int fd = open(fileName, O_RDONLY); + if (fd == -1) + return; + + const qbool hasCmdLine = read(fd, cmdLine, sizeof(cmdLine)) > 0; + close(fd); + + if (!hasCmdLine) + return; + + cmdLine[sizeof(cmdLine) - 1] = '\0'; + lin_hasParent = strcmp(Lin_GetExeName(cmdLine), Lin_GetExeName(q_argv[0])) == 0; +} + + +qbool Sys_HasCNQ3Parent() +{ + return lin_hasParent; +} + + +static int Sys_GetProcessUptime( pid_t pid ) +{ + // length must be in sync with the fscanf call! + static char word[256]; + + // The process start time is the 22nd column and + // encoded as jiffies after system boot. + const int jiffiesPerSec = sysconf(_SC_CLK_TCK); + if (jiffiesPerSec <= 0) + return -1; + + char fileName[128]; + Com_sprintf(fileName, sizeof(fileName), "/proc/%ld/stat", (long)pid); + FILE* const file = fopen(fileName, "r"); + if (file == NULL) + return -1; + + for (int i = 0; i < 21; ++i) { + if (fscanf(file, "%255s", word) != 1) { + fclose(file); + return -1; + } + } + + int jiffies; + const qbool success = fscanf(file, "%d", &jiffies) == 1; + fclose(file); + + if (!success) + return -1; + + const int secondsSinceBoot = jiffies / jiffiesPerSec; + struct sysinfo info; + sysinfo(&info); + + return (int)info.uptime - secondsSinceBoot; +} + + +int Sys_GetUptimeSeconds( qbool parent ) +{ + if (!lin_hasParent) + return -1; + + return Sys_GetProcessUptime( parent ? getppid() : getpid() ); +} + + +void Sys_LoadHistory() +{ +#ifdef DEDICATED + History_LoadFromFile( tty_GetHistory() ); +#else + History_LoadFromFile( &g_history ); +#endif +} + + +void Sys_SaveHistory() +{ +#ifdef DEDICATED + History_SaveToFile( tty_GetHistory() ); +#else + History_SaveToFile( &g_history ); +#endif +} + + +int Sys_Milliseconds() +{ + static int sys_timeBase = 0; + + struct timeval tv; + gettimeofday( &tv, NULL ); + + if (!sys_timeBase) { + sys_timeBase = tv.tv_sec; + return tv.tv_usec/1000; + } + + return ((tv.tv_sec - sys_timeBase)*1000 + tv.tv_usec/1000); +} + + +void Sys_Mkdir( const char* path ) +{ + mkdir( path, 0777 ); +} + + +#define MAX_FOUND_FILES 0x1000 + +// bk001129 - new in 1.26 +static void Sys_ListFilteredFiles( const char *basedir, const char *subdirs, const char *filter, char **list, int *numfiles ) { + char search[MAX_OSPATH], newsubdirs[MAX_OSPATH]; + char filename[MAX_OSPATH]; + DIR *fdir; + struct dirent *d; + struct stat st; + + if ( *numfiles >= MAX_FOUND_FILES - 1 ) { + return; + } + + if (strlen(subdirs)) { + Com_sprintf( search, sizeof(search), "%s/%s", basedir, subdirs ); + } + else { + Com_sprintf( search, sizeof(search), "%s", basedir ); + } + + if ((fdir = opendir(search)) == NULL) { + return; + } + + while ((d = readdir(fdir)) != NULL) { + Com_sprintf(filename, sizeof(filename), "%s/%s", search, d->d_name); + if (stat(filename, &st) == -1) + continue; + + if (st.st_mode & S_IFDIR) { + if (Q_stricmp(d->d_name, ".") && Q_stricmp(d->d_name, "..")) { + if (strlen(subdirs)) { + Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s/%s", subdirs, d->d_name); + } + else { + Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s", d->d_name); + } + Sys_ListFilteredFiles( basedir, newsubdirs, filter, list, numfiles ); + } + } + if ( *numfiles >= MAX_FOUND_FILES - 1 ) { + break; + } + Com_sprintf( filename, sizeof(filename), "%s/%s", subdirs, d->d_name ); + if (!Com_FilterPath( filter, filename )) + continue; + list[ *numfiles ] = CopyString( filename ); + (*numfiles)++; + } + + closedir(fdir); +} + +char **Sys_ListFiles( const char *directory, const char *extension, const char *filter, int *numfiles, qboolean wantsubs ) +{ + struct dirent *d; + DIR *fdir; + qboolean dironly = wantsubs; + char search[MAX_OSPATH]; + int nfiles; + char **listCopy; + char *list[MAX_FOUND_FILES]; + int i; + struct stat st; + + int extLen; + + if (filter) { + + nfiles = 0; + Sys_ListFilteredFiles( directory, "", filter, list, &nfiles ); + + list[ nfiles ] = NULL; + *numfiles = nfiles; + + if (!nfiles) + return NULL; + + listCopy = (char**)Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ) ); + for ( i = 0 ; i < nfiles ; i++ ) { + listCopy[i] = list[i]; + } + listCopy[i] = NULL; + + return listCopy; + } + + if ( !extension) + extension = ""; + + if ( extension[0] == '/' && extension[1] == 0 ) { + extension = ""; + dironly = qtrue; + } + + extLen = strlen( extension ); + + // search + nfiles = 0; + + if ((fdir = opendir(directory)) == NULL) { + *numfiles = 0; + return NULL; + } + + while ((d = readdir(fdir)) != NULL) { + Com_sprintf(search, sizeof(search), "%s/%s", directory, d->d_name); + if (stat(search, &st) == -1) + continue; + if ((dironly && !(st.st_mode & S_IFDIR)) || + (!dironly && (st.st_mode & S_IFDIR))) + continue; + + if (*extension) { + if ( strlen( d->d_name ) < strlen( extension ) || + Q_stricmp( + d->d_name + strlen( d->d_name ) - strlen( extension ), + extension ) ) { + continue; // didn't match + } + } + + if ( nfiles == MAX_FOUND_FILES - 1 ) + break; + list[ nfiles ] = CopyString( d->d_name ); + nfiles++; + } + + list[ nfiles ] = NULL; + + closedir(fdir); + + // return a copy of the list + *numfiles = nfiles; + + if ( !nfiles ) { + return NULL; + } + + listCopy = (char**)Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ) ); + for ( i = 0 ; i < nfiles ; i++ ) { + listCopy[i] = list[i]; + } + listCopy[i] = NULL; + + return listCopy; +} + +void Sys_FreeFileList( char **list ) { + int i; + + if ( !list ) { + return; + } + + for ( i = 0 ; list[i] ; i++ ) { + Z_Free( list[i] ); + } + + Z_Free( list ); +} + + +const char* Sys_Cwd() +{ + static char cwd[MAX_OSPATH]; + + getcwd( cwd, sizeof( cwd ) - 1 ); + cwd[MAX_OSPATH-1] = 0; + + return cwd; +} + + +const char* Sys_DefaultHomePath() +{ + // Used to determine where to store user-specific files + static char homePath[MAX_OSPATH]; + + if (*homePath) + return homePath; + + const char* p; + if (p = getenv("HOME")) { + Q_strncpyz(homePath, p, sizeof(homePath)); +#ifdef MACOS_X + Q_strcat(homePath, sizeof(homePath), "/Library/Application Support/Quake3"); +#else + Q_strcat(homePath, sizeof(homePath), "/.q3a"); +#endif + if (mkdir(homePath, 0777)) { + if (errno != EEXIST) + Sys_Error("Unable to create directory \"%s\", error is %s(%d)\n", homePath, strerror(errno), errno); + } + return homePath; + } + + return ""; // assume current dir +} + + +void Sys_ShowConsole( int visLevel, qboolean quitOnClose ) +{ +} + + +#define MAX_QUED_EVENTS 512 +#define MASK_QUED_EVENTS ( MAX_QUED_EVENTS - 1 ) + +static sysEvent_t eventQue[MAX_QUED_EVENTS]; +static int eventHead, eventTail; + + +// a time of 0 will get the current time +// ptr should either be null, or point to a block of data that can be freed by the game later + +void Lin_QueEvent( int time, sysEventType_t type, int value, int value2, int ptrLength, void *ptr ) +{ + sysEvent_t* ev = &eventQue[ eventHead & MASK_QUED_EVENTS ]; + + if ( eventHead - eventTail >= MAX_QUED_EVENTS ) { + Com_Printf("Sys_QueEvent: overflow\n"); + // we are discarding an event, but don't leak memory + if ( ev->evPtr ) + Z_Free( ev->evPtr ); + ++eventTail; + } + + ++eventHead; + + if ( time == 0 ) + time = Sys_Milliseconds(); + + ev->evTime = time; + ev->evType = type; + ev->evValue = value; + ev->evValue2 = value2; + ev->evPtrLength = ptrLength; + ev->evPtr = ptr; +} + + +sysEvent_t Sys_GetEvent() +{ + // return if we have data + if ( eventHead > eventTail ) { + ++eventTail; + return eventQue[ ( eventTail - 1 ) & MASK_QUED_EVENTS ]; + } + + // check for console commands + const char* s = Lin_ConsoleInput(); + if ( s ) { + const int slen = strlen( s ); + const int blen = slen + 1; + char* b = (char*)Z_Malloc( blen ); + Q_strncpyz( b, s, blen ); + Lin_QueEvent( 0, SE_CONSOLE, 0, 0, slen, b ); + } + +#ifndef DEDICATED + sdl_PollEvents(); +#endif + + // check for network packets + msg_t netmsg; + netadr_t adr; + static byte sys_packetReceived[MAX_MSGLEN]; // static or it'll blow half the stack + MSG_Init( &netmsg, sys_packetReceived, sizeof( sys_packetReceived ) ); + if ( Sys_GetPacket( &adr, &netmsg ) ) { + // copy out to a separate buffer for queuing + int len = sizeof( netadr_t ) + netmsg.cursize; + netadr_t* buf = (netadr_t*)Z_Malloc( len ); + *buf = adr; + memcpy( buf+1, netmsg.data, netmsg.cursize ); + Lin_QueEvent( 0, SE_PACKET, 0, 0, len, buf ); + } + + // return if we have data + if ( eventHead > eventTail ) { + ++eventTail; + return eventQue[ ( eventTail - 1 ) & MASK_QUED_EVENTS ]; + } + + // create an empty event to return + sysEvent_t ev; + memset( &ev, 0, sizeof( ev ) ); + ev.evTime = Sys_Milliseconds(); + return ev; +} diff --git a/code/unix/linux_signals.cpp b/code/linux/linux_signals.cpp similarity index 99% rename from code/unix/linux_signals.cpp rename to code/linux/linux_signals.cpp index beb0591..a921d68 100644 --- a/code/unix/linux_signals.cpp +++ b/code/linux/linux_signals.cpp @@ -21,7 +21,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include -#include #include #include #include @@ -263,7 +262,7 @@ static void Sig_PrintSignalCaught(int sig) static void Sig_HandleExit() { - Sys_ConsoleInputShutdown(); + Lin_ConsoleInputShutdown(); } @@ -321,7 +320,7 @@ static void Sig_HandleCrash(int sig) // Sig_PrintAttempt("restore tty settings"); - Sys_ConsoleInputShutdown(); + Lin_ConsoleInputShutdown(); Sig_PrintDone(); Sig_PrintAttempt("write JSON report"); diff --git a/code/unix/unix_main.cpp b/code/linux/linux_tty.cpp similarity index 59% rename from code/unix/unix_main.cpp rename to code/linux/linux_tty.cpp index df4c25a..e9571e8 100644 --- a/code/unix/unix_main.cpp +++ b/code/linux/linux_tty.cpp @@ -19,51 +19,24 @@ along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if defined(__sun) - #include -#endif #include +#include +#include +#include +#include -#include "../client/client.h" -#include "../qcommon/q_shared.h" -#include "../qcommon/qcommon.h" #include "linux_local.h" -/////////////////////////////////////////////////////////////// - - -int q_argc = 0; -char** q_argv = NULL; - -static qboolean stdin_active = qtrue; +static qbool stdin_active = qtrue; // enable/disable tty input mode // NOTE TTimo this is used during startup, cannot be changed during run static cvar_t *ttycon = NULL; // general flag to tell about tty console mode -static qboolean ttycon_on = qfalse; +static qbool ttycon_on = qfalse; // when printing general stuff to stdout stderr (Sys_Printf) // we need to disable the tty console stuff // this increments so we can recursively disable @@ -114,7 +87,7 @@ static void tty_Back() // clear the display of the line currently edited // bring cursor back to beginning of line -static void tty_Hide() +void tty_Hide() { assert(ttycon_on); if (ttycon_hide) @@ -166,7 +139,7 @@ static void tty_Show() // initialize the console input (tty mode if wanted and possible) -static void Sys_ConsoleInputInit() +void Lin_ConsoleInputInit() { struct termios tc; @@ -222,7 +195,7 @@ static void Sys_ConsoleInputInit() } -const char* Sys_ConsoleInput() +const char* Lin_ConsoleInput() { // we use this when sending back commands static char text[256]; @@ -470,7 +443,7 @@ const char* Sys_ConsoleInput() // never exit without calling this, or your terminal will be left in a pretty bad state -void Sys_ConsoleInputShutdown() +void Lin_ConsoleInputShutdown() { if (ttycon_on) { @@ -484,237 +457,6 @@ void Sys_ConsoleInputShutdown() } -/////////////////////////////////////////////////////////////// - - -#define MEM_THRESHOLD 96*1024*1024 - -qboolean Sys_LowPhysicalMemory() -{ - return qfalse; // FIXME -} - - -void Sys_Error( const char *error, ... ) -{ - va_list argptr; - char string[1024]; - - if (ttycon_on) - tty_Hide(); - -#ifndef DEDICATED - CL_Shutdown(); -#endif - - va_start (argptr,error); - vsprintf (string,error,argptr); - va_end (argptr); - fprintf(stderr, "Sys_Error: %s\n", string); - - Sys_ConsoleInputShutdown(); - exit(1); -} - - -void Sys_Quit( int status ) -{ - Sys_ConsoleInputShutdown(); - exit( status ); -} - - -/////////////////////////////////////////////////////////////// - - -const char* Sys_DllError() -{ - return dlerror(); -} - - -void Sys_UnloadDll( void* dllHandle ) -{ - if ( !dllHandle ) - return; - - dlclose( dllHandle ); - - const char* err = Sys_DllError(); - if ( err ) { - Com_Error( ERR_FATAL, "Sys_UnloadDll failed: %s\n", err ); - } -} - - -static void* try_dlopen( const char* base, const char* gamedir, const char* filename ) -{ - const char* fn = FS_BuildOSPath( base, gamedir, filename ); - void* libHandle = dlopen( fn, RTLD_NOW ); - - if (!libHandle) { - Com_Printf( "Sys_LoadDll(%s) failed: %s\n", fn, Sys_DllError() ); - return NULL; - } - - Com_Printf( "Sys_LoadDll(%s) succeeded\n", fn ); - return libHandle; -} - - -// used to load a development dll instead of a virtual machine -// in release builds, the load procedure matches the VFS logic (fs_homepath, then fs_basepath) -// in debug builds, the current working directory is tried first - -void* QDECL Sys_LoadDll( const char* name, dllSyscall_t *entryPoint, dllSyscall_t systemcalls ) -{ - char filename[MAX_QPATH]; - Com_sprintf( filename, sizeof( filename ), "%s" ARCH_STRING DLL_EXT, name ); - - void* libHandle = 0; - // FIXME: use fs_searchpaths from files.c - const char* homepath = Cvar_VariableString( "fs_homepath" ); - const char* basepath = Cvar_VariableString( "fs_basepath" ); - const char* gamedir = Cvar_VariableString( "fs_game" ); - -#ifndef NDEBUG - libHandle = try_dlopen( Sys_Cwd(), gamedir, filename ); -#endif - - if (!libHandle && homepath) - libHandle = try_dlopen( homepath, gamedir, filename ); - - if (!libHandle && basepath) - libHandle = try_dlopen( basepath, gamedir, filename ); - - if ( !libHandle ) - return NULL; - - dllEntry_t dllEntry = (dllEntry_t)dlsym( libHandle, "dllEntry" ); - *entryPoint = (dllSyscall_t)dlsym( libHandle, "vmMain" ); - - if ( !*entryPoint || !dllEntry ) { - const char* err = Sys_DllError(); - Com_Printf( "Sys_LoadDll(%s) failed dlsym: %s\n", name, err ); - Sys_UnloadDll( libHandle ); - return NULL; - } - - dllEntry( systemcalls ); - return libHandle; -} - - -/* -======================================================================== - -EVENT LOOP - -======================================================================== -*/ - -#define MAX_QUED_EVENTS 512 -#define MASK_QUED_EVENTS ( MAX_QUED_EVENTS - 1 ) - -static sysEvent_t eventQue[MAX_QUED_EVENTS]; -static int eventHead, eventTail; - - -// a time of 0 will get the current time -// ptr should either be null, or point to a block of data that can be freed by the game later - -void Sys_QueEvent( int time, sysEventType_t type, int value, int value2, int ptrLength, void *ptr ) -{ - sysEvent_t* ev = &eventQue[ eventHead & MASK_QUED_EVENTS ]; - - if ( eventHead - eventTail >= MAX_QUED_EVENTS ) { - Com_Printf("Sys_QueEvent: overflow\n"); - // we are discarding an event, but don't leak memory - if ( ev->evPtr ) - Z_Free( ev->evPtr ); - ++eventTail; - } - - ++eventHead; - - if ( time == 0 ) - time = Sys_Milliseconds(); - - ev->evTime = time; - ev->evType = type; - ev->evValue = value; - ev->evValue2 = value2; - ev->evPtrLength = ptrLength; - ev->evPtr = ptr; -} - - -sysEvent_t Sys_GetEvent() -{ - // return if we have data - if ( eventHead > eventTail ) { - ++eventTail; - return eventQue[ ( eventTail - 1 ) & MASK_QUED_EVENTS ]; - } - -#ifndef DEDICATED - // pump the message loop - // in vga this calls KBD_Update, under X, it calls GetEvent - Sys_SendKeyEvents(); -#endif - - // check for console commands - const char* s = Sys_ConsoleInput(); - if ( s ) { - const int slen = strlen( s ); - const int blen = slen + 1; - char* b = (char*)Z_Malloc( blen ); - Q_strncpyz( b, s, blen ); - Sys_QueEvent( 0, SE_CONSOLE, 0, 0, slen, b ); - } - -#ifndef DEDICATED - // check for other input devices - IN_Frame(); -#endif - - // check for network packets - msg_t netmsg; - netadr_t adr; - static byte sys_packetReceived[MAX_MSGLEN]; // static or it'll blow half the stack - MSG_Init( &netmsg, sys_packetReceived, sizeof( sys_packetReceived ) ); - if ( Sys_GetPacket( &adr, &netmsg ) ) { - // copy out to a separate buffer for queuing - int len = sizeof( netadr_t ) + netmsg.cursize; - netadr_t* buf = (netadr_t*)Z_Malloc( len ); - *buf = adr; - memcpy( buf+1, netmsg.data, netmsg.cursize ); - Sys_QueEvent( 0, SE_PACKET, 0, 0, len, buf ); - } - - // return if we have data - if ( eventHead > eventTail ) { - ++eventTail; - return eventQue[ ( eventTail - 1 ) & MASK_QUED_EVENTS ]; - } - - // create an empty event to return - sysEvent_t ev; - memset( &ev, 0, sizeof( ev ) ); - ev.evTime = Sys_Milliseconds(); - return ev; -} - - -/////////////////////////////////////////////////////////////// - - -char *Sys_GetClipboardData(void) -{ - return NULL; -} - - static struct Q3ToAnsiColorTable_s { char Q3color; @@ -734,7 +476,7 @@ static struct Q3ToAnsiColorTable_s static const int tty_colorTableSize = sizeof( tty_colorTable ) / sizeof( tty_colorTable[0] ); -static void Sys_ANSIColorify( const char *msg, char *buffer, int bufferSize ) +static void ANSIColorify( const char *msg, char *buffer, int bufferSize ) { int msgLength, pos; int i, j; @@ -810,7 +552,7 @@ static char* CleanTermStr( char* string ) } -void Sys_Print( const char *msg ) +void Sys_Print( const char *msg ) { static char finalMsg[ MAXPRINTMSG ]; @@ -821,7 +563,7 @@ void Sys_Print( const char *msg ) if( ttycon_on && ttycon_ansicolor && ttycon_ansicolor->integer ) { - Sys_ANSIColorify( msg, finalMsg, sizeof(finalMsg) ); + ANSIColorify( msg, finalMsg, sizeof(finalMsg) ); } else { @@ -836,226 +578,12 @@ void Sys_Print( const char *msg ) } } - -/////////////////////////////////////////////////////////////// - - -void Sys_Init() +qbool tty_Enabled() { - Cvar_Set( "arch", OS_STRING " " ARCH_STRING ); + return ttycon_on; } - -qbool Sys_HardReboot() +history_t* tty_GetHistory() { -#ifdef DEDICATED - return qtrue; -#else - return qfalse; -#endif + return &tty_history; } - - -#ifdef DEDICATED - - -static int Lin_RunProcess( char** argv ) -{ - const pid_t pid = fork(); - if (pid == 0) { - if (execve(argv[0], argv , NULL) == -1) { - fprintf(stderr, "failed to launch child process: %s\n", strerror(errno)); - _exit(1); // quit without calling atexit handlers - return 0; - } - } - - int status; - while (waitpid(pid, &status, WNOHANG) == 0) - sleep(1); // in seconds - - return WEXITSTATUS(status); -} - - -static void Lin_HardRebootHandler( int argc, char** argv ) -{ - for (int i = 0; i < argc; ++i) { - if (!Q_stricmp(argv[i], "nohardreboot")) { - return; - } - } - - static char* args[256]; - if (argc + 2 >= sizeof(args) / sizeof(args[0])) { - fprintf(stderr, "too many arguments: %d\n", argc); - _exit(1); // quit without calling atexit handlers - return; - } - - for (int i = 0; i < argc; ++i) - args[i] = argv[i]; - args[argc + 0] = (char*)"nohardreboot"; - args[argc + 1] = NULL; - - SIG_InitParent(); - - for (;;) { - if (Lin_RunProcess(args) == 0) - _exit(0); // quit without calling atexit handlers - } -} - - -#endif - - -static qbool lin_hasParent = qfalse; -static pid_t lix_parentPid; - - -static const char* Lin_GetExeName(const char* path) -{ - const char* lastSlash = strrchr(path, '/'); - if (lastSlash == NULL) - return path; - - return lastSlash + 1; -} - - -static void Lin_TrackParentProcess() -{ - static char cmdLine[1024]; - - char fileName[128]; - Com_sprintf(fileName, sizeof(fileName), "/proc/%d/cmdline", (int)getppid()); - - const int fd = open(fileName, O_RDONLY); - if (fd == -1) - return; - - const qbool hasCmdLine = read(fd, cmdLine, sizeof(cmdLine)) > 0; - close(fd); - - if (!hasCmdLine) - return; - - cmdLine[sizeof(cmdLine) - 1] = '\0'; - lin_hasParent = strcmp(Lin_GetExeName(cmdLine), Lin_GetExeName(q_argv[0])) == 0; -} - - -qbool Sys_HasCNQ3Parent() -{ - return lin_hasParent; -} - - -static int Sys_GetProcessUptime( pid_t pid ) -{ - // length must be in sync with the fscanf call! - static char word[256]; - - // The process start time is the 22nd column and - // encoded as jiffies after system boot. - const int jiffiesPerSec = sysconf(_SC_CLK_TCK); - if (jiffiesPerSec <= 0) - return -1; - - char fileName[128]; - Com_sprintf(fileName, sizeof(fileName), "/proc/%ld/stat", (long)pid); - FILE* const file = fopen(fileName, "r"); - if (file == NULL) - return -1; - - for (int i = 0; i < 21; ++i) { - if (fscanf(file, "%255s", word) != 1) { - fclose(file); - return -1; - } - } - - int jiffies; - const qbool success = fscanf(file, "%d", &jiffies) == 1; - fclose(file); - - if (!success) - return -1; - - const int secondsSinceBoot = jiffies / jiffiesPerSec; - struct sysinfo info; - sysinfo(&info); - - return (int)info.uptime - secondsSinceBoot; -} - - -int Sys_GetUptimeSeconds( qbool parent ) -{ - if (!lin_hasParent) - return -1; - - return Sys_GetProcessUptime( parent ? getppid() : getpid() ); -} - - -void Sys_LoadHistory() -{ -#ifdef DEDICATED - History_LoadFromFile( &tty_history ); -#else - History_LoadFromFile( &g_history ); -#endif -} - - -void Sys_SaveHistory() -{ -#ifdef DEDICATED - History_SaveToFile( &tty_history ); -#else - History_SaveToFile( &g_history ); -#endif -} - - -int main( int argc, char** argv ) -{ - q_argc = argc; - q_argv = argv; - -#ifdef DEDICATED - Lin_HardRebootHandler(argc, argv); -#endif - - SIG_InitChild(); - - // merge the command line: we need it in a single chunk - int len = 1, i; - for (i = 1; i < argc; ++i) - len += strlen(argv[i]) + 1; - char* cmdline = (char*)malloc(len); - *cmdline = 0; - for (i = 1; i < argc; ++i) { - if (i > 1) - strcat( cmdline, " " ); - strcat( cmdline, argv[i] ); - } - Com_Init( cmdline ); - - NET_Init(); - - Com_Printf( "Working directory: %s\n", Sys_Cwd() ); - - Sys_ConsoleInputInit(); - Lin_TrackParentProcess(); - - for (;;) { - SIG_Frame(); - Com_Frame(); - } - - return 0; -} - diff --git a/code/linux/sdl_core.cpp b/code/linux/sdl_core.cpp new file mode 100644 index 0000000..4f4c190 --- /dev/null +++ b/code/linux/sdl_core.cpp @@ -0,0 +1,459 @@ +#include "linux_local.h" + +#include +#include "sdl_local.h" +#include + + +static qbool sdl_inputActive = qfalse; + + +static cvar_t* in_noGrab; +static cvar_t* m_relative; +static cvar_t* s_autoMute; + +static const cvarTableItem_t in_cvars[] = { + { &in_noGrab, "in_noGrab", "0", CVAR_ARCHIVE, CVART_BOOL, NULL, NULL, "disables input grabbing" }, + { &m_relative, "m_relative", "1", CVAR_ARCHIVE, CVART_BOOL, NULL, NULL, "enables SDL's relative mouse mode" }, + { &s_autoMute, "s_autoMute", "1", CVAR_ARCHIVE, CVART_INTEGER, "0", "2", "0=never, 1=when unfocused, 2=when minimized" } +}; + +static void Minimize_f(); + +static const cmdTableItem_t in_cmds[] = { + { "minimize", &Minimize_f, NULL, "minimizes the window" } +}; + + +static qbool sdl_Version_AtLeast( int major, int minor, int patch ) +{ + SDL_version v; + SDL_GetVersion(&v); + + // has to be SDL 2 + if (v.major != major) + return qfalse; + + if (v.minor < minor) + return qfalse; + + if (v.minor > minor) + return qtrue; + + return v.patch >= patch; +} + + +static void Minimize_f() +{ + SDL_MinimizeWindow(glimp.window); +} + + +static int QuakeKeyFromSDLKey( SDL_Keysym key ) +{ + if (key.scancode == SDL_SCANCODE_GRAVE) + return '`'; + + const SDL_Keycode sym = key.sym; + + // these ranges map directly to ASCII chars + if ((sym >= SDLK_a && sym < SDLK_z) || + (sym >= SDLK_0 && sym < SDLK_9)) + return (int)sym; + + // F1 to F24 + // SDL splits the values 1-12 and 13-24 + // the engine splits the values 1-15 and 16-24 + + switch (sym) { + case SDLK_F1: return K_F1; + case SDLK_F2: return K_F2; + case SDLK_F3: return K_F3; + case SDLK_F4: return K_F4; + case SDLK_F5: return K_F5; + case SDLK_F6: return K_F6; + case SDLK_F7: return K_F7; + case SDLK_F8: return K_F8; + case SDLK_F9: return K_F9; + case SDLK_F10: return K_F10; + case SDLK_F11: return K_F11; + case SDLK_F12: return K_F12; + case SDLK_F13: return K_F13; + case SDLK_F14: return K_F14; + case SDLK_F15: return K_F15; + case SDLK_F16: return K_F16; + case SDLK_F17: return K_F17; + case SDLK_F18: return K_F18; + case SDLK_F19: return K_F19; + case SDLK_F20: return K_F20; + case SDLK_F21: return K_F21; + case SDLK_F22: return K_F22; + case SDLK_F23: return K_F23; + case SDLK_F24: return K_F24; + case SDLK_UP: return K_UPARROW; + case SDLK_DOWN: return K_DOWNARROW; + case SDLK_LEFT: return K_LEFTARROW; + case SDLK_RIGHT: return K_RIGHTARROW; + case SDLK_TAB: return K_TAB; + case SDLK_RETURN: return K_ENTER; + case SDLK_ESCAPE: return K_ESCAPE; + case SDLK_SPACE: return K_SPACE; + case SDLK_BACKSPACE: return K_BACKSPACE; + case SDLK_CAPSLOCK: return K_CAPSLOCK; + case SDLK_LALT: return K_ALT; + case SDLK_RALT: return K_ALT; + case SDLK_LCTRL: return K_CTRL; + case SDLK_RCTRL: return K_CTRL; + case SDLK_LSHIFT: return K_SHIFT; + case SDLK_RSHIFT: return K_SHIFT; + case SDLK_INSERT: return K_INS; + case SDLK_DELETE: return K_DEL; + case SDLK_PAGEDOWN: return K_PGDN; + case SDLK_PAGEUP: return K_PGUP; + case SDLK_HOME: return K_HOME; + case SDLK_END: return K_END; + case SDLK_KP_7: return K_KP_HOME; + case SDLK_KP_8: return K_KP_UPARROW; + case SDLK_KP_9: return K_KP_PGUP; + case SDLK_KP_4: return K_KP_LEFTARROW; + case SDLK_KP_5: return K_KP_5; + case SDLK_KP_6: return K_KP_RIGHTARROW; + case SDLK_KP_1: return K_KP_END; + case SDLK_KP_2: return K_KP_DOWNARROW; + case SDLK_KP_3: return K_KP_PGDN; + case SDLK_KP_ENTER: return K_KP_ENTER; + case SDLK_KP_0: return K_KP_INS; + case SDLK_KP_DECIMAL: return K_KP_DEL; + case SDLK_KP_DIVIDE: return K_KP_SLASH; + case SDLK_KP_MINUS: return K_KP_MINUS; + case SDLK_KP_PLUS: return K_KP_PLUS; + case SDLK_KP_MULTIPLY: return K_KP_STAR; + case SDLK_BACKSLASH: return K_BACKSLASH; + // missing: + // K_KP_NUMLOCK + // K_KP_EQUALS + default: return -1; + } +} + + +static void sdl_Key( const SDL_KeyboardEvent* event, qbool down ) +{ + const int key = QuakeKeyFromSDLKey(event->keysym); + if (key >= 0) + Lin_QueEvent(Sys_Milliseconds(), SE_KEY, key, down, 0, NULL); + + if (down && key == K_BACKSPACE) + Lin_QueEvent(Sys_Milliseconds(), SE_CHAR, 8, 0, 0, NULL); + + // ctrl+v + if (down && key == 'v' && (event->keysym.mod & KMOD_CTRL) != 0) + Lin_QueEvent(Sys_Milliseconds(), SE_CHAR, 22, 0, 0, NULL); +} + + +static void sdl_Text( const SDL_TextInputEvent* event ) +{ + // text is UTF-8 encoded but we only care for + // chars that are single-byte encoded + const byte key = (byte)event->text[0]; + if (key >= 0 && key <= 0x7F) + Lin_QueEvent(Sys_Milliseconds(), SE_CHAR, (int)key, 0, 0, NULL); +} + + +static void sdl_MouseMotion( const SDL_MouseMotionEvent* event ) +{ + if (!sdl_inputActive) + return; + + Lin_QueEvent(Sys_Milliseconds(), SE_MOUSE, event->xrel, event->yrel, 0, NULL); +} + + +static void sdl_MouseButton( const SDL_MouseButtonEvent* event, qbool down ) +{ + if (!sdl_inputActive && down) + return; + + static const int mouseButtonCount = 5; + static const int mouseButtons[mouseButtonCount][2] = { + { SDL_BUTTON_LEFT, K_MOUSE1 }, + { SDL_BUTTON_RIGHT, K_MOUSE2 }, + { SDL_BUTTON_MIDDLE, K_MOUSE3 }, + { SDL_BUTTON_X1, K_MOUSE4 }, + { SDL_BUTTON_X2, K_MOUSE5 } + }; + + int button = -1; + for(int i = 0; i < mouseButtonCount; ++i) { + if (event->button == mouseButtons[i][0]) { + button = i; + break; + } + } + + if (button < 0) + return; + + Lin_QueEvent(Sys_Milliseconds(), SE_KEY, mouseButtons[button][1], down, 0, NULL); +} + + +static void sdl_MouseWheel( const SDL_MouseWheelEvent* event ) +{ + if (event->y == 0) + return; + +#if SDL_VERSION_ATLEAST(2, 0, 4) + int delta = event->y; + if (sdl_Version_AtLeast(2, 0, 4) && + event->direction == SDL_MOUSEWHEEL_FLIPPED) + delta = -delta; +#else + const int delta = event->y; +#endif + + const int key = (delta < 0) ? K_MWHEELDOWN : K_MWHEELUP; + Lin_QueEvent(Sys_Milliseconds(), SE_KEY, key, qtrue, 0, NULL); + Lin_QueEvent(Sys_Milliseconds(), SE_KEY, key, qfalse, 0, NULL); +} + + +static void sdl_Window( const SDL_WindowEvent* event ) +{ + // events of interest: + //SDL_WINDOWEVENT_SHOWN + //SDL_WINDOWEVENT_HIDDEN + //SDL_WINDOWEVENT_RESIZED + //SDL_WINDOWEVENT_SIZE_CHANGED // should prevent this from happening except on creation? + //SDL_WINDOWEVENT_MINIMIZED + //SDL_WINDOWEVENT_MAXIMIZED + //SDL_WINDOWEVENT_RESTORED + //SDL_WINDOWEVENT_ENTER // mouse focus gained + //SDL_WINDOWEVENT_LEAVE // mouse focus lost + //SDL_WINDOWEVENT_FOCUS_GAINED // kb focus gained + //SDL_WINDOWEVENT_FOCUS_LOST // kb focus lost + //SDL_WINDOWEVENT_CLOSE + + switch (event->event) { + case SDL_WINDOWEVENT_MAXIMIZED: + case SDL_WINDOWEVENT_RESTORED: + case SDL_WINDOWEVENT_RESIZED: + case SDL_WINDOWEVENT_SIZE_CHANGED: + case SDL_WINDOWEVENT_MOVED: + // if this turns out to be too expensive, track movement and + // only call when movement stops + sdl_UpdateMonitorIndexFromWindow(); + break; + + default: + break; + } +} + + +static void sdl_Event( const SDL_Event* event ) +{ + switch (event->type) { + case SDL_QUIT: + Com_Quit(0); + break; + + case SDL_KEYDOWN: + sdl_Key(&event->key, qtrue); + break; + + case SDL_KEYUP: + sdl_Key(&event->key, qfalse); + break; + + case SDL_TEXTINPUT: + sdl_Text(&event->text); + break; + + case SDL_MOUSEMOTION: + sdl_MouseMotion(&event->motion); + break; + + case SDL_MOUSEBUTTONDOWN: + sdl_MouseButton(&event->button, qtrue); + break; + + case SDL_MOUSEBUTTONUP: + sdl_MouseButton(&event->button, qfalse); + break; + + case SDL_MOUSEWHEEL: + sdl_MouseWheel(&event->wheel); + break; + + case SDL_WINDOWEVENT: + sdl_Window(&event->window); + break; + + default: + break; + } +} + + +qbool sdl_Init() +{ + atexit(SDL_Quit); + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0) { + fprintf(stderr, "Failed to initialize SDL 2: %s\n", SDL_GetError()); + return qfalse; + } + + SDL_version version; + SDL_GetVersion(&version); + printf("Opened SDL %d.%d.%d\n", version.major, version.minor, version.patch); + + // @TODO: investigate/test these? + // SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH + // SDL_HINT_MOUSE_RELATIVE_MODE_WARP +#if SDL_VERSION_ATLEAST(2, 0, 4) + if (sdl_Version_AtLeast(2, 0, 4)) + SDL_SetHintWithPriority(SDL_HINT_NO_SIGNAL_HANDLERS, "1", SDL_HINT_OVERRIDE); +#endif + SDL_LogSetAllPriority(SDL_LOG_PRIORITY_CRITICAL); + SDL_StartTextInput(); + + return qtrue; +} + + +void sdl_InitCvarsAndCmds() +{ + Cvar_RegisterArray(in_cvars, MODULE_CLIENT); + Cmd_RegisterArray(in_cmds, MODULE_CLIENT); +} + + +void sdl_PollEvents() +{ + SDL_Event event; + while (SDL_PollEvent(&event)) + sdl_Event(&event); +} + + +static qbool sdl_IsInputActive() +{ + if (in_noGrab->integer) + return qfalse; + + const qbool hasFocus = (SDL_GetWindowFlags(glimp.window) & SDL_WINDOW_INPUT_FOCUS) != 0; + if (!hasFocus) + return qfalse; + + const qbool isConsoleDown = (cls.keyCatchers & KEYCATCH_CONSOLE) != 0; + const qbool isFullScreen = Cvar_VariableIntegerValue("r_fullscreen"); + if (isConsoleDown && !isFullScreen) + return qfalse; + + return qtrue; +} + + +void sdl_Frame() +{ + sdl_inputActive = sdl_IsInputActive(); + sdl_PollEvents(); + + SDL_SetRelativeMouseMode((sdl_inputActive && m_relative->integer) ? SDL_TRUE : SDL_FALSE); + SDL_SetWindowGrab(glimp.window, sdl_inputActive ? SDL_TRUE : SDL_FALSE); + SDL_ShowCursor(sdl_inputActive ? SDL_DISABLE : SDL_ENABLE); + // @NOTE: SDL_WarpMouseInWindow generates a motion event + + if (s_autoMute->integer == 1) { + const qbool hasFocus = (SDL_GetWindowFlags(glimp.window) & SDL_WINDOW_INPUT_FOCUS) != 0; + sdl_MuteAudio(!hasFocus); + } else if (s_autoMute->integer == 2) { + const Uint32 hidingFlags = SDL_WINDOW_HIDDEN | SDL_WINDOW_MINIMIZED; + const qbool hidden = (SDL_GetWindowFlags(glimp.window) & hidingFlags) != 0; + sdl_MuteAudio(hidden); + } else { + sdl_MuteAudio(qfalse); + } +} + + +void Sys_InitInput() +{ +} + + +void Sys_ShutdownInput() +{ +} + + +// returns the number of bytes to skip +static int UTF8_ReadNextChar( char* c, const char* input ) +{ + if (*input == '\0') + return 0; + + const byte byte0 = (byte)input[0]; + if (byte0 <= 127) { + *c = (char)byte0; + return 1; + } + + // Starts with 110? + if ((byte0 >> 5) == 6) + return 2; + + // Starts with 1110? + if ((byte0 >> 4) == 14) + return 3; + + // Starts with 11110? + if ((byte0 >> 3) == 30) + return 4; + + return 0; +} + + +char* Sys_GetClipboardData() +{ + if (SDL_HasClipboardText() == SDL_FALSE) + return NULL; + + char* const textUTF8 = SDL_GetClipboardText(); + if (textUTF8 == NULL) + return NULL; + + // the cleaned up string can only be + // as long or shorter + char* const text = (char*)Z_Malloc(strlen(textUTF8) + 1); + if (text == NULL) { + SDL_free(textUTF8); + return NULL; + } + + // clean up the text so we're sure + // the console can display it + char* d = text; + const char* s = textUTF8; + for (;;) { + char c; + const int bytes = UTF8_ReadNextChar(&c, s); + if (bytes == 0) { + *d = '\0'; + break; + } + + if (c >= 0x20 && c <= 0x7E) + *d++ = c; + s += bytes; + } + + SDL_free(textUTF8); + + return text; +} diff --git a/code/linux/sdl_glimp.cpp b/code/linux/sdl_glimp.cpp new file mode 100644 index 0000000..86b1fc6 --- /dev/null +++ b/code/linux/sdl_glimp.cpp @@ -0,0 +1,292 @@ +#include "linux_local.h" +#include "../renderer/tr_local.h" +#include "../renderer/qgl.h" + +#include +#include "sdl_local.h" + + +glImp_t glimp; + + +cvar_t* r_fullscreen; +static cvar_t* r_monitor; // 1-based, 0 means use primary monitor + +static const cvarTableItem_t glimp_cvars[] = { + { &r_fullscreen, "r_fullscreen", "0", CVAR_ARCHIVE | CVAR_LATCH, CVART_BOOL, NULL, NULL, "full-screen mode" }, + { &r_monitor, "r_monitor", "0", CVAR_ARCHIVE | CVAR_LATCH, CVART_INTEGER, "0", NULL, "1-based monitor index, 0=primary" } +}; + +static void sdl_PrintMonitorList(); + +static const cmdTableItem_t glimp_cmds[] = { + { "monitorlist", &sdl_PrintMonitorList, NULL, "prints the list of monitors" } +}; + + +static qbool sdl_IsMonitorListValid() +{ + const int count = glimp.monitorCount; + const int curr = glimp.monitor; + const int prim = glimp.primaryMonitor; + + return + count >= 1 && + curr >= 0 && + curr < count && + prim >= 0 && + prim < count && + glimp.monitorRects[prim].x == 0 && + glimp.monitorRects[prim].y == 0; +} + + +static void sdl_CreateMonitorList() +{ + const int count = SDL_GetNumVideoDisplays(); + if (count <= 0) { + glimp.monitorCount = 0; + return; + } + + int gi = 0; + for (int si = 0; si < count; ++si) { + if (gi >= MAX_MONITOR_COUNT) + break; + if (SDL_GetDisplayBounds(si, &glimp.monitorRects[gi]) == 0) + ++gi; + } + glimp.monitorCount = gi; + + glimp.primaryMonitor = -1; + const int finalCount = glimp.monitorCount; + for(int i = 0; i < finalCount; ++i) { + const SDL_Rect rect = glimp.monitorRects[i]; + if (rect.x == 0 && rect.y == 0) { + glimp.primaryMonitor = i; + break; + } + } + + if (!sdl_IsMonitorListValid()) + glimp.monitorCount = 0; +} + + +// call this before creating the window +static void sdl_UpdateMonitorIndexFromCvar() +{ + if (glimp.monitorCount <= 0) + return; + + const int monitor = Cvar_Get("r_monitor", "0", CVAR_ARCHIVE | CVAR_LATCH)->integer; + if (monitor <= 0 || monitor > glimp.monitorCount) { + glimp.monitor = glimp.primaryMonitor; + return; + } + glimp.monitor = Com_ClampInt(0, glimp.monitorCount - 1, monitor - 1); +} + + +// call this after the window has been moved +void sdl_UpdateMonitorIndexFromWindow() +{ + if (glimp.monitorCount <= 0) + return; + + // update the glimp index + const int current = SDL_GetWindowDisplayIndex(glimp.window); + if (current < 0) { + glimp.monitorCount = 0; + return; + } + glimp.monitor = current; + + // update the cvar index + if( r_monitor->integer == 0 && + glimp.monitor == glimp.primaryMonitor) + return; + Cvar_Set("r_monitor", va("%d", glimp.monitor + 1)); +} + + +static void sdl_GetSafeDesktopRect( SDL_Rect* rect ) +{ + if (glimp.monitorCount <= 0 || + glimp.monitor < 0 || + glimp.monitor >= glimp.monitorCount) { + rect->x = 0; + rect->y = 0; + rect->w = 1280; + rect->h = 720; + } + + *rect = glimp.monitorRects[glimp.monitor]; +} + + +static void sdl_PrintMonitorList() +{ + const int count = glimp.monitorCount; + Com_Printf("Monitor count: %d\n", count); + + for (int i = 0; i < count; ++i) { + const SDL_Rect rect = glimp.monitorRects[i]; + Com_Printf("Monitor #%d: %d,%d %dx%d\n", i + 1, rect.x, rect.y, rect.w, rect.h); + } +} + + +// @TODO: this should be handled by the renderer, not the platform layer! +static void GLW_InitExtensions() +{ + ri.Printf(PRINT_ALL, "Initializing OpenGL extensions\n"); + + int maxAnisotropy = 0; + if (strstr(glConfig.extensions_string, "GL_EXT_texture_filter_anisotropic")) { + if (r_ext_max_anisotropy->integer > 1) { + qglGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropy); + if (maxAnisotropy <= 0) { + ri.Printf(PRINT_DEVELOPER, "...GL_EXT_texture_filter_anisotropic not properly supported!\n"); + maxAnisotropy = 0; + } else { + ri.Printf(PRINT_DEVELOPER, "...using GL_EXT_texture_filter_anisotropic (max: %i)\n", maxAnisotropy); + } + } else { + ri.Printf(PRINT_DEVELOPER, "...ignoring GL_EXT_texture_filter_anisotropic\n"); + } + } else { + ri.Printf(PRINT_DEVELOPER, "...GL_EXT_texture_filter_anisotropic not found\n"); + } + Cvar_Set("r_ext_max_anisotropy", va("%i", maxAnisotropy)); +} + + +void GLimp_Init() +{ + if (glimp.window != NULL) + return; + + Cvar_RegisterArray(glimp_cvars, MODULE_CLIENT); + Cmd_RegisterArray(glimp_cmds, MODULE_CLIENT); + + sdl_CreateMonitorList(); + sdl_UpdateMonitorIndexFromCvar(); + sdl_PrintMonitorList(); + + SDL_Rect deskropRect; + sdl_GetSafeDesktopRect(&deskropRect); + + const qbool desktopRes = !R_GetModeInfo(&glConfig.vidWidth, &glConfig.vidHeight, &glConfig.windowAspect); + if (desktopRes) { + glConfig.vidWidth = deskropRect.w; + glConfig.vidHeight = deskropRect.h; + glConfig.windowAspect = (float)glConfig.vidWidth / (float)glConfig.vidHeight; + } + + Uint32 windowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN; + if (r_fullscreen->integer) { + if (desktopRes) + windowFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP; + else + windowFlags |= SDL_WINDOW_FULLSCREEN; + } + + // @TODO: make a cvar defaulting to an empty string for this? e.g. value: "libGL.so.1" + if (SDL_GL_LoadLibrary(NULL) < 0) + ri.Error(ERR_FATAL, "GLimp_Init - SDL_GL_LoadLibrary failed: %s\n", SDL_GetError()); + + glimp.window = SDL_CreateWindow("CNQ3", deskropRect.x, deskropRect.y, glConfig.vidWidth, glConfig.vidHeight, windowFlags); + if (glimp.window == NULL) + ri.Error(ERR_FATAL, "GLimp_Init - SDL_CreateWindow failed: %s\n", SDL_GetError()); + + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0); + SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); + glimp.glContext = SDL_GL_CreateContext(glimp.window); + if (glimp.glContext == NULL) + ri.Error(ERR_FATAL, "GLimp_Init - SDL_GL_CreateContext failed: %s\n", SDL_GetError()); + glConfig.colorBits = 32; + glConfig.depthBits = 24; + glConfig.stencilBits = 8; + + if (SDL_GL_MakeCurrent(glimp.window, glimp.glContext) < 0) + ri.Error(ERR_FATAL, "GLimp_Init - SDL_GL_MakeCurrent failed: %s\n", SDL_GetError()); + + if (!QGL_Init(NULL)) + ri.Error(ERR_FATAL, "GLimp_Init - failed to initialize core OpenGL\n"); + + GLW_InitExtensions(); + + if (!GLW_InitGL2()) + ri.Error(ERR_FATAL, "GLimp_Init - could not find or initialize a suitable OpenGL 2+ subsystem\n"); + + GLW_InitGL3(); + + if (!QGL_InitGL2()) + ri.Error(ERR_FATAL, "GLimp_Init - could not initialize OpenGL 2 objects\n"); + + SDL_GL_SetSwapInterval(r_swapInterval->integer); + + ri.Printf(PRINT_ALL, "Loaded OpenGL %s\n", (const char*)qglGetString(GL_VERSION)); +} + + +void GLimp_Shutdown() +{ + if (glimp.glContext != NULL) { + SDL_GL_DeleteContext(glimp.glContext); + glimp.glContext = NULL; + } + + if (glimp.window != NULL) { + SDL_DestroyWindow(glimp.window); + glimp.window = NULL; + } + + SDL_GL_UnloadLibrary(); + QGL_Shutdown(); + + memset(&glConfig, 0, sizeof(glConfig)); + memset(&glState, 0, sizeof(glState)); +} + + +void GLimp_EndFrame() +{ + if (r_swapInterval->modified) { + r_swapInterval->modified = qfalse; + SDL_GL_SetSwapInterval(r_swapInterval->integer); + } + + SDL_GL_SwapWindow(glimp.window); +} + + +qbool GLimp_SpawnRenderThread( void (*function)() ) +{ + return qfalse; +} + + +void* GLimp_RendererSleep() +{ + return NULL; +} + + +void GLimp_FrontEnderSleep() +{ +} + + +void GLimp_WakeRenderer( void* data ) +{ +} diff --git a/code/linux/sdl_local.h b/code/linux/sdl_local.h new file mode 100644 index 0000000..1f7f174 --- /dev/null +++ b/code/linux/sdl_local.h @@ -0,0 +1,18 @@ +#pragma once + + +#define MAX_MONITOR_COUNT 16 + + +struct glImp_t { + SDL_Window* window; + SDL_GLContext glContext; + + SDL_Rect monitorRects[MAX_MONITOR_COUNT]; + int monitorCount; + int primaryMonitor; // primary monitor, 0-based + int monitor; // current monitor, 0-based +}; + + +extern glImp_t glimp; diff --git a/code/linux/sdl_snd.cpp b/code/linux/sdl_snd.cpp new file mode 100644 index 0000000..d2e716f --- /dev/null +++ b/code/linux/sdl_snd.cpp @@ -0,0 +1,170 @@ +#include "../qcommon/q_shared.h" +#include "../client/snd_local.h" +#include + + +// @TODO: cvars for freq and samples? +// @TODO: cvar for the device name? ("alsa", "pulseaudio", etc) +static const int bits = 16; +static const int channels = 2; +static const int freq = 44100; // got issues with 22050 in the VM +static const int samples = 2048; +static const SDL_AudioFormat format = AUDIO_S16SYS; + +struct audio_t { + qbool valid; + int q3SamplePos; + int q3Bytes; + SDL_AudioDeviceID device; +}; + +static audio_t audio; + + +static void FillAudioBufferCallback( void* userData, Uint8* sdlBuffer, int sdlBytesToWrite ) +{ + if (sdlBuffer == NULL || sdlBytesToWrite == 0) + return; + + if (!audio.valid) { + memset(sdlBuffer, 0, sdlBytesToWrite); + return; + } + + // fix up sample offset if needed + const int bytesPerSample = dma.samplebits / 8; + int q3BytePos = audio.q3SamplePos * bytesPerSample; + if (q3BytePos >= audio.q3Bytes) { + q3BytePos = 0; + audio.q3SamplePos = 0; + } + + // compute the sizes for the memcpy call(s) + int q3BytesToEnd = audio.q3Bytes - q3BytePos; + int bytes1 = sdlBytesToWrite; + int bytes2 = 0; + if (bytes1 > q3BytesToEnd) { + bytes1 = q3BytesToEnd; + bytes2 = sdlBytesToWrite - q3BytesToEnd; + } + + // copy the new mixed data to the device + memcpy(sdlBuffer, dma.buffer + q3BytePos, bytes1); + if (bytes2 > 0) { + memcpy(sdlBuffer + bytes1, dma.buffer, bytes2); + audio.q3SamplePos = bytes2 / bytesPerSample; + } else { + audio.q3SamplePos += bytes1 / bytesPerSample; + } + + // fix up sample offset if needed + if (audio.q3SamplePos * bytesPerSample >= audio.q3Bytes) + audio.q3SamplePos = 0; +} + + +qbool SNDDMA_Init() +{ + if (audio.valid) + return qtrue; + + if (SDL_Init(SDL_INIT_AUDIO) < 0) { + Com_Printf("SDL_Init failed: %s\n", SDL_GetError()); + return qfalse; + } + + // open the default audio device + SDL_AudioSpec desired; + memset(&desired, 0, sizeof(desired)); + desired.freq = freq; + desired.format = format; + desired.samples = samples; + desired.channels = channels; + desired.callback = &FillAudioBufferCallback; + SDL_AudioSpec obtained; + memset(&obtained, 0, sizeof(obtained)); + audio.device = SDL_OpenAudioDevice(NULL, 0, &desired, &obtained, 0); + if (audio.device == 0) { + Com_Printf("SDL_OpenAudioDevice failed: %s\n", SDL_GetError()); + SNDDMA_Shutdown(); + return qfalse; + } + + // save all the data we need to + const int q3Samples = obtained.samples * 16; + audio.q3SamplePos = 0; + dma.samplebits = obtained.format & 0xFF; + dma.channels = obtained.channels; + dma.samples = q3Samples; + dma.submission_chunk = 1; + dma.speed = obtained.freq; + audio.q3Bytes = dma.samples * (dma.samplebits / 8); + dma.buffer = (byte*)calloc(1, audio.q3Bytes); + audio.valid = qtrue; + + // opened devices are always paused by default + SDL_PauseAudioDevice(audio.device, 0); + + return qtrue; +} + + +int SNDDMA_GetDMAPos() +{ + if (!audio.valid) + return 0; + + return audio.q3SamplePos; +} + + +void SNDDMA_Shutdown() +{ + if (audio.device != 0) { + SDL_PauseAudioDevice(audio.device, 1); + SDL_CloseAudioDevice(audio.device); + audio.device = 0; + } + + SDL_QuitSubSystem(SDL_INIT_AUDIO); + free(dma.buffer); + dma.buffer = NULL; + audio.q3SamplePos = 0; + audio.q3Bytes = 0; + audio.valid = qfalse; +} + + +void SNDDMA_Submit() +{ + if (!audio.valid) + return; + + // let SDL call our registered callback function again + SDL_UnlockAudioDevice(audio.device); +} + + +void SNDDMA_BeginPainting() +{ + if (!audio.valid) + return; + + // prevent SDL from calling our registered callback function + SDL_LockAudioDevice(audio.device); +} + + +void sdl_MuteAudio( qbool mute ) +{ + if (!audio.valid) + return; + + const qbool playing = SDL_GetAudioDeviceStatus(audio.device) == SDL_AUDIO_PLAYING; + if (mute && playing) { + SDL_PauseAudioDevice(audio.device, 1); + } else if (!mute && !playing) { + S_ClearSoundBuffer(); + SDL_PauseAudioDevice(audio.device, 0); + } +} diff --git a/code/renderer/tr_gl2.cpp b/code/renderer/tr_gl2.cpp index 2227434..f98b27b 100644 --- a/code/renderer/tr_gl2.cpp +++ b/code/renderer/tr_gl2.cpp @@ -193,7 +193,7 @@ static qbool GL2_DynLights_MultitextureStage( int stage ) qglDrawElements( GL_TRIANGLES, tess.numIndexes, GL_INDEX_TYPE, tess.indexes ); return qtrue; } - + if ( r_fullbright->integer ) { if ( pStage->type == ST_LIGHTMAP ) { Com_Memset( tess.svars.colors, tr.identityLightByte, tess.numVertexes * 4 ); @@ -270,7 +270,7 @@ void GL2_DynLights_StageIterator() // so color changes are ignored unless we "update" the color pointer again qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, tess.svars.colors ); qglTexCoordPointer( 2, GL_FLOAT, 0, tess.svars.texcoords ); - + if ( pStage->mtStages ) { // we can't really cope with massive collapses, so assert( pStage->mtStages == 1 ); @@ -282,7 +282,7 @@ void GL2_DynLights_StageIterator() qglDrawElements( GL_TRIANGLES, tess.numIndexes, GL_INDEX_TYPE, tess.indexes ); } - + if ( tess.fogNum && tess.shader->fogPass ) RB_FogPass(); @@ -414,7 +414,7 @@ static const char* GL2_GetErrorString( GLenum ec ) CASE( GL_STACK_UNDERFLOW ); CASE( GL_STACK_OVERFLOW ); default: return "?"; - } + } } @@ -540,13 +540,16 @@ static qbool GL2_FBO_CreateMS( FrameBuffer& fb ) static qbool GL2_FBO_Init() { const int msaa = r_msaa->integer; - const qbool enableAA = msaa >= 2 && msaa <= 16 && qglRenderbufferStorageMultisample != NULL; - frameBufferMultiSampling = enableAA; + const qbool validOption = msaa >= 2 && msaa <= 16; + const qbool enable = validOption && qglRenderbufferStorageMultisample != NULL; + frameBufferMultiSampling = enable; + if ( validOption && !enable ) + Com_Printf( "Warning: MSAA requested but disabled because glRenderbufferStorageMultisample wasn't found\n" ); - if ( !enableAA ) + if ( !enable ) return GL2_FBO_CreateSS( frameBuffersPostProcess[0], qtrue ) && GL2_FBO_CreateSS( frameBuffersPostProcess[1], qtrue ); - + return GL2_FBO_CreateMS( frameBufferMain ) && GL2_FBO_CreateSS( frameBuffersPostProcess[0], qfalse ) && GL2_FBO_CreateSS( frameBuffersPostProcess[1], qfalse ); @@ -641,7 +644,7 @@ static void GL2_PostProcessGamma() GL2_FBO_Swap(); GL2_FBO_Bind(); - + GL_Program( gammaProg ); qglUniform1i( gammaProgAttribs.texture, 0 ); // we use texture unit 0 qglUniform4f( gammaProgAttribs.gammaOverbright, gamma, gamma, gamma, obScale ); @@ -694,7 +697,7 @@ static void GL2_PostProcessGreyscale() GL2_FBO_Swap(); GL2_FBO_Bind(); - + GL_Program( greyscaleProg ); qglUniform1i( greyscaleProgAttribs.texture, 0 ); // we use texture unit 0 qglUniform1f( greyscaleProgAttribs.greyscale, greyscale ); @@ -734,7 +737,7 @@ qbool QGL_InitGL2() if ( !GL2_CreateProgram( dynLightProg, dynLightVS, dynLightFS ) ) { Com_Printf( "ERROR: failed to compile dynamic light shaders\n" ); - return qfalse; + return qfalse; } dynLightProgAttribs.osEyePos = qglGetUniformLocation( dynLightProg.p, "osEyePos" ); dynLightProgAttribs.osLightPos = qglGetUniformLocation( dynLightProg.p, "osLightPos" ); @@ -751,7 +754,7 @@ qbool QGL_InitGL2() greyscaleProgramValid = GL2_CreateProgram( greyscaleProg, greyscaleVS, greyscaleFS ); if ( greyscaleProgramValid ) { greyscaleProgAttribs.texture = qglGetUniformLocation( greyscaleProg.p, "texture" ); - greyscaleProgAttribs.greyscale = qglGetUniformLocation( greyscaleProg.p, "greyscale" ); + greyscaleProgAttribs.greyscale = qglGetUniformLocation( greyscaleProg.p, "greyscale" ); } else { Com_Printf( "ERROR: failed to compile greyscale shaders\n" ); } diff --git a/code/unix/linux_glimp.cpp b/code/unix/linux_glimp.cpp deleted file mode 100644 index d025691..0000000 --- a/code/unix/linux_glimp.cpp +++ /dev/null @@ -1,1373 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code 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 2 of the License, -or (at your option) any later version. - -Quake III Arena source code 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. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -/* -** GLW_IMP.C -** -** This file contains ALL Linux specific stuff having to do with the -** OpenGL refresh. When a port is being made the following functions -** must be implemented by the port: -** -** GLimp_EndFrame -** GLimp_Init -** GLimp_Shutdown -** GLimp_SwitchFullscreen -** -*/ - -#include -#include -#ifdef __linux__ - #include - #include -#endif -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include "../renderer/tr_local.h" -#include "../client/client.h" -#include "linux_local.h" -#include "unix_glw.h" - - -static Display *dpy = NULL; -static Window win = 0; - - -/////////////////////////////////////////////////////////////// - - -struct Mouse { - virtual qbool Init() { return qtrue; } - virtual qbool Activate( qbool active ); - virtual void Shutdown() {} - virtual void Read( int* mx, int* my ) = 0; - virtual qbool ProcessEvent( XEvent& event ) { return qfalse; } // returns true if the event was handled - - Mouse() : active(qfalse) {} - - static const int buttons[]; - -private: - qbool active; -}; - -static Mouse* mouse; -const int Mouse::buttons[] = { 0, K_MOUSE1, K_MOUSE3, K_MOUSE2, K_MWHEELUP, K_MWHEELDOWN, 0, 0, K_MOUSE4, K_MOUSE5 }; - - -qbool Mouse::Activate( qbool _active ) -{ - if (active == _active) - return qfalse; - - active = _active; - - return qtrue; -} - - -/////////////////////////////////////////////////////////////// - - -struct xmouse_t : public Mouse { - virtual qbool Activate( qbool active ); - virtual void Read( int* mx, int* my ); - virtual qbool ProcessEvent( XEvent& event ); - - int x, y, prev_x, prev_y; - int window_center_x, window_center_y; -}; - -static xmouse_t xmouse; - - -qbool xmouse_t::Activate( qbool active ) -{ - if (!active) - return qtrue; - - window_center_x = glConfig.vidWidth / 2; - window_center_y = glConfig.vidHeight / 2; - - XWarpPointer( dpy, None, win, 0,0,0,0, window_center_x, window_center_y ); - return qtrue; -} - - -void xmouse_t::Read( int* mx, int* my ) -{ - // we could just use XQueryPointer here - // but since we're processing events anyway we might as well use the data from those - *mx = x; - *my = y; - x = y = 0; - - if (*mx || *my) { - XWarpPointer( dpy, None, win, 0,0,0,0, window_center_x, window_center_y ); - } -} - - -qbool xmouse_t::ProcessEvent( XEvent& event ) -{ - switch (event.type) { - - case MotionNotify: { - // if this is exactly recentering the mouse, it's probably from our warp - // but there's no way to actually handle this problem well in X - if ((event.xmotion.x == window_center_x) && (event.xmotion.y == window_center_y)) { - //Com_Printf( "WARP: mx %d my %d \n", event.xmotion.x, event.xmotion.y ); - prev_x = event.xmotion.x; - prev_y = event.xmotion.y; - break; - } - - //Com_Printf( "mx %d my %d \n", event.xmotion.x, event.xmotion.y ); - // note that accumulating motion events like this is actually wrong - // because button events are processed immediately - // but the windows code has the same bug, and nobody seems upset by it there - x += (event.xmotion.x - prev_x); - y += (event.xmotion.y - prev_y); - prev_x = event.xmotion.x; - prev_y = event.xmotion.y; - return qtrue; - } - break; - - case ButtonPress: - case ButtonRelease: - //Com_Printf( "Button %u \n", event.xbutton.button ); - if ((event.xbutton.button > 0) && (event.xbutton.button < (sizeof(buttons) / sizeof(buttons[0])))) { - int button = buttons[event.xbutton.button]; - if (button) { - Sys_QueEvent( 0, SE_KEY, button, (event.type == ButtonPress), 0, NULL ); - } - } - break; - - } - - return qfalse; -} - - -/////////////////////////////////////////////////////////////// - - -// this is a total suckfest. debian didn't multiarch xinput for some reason, -// so using it means having to dlsym everything or you can't build both i386 and x64 - - -typedef XIDeviceInfo* (*xiQueryDevice)( Display *display, int deviceid, int *ndevices_return ); -typedef void (*xiFreeDeviceInfo)( XIDeviceInfo* ); -typedef Status (*xiQueryVersion)( Display *display, int *major_version_inout, int *minor_version_inout ); -typedef Status (*xiSelectEvents)( Display *display, Window win, XIEventMask *masks, int num_masks ); - -static void* libxi = 0; -static xiQueryDevice pxiQueryDevice; -static xiFreeDeviceInfo pxiFreeDeviceInfo; -static xiQueryVersion pxiQueryVersion; -static xiSelectEvents pxiSelectEvents; -static int xiOPCode; - - -static void* XI_GetProcAddress( const char* symbol ) -{ - if (!libxi) - return 0; - return dlsym( libxi, symbol ); -} -#define XI(F) { pxi##F = (xi##F)XI_GetProcAddress( "XI"#F ); } - - -struct rawmouse_t : public Mouse { - virtual qbool Init(); - //virtual qbool Activate( qbool active ); - virtual void Shutdown(); - virtual void Read( int* mx, int* my ); - virtual qbool ProcessEvent( XEvent& event ); - -private: - int x, y, prev_x, prev_y; - int mode; - - static int FindMouse(); - static qbool ValidateMouse( const XIDeviceInfo* info ); -}; - -static rawmouse_t rawmouse; - - -qbool rawmouse_t::ValidateMouse( const XIDeviceInfo* info ) -{ - if (!info->enabled) - return qfalse; - - const XIAnyClassInfo** classes = (const XIAnyClassInfo**)info->classes; - for (int i = 0; i < info->num_classes; ++i) { - if (classes[i]->type == XIValuatorClass) { - const XIValuatorClassInfo* v = (const XIValuatorClassInfo*)classes[i]; - if ((v->mode == XIModeRelative) && !v->resolution) - return qfalse; // invalid combination (eg XTEST mouse) - ri.Printf( PRINT_DEVELOPER, "ValidateMouse: accepted device %d\n", info->deviceid ); - rawmouse.mode = v->mode; - return qtrue; - } - } - - return qfalse; -} - - -int rawmouse_t::FindMouse() -{ - int n, device = 0; - XIDeviceInfo* info = pxiQueryDevice( dpy, XIAllDevices, &n ); - - cvar_t* m_device = Cvar_Get( "m_device", "0", 0 ); - if (m_device->integer) { - for (int i = 0; i < n; ++i) { - if ((info[i].deviceid == m_device->integer) && ValidateMouse(&info[i])) { - device = info[i].deviceid; - pxiFreeDeviceInfo( info ); - return device; - } - } - } - - for (int i = 0; i < n; ++i) { - if ((info[i].use == XIMasterPointer) && ValidateMouse(&info[i])) { - device = info[i].deviceid; - break; - } - } - - pxiFreeDeviceInfo( info ); - return device; -} - - -qbool rawmouse_t::Init() -{ - int event, error; - if (!XQueryExtension(dpy, "XInputExtension", &xiOPCode, &event, &error)) - return qfalse; - - libxi = dlopen( "libXi.so.6", RTLD_GLOBAL | RTLD_LAZY ); - XI(QueryDevice); - XI(FreeDeviceInfo); - XI(QueryVersion); - XI(SelectEvents); - - int major = 2, minor = 1; - if (pxiQueryVersion(dpy, &major, &minor) == BadRequest) { - return qfalse; - } - - XIEventMask eventmask; - unsigned char mask[4] = { 0 }; - eventmask.deviceid = FindMouse(); - if (!eventmask.deviceid) { - return qfalse; - } - - eventmask.mask_len = sizeof(mask); - eventmask.mask = mask; - XISetMask( mask, XI_RawMotion ); - XISetMask( mask, XI_RawButtonPress ); - XISetMask( mask, XI_RawButtonRelease ); - if (pxiSelectEvents( dpy, DefaultRootWindow(dpy), &eventmask, 1 ) == BadRequest) { - return qfalse; - } - - XSelectInput( dpy, win, KeyPressMask | KeyReleaseMask | FocusChangeMask ); - - return qtrue; -} - - -void rawmouse_t::Shutdown() -{ - if (libxi) { - dlclose( libxi ); - libxi = 0; - } -} - - -void rawmouse_t::Read( int* mx, int* my ) -{ - *mx = x; - *my = y; - x = y = 0; -} - - -qbool rawmouse_t::ProcessEvent( XEvent& event ) -{ - if ((event.xcookie.type != GenericEvent) || (event.xcookie.extension != xiOPCode)) - return qfalse; - - if (!XGetEventData(dpy, &event.xcookie)) - return qfalse; - - const XIRawEvent* raw = (const XIRawEvent*)event.xcookie.data; - switch (event.xcookie.evtype) { - case XI_RawMotion: { - int mx = 0, my = 0; - const double* val = raw->raw_values; - for (int i = 0; i < raw->valuators.mask_len * 8; ++i) { - if (XIMaskIsSet(raw->valuators.mask, i)) { - //Com_Printf( "RawMotion on axis %d: %lf \n", i, *val ); - if (i == 0) - mx += *val; - else if (i == 1) - my += *val; - ++val; - } - } - if (mode == XIModeRelative) { - x += mx; - y += my; - } - // workaround for virtualbox bugs - if (mode == XIModeAbsolute) { - //Com_Printf( "mx %d my %d dx %d dy %d \n", mx, my, (mx - prev_x), (my - prev_y) ); - if (mx) { x += (mx - prev_x) * 1920 / 0x8000; prev_x = mx; } - if (my) { y += (my - prev_y) * 1200 / 0x8000; prev_y = my; } - } - } - break; - - case XI_RawButtonPress: - case XI_RawButtonRelease: - //Com_Printf( "XI_RawButton %u \n", raw->detail ); - if ((raw->detail > 0) && (raw->detail < (sizeof(buttons) / sizeof(buttons[0])))) { - int button = buttons[raw->detail]; - if (button) { - Sys_QueEvent( 0, SE_KEY, button, (event.xcookie.evtype == XI_RawButtonPress), 0, NULL ); - } - } - break; - } - - XFreeEventData(dpy, &event.xcookie); - return qtrue; -} - - -/////////////////////////////////////////////////////////////// - - -/* -** NOTE TTimo the keyboard handling is done with KeySyms -** that means relying on the keyboard mapping provided by X -** in-game it would probably be better to use KeyCode (i.e. hardware key codes) -** you would still need the KeySyms in some cases, such as for the console and all entry textboxes -** (cause there's nothing worse than a qwerty mapping on a french keyboard) -*/ - -static const char* TranslateKey( XKeyEvent* ev, int* key ) -{ - static char raw[2], translated[2]; - KeySym keysym; - int len; - - *key = 0; - - // get the normal interpretation of the key without messing with shifts, for SE_CHAR - XLookupString( ev, translated, sizeof(translated), &keysym, 0 ); - - // then get the keysym that we actually want with no shifts at all, for SE_KEY - ev->state = 0; - len = XLookupString( ev, raw, sizeof(raw), &keysym, 0 ); - - - switch (keysym) - { - case XK_KP_Page_Up: - case XK_KP_9: *key = K_KP_PGUP; break; - case XK_Page_Up: *key = K_PGUP; break; - - case XK_KP_Page_Down: - case XK_KP_3: *key = K_KP_PGDN; break; - case XK_Page_Down: *key = K_PGDN; break; - - case XK_KP_Home: *key = K_KP_HOME; break; - case XK_KP_7: *key = K_KP_HOME; break; - case XK_Home: *key = K_HOME; break; - - case XK_KP_End: - case XK_KP_1: *key = K_KP_END; break; - case XK_End: *key = K_END; break; - - case XK_KP_Left: *key = K_KP_LEFTARROW; break; - case XK_KP_4: *key = K_KP_LEFTARROW; break; - case XK_Left: *key = K_LEFTARROW; break; - - case XK_KP_Right: *key = K_KP_RIGHTARROW; break; - case XK_KP_6: *key = K_KP_RIGHTARROW; break; - case XK_Right: *key = K_RIGHTARROW; break; - - case XK_KP_Down: - case XK_KP_2: *key = K_KP_DOWNARROW; break; - case XK_Down: *key = K_DOWNARROW; break; - - case XK_KP_Up: - case XK_KP_8: *key = K_KP_UPARROW; break; - case XK_Up: *key = K_UPARROW; break; - - case XK_Escape: *key = K_ESCAPE; break; - - case XK_KP_Enter: *key = K_KP_ENTER; break; - case XK_Return: *key = K_ENTER; break; - - case XK_Tab: *key = K_TAB; break; - - case XK_F1: *key = K_F1; break; - case XK_F2: *key = K_F2; break; - case XK_F3: *key = K_F3; break; - case XK_F4: *key = K_F4; break; - case XK_F5: *key = K_F5; break; - case XK_F6: *key = K_F6; break; - case XK_F7: *key = K_F7; break; - case XK_F8: *key = K_F8; break; - case XK_F9: *key = K_F9; break; - case XK_F10: *key = K_F10; break; - case XK_F11: *key = K_F11; break; - case XK_F12: *key = K_F12; break; - - case XK_BackSpace: *key = K_BACKSPACE; break; - - case XK_KP_Delete: - case XK_KP_Decimal: *key = K_KP_DEL; break; - case XK_Delete: *key = K_DEL; break; - - case XK_Pause: *key = K_PAUSE; break; - - case XK_Shift_L: - case XK_Shift_R: *key = K_SHIFT; break; - - case XK_Execute: - case XK_Control_L: - case XK_Control_R: *key = K_CTRL; break; - - case XK_Alt_L: - case XK_Meta_L: - case XK_Alt_R: - case XK_Meta_R: *key = K_ALT; break; - - case XK_KP_Begin: *key = K_KP_5; break; - - case XK_Insert: *key = K_INS; break; - case XK_KP_Insert: - case XK_KP_0: *key = K_KP_INS; break; - - case XK_KP_Add: *key = K_KP_PLUS; break; - case XK_KP_Divide: *key = K_KP_SLASH; break; - case XK_KP_Multiply: *key = K_KP_STAR; break; - case XK_KP_Subtract: *key = K_KP_MINUS; break; -/* - // bk001130 - from cvs1.17 (mkv) - case XK_exclam: *key = '1'; break; - case XK_at: *key = '2'; break; - case XK_numbersign: *key = '3'; break; - case XK_dollar: *key = '4'; break; - case XK_percent: *key = '5'; break; - case XK_asciicircum: *key = '6'; break; - case XK_ampersand: *key = '7'; break; - case XK_asterisk: *key = '8'; break; - case XK_parenleft: *key = '9'; break; - case XK_parenright: *key = '0'; break; -*/ - // weird french keyboards .. - // NOTE: console toggle is hardcoded in cl_keys.c, can't be unbound - // cleaner would be .. using hardware key codes instead of the key syms - // could also add a new K_KP_CONSOLE - case XK_twosuperior: *key = '~'; break; - - // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=472 - case XK_space: - case XK_KP_Space: *key = K_SPACE; break; - - default: - if (len) { - *key = raw[0]; - } else { - ri.Printf( PRINT_DEVELOPER, "XLookupString failed on KeySym %d\n", keysym ); - return NULL; - } - break; - } - - return translated; -} - - -/////////////////////////////////////////////////////////////// - - -static qboolean mouse_avail; -static qboolean mouse_active = qfalse; - -static int mouse_accel_numerator; -static int mouse_accel_denominator; -static int mouse_threshold; - -#define KEY_MASK (KeyPressMask | KeyReleaseMask) -#define MOUSE_MASK (ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ButtonMotionMask ) -#define X_MASK (KEY_MASK | MOUSE_MASK | VisibilityChangeMask | StructureNotifyMask | FocusChangeMask ) -cvar_t* in_nograb; - - -static Cursor CreateNullCursor() -{ - Pixmap cursormask = XCreatePixmap( dpy, win, 1, 1, 1 ); - - XGCValues xgc; - xgc.function = GXclear; - GC gc = XCreateGC( dpy, cursormask, GCFunction, &xgc ); - XFillRectangle( dpy, cursormask, gc, 0, 0, 1, 1 ); - - XColor colour; - colour.pixel = 0; - colour.red = 0; - colour.flags = DoRed; - - Cursor cursor = XCreatePixmapCursor( dpy, cursormask, cursormask, &colour, &colour, 0, 0 ); - - XFreePixmap( dpy, cursormask ); - XFreeGC( dpy, gc ); - return cursor; -} - - -static void install_grabs() -{ - XSync( dpy, False ); - - XDefineCursor( dpy, win, CreateNullCursor() ); - - XGrabPointer( dpy, win, False, MOUSE_MASK, GrabModeAsync, GrabModeAsync, win, None, CurrentTime ); - - XGetPointerControl( dpy, &mouse_accel_numerator, &mouse_accel_denominator, &mouse_threshold ); - XChangePointerControl( dpy, True, True, 1, 1, 0 ); - - XWarpPointer( dpy, None, win, 0, 0, 0, 0, glConfig.vidWidth / 2, glConfig.vidHeight / 2 ); - - XGrabKeyboard( dpy, win, False, GrabModeAsync, GrabModeAsync, CurrentTime ); - - XSync( dpy, False ); -} - - -static void uninstall_grabs() -{ - XChangePointerControl( dpy, True, True, mouse_accel_numerator, mouse_accel_denominator, mouse_threshold ); - - XUngrabPointer( dpy, CurrentTime ); - XUngrabKeyboard( dpy, CurrentTime ); - - XUndefineCursor( dpy, win ); -} - - -// bk001206 - from Ryan's Fakk2 -/** - * XPending() actually performs a blocking read - * if no events available. From Fakk2, by way of - * Heretic2, by way of SDL, original idea GGI project. - * The benefit of this approach over the quite - * badly behaved XAutoRepeatOn/Off is that you get - * focus handling for free, which is a major win - * with debug and windowed mode. It rests on the - * assumption that the X server will use the - * same timestamp on press/release event pairs - * for key repeats. - */ -static qboolean X11_PendingInput(void) { - - assert(dpy != NULL); - - // Flush the display connection - // and look to see if events are queued - XFlush( dpy ); - if ( XEventsQueued( dpy, QueuedAlready) ) - { - return qtrue; - } - - // More drastic measures are required -- see if X is ready to talk - { - static struct timeval zero_time; - int x11_fd; - fd_set fdset; - - x11_fd = ConnectionNumber( dpy ); - FD_ZERO(&fdset); - FD_SET(x11_fd, &fdset); - if ( select(x11_fd+1, &fdset, NULL, NULL, &zero_time) == 1 ) - { - return(XPending(dpy)); - } - } - - // Oh well, nothing is ready .. - return qfalse; -} - - -// X sends Release/Press pairs for auto-repeat even though the key hasn't actually been released - -static qboolean KeyRepeat( const XEvent* event ) -{ - if (!X11_PendingInput()) - return qfalse; - - XEvent peek; - XPeekEvent( dpy, &peek ); - - if ( (peek.type == KeyPress) && (peek.xkey.keycode == event->xkey.keycode) && (peek.xkey.time == event->xkey.time) ) { - XNextEvent( dpy, &peek ); // discard the CURRENT event, which is the RELEASE - return qtrue; - } - - return qfalse; -} - - -static void HandleEvents() -{ - XEvent event; - int key; - const char* p; - - if (!dpy) - return; - - while (XPending(dpy)) - { - XNextEvent(dpy, &event); - - if (mouse && mouse->ProcessEvent(event)) - continue; - - switch (event.type) - { - case KeyPress: - p = TranslateKey( &event.xkey, &key ); - if (key) { - Sys_QueEvent( 0, SE_KEY, key, qtrue, 0, NULL ); - } - if (p) { - while (*p) { - Sys_QueEvent( 0, SE_CHAR, *p++, 0, 0, NULL ); - } - } - break; - - case KeyRelease: - if (!cls.keyCatchers && KeyRepeat(&event)) { - continue; - } - TranslateKey( &event.xkey, &key ); - if (key) { - Sys_QueEvent( 0, SE_KEY, key, qfalse, 0, NULL ); - } - break; - - case FocusIn: - case FocusOut: - // reset all modifiers on focus change - Sys_QueEvent( 0, SE_KEY, K_ALT, qfalse, 0, NULL ); - Sys_QueEvent( 0, SE_KEY, K_CTRL, qfalse, 0, NULL ); - Sys_QueEvent( 0, SE_KEY, K_SHIFT, qfalse, 0, NULL ); - break; - } - } - -} - - - -static void IN_ActivateMouse() -{ - if (!mouse_avail || !dpy || !win) - return; - - if (!mouse_active) { - if (!in_nograb->value) - install_grabs(); - mouse_active = qtrue; - } - - mouse->Activate( qtrue ); -} - - -static void IN_DeactivateMouse() -{ - if (!mouse_avail || !dpy || !win) - return; - - if (mouse_active) { - if (!in_nograb->value) - uninstall_grabs(); - mouse_active = qfalse; - } -} - - -/////////////////////////////////////////////////////////////// - - -#define WINDOW_CLASS_NAME "CNQ3" - -// OpenGL driver -#define OPENGL_DRIVER_NAME "libGL.so.1" - -typedef enum -{ - RSERR_OK, - - RSERR_INVALID_FULLSCREEN, - RSERR_INVALID_MODE, - - RSERR_UNKNOWN -} rserr_t; - -glwstate_t glw_state; - -static GLXContext ctx = NULL; - - - -static cvar_t *in_mouse; - -// bk001130 - from cvs1.17 (mkv), but not static -cvar_t *in_joystick = NULL; -cvar_t *in_joystickDebug = NULL; -cvar_t *joy_threshold = NULL; - -cvar_t *r_fullscreen; - -static qboolean vidmode_active = qfalse; - -static int scrnum; - - -/* -** GLimp_Shutdown -** -** This routine does all OS specific shutdown procedures for the OpenGL -** subsystem. Under OpenGL this means NULLing out the current DC and -** HGLRC, deleting the rendering context, and releasing the DC acquired -** for the window. The state structure is also nulled out. -** -*/ -void GLimp_Shutdown( void ) -{ - if (!ctx || !dpy) - return; - - ri.Printf( PRINT_DEVELOPER, "Shutting down OpenGL subsystem\n" ); - -// IN_DeactivateMouse(); - - qglXDestroyContext(dpy, ctx); - ctx = NULL; - - if (win) { - XDestroyWindow(dpy, win); - win = 0; - } - - // NOTE TTimo opening/closing the display should be necessary only once per run - // but it seems QGL_Shutdown gets called in a lot of occasion - // in some cases, this XCloseDisplay is known to raise some X errors - // ( https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=33 ) - XCloseDisplay(dpy); - dpy = NULL; - - vidmode_active = qfalse; - - QGL_Shutdown(); - - memset( &glConfig, 0, sizeof( glConfig ) ); - memset( &glState, 0, sizeof( glState ) ); -} - - -static rserr_t GLW_SetMode( qboolean fullscreen ) -{ - int attrib[] = { - GLX_RGBA, - GLX_RED_SIZE, 8, - GLX_GREEN_SIZE, 8, - GLX_BLUE_SIZE, 8, - GLX_DOUBLEBUFFER, - GLX_DEPTH_SIZE, 24, - GLX_STENCIL_SIZE, 8, - None - }; - - XVisualInfo *visinfo; - XSetWindowAttributes attr; - XSizeHints sizehints; - unsigned long mask; - int actualWidth, actualHeight; - int i; - - ri.Printf( PRINT_ALL, "Initializing OpenGL\n" ); - - if (!(dpy = XOpenDisplay(NULL))) { - ri.Error( ERR_FATAL, "GLW_SetMode - Couldn't open the X display" ); - return RSERR_INVALID_MODE; - } - - scrnum = DefaultScreen(dpy); - Window root = RootWindow(dpy, scrnum); - - if ( !R_GetModeInfo( &glConfig.vidWidth, &glConfig.vidHeight, &glConfig.windowAspect ) ) { - glConfig.vidWidth = XDisplayWidth(dpy, scrnum); - glConfig.vidHeight = XDisplayHeight(dpy, scrnum); - glConfig.windowAspect = (float)glConfig.vidWidth / glConfig.vidHeight; - vidmode_active = qtrue; - fullscreen = qfalse; - } - ri.Printf( PRINT_DEVELOPER, "...setting mode %dx%d %s\n", glConfig.vidWidth, glConfig.vidHeight, fullscreen ? "FS" : "W" ); - - actualWidth = glConfig.vidWidth; - actualHeight = glConfig.vidHeight; - - visinfo = qglXChooseVisual(dpy, scrnum, attrib); - if (!visinfo) - { - ri.Printf( PRINT_ALL, "Couldn't get a visual\n" ); - return RSERR_INVALID_MODE; - } - - glConfig.colorBits = 32; - glConfig.depthBits = 24; - glConfig.stencilBits = 8; - - /* window attributes */ - attr.background_pixel = BlackPixel(dpy, scrnum); - attr.border_pixel = 0; - attr.colormap = XCreateColormap(dpy, root, visinfo->visual, AllocNone); - attr.event_mask = X_MASK; - if (vidmode_active) - { - mask = CWBackPixel | CWColormap | CWSaveUnder | CWBackingStore | - CWEventMask | CWOverrideRedirect; - attr.override_redirect = True; - attr.backing_store = NotUseful; - attr.save_under = False; - } else - mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; - - win = XCreateWindow(dpy, root, 0, 0, - actualWidth, actualHeight, - 0, visinfo->depth, InputOutput, - visinfo->visual, mask, &attr); - XStoreName( dpy, win, WINDOW_CLASS_NAME ); - - /* GH: Don't let the window be resized */ - sizehints.flags = PMinSize | PMaxSize; - sizehints.min_width = sizehints.max_width = actualWidth; - sizehints.min_height = sizehints.max_height = actualHeight; - - XSetWMNormalHints( dpy, win, &sizehints ); - - XMapWindow( dpy, win ); - - if (vidmode_active) - XMoveWindow(dpy, win, 0, 0); - - XFlush(dpy); - XSync(dpy,False); // bk001130 - from cvs1.17 (mkv) - ctx = qglXCreateContext(dpy, visinfo, NULL, True); - XSync(dpy,False); // bk001130 - from cvs1.17 (mkv) - - XFree( visinfo ); - - qglXMakeCurrent(dpy, win, ctx); - - ri.Printf( PRINT_ALL, "GL_RENDERER: %s\n", (const char*)qglGetString( GL_RENDERER ) ); - - return RSERR_OK; -} - - - -static qboolean GLW_StartDriverAndSetMode( qboolean fullscreen ) -{ - rserr_t err; - - if (fullscreen && in_nograb->value) - { - ri.Printf( PRINT_ALL, "Fullscreen not allowed with in_nograb 1\n" ); - ri.Cvar_Set( "r_fullscreen", "0" ); - r_fullscreen->modified = qfalse; - fullscreen = qfalse; - } - - err = GLW_SetMode( fullscreen ); - - switch ( err ) - { - case RSERR_INVALID_FULLSCREEN: - ri.Printf( PRINT_ALL, "...WARNING: fullscreen unavailable in this mode\n" ); - return qfalse; - case RSERR_INVALID_MODE: - ri.Printf( PRINT_ALL, "...WARNING: could not set the given mode\n" ); - return qfalse; - default: - break; - } - return qtrue; -} - - -static void GLW_InitExtensions() -{ - ri.Printf( PRINT_ALL, "Initializing OpenGL extensions\n" ); - - int maxAnisotropy = 0; - if ( strstr( glConfig.extensions_string, "GL_EXT_texture_filter_anisotropic" ) ) - { - if ( r_ext_max_anisotropy->integer > 1 ) - { - qglGetIntegerv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropy ); - if ( maxAnisotropy <= 0 ) { - ri.Printf( PRINT_DEVELOPER, "...GL_EXT_texture_filter_anisotropic not properly supported!\n" ); - maxAnisotropy = 0; - } - else - { - ri.Printf( PRINT_DEVELOPER, "...using GL_EXT_texture_filter_anisotropic (max: %i)\n", maxAnisotropy ); - } - } - else - { - ri.Printf( PRINT_DEVELOPER, "...ignoring GL_EXT_texture_filter_anisotropic\n" ); - } - } - else - { - ri.Printf( PRINT_DEVELOPER, "...GL_EXT_texture_filter_anisotropic not found\n" ); - } - Cvar_Set( "r_ext_max_anisotropy", va("%i", maxAnisotropy) ); -} - - -/* -** GLW_LoadOpenGL -** -** GLimp_win.c internal function that that attempts to load and use -** a specific OpenGL DLL. -*/ -static qboolean GLW_LoadOpenGL( void ) -{ - qboolean fullscreen; - - ri.Printf( PRINT_ALL, "...loading %s: ", OPENGL_DRIVER_NAME ); - - // load the QGL layer - if ( QGL_Init( OPENGL_DRIVER_NAME ) ) - { - fullscreen = r_fullscreen->integer; - - // create the window and set up the context - if ( !GLW_StartDriverAndSetMode( fullscreen ) ) - { - goto fail; - } - - return qtrue; - } else - { - ri.Printf( PRINT_ALL, "failed\n" ); - } - fail: - - QGL_Shutdown(); - - return qfalse; -} - -/* -** XErrorHandler -** the default X error handler exits the application -** I found out that on some hosts some operations would raise X errors (GLXUnsupportedPrivateRequest) -** but those don't seem to be fatal .. so the default would be to just ignore them -** our implementation mimics the default handler behaviour (not completely cause I'm lazy) -*/ -int qXErrorHandler(Display *dpy, XErrorEvent *ev) -{ - static char buf[1024]; - XGetErrorText(dpy, ev->error_code, buf, 1024); - ri.Printf( PRINT_ALL, "X Error of failed request: %s\n", buf); - ri.Printf( PRINT_ALL, " Major opcode of failed request: %d\n", ev->request_code, buf); - ri.Printf( PRINT_ALL, " Minor opcode of failed request: %d\n", ev->minor_code); - ri.Printf( PRINT_ALL, " Serial number of failed request: %d\n", ev->serial); - return 0; -} - -void QGL_SwapInterval( Display *dpy, Window win, int interval ); - -/* -** GLimp_Init -** -** This routine is responsible for initializing the OS specific portions -** of OpenGL. -*/ -void GLimp_Init( void ) -{ - qboolean attemptedlibGL = qfalse; - qboolean success = qfalse; - - cvar_t *lastValidRenderer = ri.Cvar_Get( "r_lastValidRenderer", "(uninitialized)", CVAR_ARCHIVE ); - - // guarded, as this is only relevant to SMP renderer thread -#ifdef SMP - if (!XInitThreads()) - { - Com_Printf("GLimp_Init() - XInitThreads() failed, disabling r_smp\n"); - ri.Cvar_Set( "r_smp", "0" ); - } -#endif - - // set up our custom error handler for X failures - XSetErrorHandler(&qXErrorHandler); - - in_nograb = Cvar_Get( "in_nograb", "0", 0 ); - - // load appropriate DLL and initialize subsystem - if (!GLW_LoadOpenGL() ) - ri.Error( ERR_FATAL, "GLimp_Init()->GLW_LoadOpenGL() - could not load OpenGL subsystem (using '%s')\n", OPENGL_DRIVER_NAME ); - - // get our config strings - Q_strncpyz( glConfig.vendor_string, (char *)qglGetString (GL_VENDOR), sizeof( glConfig.vendor_string ) ); - Q_strncpyz( glConfig.renderer_string, (char *)qglGetString (GL_RENDERER), sizeof( glConfig.renderer_string ) ); - if (*glConfig.renderer_string && glConfig.renderer_string[strlen(glConfig.renderer_string) - 1] == '\n') - glConfig.renderer_string[strlen(glConfig.renderer_string) - 1] = 0; - Q_strncpyz( glConfig.version_string, (char *)qglGetString (GL_VERSION), sizeof( glConfig.version_string ) ); - Q_strncpyz( glConfig.extensions_string, (char *)qglGetString (GL_EXTENSIONS), sizeof( glConfig.extensions_string ) ); - - ri.Cvar_Set( "r_lastValidRenderer", glConfig.renderer_string ); - - // initialize extensions - GLW_InitExtensions(); - - if ( !GLW_InitGL2() || !QGL_InitGL2() ) - ri.Error( ERR_FATAL, "GLimp_Init - could not find or initialize a suitable OpenGL 2 subsystem\n" ); - - QGL_SwapInterval( dpy, win, r_swapInterval->integer ); - - Sys_InitInput(); -} - - -/* -** GLimp_EndFrame -** -** Responsible for doing a swapbuffers and possibly for other stuff -** as yet to be determined. Probably better not to make this a GLimp -** function and instead do a call to GLimp_SwapBuffers. -*/ -void GLimp_EndFrame (void) -{ - // update the swap interval - if ( r_swapInterval->modified ) - { - r_swapInterval->modified = qfalse; - QGL_SwapInterval( dpy, win, r_swapInterval->integer ); - } - - qglXSwapBuffers( dpy, win ); -} - - -#ifdef SMP -/* -=========================================================== - -SMP acceleration - -=========================================================== -*/ - -static pthread_mutex_t smpMutex = PTHREAD_MUTEX_INITIALIZER; - -static pthread_cond_t renderCommandsEvent = PTHREAD_COND_INITIALIZER; -static pthread_cond_t renderCompletedEvent = PTHREAD_COND_INITIALIZER; - -static void (*glimpRenderThread)( void ); - -static void *GLimp_RenderThreadWrapper( void *arg ) -{ - Com_Printf( "Render thread starting\n" ); - - glimpRenderThread(); - - qglXMakeCurrent( dpy, None, NULL ); - - Com_Printf( "Render thread terminating\n" ); - - return arg; -} - -qboolean GLimp_SpawnRenderThread( void (*function)( void ) ) -{ - pthread_t renderThread; - int ret; - - pthread_mutex_init( &smpMutex, NULL ); - - pthread_cond_init( &renderCommandsEvent, NULL ); - pthread_cond_init( &renderCompletedEvent, NULL ); - - glimpRenderThread = function; - - ret = pthread_create( &renderThread, - NULL, // attributes - GLimp_RenderThreadWrapper, - NULL ); // argument - if ( ret ) { - ri.Printf( PRINT_ALL, "pthread_create returned %d: %s", ret, strerror( ret ) ); - return qfalse; - } else { - ret = pthread_detach( renderThread ); - if ( ret ) { - ri.Printf( PRINT_ALL, "pthread_detach returned %d: %s", ret, strerror( ret ) ); - } - } - - return qtrue; -} - -static volatile void *smpData = NULL; -static volatile qboolean smpDataReady; - -void *GLimp_RendererSleep( void ) -{ - void *data; - - qglXMakeCurrent( dpy, None, NULL ); - - pthread_mutex_lock( &smpMutex ); - { - smpData = NULL; - smpDataReady = qfalse; - - // after this, the front end can exit GLimp_FrontEndSleep - pthread_cond_signal( &renderCompletedEvent ); - - while ( !smpDataReady ) { - pthread_cond_wait( &renderCommandsEvent, &smpMutex ); - } - - data = (void *)smpData; - } - pthread_mutex_unlock( &smpMutex ); - - qglXMakeCurrent( dpy, win, ctx ); - - return data; -} - -void GLimp_FrontEndSleep( void ) -{ - pthread_mutex_lock( &smpMutex ); - { - while ( smpData ) { - pthread_cond_wait( &renderCompletedEvent, &smpMutex ); - } - } - pthread_mutex_unlock( &smpMutex ); - - qglXMakeCurrent( dpy, win, ctx ); -} - -void GLimp_WakeRenderer( void *data ) -{ - qglXMakeCurrent( dpy, None, NULL ); - - pthread_mutex_lock( &smpMutex ); - { - assert( smpData == NULL ); - smpData = data; - smpDataReady = qtrue; - - // after this, the renderer can continue through GLimp_RendererSleep - pthread_cond_signal( &renderCommandsEvent ); - } - pthread_mutex_unlock( &smpMutex ); -} - -#else - -void GLimp_RenderThreadWrapper( void *stub ) {} -qboolean GLimp_SpawnRenderThread( void (*function)( void ) ) { - ri.Printf( PRINT_WARNING, "ERROR: SMP support was disabled at compile time\n"); - return qfalse; -} -void *GLimp_RendererSleep( void ) { - return NULL; -} -void GLimp_FrontEndSleep( void ) {} -void GLimp_WakeRenderer( void *data ) {} - -#endif - - -static void IN_StartupMouse() -{ - assert( !mouse ); - mouse = 0; - - cvar_t* in_mouse = Cvar_Get( "in_mouse", "1", CVAR_ARCHIVE|CVAR_LATCH ); - in_mouse->modified = qfalse; - - if (!in_mouse->integer) { - Com_Printf( "Mouse not active.\n" ); - return; - } - - if (in_mouse->integer == 1) { - if (rawmouse.Init()) { - mouse = &rawmouse; - Com_Printf( "Using XInput2\n" ); - return; - } - Com_Printf( "XInput2 mouse initialization failed\n" ); - } - - mouse = &xmouse; - mouse->Init(); - Com_Printf( "Using XWindows mouse input\n" ); -} - - -void Sys_InitInput() -{ - QSUBSYSTEM_INIT_START( "Input" ); - //IN_InitKeyboard(); - IN_StartupMouse(); - - - in_mouse = Cvar_Get( "in_mouse", "1", CVAR_ARCHIVE|CVAR_LATCH ); - - // bk001130 - from cvs.17 (mkv), joystick variables - in_joystick = Cvar_Get ("in_joystick", "0", CVAR_ARCHIVE|CVAR_LATCH); - // bk001130 - changed this to match win32 - in_joystickDebug = Cvar_Get ("in_debugjoystick", "0", CVAR_TEMP); - joy_threshold = Cvar_Get ("joy_threshold", "0.15", CVAR_ARCHIVE); // FIXME: in_joythreshold - -// fix this: it's crap AND wrong: the mouse is what decides if the mouse is available or not - if (in_mouse->value) - mouse_avail = qtrue; - else - mouse_avail = qfalse; - - IN_StartupJoystick(); - - QSUBSYSTEM_INIT_DONE( "Input" ); -} - - -void Sys_ShutdownInput(void) -{ - mouse_avail = qfalse; - - //IN_Activate( qfalse ); - - if (mouse) { - mouse->Shutdown(); - mouse = NULL; - } -} - - -void IN_Frame (void) { - - // bk001130 - from cvs 1.17 (mkv) - IN_JoyMove(); // FIXME: disable if on desktop? - - if ( cls.keyCatchers & KEYCATCH_CONSOLE ) - { - // temporarily deactivate if not in the game and - // running on the desktop - if ( Cvar_VariableValue ("r_fullscreen") == 0 ) - { - IN_DeactivateMouse (); - return; - } - } - - IN_ActivateMouse(); - - if (!mouse) - return; - - int mx, my; - mouse->Read( &mx, &my ); - - if ( !mx && !my ) - return; - - Sys_QueEvent( 0, SE_MOUSE, mx, my, 0, NULL ); -} - - -void IN_Activate(void) -{ -} - -// bk001130 - cvs1.17 joystick code (mkv) was here, no linux_joystick.c - -void Sys_SendKeyEvents (void) { - // XEvent event; // bk001204 - unused - - if (!dpy) - return; - HandleEvents(); -} - - -// bk010216 - added stubs for non-Linux UNIXes here -// FIXME - use NO_JOYSTICK or something else generic - -#if (defined( __FreeBSD__ ) || defined( __sun)) // rb010123 -void IN_StartupJoystick( void ) {} -void IN_JoyMove( void ) {} -#endif - diff --git a/code/unix/linux_joystick.c b/code/unix/linux_joystick.c deleted file mode 100644 index fb42df9..0000000 --- a/code/unix/linux_joystick.c +++ /dev/null @@ -1,211 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code 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 2 of the License, -or (at your option) any later version. - -Quake III Arena source code 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. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -/* -** linux_joystick.c -** -** This file contains ALL Linux specific stuff having to do with the -** Joystick input. When a port is being made the following functions -** must be implemented by the port: -** -** Authors: mkv, bk -** -*/ - -#if !USE_SDL_VIDEO - -#include -#include -#include -#include -#include // bk001204 - - -#include "../client/client.h" -#include "linux_local.h" - -/* We translate axes movement into keypresses. */ -int joy_keys[16] = { - K_LEFTARROW, K_RIGHTARROW, - K_UPARROW, K_DOWNARROW, - K_JOY16, K_JOY17, - K_JOY18, K_JOY19, - K_JOY20, K_JOY21, - K_JOY22, K_JOY23, - - K_JOY24, K_JOY25, - K_JOY26, K_JOY27 -}; - -/* Our file descriptor for the joystick device. */ -static int joy_fd = -1; - - -// bk001130 - from linux_glimp.c -extern cvar_t * in_joystick; -extern cvar_t * in_joystickDebug; -extern cvar_t * joy_threshold; - - -/**********************************************/ -/* Joystick routines. */ -/**********************************************/ -// bk001130 - from cvs1.17 (mkv), removed from linux_glimp.c -void IN_StartupJoystick( void ) -{ - int i = 0; - - joy_fd = -1; - - if( !in_joystick->integer ) { - Com_Printf( "Joystick is not active.\n" ); - return; - } - - for( i = 0; i < 4; i++ ) { - char filename[MAX_OSPATH]; - - snprintf( filename, MAX_OSPATH, "/dev/js%d", i ); - - joy_fd = open( filename, O_RDONLY | O_NONBLOCK ); - - if( joy_fd != -1 ) { - struct js_event event; - char axes = 0; - char buttons = 0; - char name[128]; - int n = -1; - - Com_Printf( "Joystick %s found\n", filename ); - - /* Get rid of initialization messages. */ - do { - n = read( joy_fd, &event, sizeof( event ) ); - - if( n == -1 ) { - break; - } - - } while( ( event.type & JS_EVENT_INIT ) ); - - /* Get joystick statistics. */ - ioctl( joy_fd, JSIOCGAXES, &axes ); - ioctl( joy_fd, JSIOCGBUTTONS, &buttons ); - - if( ioctl( joy_fd, JSIOCGNAME( sizeof( name ) ), name ) < 0 ) { - strncpy( name, "Unknown", sizeof( name ) ); - } - - Com_Printf( "Name: %s\n", name ); - Com_Printf( "Axes: %d\n", axes ); - Com_Printf( "Buttons: %d\n", buttons ); - - /* Our work here is done. */ - return; - } - - } - - /* No soup for you. */ - if( joy_fd == -1 ) { - Com_Printf( "No joystick found.\n" ); - return; - } - -} - -void IN_JoyMove( void ) -{ - /* Store instantaneous joystick state. Hack to get around - * event model used in Linux joystick driver. - */ - static int axes_state[16]; - /* Old bits for Quake-style input compares. */ - static unsigned int old_axes = 0; - /* Our current goodies. */ - unsigned int axes = 0; - int i = 0; - - if( joy_fd == -1 ) { - return; - } - - /* Empty the queue, dispatching button presses immediately - * and updating the instantaneous state for the axes. - */ - do { - int n = -1; - struct js_event event; - - n = read( joy_fd, &event, sizeof( event ) ); - - if( n == -1 ) { - /* No error, we're non-blocking. */ - break; - } - - if( event.type & JS_EVENT_BUTTON ) { - Sys_QueEvent( 0, SE_KEY, K_JOY1 + event.number, event.value, 0, NULL ); - } else if( event.type & JS_EVENT_AXIS ) { - - if( event.number >= 16 ) { - continue; - } - - axes_state[event.number] = event.value; - } else { - Com_Printf( "Unknown joystick event type\n" ); - } - - } while( 1 ); - - - /* Translate our instantaneous state to bits. */ - for( i = 0; i < 16; i++ ) { - float f = ( (float) axes_state[i] ) / 32767.0f; - - if( f < -joy_threshold->value ) { - axes |= ( 1 << ( i * 2 ) ); - } else if( f > joy_threshold->value ) { - axes |= ( 1 << ( ( i * 2 ) + 1 ) ); - } - - } - - /* Time to update axes state based on old vs. new. */ - for( i = 0; i < 16; i++ ) { - - if( ( axes & ( 1 << i ) ) && !( old_axes & ( 1 << i ) ) ) { - Sys_QueEvent( 0, SE_KEY, joy_keys[i], qtrue, 0, NULL ); - } - - if( !( axes & ( 1 << i ) ) && ( old_axes & ( 1 << i ) ) ) { - Sys_QueEvent( 0, SE_KEY, joy_keys[i], qfalse, 0, NULL ); - } - } - - /* Save for future generations. */ - old_axes = axes; -} - -#endif // !USE_SDL_VIDEO - - diff --git a/code/unix/linux_snd.c b/code/unix/linux_snd.c deleted file mode 100644 index 5193769..0000000 --- a/code/unix/linux_snd.c +++ /dev/null @@ -1,294 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code 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 2 of the License, -or (at your option) any later version. - -Quake III Arena source code 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. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#if !USE_SDL_SOUND - -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef __linux__ // rb0101023 - guard this -#include -#endif -#ifdef __FreeBSD__ // rb0101023 - added -#include -#endif -#include - -#include "../qcommon/q_shared.h" -#include "../client/snd_local.h" - -int audio_fd; -int snd_inited=0; - -cvar_t *sndbits; -cvar_t *sndspeed; -cvar_t *sndchannels; - -cvar_t *snddevice; - -/* Some devices may work only with 48000 */ -static int tryrates[] = { 22050, 11025, 44100, 48000, 8000 }; - -static qboolean use_custom_memset = qfalse; -// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=371 -void Snd_Memset (void* dest, const int val, const size_t count) -{ - int *pDest; - int i, iterate; - - if (!use_custom_memset) - { - Com_Memset(dest,val,count); - return; - } - iterate = count / sizeof(int); - pDest = (int*)dest; - for(i=0; istring, O_RDWR); - - if (audio_fd < 0) { - perror(snddevice->string); - Com_Printf("Could not open %s\n", snddevice->string); - return 0; - } - } - - if (ioctl(audio_fd, SNDCTL_DSP_GETCAPS, &caps) == -1) { - perror(snddevice->string); - Com_Printf("Sound driver too old\n"); - close(audio_fd); - return 0; - } - - if (!(caps & DSP_CAP_TRIGGER) || !(caps & DSP_CAP_MMAP)) { - Com_Printf("Sorry but your soundcard can't do this\n"); - close(audio_fd); - return 0; - } - - - /* SNDCTL_DSP_GETOSPACE moved to be called later */ - - // set sample bits & speed - dma.samplebits = (int)sndbits->value; - if (dma.samplebits != 16 && dma.samplebits != 8) { - ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &fmt); - if (fmt & AFMT_S16_LE) - dma.samplebits = 16; - else if (fmt & AFMT_U8) - dma.samplebits = 8; - } - - dma.speed = (int)sndspeed->value; - if (!dma.speed) { - for (i=0 ; ivalue; - if (dma.channels < 1 || dma.channels > 2) - dma.channels = 2; - -/* mmap() call moved forward */ - - tmp = 0; - if (dma.channels == 2) - tmp = 1; - rc = ioctl(audio_fd, SNDCTL_DSP_STEREO, &tmp); - if (rc < 0) { - perror(snddevice->string); - Com_Printf("Could not set %s to stereo=%d", snddevice->string, dma.channels); - close(audio_fd); - return 0; - } - - if (tmp) - dma.channels = 2; - else - dma.channels = 1; - - rc = ioctl(audio_fd, SNDCTL_DSP_SPEED, &dma.speed); - if (rc < 0) { - perror(snddevice->string); - Com_Printf("Could not set %s speed to %d", snddevice->string, dma.speed); - close(audio_fd); - return 0; - } - - if (dma.samplebits == 16) { - rc = AFMT_S16_LE; - rc = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rc); - if (rc < 0) { - perror(snddevice->string); - Com_Printf("Could not support 16-bit data. Try 8-bit.\n"); - close(audio_fd); - return 0; - } - } else if (dma.samplebits == 8) { - rc = AFMT_U8; - rc = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rc); - if (rc < 0) { - perror(snddevice->string); - Com_Printf("Could not support 8-bit data.\n"); - close(audio_fd); - return 0; - } - } else { - perror(snddevice->string); - Com_Printf("%d-bit sound not supported.", dma.samplebits); - close(audio_fd); - return 0; - } - - if (ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info)==-1) { - perror("GETOSPACE"); - Com_Printf("Um, can't do GETOSPACE?\n"); - close(audio_fd); - return 0; - } - - dma.samples = info.fragstotal * info.fragsize / (dma.samplebits/8); - dma.submission_chunk = 1; - - // memory map the dma buffer - - // TTimo 2001/10/08 added PROT_READ to the mmap - // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=371 - // checking Alsa bug, doesn't allow dma alloc with PROT_READ? - - if (!dma.buffer) - dma.buffer = (unsigned char *) mmap(NULL, info.fragstotal - * info.fragsize, PROT_WRITE|PROT_READ, MAP_FILE|MAP_SHARED, audio_fd, 0); - - if (dma.buffer == MAP_FAILED) - { - Com_Printf("Could not mmap dma buffer PROT_WRITE|PROT_READ\n"); - Com_Printf("trying mmap PROT_WRITE (with associated better compatibility / less performance code)\n"); - dma.buffer = (unsigned char *) mmap(NULL, info.fragstotal - * info.fragsize, PROT_WRITE, MAP_FILE|MAP_SHARED, audio_fd, 0); - // NOTE TTimo could add a variable to force using regular memset on systems that are known to be safe - use_custom_memset = qtrue; - } - - if (dma.buffer == MAP_FAILED) { - perror(snddevice->string); - Com_Printf("Could not mmap %s\n", snddevice->string); - close(audio_fd); - return 0; - } - - // toggle the trigger & start her up - - tmp = 0; - rc = ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp); - if (rc < 0) { - perror(snddevice->string); - Com_Printf("Could not toggle.\n"); - close(audio_fd); - return 0; - } - - tmp = PCM_ENABLE_OUTPUT; - rc = ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp); - if (rc < 0) { - perror(snddevice->string); - Com_Printf("Could not toggle.\n"); - close(audio_fd); - - return 0; - } - - snd_inited = 1; - return 1; -} - -int SNDDMA_GetDMAPos(void) -{ - struct count_info count; - - if (!snd_inited) return 0; - - if (ioctl(audio_fd, SNDCTL_DSP_GETOPTR, &count) == -1) { - perror(snddevice->string); - Com_Printf("Uh, sound dead.\n"); - close(audio_fd); - snd_inited = 0; - return 0; - } - return count.ptr / (dma.samplebits / 8); -} - -void SNDDMA_Shutdown(void) -{ -} - -/* -============== -SNDDMA_Submit - -Send sound to device if buffer isn't really the dma buffer -=============== -*/ -void SNDDMA_Submit(void) -{ -} - -void SNDDMA_BeginPainting (void) -{ -} - -#endif // !USE_SDL_SOUND - diff --git a/code/unix/sdl_snd.c b/code/unix/sdl_snd.c deleted file mode 100644 index 78f84e5..0000000 --- a/code/unix/sdl_snd.c +++ /dev/null @@ -1,362 +0,0 @@ -#if USE_SDL_SOUND - -/* - * SDL implementation for Quake 3: Arena's GPL source release. - * - * This is a replacement of the Linux/OpenSoundSystem code with - * an SDL backend, since it allows us to trivially point just about any - * existing 2D audio backend known to man on any platform at the code, - * plus it benefits from all of SDL's tapdancing to support buggy drivers, - * etc, and gets us free ALSA support, too. - * - * This is the best idea for a direct modernization of the Linux sound code - * in Quake 3. However, it would be nice to replace this with true 3D - * positional audio, compliments of OpenAL... - * - * Written by Ryan C. Gordon (icculus@icculus.org). Please refer to - * http://icculus.org/quake3/ for the latest version of this code. - * - * Patches and comments are welcome at the above address. - * - * I cut-and-pasted this from linux_snd.c, and moved it to SDL line-by-line. - * There is probably some cruft that could be removed here. - * - * You should define USE_SDL=1 and then add this to the makefile. - * USE_SDL will disable the Open Sound System target. - */ - -/* -Original copyright on Q3A sources: -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code 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 2 of the License, -or (at your option) any later version. - -Quake III Arena source code 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. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include -#include - -#include "SDL.h" - -#include "../qcommon/q_shared.h" -#include "../client/snd_local.h" - -qboolean snd_inited = qfalse; - -cvar_t *s_sdlBits; -cvar_t *s_sdlSpeed; -cvar_t *s_sdlChannels; -cvar_t *s_sdlDevSamps; -cvar_t *s_sdlMixSamps; - -static qboolean use_custom_memset = qfalse; - -/* -=============== -Snd_Memset - -https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=371 - - some shitty mess with DMA buffers - the mmap'ing permissions were write only - and glibc optimized for mmx would do memcpy with a prefetch and a read - causing segfaults - some other systems would not let you mmap the DMA with read permissions - so I think I ended up attempting opening with read/write, then try write only - and use my own copy instead of the glibc crap -=============== -*/ - -#ifdef Snd_Memset -#undef Snd_Memset -#endif -void Snd_Memset (void* dest, const int val, const size_t count) -{ - int *pDest; - int i, iterate; - - if (!use_custom_memset) - { - Com_Memset(dest,val,count); - return; - } - iterate = count / sizeof(int); - pDest = (int*)dest; - for(i=0; i= dmasize) - dmapos = pos = 0; - - if (!snd_inited) /* shouldn't happen, but just in case... */ - { - memset(stream, '\0', len); - return; - } - else - { - int tobufend = dmasize - pos; /* bytes to buffer's end. */ - int len1 = len; - int len2 = 0; - - if (len1 > tobufend) - { - len1 = tobufend; - len2 = len - len1; - } - memcpy(stream, dma.buffer + pos, len1); - if (len2 <= 0) - dmapos += (len1 / (dma.samplebits/8)); - else /* wraparound? */ - { - memcpy(stream+len1, dma.buffer, len2); - dmapos = (len2 / (dma.samplebits/8)); - } - } - - if (dmapos >= dmasize) - dmapos = 0; -} - -static struct -{ - Uint16 enumFormat; - char *stringFormat; -} formatToStringTable[ ] = -{ - { AUDIO_U8, "AUDIO_U8" }, - { AUDIO_S8, "AUDIO_S8" }, - { AUDIO_U16LSB, "AUDIO_U16LSB" }, - { AUDIO_S16LSB, "AUDIO_S16LSB" }, - { AUDIO_U16MSB, "AUDIO_U16MSB" }, - { AUDIO_S16MSB, "AUDIO_S16MSB" } -}; - -static int formatToStringTableSize = - sizeof( formatToStringTable ) / sizeof( formatToStringTable[ 0 ] ); - -/* -=============== -print_audiospec -=============== -*/ -static void print_audiospec(const char *str, const SDL_AudioSpec *spec) -{ - int i; - char *fmt = NULL; - - Com_Printf("%s:\n", str); - - for( i = 0; i < formatToStringTableSize; i++ ) { - if( spec->format == formatToStringTable[ i ].enumFormat ) { - fmt = formatToStringTable[ i ].stringFormat; - } - } - - if( fmt ) { - Com_Printf( " Format: %s\n", fmt ); - } else { - Com_Printf( " Format: " S_COLOR_RED "UNKNOWN\n", fmt ); - } - - Com_Printf( " Freq: %d\n", (int) spec->freq ); - Com_Printf( " Samples: %d\n", (int) spec->samples ); - Com_Printf( " Channels: %d\n", (int) spec->channels ); -} - -/* -=============== -SNDDMA_Init -=============== -*/ -qboolean SNDDMA_Init(void) -{ - char drivername[128]; - SDL_AudioSpec desired; - SDL_AudioSpec obtained; - int tmp; - - if (snd_inited) - return qtrue; - - Com_Printf("Initializing SDL audio driver...\n"); - - if (!s_sdlBits) { - s_sdlBits = Cvar_Get("s_sdlBits", "16", CVAR_ARCHIVE); - s_sdlSpeed = Cvar_Get("s_sdlSpeed", "0", CVAR_ARCHIVE); - s_sdlChannels = Cvar_Get("s_sdlChannels", "2", CVAR_ARCHIVE); - s_sdlDevSamps = Cvar_Get("s_sdlDevSamps", "0", CVAR_ARCHIVE); - s_sdlMixSamps = Cvar_Get("s_sdlMixSamps", "0", CVAR_ARCHIVE); - } - - if (!SDL_WasInit(SDL_INIT_AUDIO)) - { - if (SDL_Init(SDL_INIT_AUDIO) == -1) - { - Com_Printf("SDL_Init(SDL_INIT_AUDIO) failed: %s\n", SDL_GetError()); - return qfalse; - } - } - - if (SDL_AudioDriverName(drivername, sizeof (drivername)) == NULL) - strcpy(drivername, "(UNKNOWN)"); - Com_Printf("SDL audio driver is \"%s\".\n", drivername); - - memset(&desired, '\0', sizeof (desired)); - memset(&obtained, '\0', sizeof (obtained)); - - tmp = ((int) s_sdlBits->value); - if ((tmp != 16) && (tmp != 8)) - tmp = 16; - - desired.freq = (int) s_sdlSpeed->value; - if(!desired.freq) desired.freq = 22050; - desired.format = ((tmp == 16) ? AUDIO_S16SYS : AUDIO_U8); - - // I dunno if this is the best idea, but I'll give it a try... - // should probably check a cvar for this... - if (s_sdlDevSamps->value) - desired.samples = s_sdlDevSamps->value; - else - { - // just pick a sane default. - if (desired.freq <= 11025) - desired.samples = 256; - else if (desired.freq <= 22050) - desired.samples = 512; - else if (desired.freq <= 44100) - desired.samples = 1024; - else - desired.samples = 2048; // (*shrug*) - } - - desired.channels = (int) s_sdlChannels->value; - desired.callback = sdl_audio_callback; - - if (SDL_OpenAudio(&desired, &obtained) == -1) - { - Com_Printf("SDL_OpenAudio() failed: %s\n", SDL_GetError()); - SDL_QuitSubSystem(SDL_INIT_AUDIO); - return qfalse; - } // if - - print_audiospec("SDL_AudioSpec", &obtained); - - // dma.samples needs to be big, or id's mixer will just refuse to - // work at all; we need to keep it significantly bigger than the - // amount of SDL callback samples, and just copy a little each time - // the callback runs. - // 32768 is what the OSS driver filled in here on my system. I don't - // know if it's a good value overall, but at least we know it's - // reasonable...this is why I let the user override. - tmp = s_sdlMixSamps->value; - if (!tmp) - tmp = (obtained.samples * obtained.channels) * 10; - - if (tmp & (tmp - 1)) // not a power of two? Seems to confuse something. - { - int val = 1; - while (val < tmp) - val <<= 1; - - tmp = val; - } - - dmapos = 0; - dma.samplebits = obtained.format & 0xFF; // first byte of format is bits. - dma.channels = obtained.channels; - dma.samples = tmp; - dma.submission_chunk = 1; - dma.speed = obtained.freq; - dmasize = (dma.samples * (dma.samplebits/8)); - dma.buffer = (byte*)calloc(1, dmasize); - - Com_Printf("Starting SDL audio callback...\n"); - SDL_PauseAudio(0); // start callback. - - Com_Printf("SDL audio initialized.\n"); - snd_inited = qtrue; - return qtrue; -} - -/* -=============== -SNDDMA_GetDMAPos -=============== -*/ -int SNDDMA_GetDMAPos(void) -{ - return dmapos; -} - -/* -=============== -SNDDMA_Shutdown -=============== -*/ -void SNDDMA_Shutdown(void) -{ - Com_Printf("Closing SDL audio device...\n"); - SDL_PauseAudio(1); - SDL_CloseAudio(); - SDL_QuitSubSystem(SDL_INIT_AUDIO); - free(dma.buffer); - dma.buffer = NULL; - dmapos = dmasize = 0; - snd_inited = qfalse; - Com_Printf("SDL audio device shut down.\n"); -} - -/* -=============== -SNDDMA_Submit - -Send sound to device if buffer isn't really the dma buffer -=============== -*/ -void SNDDMA_Submit(void) -{ - SDL_UnlockAudio(); -} - -/* -=============== -SNDDMA_BeginPainting -=============== -*/ -void SNDDMA_BeginPainting (void) -{ - SDL_LockAudio(); -} - -#endif // USE_SDL_SOUND diff --git a/code/unix/unix_glw.h b/code/unix/unix_glw.h deleted file mode 100644 index dc9b272..0000000 --- a/code/unix/unix_glw.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code 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 2 of the License, -or (at your option) any later version. - -Quake III Arena source code 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. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -#if !( defined __linux__ || defined __FreeBSD__ || defined __sun || defined MACOS_X ) -#error You should include this file only on Linux/FreeBSD/Solaris platforms -#endif - -#ifndef __GLW_LINUX_H__ -#define __GLW_LINUX_H__ - -typedef struct -{ - void *OpenGLLib; // instance of OpenGL library - - int desktopWidth, desktopHeight, desktopBPP; -} glwstate_t; - -extern glwstate_t glw_state; - -#endif diff --git a/code/unix/unix_shared.cpp b/code/unix/unix_shared.cpp deleted file mode 100644 index e537921..0000000 --- a/code/unix/unix_shared.cpp +++ /dev/null @@ -1,263 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code 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 2 of the License, -or (at your option) any later version. - -Quake III Arena source code 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. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../qcommon/q_shared.h" -#include "../qcommon/qcommon.h" - - -// Used to determine where to store user-specific files -static char homePath[MAX_OSPATH]; - - -int Sys_Milliseconds() -{ - static int sys_timeBase = 0; - - struct timeval tv; - gettimeofday( &tv, NULL ); - - if (!sys_timeBase) { - sys_timeBase = tv.tv_sec; - return tv.tv_usec/1000; - } - - return ((tv.tv_sec - sys_timeBase)*1000 + tv.tv_usec/1000); -} - - -void Sys_Mkdir( const char* path ) -{ - mkdir( path, 0777 ); -} - - -#define MAX_FOUND_FILES 0x1000 - -// bk001129 - new in 1.26 -static void Sys_ListFilteredFiles( const char *basedir, const char *subdirs, const char *filter, char **list, int *numfiles ) { - char search[MAX_OSPATH], newsubdirs[MAX_OSPATH]; - char filename[MAX_OSPATH]; - DIR *fdir; - struct dirent *d; - struct stat st; - - if ( *numfiles >= MAX_FOUND_FILES - 1 ) { - return; - } - - if (strlen(subdirs)) { - Com_sprintf( search, sizeof(search), "%s/%s", basedir, subdirs ); - } - else { - Com_sprintf( search, sizeof(search), "%s", basedir ); - } - - if ((fdir = opendir(search)) == NULL) { - return; - } - - while ((d = readdir(fdir)) != NULL) { - Com_sprintf(filename, sizeof(filename), "%s/%s", search, d->d_name); - if (stat(filename, &st) == -1) - continue; - - if (st.st_mode & S_IFDIR) { - if (Q_stricmp(d->d_name, ".") && Q_stricmp(d->d_name, "..")) { - if (strlen(subdirs)) { - Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s/%s", subdirs, d->d_name); - } - else { - Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s", d->d_name); - } - Sys_ListFilteredFiles( basedir, newsubdirs, filter, list, numfiles ); - } - } - if ( *numfiles >= MAX_FOUND_FILES - 1 ) { - break; - } - Com_sprintf( filename, sizeof(filename), "%s/%s", subdirs, d->d_name ); - if (!Com_FilterPath( filter, filename )) - continue; - list[ *numfiles ] = CopyString( filename ); - (*numfiles)++; - } - - closedir(fdir); -} - -char **Sys_ListFiles( const char *directory, const char *extension, const char *filter, int *numfiles, qboolean wantsubs ) -{ - struct dirent *d; - DIR *fdir; - qboolean dironly = wantsubs; - char search[MAX_OSPATH]; - int nfiles; - char **listCopy; - char *list[MAX_FOUND_FILES]; - int i; - struct stat st; - - int extLen; - - if (filter) { - - nfiles = 0; - Sys_ListFilteredFiles( directory, "", filter, list, &nfiles ); - - list[ nfiles ] = NULL; - *numfiles = nfiles; - - if (!nfiles) - return NULL; - - listCopy = (char**)Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ) ); - for ( i = 0 ; i < nfiles ; i++ ) { - listCopy[i] = list[i]; - } - listCopy[i] = NULL; - - return listCopy; - } - - if ( !extension) - extension = ""; - - if ( extension[0] == '/' && extension[1] == 0 ) { - extension = ""; - dironly = qtrue; - } - - extLen = strlen( extension ); - - // search - nfiles = 0; - - if ((fdir = opendir(directory)) == NULL) { - *numfiles = 0; - return NULL; - } - - while ((d = readdir(fdir)) != NULL) { - Com_sprintf(search, sizeof(search), "%s/%s", directory, d->d_name); - if (stat(search, &st) == -1) - continue; - if ((dironly && !(st.st_mode & S_IFDIR)) || - (!dironly && (st.st_mode & S_IFDIR))) - continue; - - if (*extension) { - if ( strlen( d->d_name ) < strlen( extension ) || - Q_stricmp( - d->d_name + strlen( d->d_name ) - strlen( extension ), - extension ) ) { - continue; // didn't match - } - } - - if ( nfiles == MAX_FOUND_FILES - 1 ) - break; - list[ nfiles ] = CopyString( d->d_name ); - nfiles++; - } - - list[ nfiles ] = NULL; - - closedir(fdir); - - // return a copy of the list - *numfiles = nfiles; - - if ( !nfiles ) { - return NULL; - } - - listCopy = (char**)Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ) ); - for ( i = 0 ; i < nfiles ; i++ ) { - listCopy[i] = list[i]; - } - listCopy[i] = NULL; - - return listCopy; -} - -void Sys_FreeFileList( char **list ) { - int i; - - if ( !list ) { - return; - } - - for ( i = 0 ; list[i] ; i++ ) { - Z_Free( list[i] ); - } - - Z_Free( list ); -} - - -const char* Sys_Cwd() -{ - static char cwd[MAX_OSPATH]; - - getcwd( cwd, sizeof( cwd ) - 1 ); - cwd[MAX_OSPATH-1] = 0; - - return cwd; -} - - -const char* Sys_DefaultHomePath() -{ - if (*homePath) - return homePath; - - const char* p; - if (p = getenv("HOME")) { - Q_strncpyz(homePath, p, sizeof(homePath)); -#ifdef MACOS_X - Q_strcat(homePath, sizeof(homePath), "/Library/Application Support/Quake3"); -#else - Q_strcat(homePath, sizeof(homePath), "/.q3a"); -#endif - if (mkdir(homePath, 0777)) { - if (errno != EEXIST) - Sys_Error("Unable to create directory \"%s\", error is %s(%d)\n", homePath, strerror(errno), errno); - } - return homePath; - } - - return ""; // assume current dir -} - - -void Sys_ShowConsole( int visLevel, qboolean quitOnClose ) -{ -} diff --git a/makefiles/gmake/cnq3-server.make b/makefiles/gmake/cnq3-server.make index 7a0005e..c1a2fac 100644 --- a/makefiles/gmake/cnq3-server.make +++ b/makefiles/gmake/cnq3-server.make @@ -135,6 +135,10 @@ all: $(TARGETDIR) $(OBJDIR) prebuild prelink $(TARGET) endif OBJECTS := \ + $(OBJDIR)/linux_main.o \ + $(OBJDIR)/linux_shared.o \ + $(OBJDIR)/linux_signals.o \ + $(OBJDIR)/linux_tty.o \ $(OBJDIR)/cm_load.o \ $(OBJDIR)/cm_patch.o \ $(OBJDIR)/cm_polylib.o \ @@ -168,9 +172,6 @@ OBJECTS := \ $(OBJDIR)/sv_net_chan.o \ $(OBJDIR)/sv_snapshot.o \ $(OBJDIR)/sv_world.o \ - $(OBJDIR)/linux_signals.o \ - $(OBJDIR)/unix_main.o \ - $(OBJDIR)/unix_shared.o \ RESOURCES := \ @@ -228,6 +229,18 @@ $(GCH): $(PCH) $(SILENT) $(CXX) -x c++-header $(ALL_CXXFLAGS) -o "$@" -MF "$(@:%.gch=%.d)" -c "$<" endif +$(OBJDIR)/linux_main.o: ../../code/linux/linux_main.cpp + @echo $(notdir $<) + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/linux_shared.o: ../../code/linux/linux_shared.cpp + @echo $(notdir $<) + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/linux_signals.o: ../../code/linux/linux_signals.cpp + @echo $(notdir $<) + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/linux_tty.o: ../../code/linux/linux_tty.cpp + @echo $(notdir $<) + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" $(OBJDIR)/cm_load.o: ../../code/qcommon/cm_load.cpp @echo $(notdir $<) $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" @@ -327,15 +340,6 @@ $(OBJDIR)/sv_snapshot.o: ../../code/server/sv_snapshot.cpp $(OBJDIR)/sv_world.o: ../../code/server/sv_world.cpp @echo $(notdir $<) $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" -$(OBJDIR)/linux_signals.o: ../../code/unix/linux_signals.cpp - @echo $(notdir $<) - $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" -$(OBJDIR)/unix_main.o: ../../code/unix/unix_main.cpp - @echo $(notdir $<) - $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" -$(OBJDIR)/unix_shared.o: ../../code/unix/unix_shared.cpp - @echo $(notdir $<) - $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" -include $(OBJECTS:%.o=%.d) ifneq (,$(PCH)) diff --git a/makefiles/gmake/cnq3.make b/makefiles/gmake/cnq3.make index 66bda8a..3b4037c 100644 --- a/makefiles/gmake/cnq3.make +++ b/makefiles/gmake/cnq3.make @@ -19,10 +19,10 @@ ifeq ($(config),debug_x32) INCLUDES += -I../../code/freetype/include FORCE_INCLUDE += ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES) - ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m32 -g -Wno-unused-parameter -Wno-write-strings -pthread -x c++ -std=c++98 + ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m32 -g -Wno-unused-parameter -Wno-write-strings -x c++ -std=c++98 ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES) - LIBS += ../../.build/debug_x32/libbotlib.a ../../.build/debug_x32/librenderer.a ../../.build/debug_x32/libfreetype.a ../../.build/debug_x32/liblibjpeg-turbo.a -ldl -lm -lX11 -lpthread + LIBS += ../../.build/debug_x32/libbotlib.a ../../.build/debug_x32/librenderer.a ../../.build/debug_x32/libfreetype.a ../../.build/debug_x32/liblibjpeg-turbo.a -ldl -lm -lSDL2 LDDEPS += ../../.build/debug_x32/libbotlib.a ../../.build/debug_x32/librenderer.a ../../.build/debug_x32/libfreetype.a ../../.build/debug_x32/liblibjpeg-turbo.a ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib32 -L../../.build/debug_x32 -m32 LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS) @@ -50,10 +50,10 @@ ifeq ($(config),debug_x64) INCLUDES += -I../../code/freetype/include FORCE_INCLUDE += ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES) - ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m64 -g -Wno-unused-parameter -Wno-write-strings -pthread -x c++ -std=c++98 + ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m64 -g -Wno-unused-parameter -Wno-write-strings -x c++ -std=c++98 ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES) - LIBS += ../../.build/debug_x64/libbotlib.a ../../.build/debug_x64/librenderer.a ../../.build/debug_x64/libfreetype.a ../../.build/debug_x64/liblibjpeg-turbo.a -ldl -lm -lX11 -lpthread + LIBS += ../../.build/debug_x64/libbotlib.a ../../.build/debug_x64/librenderer.a ../../.build/debug_x64/libfreetype.a ../../.build/debug_x64/liblibjpeg-turbo.a -ldl -lm -lSDL2 LDDEPS += ../../.build/debug_x64/libbotlib.a ../../.build/debug_x64/librenderer.a ../../.build/debug_x64/libfreetype.a ../../.build/debug_x64/liblibjpeg-turbo.a ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib64 -L../../.build/debug_x64 -m64 LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS) @@ -81,10 +81,10 @@ ifeq ($(config),release_x32) INCLUDES += -I../../code/freetype/include FORCE_INCLUDE += ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES) - ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m32 -fomit-frame-pointer -ffast-math -Os -g -msse2 -Wno-unused-parameter -Wno-write-strings -g1 -pthread -x c++ -std=c++98 + ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m32 -fomit-frame-pointer -ffast-math -Os -g -msse2 -Wno-unused-parameter -Wno-write-strings -g1 -x c++ -std=c++98 ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES) - LIBS += ../../.build/release_x32/libbotlib.a ../../.build/release_x32/librenderer.a ../../.build/release_x32/libfreetype.a ../../.build/release_x32/liblibjpeg-turbo.a -ldl -lm -lX11 -lpthread + LIBS += ../../.build/release_x32/libbotlib.a ../../.build/release_x32/librenderer.a ../../.build/release_x32/libfreetype.a ../../.build/release_x32/liblibjpeg-turbo.a -ldl -lm -lSDL2 LDDEPS += ../../.build/release_x32/libbotlib.a ../../.build/release_x32/librenderer.a ../../.build/release_x32/libfreetype.a ../../.build/release_x32/liblibjpeg-turbo.a ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib32 -L../../.build/release_x32 -m32 LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS) @@ -112,10 +112,10 @@ ifeq ($(config),release_x64) INCLUDES += -I../../code/freetype/include FORCE_INCLUDE += ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES) - ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m64 -fomit-frame-pointer -ffast-math -Os -g -msse2 -Wno-unused-parameter -Wno-write-strings -g1 -pthread -x c++ -std=c++98 + ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m64 -fomit-frame-pointer -ffast-math -Os -g -msse2 -Wno-unused-parameter -Wno-write-strings -g1 -x c++ -std=c++98 ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES) - LIBS += ../../.build/release_x64/libbotlib.a ../../.build/release_x64/librenderer.a ../../.build/release_x64/libfreetype.a ../../.build/release_x64/liblibjpeg-turbo.a -ldl -lm -lX11 -lpthread + LIBS += ../../.build/release_x64/libbotlib.a ../../.build/release_x64/librenderer.a ../../.build/release_x64/libfreetype.a ../../.build/release_x64/liblibjpeg-turbo.a -ldl -lm -lSDL2 LDDEPS += ../../.build/release_x64/libbotlib.a ../../.build/release_x64/librenderer.a ../../.build/release_x64/libfreetype.a ../../.build/release_x64/liblibjpeg-turbo.a ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib64 -L../../.build/release_x64 -m64 LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS) @@ -154,6 +154,14 @@ OBJECTS := \ $(OBJDIR)/snd_main.o \ $(OBJDIR)/snd_mem.o \ $(OBJDIR)/snd_mix.o \ + $(OBJDIR)/linux_main.o \ + $(OBJDIR)/linux_qgl.o \ + $(OBJDIR)/linux_shared.o \ + $(OBJDIR)/linux_signals.o \ + $(OBJDIR)/linux_tty.o \ + $(OBJDIR)/sdl_core.o \ + $(OBJDIR)/sdl_glimp.o \ + $(OBJDIR)/sdl_snd.o \ $(OBJDIR)/cm_load.o \ $(OBJDIR)/cm_patch.o \ $(OBJDIR)/cm_polylib.o \ @@ -187,13 +195,6 @@ OBJECTS := \ $(OBJDIR)/sv_net_chan.o \ $(OBJDIR)/sv_snapshot.o \ $(OBJDIR)/sv_world.o \ - $(OBJDIR)/linux_glimp.o \ - $(OBJDIR)/linux_joystick.o \ - $(OBJDIR)/linux_qgl.o \ - $(OBJDIR)/linux_signals.o \ - $(OBJDIR)/linux_snd.o \ - $(OBJDIR)/unix_main.o \ - $(OBJDIR)/unix_shared.o \ RESOURCES := \ @@ -308,6 +309,30 @@ $(OBJDIR)/snd_mem.o: ../../code/client/snd_mem.cpp $(OBJDIR)/snd_mix.o: ../../code/client/snd_mix.cpp @echo $(notdir $<) $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/linux_main.o: ../../code/linux/linux_main.cpp + @echo $(notdir $<) + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/linux_qgl.o: ../../code/linux/linux_qgl.c + @echo $(notdir $<) + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/linux_shared.o: ../../code/linux/linux_shared.cpp + @echo $(notdir $<) + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/linux_signals.o: ../../code/linux/linux_signals.cpp + @echo $(notdir $<) + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/linux_tty.o: ../../code/linux/linux_tty.cpp + @echo $(notdir $<) + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/sdl_core.o: ../../code/linux/sdl_core.cpp + @echo $(notdir $<) + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/sdl_glimp.o: ../../code/linux/sdl_glimp.cpp + @echo $(notdir $<) + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/sdl_snd.o: ../../code/linux/sdl_snd.cpp + @echo $(notdir $<) + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" $(OBJDIR)/cm_load.o: ../../code/qcommon/cm_load.cpp @echo $(notdir $<) $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" @@ -407,27 +432,6 @@ $(OBJDIR)/sv_snapshot.o: ../../code/server/sv_snapshot.cpp $(OBJDIR)/sv_world.o: ../../code/server/sv_world.cpp @echo $(notdir $<) $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" -$(OBJDIR)/linux_glimp.o: ../../code/unix/linux_glimp.cpp - @echo $(notdir $<) - $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" -$(OBJDIR)/linux_joystick.o: ../../code/unix/linux_joystick.c - @echo $(notdir $<) - $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" -$(OBJDIR)/linux_qgl.o: ../../code/unix/linux_qgl.c - @echo $(notdir $<) - $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" -$(OBJDIR)/linux_signals.o: ../../code/unix/linux_signals.cpp - @echo $(notdir $<) - $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" -$(OBJDIR)/linux_snd.o: ../../code/unix/linux_snd.c - @echo $(notdir $<) - $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" -$(OBJDIR)/unix_main.o: ../../code/unix/unix_main.cpp - @echo $(notdir $<) - $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" -$(OBJDIR)/unix_shared.o: ../../code/unix/unix_shared.cpp - @echo $(notdir $<) - $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" -include $(OBJECTS:%.o=%.d) ifneq (,$(PCH)) diff --git a/makefiles/premake5.lua b/makefiles/premake5.lua index 3429c9e..294f678 100644 --- a/makefiles/premake5.lua +++ b/makefiles/premake5.lua @@ -319,11 +319,12 @@ local function ApplyExeProjectSettings(exeName, server) "win32/win_syscon.cpp" } - local server_sources_unix = + local server_sources_linux = { - "unix/unix_main.cpp", - "unix/unix_shared.cpp", - "unix/linux_signals.cpp" + "linux/linux_main.cpp", + "linux/linux_shared.cpp", + "linux/linux_signals.cpp", + "linux/linux_tty.cpp" } local client_sources = @@ -395,15 +396,16 @@ local function ApplyExeProjectSettings(exeName, server) "win32/win_qgl.c" } - local client_sources_unix = + local client_sources_linux = { - "unix/unix_main.cpp", - "unix/unix_shared.cpp", - "unix/linux_joystick.c", - "unix/linux_signals.cpp", - "unix/linux_qgl.c", - "unix/linux_snd.c", - "unix/linux_glimp.cpp" + "linux/linux_main.cpp", + "linux/linux_qgl.c", + "linux/linux_shared.cpp", + "linux/linux_signals.cpp", + "linux/linux_tty.cpp", + "linux/sdl_core.cpp", + "linux/sdl_glimp.cpp", + "linux/sdl_snd.cpp" } AddHeaders("botlib") @@ -435,9 +437,9 @@ local function ApplyExeProjectSettings(exeName, server) filter { "system:not windows" } if (server == 1) then - AddSourcesFromArray(".", server_sources_unix) + AddSourcesFromArray(".", server_sources_linux) else - AddSourcesFromArray(".", client_sources_unix) + AddSourcesFromArray(".", client_sources_linux) end -- create git info header @@ -474,8 +476,7 @@ local function ApplyExeProjectSettings(exeName, server) filter "system:not windows" links { "dl", "m" } if (server == 0) then - buildoptions { "-pthread" } - links { "X11", "pthread" } + links { "SDL2" } end -- RC will compile the .rc into a .res