From 5a85e81685300e2299dabfeb25d513b99df471be Mon Sep 17 00:00:00 2001 From: PaweÅ‚ Redman Date: Fri, 6 Sep 2013 22:40:51 +0200 Subject: Initial commit --- src/sys/con_log.c | 130 ++++++++ src/sys/con_passive.c | 69 ++++ src/sys/con_tty.c | 478 ++++++++++++++++++++++++++++ src/sys/con_win32.c | 359 +++++++++++++++++++++ src/sys/sys_loadlib.h | 50 +++ src/sys/sys_local.h | 61 ++++ src/sys/sys_main.c | 605 +++++++++++++++++++++++++++++++++++ src/sys/sys_osx.m | 141 +++++++++ src/sys/sys_unix.c | 818 ++++++++++++++++++++++++++++++++++++++++++++++++ src/sys/sys_win32.c | 811 +++++++++++++++++++++++++++++++++++++++++++++++ src/sys/win_resource.h | 45 +++ src/sys/win_resource.rc | 72 +++++ 12 files changed, 3639 insertions(+) create mode 100644 src/sys/con_log.c create mode 100644 src/sys/con_passive.c create mode 100644 src/sys/con_tty.c create mode 100644 src/sys/con_win32.c create mode 100644 src/sys/sys_loadlib.h create mode 100644 src/sys/sys_local.h create mode 100644 src/sys/sys_main.c create mode 100644 src/sys/sys_osx.m create mode 100644 src/sys/sys_unix.c create mode 100644 src/sys/sys_win32.c create mode 100644 src/sys/win_resource.h create mode 100644 src/sys/win_resource.rc (limited to 'src/sys') diff --git a/src/sys/con_log.c b/src/sys/con_log.c new file mode 100644 index 0000000..a54117c --- /dev/null +++ b/src/sys/con_log.c @@ -0,0 +1,130 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous 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. + +Tremulous 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 Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "../qcommon/q_shared.h" +#include "../qcommon/qcommon.h" +#include "sys_local.h" + +#define MAX_LOG 32768 + +static char consoleLog[ MAX_LOG ]; +static unsigned int writePos = 0; +static unsigned int readPos = 0; + +/* +================== +CON_LogSize +================== +*/ +unsigned int CON_LogSize( void ) +{ + if( readPos <= writePos ) + return writePos - readPos; + else + return writePos + MAX_LOG - readPos; +} + +/* +================== +CON_LogFree +================== +*/ +static unsigned int CON_LogFree( void ) +{ + return MAX_LOG - CON_LogSize( ) - 1; +} + +/* +================== +CON_LogWrite +================== +*/ +unsigned int CON_LogWrite( const char *in ) +{ + unsigned int length = strlen( in ); + unsigned int firstChunk; + unsigned int secondChunk; + + while( CON_LogFree( ) < length && CON_LogSize( ) > 0 ) + { + // Free enough space + while( consoleLog[ readPos ] != '\n' && CON_LogSize( ) > 1 ) + readPos = ( readPos + 1 ) % MAX_LOG; + + // Skip past the '\n' + readPos = ( readPos + 1 ) % MAX_LOG; + } + + if( CON_LogFree( ) < length ) + return 0; + + if( writePos + length > MAX_LOG ) + { + firstChunk = MAX_LOG - writePos; + secondChunk = length - firstChunk; + } + else + { + firstChunk = length; + secondChunk = 0; + } + + Com_Memcpy( consoleLog + writePos, in, firstChunk ); + Com_Memcpy( consoleLog, in + firstChunk, secondChunk ); + + writePos = ( writePos + length ) % MAX_LOG; + + return length; +} + +/* +================== +CON_LogRead +================== +*/ +unsigned int CON_LogRead( char *out, unsigned int outSize ) +{ + unsigned int firstChunk; + unsigned int secondChunk; + + if( CON_LogSize( ) < outSize ) + outSize = CON_LogSize( ); + + if( readPos + outSize > MAX_LOG ) + { + firstChunk = MAX_LOG - readPos; + secondChunk = outSize - firstChunk; + } + else + { + firstChunk = outSize; + secondChunk = 0; + } + + Com_Memcpy( out, consoleLog + readPos, firstChunk ); + Com_Memcpy( out + firstChunk, out, secondChunk ); + + readPos = ( readPos + outSize ) % MAX_LOG; + + return outSize; +} diff --git a/src/sys/con_passive.c b/src/sys/con_passive.c new file mode 100644 index 0000000..1650b10 --- /dev/null +++ b/src/sys/con_passive.c @@ -0,0 +1,69 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous 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. + +Tremulous 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 Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "../qcommon/q_shared.h" +#include "../qcommon/qcommon.h" +#include "sys_local.h" + +#include + +/* +================== +CON_Shutdown +================== +*/ +void CON_Shutdown( void ) +{ +} + +/* +================== +CON_Init +================== +*/ +void CON_Init( void ) +{ +} + +/* +================== +CON_Input +================== +*/ +char *CON_Input( void ) +{ + return NULL; +} + +/* +================== +CON_Print +================== +*/ +void CON_Print( const char *msg ) +{ + if( com_ansiColor && com_ansiColor->integer ) + Sys_AnsiColorPrint( msg ); + else + fputs( msg, stderr ); +} diff --git a/src/sys/con_tty.c b/src/sys/con_tty.c new file mode 100644 index 0000000..e4b2ad5 --- /dev/null +++ b/src/sys/con_tty.c @@ -0,0 +1,478 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous 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. + +Tremulous 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 Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + + +#include "../qcommon/q_shared.h" +#include "../qcommon/qcommon.h" +#include "sys_local.h" + +#include +#include +#include +#include +#include + +/* +============================================================= +tty console routines + +NOTE: if the user is editing a line when something gets printed to the early +console then it won't look good so we provide CON_Hide and CON_Show to be +called before and after a stdout or stderr output +============================================================= +*/ + +extern qboolean stdinIsATTY; +static qboolean stdin_active; +// general flag to tell about tty console mode +static qboolean ttycon_on = qfalse; +static int ttycon_hide = 0; + +// some key codes that the terminal may be using, initialised on start up +static int TTY_erase; +static int TTY_eof; + +static struct termios TTY_tc; + +static field_t TTY_con; + +// This is somewhat of aduplicate of the graphical console history +// but it's safer more modular to have our own here +#define CON_HISTORY 32 +static field_t ttyEditLines[ CON_HISTORY ]; +static int hist_current = -1, hist_count = 0; + +/* +================== +CON_FlushIn + +Flush stdin, I suspect some terminals are sending a LOT of shit +FIXME relevant? +================== +*/ +static void CON_FlushIn( void ) +{ + char key; + while (read(STDIN_FILENO, &key, 1)!=-1); +} + +/* +================== +CON_Back + +Output a backspace + +NOTE: it seems on some terminals just sending '\b' is not enough so instead we +send "\b \b" +(FIXME there may be a way to find out if '\b' alone would work though) +================== +*/ +static void CON_Back( void ) +{ + char key; + size_t size; + + key = '\b'; + size = write(STDOUT_FILENO, &key, 1); + key = ' '; + size = write(STDOUT_FILENO, &key, 1); + key = '\b'; + size = write(STDOUT_FILENO, &key, 1); +} + +/* +================== +CON_Hide + +Clear the display of the line currently edited +bring cursor back to beginning of line +================== +*/ +static void CON_Hide( void ) +{ + if( ttycon_on ) + { + int i; + if (ttycon_hide) + { + ttycon_hide++; + return; + } + if (TTY_con.cursor>0) + { + for (i=0; i0); + ttycon_hide--; + if (ttycon_hide == 0) + { + size_t size; + size = write(STDOUT_FILENO, "]", 1); + if (TTY_con.cursor) + { + for (i=0; i= 0); + assert(hist_current >= -1); + assert(hist_current <= hist_count); + // make some room + for (i=CON_HISTORY-1; i>0; i--) + { + ttyEditLines[i] = ttyEditLines[i-1]; + } + ttyEditLines[0] = *field; + if (hist_count= 0); + assert(hist_current >= -1); + assert(hist_current <= hist_count); + hist_prev = hist_current + 1; + if (hist_prev >= hist_count) + { + return NULL; + } + hist_current++; + return &(ttyEditLines[hist_current]); +} + +/* +================== +Hist_Next +================== +*/ +field_t *Hist_Next( void ) +{ + assert(hist_count <= CON_HISTORY); + assert(hist_count >= 0); + assert(hist_current >= -1); + assert(hist_current <= hist_count); + if (hist_current >= 0) + { + hist_current--; + } + if (hist_current == -1) + { + return NULL; + } + return &(ttyEditLines[hist_current]); +} + +/* +================== +CON_SigCont +Reinitialize console input after receiving SIGCONT, as on Linux the terminal seems to lose all +set attributes if user did CTRL+Z and then does fg again. +================== +*/ + +void CON_SigCont(int signum) +{ + CON_Init(); +} + +/* +================== +CON_Init + +Initialize the console input (tty mode if possible) +================== +*/ +void CON_Init( void ) +{ + struct termios tc; + + // If the process is backgrounded (running non interactively) + // then SIGTTIN or SIGTOU is emitted, if not caught, turns into a SIGSTP + signal(SIGTTIN, SIG_IGN); + signal(SIGTTOU, SIG_IGN); + + // If SIGCONT is received, reinitialize console + signal(SIGCONT, CON_SigCont); + + // Make stdin reads non-blocking + fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL, 0) | O_NONBLOCK ); + + if (!stdinIsATTY) + { + Com_Printf("tty console mode disabled\n"); + ttycon_on = qfalse; + stdin_active = qtrue; + return; + } + + Field_Clear(&TTY_con); + tcgetattr (STDIN_FILENO, &TTY_tc); + TTY_erase = TTY_tc.c_cc[VERASE]; + TTY_eof = TTY_tc.c_cc[VEOF]; + tc = TTY_tc; + + /* + ECHO: don't echo input characters + ICANON: enable canonical mode. This enables the special + characters EOF, EOL, EOL2, ERASE, KILL, REPRINT, + STATUS, and WERASE, and buffers by lines. + ISIG: when any of the characters INTR, QUIT, SUSP, or + DSUSP are received, generate the corresponding sig­ + nal + */ + tc.c_lflag &= ~(ECHO | ICANON); + + /* + ISTRIP strip off bit 8 + INPCK enable input parity checking + */ + tc.c_iflag &= ~(ISTRIP | INPCK); + tc.c_cc[VMIN] = 1; + tc.c_cc[VTIME] = 0; + tcsetattr (STDIN_FILENO, TCSADRAIN, &tc); + ttycon_on = qtrue; +} + +/* +================== +CON_Input +================== +*/ +char *CON_Input( void ) +{ + // we use this when sending back commands + static char text[MAX_EDIT_LINE]; + int avail; + char key; + field_t *history; + size_t size; + + if(ttycon_on) + { + avail = read(STDIN_FILENO, &key, 1); + if (avail != -1) + { + // we have something + // backspace? + // NOTE TTimo testing a lot of values .. seems it's the only way to get it to work everywhere + if ((key == TTY_erase) || (key == 127) || (key == 8)) + { + if (TTY_con.cursor > 0) + { + TTY_con.cursor--; + TTY_con.buffer[TTY_con.cursor] = '\0'; + CON_Back(); + } + return NULL; + } + // check if this is a control char + if ((key) && (key) < ' ') + { + if (key == '\n') + { + // push it in history + Hist_Add(&TTY_con); + Q_strncpyz(text, TTY_con.buffer, sizeof(text)); + Field_Clear(&TTY_con); + key = '\n'; + size = write(1, &key, 1); + size = write( 1, "]", 1 ); + return text; + } + if (key == '\t') + { + CON_Hide(); + Field_AutoComplete( &TTY_con ); + CON_Show(); + return NULL; + } + avail = read(STDIN_FILENO, &key, 1); + if (avail != -1) + { + // VT 100 keys + if (key == '[' || key == 'O') + { + avail = read(STDIN_FILENO, &key, 1); + if (avail != -1) + { + switch (key) + { + case 'A': + history = Hist_Prev(); + if (history) + { + CON_Hide(); + TTY_con = *history; + CON_Show(); + } + CON_FlushIn(); + return NULL; + break; + case 'B': + history = Hist_Next(); + CON_Hide(); + if (history) + { + TTY_con = *history; + } else + { + Field_Clear(&TTY_con); + } + CON_Show(); + CON_FlushIn(); + return NULL; + break; + case 'C': + return NULL; + case 'D': + return NULL; + } + } + } + } + Com_DPrintf("droping ISCTL sequence: %d, TTY_erase: %d\n", key, TTY_erase); + CON_FlushIn(); + return NULL; + } + if (TTY_con.cursor >= sizeof(text) - 1) + return NULL; + // push regular character + TTY_con.buffer[TTY_con.cursor] = key; + TTY_con.cursor++; + // print the current line (this is differential) + size = write(STDOUT_FILENO, &key, 1); + } + + return NULL; + } + else if (stdin_active) + { + int len; + fd_set fdset; + struct timeval timeout; + + FD_ZERO(&fdset); + FD_SET(STDIN_FILENO, &fdset); // stdin + timeout.tv_sec = 0; + timeout.tv_usec = 0; + if(select (STDIN_FILENO + 1, &fdset, NULL, NULL, &timeout) == -1 || !FD_ISSET(STDIN_FILENO, &fdset)) + return NULL; + + len = read(STDIN_FILENO, text, sizeof(text)); + if (len == 0) + { // eof! + stdin_active = qfalse; + return NULL; + } + + if (len < 1) + return NULL; + text[len-1] = 0; // rip off the /n and terminate + + return text; + } + return NULL; +} + +/* +================== +CON_Print +================== +*/ +void CON_Print( const char *msg ) +{ + CON_Hide( ); + + if( com_ansiColor && com_ansiColor->integer ) + Sys_AnsiColorPrint( msg ); + else + fputs( msg, stderr ); + + CON_Show( ); +} diff --git a/src/sys/con_win32.c b/src/sys/con_win32.c new file mode 100644 index 0000000..987c553 --- /dev/null +++ b/src/sys/con_win32.c @@ -0,0 +1,359 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous 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. + +Tremulous 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 Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "../qcommon/q_shared.h" +#include "../qcommon/qcommon.h" +#include "sys_local.h" +#include "windows.h" + +#define QCONSOLE_HISTORY 32 + +static WORD qconsole_attrib; + +// saved console status +static DWORD qconsole_orig_mode; +static CONSOLE_CURSOR_INFO qconsole_orig_cursorinfo; + +// cmd history +static char qconsole_history[ QCONSOLE_HISTORY ][ MAX_EDIT_LINE ]; +static int qconsole_history_pos = -1; +static int qconsole_history_oldest = 0; + +// current edit buffer +static char qconsole_line[ MAX_EDIT_LINE ]; +static int qconsole_linelen = 0; + +static HANDLE qconsole_hout; +static HANDLE qconsole_hin; + +/* +================== +CON_CtrlHandler + +The Windows Console doesn't use signals for terminating the application +with Ctrl-C, logging off, window closing, etc. Instead it uses a special +handler routine. Fortunately, the values for Ctrl signals don't seem to +overlap with true signal codes that Windows provides, so calling +Sys_SigHandler() with those numbers should be safe for generating unique +shutdown messages. +================== +*/ +static BOOL WINAPI CON_CtrlHandler( DWORD sig ) +{ + Sys_SigHandler( sig ); + return TRUE; +} + +/* +================== +CON_HistAdd +================== +*/ +static void CON_HistAdd( void ) +{ + Q_strncpyz( qconsole_history[ qconsole_history_oldest ], qconsole_line, + sizeof( qconsole_history[ qconsole_history_oldest ] ) ); + + if( qconsole_history_oldest >= QCONSOLE_HISTORY - 1 ) + qconsole_history_oldest = 0; + else + qconsole_history_oldest++; + + qconsole_history_pos = qconsole_history_oldest; +} + +/* +================== +CON_HistPrev +================== +*/ +static void CON_HistPrev( void ) +{ + int pos; + + pos = ( qconsole_history_pos < 1 ) ? + ( QCONSOLE_HISTORY - 1 ) : ( qconsole_history_pos - 1 ); + + // don' t allow looping through history + if( pos == qconsole_history_oldest ) + return; + + qconsole_history_pos = pos; + Q_strncpyz( qconsole_line, qconsole_history[ qconsole_history_pos ], + sizeof( qconsole_line ) ); + qconsole_linelen = strlen( qconsole_line ); +} + +/* +================== +CON_HistNext +================== +*/ +static void CON_HistNext( void ) +{ + int pos; + + pos = ( qconsole_history_pos >= QCONSOLE_HISTORY - 1 ) ? + 0 : ( qconsole_history_pos + 1 ); + + // clear the edit buffer if they try to advance to a future command + if( pos == qconsole_history_oldest ) + { + qconsole_line[ 0 ] = '\0'; + qconsole_linelen = 0; + return; + } + + qconsole_history_pos = pos; + Q_strncpyz( qconsole_line, qconsole_history[ qconsole_history_pos ], + sizeof( qconsole_line ) ); + qconsole_linelen = strlen( qconsole_line ); +} + + +/* +================== +CON_Show +================== +*/ +static void CON_Show( void ) +{ + CONSOLE_SCREEN_BUFFER_INFO binfo; + COORD writeSize = { MAX_EDIT_LINE, 1 }; + COORD writePos = { 0, 0 }; + SMALL_RECT writeArea = { 0, 0, 0, 0 }; + int i; + CHAR_INFO line[ MAX_EDIT_LINE ]; + + GetConsoleScreenBufferInfo( qconsole_hout, &binfo ); + + // if we're in the middle of printf, don't bother writing the buffer + if( binfo.dwCursorPosition.X != 0 ) + return; + + writeArea.Left = 0; + writeArea.Top = binfo.dwCursorPosition.Y; + writeArea.Bottom = binfo.dwCursorPosition.Y; + writeArea.Right = MAX_EDIT_LINE; + + // build a space-padded CHAR_INFO array + for( i = 0; i < MAX_EDIT_LINE; i++ ) + { + if( i < qconsole_linelen ) + line[ i ].Char.AsciiChar = qconsole_line[ i ]; + else + line[ i ].Char.AsciiChar = ' '; + + line[ i ].Attributes = qconsole_attrib; + } + + if( qconsole_linelen > binfo.srWindow.Right ) + { + WriteConsoleOutput( qconsole_hout, + line + (qconsole_linelen - binfo.srWindow.Right ), + writeSize, writePos, &writeArea ); + } + else + { + WriteConsoleOutput( qconsole_hout, line, writeSize, + writePos, &writeArea ); + } +} + +/* +================== +CON_Shutdown +================== +*/ +void CON_Shutdown( void ) +{ + SetConsoleMode( qconsole_hin, qconsole_orig_mode ); + SetConsoleCursorInfo( qconsole_hout, &qconsole_orig_cursorinfo ); + CloseHandle( qconsole_hout ); + CloseHandle( qconsole_hin ); +} + +/* +================== +CON_Init +================== +*/ +void CON_Init( void ) +{ + CONSOLE_CURSOR_INFO curs; + CONSOLE_SCREEN_BUFFER_INFO info; + int i; + + // handle Ctrl-C or other console termination + SetConsoleCtrlHandler( CON_CtrlHandler, TRUE ); + + qconsole_hin = GetStdHandle( STD_INPUT_HANDLE ); + if( qconsole_hin == INVALID_HANDLE_VALUE ) + return; + + qconsole_hout = GetStdHandle( STD_OUTPUT_HANDLE ); + if( qconsole_hout == INVALID_HANDLE_VALUE ) + return; + + GetConsoleMode( qconsole_hin, &qconsole_orig_mode ); + + // allow mouse wheel scrolling + SetConsoleMode( qconsole_hin, + qconsole_orig_mode & ~ENABLE_MOUSE_INPUT ); + + FlushConsoleInputBuffer( qconsole_hin ); + + GetConsoleScreenBufferInfo( qconsole_hout, &info ); + qconsole_attrib = info.wAttributes; + + SetConsoleTitle("Tremulous Dedicated Server Console"); + + // make cursor invisible + GetConsoleCursorInfo( qconsole_hout, &qconsole_orig_cursorinfo ); + curs.dwSize = 1; + curs.bVisible = FALSE; + SetConsoleCursorInfo( qconsole_hout, &curs ); + + // initialize history + for( i = 0; i < QCONSOLE_HISTORY; i++ ) + qconsole_history[ i ][ 0 ] = '\0'; +} + +/* +================== +CON_Input +================== +*/ +char *CON_Input( void ) +{ + INPUT_RECORD buff[ MAX_EDIT_LINE ]; + DWORD count = 0, events = 0; + WORD key = 0; + int i; + int newlinepos = -1; + + if( !GetNumberOfConsoleInputEvents( qconsole_hin, &events ) ) + return NULL; + + if( events < 1 ) + return NULL; + + // if we have overflowed, start dropping oldest input events + if( events >= MAX_EDIT_LINE ) + { + ReadConsoleInput( qconsole_hin, buff, 1, &events ); + return NULL; + } + + if( !ReadConsoleInput( qconsole_hin, buff, events, &count ) ) + return NULL; + + FlushConsoleInputBuffer( qconsole_hin ); + + for( i = 0; i < count; i++ ) + { + if( buff[ i ].EventType != KEY_EVENT ) + continue; + if( !buff[ i ].Event.KeyEvent.bKeyDown ) + continue; + + key = buff[ i ].Event.KeyEvent.wVirtualKeyCode; + + if( key == VK_RETURN ) + { + newlinepos = i; + break; + } + else if( key == VK_UP ) + { + CON_HistPrev(); + break; + } + else if( key == VK_DOWN ) + { + CON_HistNext(); + break; + } + else if( key == VK_TAB ) + { + field_t f; + + Q_strncpyz( f.buffer, qconsole_line, + sizeof( f.buffer ) ); + Field_AutoComplete( &f ); + Q_strncpyz( qconsole_line, f.buffer, + sizeof( qconsole_line ) ); + qconsole_linelen = strlen( qconsole_line ); + break; + } + + if( qconsole_linelen < sizeof( qconsole_line ) - 1 ) + { + char c = buff[ i ].Event.KeyEvent.uChar.AsciiChar; + + if( key == VK_BACK ) + { + int pos = ( qconsole_linelen > 0 ) ? + qconsole_linelen - 1 : 0; + + qconsole_line[ pos ] = '\0'; + qconsole_linelen = pos; + } + else if( c ) + { + qconsole_line[ qconsole_linelen++ ] = c; + qconsole_line[ qconsole_linelen ] = '\0'; + } + } + } + + CON_Show(); + + if( newlinepos < 0) + return NULL; + + if( !qconsole_linelen ) + { + Com_Printf( "\n" ); + return NULL; + } + + CON_HistAdd(); + Com_Printf( "%s\n", qconsole_line ); + + qconsole_linelen = 0; + + return qconsole_line; +} + +/* +================== +CON_Print +================== +*/ +void CON_Print( const char *msg ) +{ + fputs( msg, stderr ); + + CON_Show( ); +} diff --git a/src/sys/sys_loadlib.h b/src/sys/sys_loadlib.h new file mode 100644 index 0000000..02bb762 --- /dev/null +++ b/src/sys/sys_loadlib.h @@ -0,0 +1,50 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous 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. + +Tremulous 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 Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#ifdef DEDICATED +# ifdef _WIN32 +# include +# define Sys_LoadLibrary(f) (void*)LoadLibrary(f) +# define Sys_UnloadLibrary(h) FreeLibrary((HMODULE)h) +# define Sys_LoadFunction(h,fn) (void*)GetProcAddress((HMODULE)h,fn) +# define Sys_LibraryError() "unknown" +# else +# include +# define Sys_LoadLibrary(f) dlopen(f,RTLD_NOW) +# define Sys_UnloadLibrary(h) dlclose(h) +# define Sys_LoadFunction(h,fn) dlsym(h,fn) +# define Sys_LibraryError() dlerror() +# endif +#else +# ifdef USE_LOCAL_HEADERS +# include "SDL.h" +# include "SDL_loadso.h" +# else +# include +# include +# endif +# define Sys_LoadLibrary(f) SDL_LoadObject(f) +# define Sys_UnloadLibrary(h) SDL_UnloadObject(h) +# define Sys_LoadFunction(h,fn) SDL_LoadFunction(h,fn) +# define Sys_LibraryError() SDL_GetError() +#endif diff --git a/src/sys/sys_local.h b/src/sys/sys_local.h new file mode 100644 index 0000000..8f99e7c --- /dev/null +++ b/src/sys/sys_local.h @@ -0,0 +1,61 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous 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. + +Tremulous 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 Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "../qcommon/q_shared.h" +#include "../qcommon/qcommon.h" + +// Require a minimum version of SDL +#define MINSDL_MAJOR 1 +#define MINSDL_MINOR 2 +#define MINSDL_PATCH 10 + +// Input subsystem +void IN_Init( void ); +void IN_Frame( void ); +void IN_Shutdown( void ); +void IN_Restart( void ); + +// Console +void CON_Shutdown( void ); +void CON_Init( void ); +char *CON_Input( void ); +void CON_Print( const char *message ); + +unsigned int CON_LogSize( void ); +unsigned int CON_LogWrite( const char *in ); +unsigned int CON_LogRead( char *out, unsigned int outSize ); + +#ifdef MACOS_X +char *Sys_StripAppBundle( char *pwd ); +#endif + +void Sys_GLimpSafeInit( void ); +void Sys_GLimpInit( void ); +void Sys_PlatformInit( void ); +void Sys_PlatformExit( void ); +void Sys_SigHandler( int signal ); +void Sys_ErrorDialog( const char *error ); +void Sys_AnsiColorPrint( const char *msg ); + +int Sys_PID( void ); +qboolean Sys_PIDIsRunning( int pid ); diff --git a/src/sys/sys_main.c b/src/sys/sys_main.c new file mode 100644 index 0000000..46a795e --- /dev/null +++ b/src/sys/sys_main.c @@ -0,0 +1,605 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous 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. + +Tremulous 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 Tremulous; 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 + +#ifndef DEDICATED +#ifdef USE_LOCAL_HEADERS +# include "SDL.h" +# include "SDL_cpuinfo.h" +#else +# include +# include +#endif +#endif + +#include "sys_local.h" +#include "sys_loadlib.h" + +#include "../qcommon/q_shared.h" +#include "../qcommon/qcommon.h" + +static char binaryPath[ MAX_OSPATH ] = { 0 }; +static char installPath[ MAX_OSPATH ] = { 0 }; + +/* +================= +Sys_SetBinaryPath +================= +*/ +void Sys_SetBinaryPath(const char *path) +{ + Q_strncpyz(binaryPath, path, sizeof(binaryPath)); +} + +/* +================= +Sys_BinaryPath +================= +*/ +char *Sys_BinaryPath(void) +{ + return binaryPath; +} + +/* +================= +Sys_SetDefaultInstallPath +================= +*/ +void Sys_SetDefaultInstallPath(const char *path) +{ + Q_strncpyz(installPath, path, sizeof(installPath)); +} + +/* +================= +Sys_DefaultInstallPath +================= +*/ +char *Sys_DefaultInstallPath(void) +{ + if (*installPath) + return installPath; + else + return Sys_Cwd(); +} + +/* +================= +Sys_DefaultAppPath +================= +*/ +char *Sys_DefaultAppPath(void) +{ + return Sys_BinaryPath(); +} + +/* +================= +Sys_In_Restart_f + +Restart the input subsystem +================= +*/ +void Sys_In_Restart_f( void ) +{ + IN_Restart( ); +} + +/* +================= +Sys_ConsoleInput + +Handle new console input +================= +*/ +char *Sys_ConsoleInput(void) +{ + return CON_Input( ); +} + +#ifdef DEDICATED +# define PID_FILENAME PRODUCT_NAME "_server.pid" +#else +# define PID_FILENAME PRODUCT_NAME ".pid" +#endif + +/* +================= +Sys_PIDFileName +================= +*/ +static char *Sys_PIDFileName( void ) +{ + return va( "%s/%s", Sys_TempPath( ), PID_FILENAME ); +} + +/* +================= +Sys_WritePIDFile + +Return qtrue if there is an existing stale PID file +================= +*/ +qboolean Sys_WritePIDFile( void ) +{ + char *pidFile = Sys_PIDFileName( ); + FILE *f; + qboolean stale = qfalse; + + // First, check if the pid file is already there + if( ( f = fopen( pidFile, "r" ) ) != NULL ) + { + char pidBuffer[ 64 ] = { 0 }; + int pid; + + pid = fread( pidBuffer, sizeof( char ), sizeof( pidBuffer ) - 1, f ); + fclose( f ); + + if(pid > 0) + { + pid = atoi( pidBuffer ); + if( !Sys_PIDIsRunning( pid ) ) + stale = qtrue; + } + else + stale = qtrue; + } + + if( ( f = fopen( pidFile, "w" ) ) != NULL ) + { + fprintf( f, "%d", Sys_PID( ) ); + fclose( f ); + } + else + Com_Printf( S_COLOR_YELLOW "Couldn't write %s.\n", pidFile ); + + return stale; +} + +/* +================= +Sys_Exit + +Single exit point (regular exit or in case of error) +================= +*/ +static void Sys_Exit( int exitCode ) +{ + CON_Shutdown( ); + +#ifndef DEDICATED + SDL_Quit( ); +#endif + + if( exitCode < 2 ) + { + // Normal exit + remove( Sys_PIDFileName( ) ); + } + + Sys_PlatformExit( ); + + exit( exitCode ); +} + +/* +================= +Sys_Quit +================= +*/ +void Sys_Quit( void ) +{ + Sys_Exit( 0 ); +} + +/* +================= +Sys_GetProcessorFeatures +================= +*/ +cpuFeatures_t Sys_GetProcessorFeatures( void ) +{ + cpuFeatures_t features = 0; + +#ifndef DEDICATED + if( SDL_HasRDTSC( ) ) features |= CF_RDTSC; + if( SDL_HasMMX( ) ) features |= CF_MMX; + if( SDL_HasMMXExt( ) ) features |= CF_MMX_EXT; + if( SDL_Has3DNow( ) ) features |= CF_3DNOW; + if( SDL_Has3DNowExt( ) ) features |= CF_3DNOW_EXT; + if( SDL_HasSSE( ) ) features |= CF_SSE; + if( SDL_HasSSE2( ) ) features |= CF_SSE2; + if( SDL_HasAltiVec( ) ) features |= CF_ALTIVEC; +#endif + + return features; +} + +/* +================= +Sys_Init +================= +*/ +void Sys_Init(void) +{ + Cmd_AddCommand( "in_restart", Sys_In_Restart_f ); + Cvar_Set( "arch", OS_STRING " " ARCH_STRING ); + Cvar_Set( "username", Sys_GetCurrentUser( ) ); +} + +/* +================= +Sys_AnsiColorPrint + +Transform Q3 colour codes to ANSI escape sequences +================= +*/ +void Sys_AnsiColorPrint( const char *msg ) +{ + static char buffer[ MAXPRINTMSG ]; + int length = 0; + static int q3ToAnsi[ 8 ] = + { + 7, // COLOR_BLACK + 31, // COLOR_RED + 32, // COLOR_GREEN + 33, // COLOR_YELLOW + 34, // COLOR_BLUE + 36, // COLOR_CYAN + 35, // COLOR_MAGENTA + 0 // COLOR_WHITE + }; + + while( *msg ) + { + if( Q_IsColorString( msg ) || *msg == '\n' ) + { + // First empty the buffer + if( length > 0 ) + { + buffer[ length ] = '\0'; + fputs( buffer, stderr ); + length = 0; + } + + if( *msg == '\n' ) + { + // Issue a reset and then the newline + fputs( "\033[0m\n", stderr ); + msg++; + } + else + { + // Print the color code (reset first to clear potential inverse (black)) + Com_sprintf( buffer, sizeof( buffer ), "\033[0m\033[%dm", + q3ToAnsi[ ColorIndex( *( msg + 1 ) ) ] ); + fputs( buffer, stderr ); + msg += 2; + } + } + else + { + if( length >= MAXPRINTMSG - 1 ) + break; + + buffer[ length ] = *msg; + length++; + msg++; + } + } + + // Empty anything still left in the buffer + if( length > 0 ) + { + buffer[ length ] = '\0'; + fputs( buffer, stderr ); + } +} + +/* +================= +Sys_Print +================= +*/ +void Sys_Print( const char *msg ) +{ + CON_LogWrite( msg ); + CON_Print( msg ); +} + +/* +================= +Sys_Error +================= +*/ +void Sys_Error( const char *error, ... ) +{ + va_list argptr; + char string[1024]; + + va_start (argptr,error); + Q_vsnprintf (string, sizeof(string), error, argptr); + va_end (argptr); + + CL_Shutdown( string ); + Sys_ErrorDialog( string ); + + Sys_Exit( 3 ); +} + +/* +================= +Sys_Warn +================= +*/ +void Sys_Warn( char *warning, ... ) +{ + va_list argptr; + char string[1024]; + + va_start (argptr,warning); + Q_vsnprintf (string, sizeof(string), warning, argptr); + va_end (argptr); + + CON_Print( va( "Warning: %s", string ) ); +} + +/* +============ +Sys_FileTime + +returns -1 if not present +============ +*/ +int Sys_FileTime( char *path ) +{ + struct stat buf; + + if (stat (path,&buf) == -1) + return -1; + + return buf.st_mtime; +} + +/* +================= +Sys_UnloadDll +================= +*/ +void Sys_UnloadDll( void *dllHandle ) +{ + if( !dllHandle ) + { + Com_Printf("Sys_UnloadDll(NULL)\n"); + return; + } + + Sys_UnloadLibrary(dllHandle); +} + +/* +================= +Sys_LoadDll + +Used to load a development dll instead of a virtual machine +#1 look in fs_homepath +#2 look in fs_basepath +================= +*/ +void *Sys_LoadDll( const char *name, + intptr_t (**entryPoint)(int, ...), + intptr_t (*systemcalls)(intptr_t, ...) ) +{ + void *libHandle; + void (*dllEntry)( intptr_t (*syscallptr)(intptr_t, ...) ); + char fname[MAX_OSPATH]; + char *netpath; + + assert( name ); + + Com_sprintf(fname, sizeof(fname), "%s" ARCH_STRING DLL_EXT, name); + + netpath = FS_FindDll(fname); + + if(!netpath) { + Com_Printf( "Sys_LoadDll(%s) could not find it\n", fname ); + return NULL; + } + + Com_Printf( "Loading DLL file: %s\n", netpath); + libHandle = Sys_LoadLibrary(netpath); + + if(!libHandle) { + Com_Printf( "Sys_LoadDll(%s) failed:\n\"%s\"\n", netpath, Sys_LibraryError() ); + return NULL; + } + + dllEntry = Sys_LoadFunction( libHandle, "dllEntry" ); + *entryPoint = Sys_LoadFunction( libHandle, "vmMain" ); + + if ( !*entryPoint || !dllEntry ) + { + Com_Printf ( "Sys_LoadDll(%s) failed to find vmMain function:\n\"%s\" !\n", name, Sys_LibraryError( ) ); + Sys_UnloadLibrary(libHandle); + + return NULL; + } + + Com_Printf ( "Sys_LoadDll(%s) found vmMain function at %p\n", name, *entryPoint ); + dllEntry( systemcalls ); + + return libHandle; +} + +/* +================= +Sys_ParseArgs +================= +*/ +void Sys_ParseArgs( int argc, char **argv ) +{ + if( argc == 2 ) + { + if( !strcmp( argv[1], "--version" ) || + !strcmp( argv[1], "-v" ) ) + { + const char* date = __DATE__; +#ifdef DEDICATED + fprintf( stdout, Q3_VERSION " dedicated server (%s)\n", date ); +#else + fprintf( stdout, Q3_VERSION " client (%s)\n", date ); +#endif + Sys_Exit( 0 ); + } + } +} + +#ifndef DEFAULT_BASEDIR +# ifdef MACOS_X +# define DEFAULT_BASEDIR Sys_StripAppBundle(Sys_BinaryPath()) +# else +# define DEFAULT_BASEDIR Sys_BinaryPath() +# endif +#endif + +/* +================= +Sys_SigHandler +================= +*/ +void Sys_SigHandler( int signal ) +{ + static qboolean signalcaught = qfalse; + + if( signalcaught ) + { + fprintf( stderr, "DOUBLE SIGNAL FAULT: Received signal %d, exiting...\n", + signal ); + } + else + { + signalcaught = qtrue; +#ifndef DEDICATED + CL_Shutdown( va( "Received signal %d", signal ) ); +#endif + SV_Shutdown( va( "Received signal %d", signal ) ); + } + + if( signal == SIGTERM || signal == SIGINT ) + Sys_Exit( 1 ); + else + Sys_Exit( 2 ); +} + +/* +================= +main +================= +*/ +int main( int argc, char **argv ) +{ + int i; + char commandLine[ MAX_STRING_CHARS ] = { 0 }; + +#ifndef DEDICATED + // SDL version check + + // Compile time +# if !SDL_VERSION_ATLEAST(MINSDL_MAJOR,MINSDL_MINOR,MINSDL_PATCH) +# error A more recent version of SDL is required +# endif + + // Run time + const SDL_version *ver = SDL_Linked_Version( ); + +#define MINSDL_VERSION \ + XSTRING(MINSDL_MAJOR) "." \ + XSTRING(MINSDL_MINOR) "." \ + XSTRING(MINSDL_PATCH) + + if( SDL_VERSIONNUM( ver->major, ver->minor, ver->patch ) < + SDL_VERSIONNUM( MINSDL_MAJOR, MINSDL_MINOR, MINSDL_PATCH ) ) + { + Sys_Dialog( DT_ERROR, va( "SDL version " MINSDL_VERSION " or greater is required, " + "but only version %d.%d.%d was found. You may be able to obtain a more recent copy " + "from http://www.libsdl.org/.", ver->major, ver->minor, ver->patch ), "SDL Library Too Old" ); + + Sys_Exit( 1 ); + } +#endif + + Sys_PlatformInit( ); + + // Set the initial time base + Sys_Milliseconds( ); + + Sys_ParseArgs( argc, argv ); + Sys_SetBinaryPath( Sys_Dirname( argv[ 0 ] ) ); + Sys_SetDefaultInstallPath( DEFAULT_BASEDIR ); + + // Concatenate the command line for passing to Com_Init + for( i = 1; i < argc; i++ ) + { + const qboolean containsSpaces = strchr(argv[i], ' ') != NULL; + if (containsSpaces) + Q_strcat( commandLine, sizeof( commandLine ), "\"" ); + + Q_strcat( commandLine, sizeof( commandLine ), argv[ i ] ); + + if (containsSpaces) + Q_strcat( commandLine, sizeof( commandLine ), "\"" ); + + Q_strcat( commandLine, sizeof( commandLine ), " " ); + } + + Com_Init( commandLine ); + NET_Init( ); + + CON_Init( ); + + signal( SIGILL, Sys_SigHandler ); + signal( SIGFPE, Sys_SigHandler ); + signal( SIGSEGV, Sys_SigHandler ); + signal( SIGTERM, Sys_SigHandler ); + signal( SIGINT, Sys_SigHandler ); + + while( 1 ) + { + IN_Frame( ); + Com_Frame( ); + } + + return 0; +} + diff --git a/src/sys/sys_osx.m b/src/sys/sys_osx.m new file mode 100644 index 0000000..1efd08c --- /dev/null +++ b/src/sys/sys_osx.m @@ -0,0 +1,141 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + + +This file is part of Tremulous. + +Tremulous 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. + +Tremulous 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 Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#ifndef MACOS_X +#error This file is for Mac OS X only. You probably should not compile it. +#endif + +// Please note that this file is just some Mac-specific bits. Most of the +// Mac OS X code is shared with other Unix platforms in sys_unix.c ... + +#include "../qcommon/q_shared.h" +#include "../qcommon/qcommon.h" +#include "sys_local.h" + +#import +#import + +/* +================ +Sys_TempPath +================ +*/ +const char *Sys_TempPath( void ) +{ + static UInt8 posixPath[ MAX_OSPATH ]; + FSRef ref; + if( FSFindFolder( kOnAppropriateDisk, + kTemporaryFolderType, kCreateFolder, &ref ) == noErr ) + { + if( FSRefMakePath( &ref, posixPath, + sizeof( posixPath ) - 1 ) == noErr ) + { + return (const char *)posixPath; + } + } + + return "/tmp"; +} + +/* +============== +Sys_Dialog + +Display an OS X dialog box +============== +*/ +dialogResult_t Sys_Dialog( dialogType_t type, const char *message, const char *title ) +{ + dialogResult_t result = DR_OK; + NSAlert *alert = [NSAlert new]; + + [alert setMessageText: [NSString stringWithUTF8String: title]]; + [alert setInformativeText: [NSString stringWithUTF8String: message]]; + + if( type == DT_ERROR ) + [alert setAlertStyle: NSCriticalAlertStyle]; + else + [alert setAlertStyle: NSWarningAlertStyle]; + + switch( type ) + { + default: + [alert runModal]; + result = DR_OK; + break; + + case DT_YES_NO: + [alert addButtonWithTitle: @"Yes"]; + [alert addButtonWithTitle: @"No"]; + switch( [alert runModal] ) + { + default: + case NSAlertFirstButtonReturn: result = DR_YES; break; + case NSAlertSecondButtonReturn: result = DR_NO; break; + } + break; + + case DT_OK_CANCEL: + [alert addButtonWithTitle: @"OK"]; + [alert addButtonWithTitle: @"Cancel"]; + + switch( [alert runModal] ) + { + default: + case NSAlertFirstButtonReturn: result = DR_OK; break; + case NSAlertSecondButtonReturn: result = DR_CANCEL; break; + } + break; + } + + [alert release]; + + return result; +} + +/* +================= +Sys_StripAppBundle + +Discovers if passed dir is suffixed with the directory structure of a Mac OS X +.app bundle. If it is, the .app directory structure is stripped off the end and +the result is returned. If not, dir is returned untouched. +================= +*/ +char *Sys_StripAppBundle( char *dir ) +{ + static char cwd[MAX_OSPATH]; + + Q_strncpyz(cwd, dir, sizeof(cwd)); + if(strcmp(Sys_Basename(cwd), "MacOS")) + return dir; + Q_strncpyz(cwd, Sys_Dirname(cwd), sizeof(cwd)); + if(strcmp(Sys_Basename(cwd), "Contents")) + return dir; + Q_strncpyz(cwd, Sys_Dirname(cwd), sizeof(cwd)); + if(!strstr(Sys_Basename(cwd), ".app")) + return dir; + Q_strncpyz(cwd, Sys_Dirname(cwd), sizeof(cwd)); + return cwd; +} diff --git a/src/sys/sys_unix.c b/src/sys/sys_unix.c new file mode 100644 index 0000000..abbe623 --- /dev/null +++ b/src/sys/sys_unix.c @@ -0,0 +1,818 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous 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. + +Tremulous 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 Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "../qcommon/q_shared.h" +#include "../qcommon/qcommon.h" +#include "sys_local.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +qboolean stdinIsATTY; + +// Used to determine where to store user-specific files +static char homePath[ MAX_OSPATH ] = { 0 }; + +/* +================== +Sys_DefaultHomePath +================== +*/ +char *Sys_DefaultHomePath(void) +{ + char *p; + + if( !*homePath ) + { + if( ( p = getenv( "HOME" ) ) != NULL ) + { + Com_sprintf(homePath, sizeof(homePath), "%s%c", p, PATH_SEP); +#ifdef MACOS_X + Q_strcat( homePath, sizeof( homePath ), + "/Library/Application Support/Tremulous" ); +#else + Q_strcat( homePath, sizeof( homePath ), "/.tremulous" ); +#endif + } + } + + return homePath; +} + +#ifndef MACOS_X +/* +================ +Sys_TempPath +================ +*/ +const char *Sys_TempPath( void ) +{ + const char *TMPDIR = getenv( "TMPDIR" ); + + if( TMPDIR == NULL || TMPDIR[ 0 ] == '\0' ) + return "/tmp"; + else + return TMPDIR; +} +#endif + +/* +================ +Sys_Milliseconds +================ +*/ +/* base time in seconds, that's our origin + timeval:tv_sec is an int: + assuming this wraps every 0x7fffffff - ~68 years since the Epoch (1970) - we're safe till 2038 */ +unsigned long sys_timeBase = 0; +/* current time in ms, using sys_timeBase as origin + NOTE: sys_timeBase*1000 + curtime -> ms since the Epoch + 0x7fffffff ms - ~24 days + although timeval:tv_usec is an int, I'm not sure wether it is actually used as an unsigned int + (which would affect the wrap period) */ +int curtime; +int Sys_Milliseconds (void) +{ + struct timeval tp; + + gettimeofday(&tp, NULL); + + if (!sys_timeBase) + { + sys_timeBase = tp.tv_sec; + return tp.tv_usec/1000; + } + + curtime = (tp.tv_sec - sys_timeBase)*1000 + tp.tv_usec/1000; + + return curtime; +} + +#if !id386 +/* +================== +fastftol +================== +*/ +long fastftol( float f ) +{ + return (long)f; +} + +/* +================== +Sys_SnapVector +================== +*/ +void Sys_SnapVector( float *v ) +{ + v[0] = rint(v[0]); + v[1] = rint(v[1]); + v[2] = rint(v[2]); +} +#endif + + +/* +================== +Sys_RandomBytes +================== +*/ +qboolean Sys_RandomBytes( byte *string, int len ) +{ + FILE *fp; + + fp = fopen( "/dev/urandom", "r" ); + if( !fp ) + return qfalse; + + if( !fread( string, sizeof( byte ), len, fp ) ) + { + fclose( fp ); + return qfalse; + } + + fclose( fp ); + return qtrue; +} + +/* +================== +Sys_GetCurrentUser +================== +*/ +char *Sys_GetCurrentUser( void ) +{ + struct passwd *p; + + if ( (p = getpwuid( getuid() )) == NULL ) { + return "player"; + } + return p->pw_name; +} + +/* +================== +Sys_GetClipboardData +================== +*/ +char *Sys_GetClipboardData(void) +{ + return NULL; +} + +#define MEM_THRESHOLD 96*1024*1024 + +/* +================== +Sys_LowPhysicalMemory + +TODO +================== +*/ +qboolean Sys_LowPhysicalMemory( void ) +{ + return qfalse; +} + +/* +================== +Sys_Basename +================== +*/ +const char *Sys_Basename( char *path ) +{ + return basename( path ); +} + +/* +================== +Sys_Dirname +================== +*/ +const char *Sys_Dirname( char *path ) +{ + return dirname( path ); +} + +/* +================== +Sys_Mkdir +================== +*/ +qboolean Sys_Mkdir( const char *path ) +{ + int result = mkdir( path, 0750 ); + + if( result != 0 ) + return errno == EEXIST; + + return qtrue; +} + +/* +================== +Sys_Mkfifo +================== +*/ +FILE *Sys_Mkfifo( const char *ospath ) +{ + FILE *fifo; + int result; + int fn; + struct stat buf; + + // if file already exists AND is a pipefile, remove it + if( !stat( ospath, &buf ) && S_ISFIFO( buf.st_mode ) ) + FS_Remove( ospath ); + + result = mkfifo( ospath, 0600 ); + if( result != 0 ) + return NULL; + + fifo = fopen( ospath, "w+" ); + if( fifo ) + { + fn = fileno( fifo ); + fcntl( fn, F_SETFL, O_NONBLOCK ); + } + + return fifo; +} + +/* +================== +Sys_Cwd +================== +*/ +char *Sys_Cwd( void ) +{ + static char cwd[MAX_OSPATH]; + + char *result = getcwd( cwd, sizeof( cwd ) - 1 ); + if( result != cwd ) + return NULL; + + cwd[MAX_OSPATH-1] = 0; + + return cwd; +} + +/* +============================================================== + +DIRECTORY SCANNING + +============================================================== +*/ + +#define MAX_FOUND_FILES 0x1000 + +/* +================== +Sys_ListFilteredFiles +================== +*/ +void Sys_ListFilteredFiles( const char *basedir, char *subdirs, 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, qfalse )) + continue; + list[ *numfiles ] = CopyString( filename ); + (*numfiles)++; + } + + closedir(fdir); +} + +/* +================== +Sys_ListFiles +================== +*/ +char **Sys_ListFiles( const char *directory, const char *extension, 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 = 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 = Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ) ); + for ( i = 0 ; i < nfiles ; i++ ) { + listCopy[i] = list[i]; + } + listCopy[i] = NULL; + + return listCopy; +} + +/* +================== +Sys_FreeFileList +================== +*/ +void Sys_FreeFileList( char **list ) +{ + int i; + + if ( !list ) { + return; + } + + for ( i = 0 ; list[i] ; i++ ) { + Z_Free( list[i] ); + } + + Z_Free( list ); +} + +/* +================== +Sys_Sleep + +Block execution for msec or until input is recieved. +================== +*/ +void Sys_Sleep( int msec ) +{ + if( msec == 0 ) + return; + + if( stdinIsATTY ) + { + fd_set fdset; + + FD_ZERO(&fdset); + FD_SET(STDIN_FILENO, &fdset); + if( msec < 0 ) + { + select(STDIN_FILENO + 1, &fdset, NULL, NULL, NULL); + } + else + { + struct timeval timeout; + + timeout.tv_sec = msec/1000; + timeout.tv_usec = (msec%1000)*1000; + select(STDIN_FILENO + 1, &fdset, NULL, NULL, &timeout); + } + } + else + { + // With nothing to select() on, we can't wait indefinitely + if( msec < 0 ) + msec = 10; + + usleep( msec * 1000 ); + } +} + +/* +============== +Sys_ErrorDialog + +Display an error message +============== +*/ +void Sys_ErrorDialog( const char *error ) +{ + char buffer[ 1024 ]; + unsigned int size; + int f = -1; + const char *homepath = Cvar_VariableString( "fs_homepath" ); + const char *gamedir = Cvar_VariableString( "fs_game" ); + const char *fileName = "crashlog.txt"; + char *ospath = FS_BuildOSPath( homepath, gamedir, fileName ); + + Sys_Print( va( "%s\n", error ) ); + +#ifndef DEDICATED + Sys_Dialog( DT_ERROR, va( "%s. See \"%s\" for details.", error, ospath ), "Error" ); +#endif + + // Make sure the write path for the crashlog exists... + if( FS_CreatePath( ospath ) ) { + Com_Printf( "ERROR: couldn't create path '%s' for crash log.\n", ospath ); + return; + } + + // We might be crashing because we maxed out the Quake MAX_FILE_HANDLES, + // which will come through here, so we don't want to recurse forever by + // calling FS_FOpenFileWrite()...use the Unix system APIs instead. + f = open( ospath, O_CREAT | O_TRUNC | O_WRONLY, 0640 ); + if( f == -1 ) + { + Com_Printf( "ERROR: couldn't open %s\n", fileName ); + return; + } + + // We're crashing, so we don't care much if write() or close() fails. + while( ( size = CON_LogRead( buffer, sizeof( buffer ) ) ) > 0 ) { + if( write( f, buffer, size ) != size ) { + Com_Printf( "ERROR: couldn't fully write to %s\n", fileName ); + break; + } + } + + close( f ); +} + +#ifndef MACOS_X +/* +============== +Sys_ZenityCommand +============== +*/ +static int Sys_ZenityCommand( dialogType_t type, const char *message, const char *title ) +{ + const char *options = ""; + char command[ 1024 ]; + + switch( type ) + { + default: + case DT_INFO: options = "--info"; break; + case DT_WARNING: options = "--warning"; break; + case DT_ERROR: options = "--error"; break; + case DT_YES_NO: options = "--question --ok-label=\"Yes\" --cancel-label=\"No\""; break; + case DT_OK_CANCEL: options = "--question --ok-label=\"OK\" --cancel-label=\"Cancel\""; break; + } + + Com_sprintf( command, sizeof( command ), "zenity %s --text=\"%s\" --title=\"%s\"", + options, message, title ); + + return system( command ); +} + +/* +============== +Sys_KdialogCommand +============== +*/ +static int Sys_KdialogCommand( dialogType_t type, const char *message, const char *title ) +{ + const char *options = ""; + char command[ 1024 ]; + + switch( type ) + { + default: + case DT_INFO: options = "--msgbox"; break; + case DT_WARNING: options = "--sorry"; break; + case DT_ERROR: options = "--error"; break; + case DT_YES_NO: options = "--warningyesno"; break; + case DT_OK_CANCEL: options = "--warningcontinuecancel"; break; + } + + Com_sprintf( command, sizeof( command ), "kdialog %s \"%s\" --title \"%s\"", + options, message, title ); + + return system( command ); +} + +/* +============== +Sys_XmessageCommand +============== +*/ +static int Sys_XmessageCommand( dialogType_t type, const char *message, const char *title ) +{ + const char *options = ""; + char command[ 1024 ]; + + switch( type ) + { + default: options = "-buttons OK"; break; + case DT_YES_NO: options = "-buttons Yes:0,No:1"; break; + case DT_OK_CANCEL: options = "-buttons OK:0,Cancel:1"; break; + } + + Com_sprintf( command, sizeof( command ), "xmessage -center %s \"%s\"", + options, message ); + + return system( command ); +} + +/* +============== +Sys_Dialog + +Display a *nix dialog box +============== +*/ +dialogResult_t Sys_Dialog( dialogType_t type, const char *message, const char *title ) +{ + typedef enum + { + NONE = 0, + ZENITY, + KDIALOG, + XMESSAGE, + NUM_DIALOG_PROGRAMS + } dialogCommandType_t; + typedef int (*dialogCommandBuilder_t)( dialogType_t, const char *, const char * ); + + const char *session = getenv( "DESKTOP_SESSION" ); + qboolean tried[ NUM_DIALOG_PROGRAMS ] = { qfalse }; + dialogCommandBuilder_t commands[ NUM_DIALOG_PROGRAMS ] = { NULL }; + dialogCommandType_t preferredCommandType = NONE; + + commands[ ZENITY ] = &Sys_ZenityCommand; + commands[ KDIALOG ] = &Sys_KdialogCommand; + commands[ XMESSAGE ] = &Sys_XmessageCommand; + + // This may not be the best way + if( !Q_stricmp( session, "gnome" ) ) + preferredCommandType = ZENITY; + else if( !Q_stricmp( session, "kde" ) ) + preferredCommandType = KDIALOG; + + while( 1 ) + { + int i; + int exitCode; + + for( i = NONE + 1; i < NUM_DIALOG_PROGRAMS; i++ ) + { + if( preferredCommandType != NONE && preferredCommandType != i ) + continue; + + if( !tried[ i ] ) + { + exitCode = commands[ i ]( type, message, title ); + + if( exitCode >= 0 ) + { + switch( type ) + { + case DT_YES_NO: return exitCode ? DR_NO : DR_YES; + case DT_OK_CANCEL: return exitCode ? DR_CANCEL : DR_OK; + default: return DR_OK; + } + } + + tried[ i ] = qtrue; + + // The preference failed, so start again in order + if( preferredCommandType != NONE ) + { + preferredCommandType = NONE; + break; + } + } + } + + for( i = NONE + 1; i < NUM_DIALOG_PROGRAMS; i++ ) + { + if( !tried[ i ] ) + continue; + } + + break; + } + + Com_DPrintf( S_COLOR_YELLOW "WARNING: failed to show a dialog\n" ); + return DR_OK; +} +#endif + +/* +============== +Sys_GLimpSafeInit + +Unix specific "safe" GL implementation initialisation +============== +*/ +void Sys_GLimpSafeInit( void ) +{ + // NOP +} + +/* +============== +Sys_GLimpInit + +Unix specific GL implementation initialisation +============== +*/ +void Sys_GLimpInit( void ) +{ + // NOP +} + +/* +============== +Sys_PlatformInit + +Unix specific initialisation +============== +*/ +void Sys_PlatformInit( void ) +{ + const char* term = getenv( "TERM" ); + + signal( SIGHUP, Sys_SigHandler ); + signal( SIGQUIT, Sys_SigHandler ); + signal( SIGTRAP, Sys_SigHandler ); + signal( SIGIOT, Sys_SigHandler ); + signal( SIGBUS, Sys_SigHandler ); + + stdinIsATTY = isatty( STDIN_FILENO ) && + !( term && ( !strcmp( term, "raw" ) || !strcmp( term, "dumb" ) ) ); +} + +/* +============== +Sys_PlatformExit + +Unix specific deinitialisation +============== +*/ +void Sys_PlatformExit( void ) +{ +} + +/* +============== +Sys_SetEnv + +set/unset environment variables (empty value removes it) +============== +*/ + +void Sys_SetEnv(const char *name, const char *value) +{ + if(value && *value) + setenv(name, value, 1); + else + unsetenv(name); +} + +/* +============== +Sys_PID +============== +*/ +int Sys_PID( void ) +{ + return getpid( ); +} + +/* +============== +Sys_PIDIsRunning +============== +*/ +qboolean Sys_PIDIsRunning( int pid ) +{ + return kill( pid, 0 ) == 0; +} diff --git a/src/sys/sys_win32.c b/src/sys/sys_win32.c new file mode 100644 index 0000000..8ca1bf6 --- /dev/null +++ b/src/sys/sys_win32.c @@ -0,0 +1,811 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + + +This file is part of Tremulous. + +Tremulous 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. + +Tremulous 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 Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "../qcommon/q_shared.h" +#include "../qcommon/qcommon.h" +#include "sys_local.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Used to determine where to store user-specific files +static char homePath[ MAX_OSPATH ] = { 0 }; + +#ifndef DEDICATED +static UINT timerResolution = 0; +#endif + +#ifdef __WIN64__ +void Sys_SnapVector( float *v ) +{ + v[0] = rint(v[0]); + v[1] = rint(v[1]); + v[2] = rint(v[2]); +} +#endif + +/* +================ +Sys_DefaultHomePath +================ +*/ +char *Sys_DefaultHomePath( void ) +{ + TCHAR szPath[MAX_PATH]; + FARPROC qSHGetFolderPath; + HMODULE shfolder = LoadLibrary("shfolder.dll"); + + if( !*homePath ) + { + if(shfolder == NULL) + { + Com_Printf("Unable to load SHFolder.dll\n"); + return NULL; + } + + qSHGetFolderPath = GetProcAddress(shfolder, "SHGetFolderPathA"); + if(qSHGetFolderPath == NULL) + { + Com_Printf("Unable to find SHGetFolderPath in SHFolder.dll\n"); + FreeLibrary(shfolder); + return NULL; + } + + if( !SUCCEEDED( qSHGetFolderPath( NULL, CSIDL_APPDATA, + NULL, 0, szPath ) ) ) + { + Com_Printf("Unable to detect CSIDL_APPDATA\n"); + FreeLibrary(shfolder); + return NULL; + } + Q_strncpyz( homePath, szPath, sizeof( homePath ) ); + Q_strcat( homePath, sizeof( homePath ), "\\Tremulous" ); + FreeLibrary(shfolder); + } + + return homePath; +} + +/* +================ +Sys_TempPath +================ +*/ +const char *Sys_TempPath( void ) +{ + static TCHAR path[ MAX_PATH ]; + DWORD length; + + length = GetTempPath( sizeof( path ), path ); + + if( length > sizeof( path ) || length == 0 ) + return Sys_DefaultHomePath( ); + else + return path; +} + +/* +================ +Sys_Milliseconds +================ +*/ +int sys_timeBase; +int Sys_Milliseconds (void) +{ + int sys_curtime; + static qboolean initialized = qfalse; + + if (!initialized) { + sys_timeBase = timeGetTime(); + initialized = qtrue; + } + sys_curtime = timeGetTime() - sys_timeBase; + + return sys_curtime; +} + +#ifndef __GNUC__ //see snapvectora.s +/* +================ +Sys_SnapVector +================ +*/ +void Sys_SnapVector( float *v ) +{ + int i; + float f; + + f = *v; + __asm fld f; + __asm fistp i; + *v = i; + v++; + f = *v; + __asm fld f; + __asm fistp i; + *v = i; + v++; + f = *v; + __asm fld f; + __asm fistp i; + *v = i; +} +#endif + +/* +================ +Sys_RandomBytes +================ +*/ +qboolean Sys_RandomBytes( byte *string, int len ) +{ + HCRYPTPROV prov; + + if( !CryptAcquireContext( &prov, NULL, NULL, + PROV_RSA_FULL, CRYPT_VERIFYCONTEXT ) ) { + + return qfalse; + } + + if( !CryptGenRandom( prov, len, (BYTE *)string ) ) { + CryptReleaseContext( prov, 0 ); + return qfalse; + } + CryptReleaseContext( prov, 0 ); + return qtrue; +} + +/* +================ +Sys_GetCurrentUser +================ +*/ +char *Sys_GetCurrentUser( void ) +{ + static char s_userName[1024]; + unsigned long size = sizeof( s_userName ); + + if( !GetUserName( s_userName, &size ) ) + strcpy( s_userName, "player" ); + + if( !s_userName[0] ) + { + strcpy( s_userName, "player" ); + } + + return s_userName; +} + +/* +================ +Sys_GetClipboardData +================ +*/ +char *Sys_GetClipboardData( void ) +{ + char *data = NULL; + char *cliptext; + + if ( OpenClipboard( NULL ) != 0 ) { + HANDLE hClipboardData; + + if ( ( hClipboardData = GetClipboardData( CF_TEXT ) ) != 0 ) { + if ( ( cliptext = GlobalLock( hClipboardData ) ) != 0 ) { + data = Z_Malloc( GlobalSize( hClipboardData ) + 1 ); + Q_strncpyz( data, cliptext, GlobalSize( hClipboardData ) ); + GlobalUnlock( hClipboardData ); + + strtok( data, "\n\r\b" ); + } + } + CloseClipboard(); + } + return data; +} + +#define MEM_THRESHOLD 96*1024*1024 + +/* +================== +Sys_LowPhysicalMemory +================== +*/ +qboolean Sys_LowPhysicalMemory( void ) +{ + MEMORYSTATUS stat; + GlobalMemoryStatus (&stat); + return (stat.dwTotalPhys <= MEM_THRESHOLD) ? qtrue : qfalse; +} + +/* +============== +Sys_Basename +============== +*/ +const char *Sys_Basename( char *path ) +{ + static char base[ MAX_OSPATH ] = { 0 }; + int length; + + length = strlen( path ) - 1; + + // Skip trailing slashes + while( length > 0 && path[ length ] == '\\' ) + length--; + + while( length > 0 && path[ length - 1 ] != '\\' ) + length--; + + Q_strncpyz( base, &path[ length ], sizeof( base ) ); + + length = strlen( base ) - 1; + + // Strip trailing slashes + while( length > 0 && base[ length ] == '\\' ) + base[ length-- ] = '\0'; + + return base; +} + +/* +============== +Sys_Dirname +============== +*/ +const char *Sys_Dirname( char *path ) +{ + static char dir[ MAX_OSPATH ] = { 0 }; + int length; + + Q_strncpyz( dir, path, sizeof( dir ) ); + length = strlen( dir ) - 1; + + while( length > 0 && dir[ length ] != '\\' ) + length--; + + dir[ length ] = '\0'; + + return dir; +} + +/* +============== +Sys_Mkdir +============== +*/ +qboolean Sys_Mkdir( const char *path ) +{ + if( !CreateDirectory( path, NULL ) ) + { + if( GetLastError( ) != ERROR_ALREADY_EXISTS ) + return qfalse; + } + + return qtrue; +} + +/* +================== +Sys_Mkfifo +Noop on windows because named pipes do not function the same way +================== +*/ +FILE *Sys_Mkfifo( const char *ospath ) +{ + return NULL; +} + +/* +============== +Sys_Cwd +============== +*/ +char *Sys_Cwd( void ) { + static char cwd[MAX_OSPATH]; + + _getcwd( cwd, sizeof( cwd ) - 1 ); + cwd[MAX_OSPATH-1] = 0; + + return cwd; +} + +/* +============================================================== + +DIRECTORY SCANNING + +============================================================== +*/ + +#define MAX_FOUND_FILES 0x1000 + +/* +============== +Sys_ListFilteredFiles +============== +*/ +void Sys_ListFilteredFiles( const char *basedir, char *subdirs, char *filter, char **list, int *numfiles ) +{ + char search[MAX_OSPATH], newsubdirs[MAX_OSPATH]; + char filename[MAX_OSPATH]; + intptr_t findhandle; + struct _finddata_t findinfo; + + 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 ); + } + + findhandle = _findfirst (search, &findinfo); + if (findhandle == -1) { + return; + } + + do { + if (findinfo.attrib & _A_SUBDIR) { + if (Q_stricmp(findinfo.name, ".") && Q_stricmp(findinfo.name, "..")) { + if (strlen(subdirs)) { + Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s\\%s", subdirs, findinfo.name); + } + else { + Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s", findinfo.name); + } + Sys_ListFilteredFiles( basedir, newsubdirs, filter, list, numfiles ); + } + } + if ( *numfiles >= MAX_FOUND_FILES - 1 ) { + break; + } + Com_sprintf( filename, sizeof(filename), "%s\\%s", subdirs, findinfo.name ); + if (!Com_FilterPath( filter, filename, qfalse )) + continue; + list[ *numfiles ] = CopyString( filename ); + (*numfiles)++; + } while ( _findnext (findhandle, &findinfo) != -1 ); + + _findclose (findhandle); +} + +/* +============== +strgtr +============== +*/ +static qboolean strgtr(const char *s0, const char *s1) +{ + int l0, l1, i; + + l0 = strlen(s0); + l1 = strlen(s1); + + if (l1 s0[i]) { + return qtrue; + } + if (s1[i] < s0[i]) { + return qfalse; + } + } + return qfalse; +} + +/* +============== +Sys_ListFiles +============== +*/ +char **Sys_ListFiles( const char *directory, const char *extension, char *filter, int *numfiles, qboolean wantsubs ) +{ + char search[MAX_OSPATH]; + int nfiles; + char **listCopy; + char *list[MAX_FOUND_FILES]; + struct _finddata_t findinfo; + intptr_t findhandle; + int flag; + int i; + + if (filter) { + + nfiles = 0; + Sys_ListFilteredFiles( directory, "", filter, list, &nfiles ); + + list[ nfiles ] = 0; + *numfiles = nfiles; + + if (!nfiles) + return NULL; + + listCopy = Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ) ); + for ( i = 0 ; i < nfiles ; i++ ) { + listCopy[i] = list[i]; + } + listCopy[i] = NULL; + + return listCopy; + } + + if ( !extension) { + extension = ""; + } + + // passing a slash as extension will find directories + if ( extension[0] == '/' && extension[1] == 0 ) { + extension = ""; + flag = 0; + } else { + flag = _A_SUBDIR; + } + + Com_sprintf( search, sizeof(search), "%s\\*%s", directory, extension ); + + // search + nfiles = 0; + + findhandle = _findfirst (search, &findinfo); + if (findhandle == -1) { + *numfiles = 0; + return NULL; + } + + do { + if ( (!wantsubs && flag ^ ( findinfo.attrib & _A_SUBDIR )) || (wantsubs && findinfo.attrib & _A_SUBDIR) ) { + if ( nfiles == MAX_FOUND_FILES - 1 ) { + break; + } + list[ nfiles ] = CopyString( findinfo.name ); + nfiles++; + } + } while ( _findnext (findhandle, &findinfo) != -1 ); + + list[ nfiles ] = 0; + + _findclose (findhandle); + + // return a copy of the list + *numfiles = nfiles; + + if ( !nfiles ) { + return NULL; + } + + listCopy = Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ) ); + for ( i = 0 ; i < nfiles ; i++ ) { + listCopy[i] = list[i]; + } + listCopy[i] = NULL; + + do { + flag = 0; + for(i=1; i 0 ) + { + Com_Memcpy( p, buffer, size ); + p += size; + } + + *p = '\0'; + + if( OpenClipboard( NULL ) && EmptyClipboard( ) ) + SetClipboardData( CF_TEXT, memoryHandle ); + + GlobalUnlock( clipMemory ); + CloseClipboard( ); + } + } +} + +/* +============== +Sys_Dialog + +Display a win32 dialog box +============== +*/ +dialogResult_t Sys_Dialog( dialogType_t type, const char *message, const char *title ) +{ + UINT uType; + + switch( type ) + { + default: + case DT_INFO: uType = MB_ICONINFORMATION|MB_OK; break; + case DT_WARNING: uType = MB_ICONWARNING|MB_OK; break; + case DT_ERROR: uType = MB_ICONERROR|MB_OK; break; + case DT_YES_NO: uType = MB_ICONQUESTION|MB_YESNO; break; + case DT_OK_CANCEL: uType = MB_ICONWARNING|MB_OKCANCEL; break; + } + + switch( MessageBox( NULL, message, title, uType ) ) + { + default: + case IDOK: return DR_OK; + case IDCANCEL: return DR_CANCEL; + case IDYES: return DR_YES; + case IDNO: return DR_NO; + } +} + +#ifndef DEDICATED +static qboolean SDL_VIDEODRIVER_externallySet = qfalse; +#endif + +/* +============== +Sys_GLimpSafeInit + +Windows specific "safe" GL implementation initialisation +============== +*/ +void Sys_GLimpSafeInit( void ) +{ +#ifndef DEDICATED + if( !SDL_VIDEODRIVER_externallySet ) + { + // Here, we want to let SDL decide what do to unless + // explicitly requested otherwise + _putenv( "SDL_VIDEODRIVER=" ); + } +#endif +} + +/* +============== +Sys_GLimpInit + +Windows specific GL implementation initialisation +============== +*/ +void Sys_GLimpInit( void ) +{ +#ifndef DEDICATED + if( !SDL_VIDEODRIVER_externallySet ) + { + // It's a little bit weird having in_mouse control the + // video driver, but from ioq3's point of view they're + // virtually the same except for the mouse input anyway + if( Cvar_VariableIntegerValue( "in_mouse" ) == -1 ) + { + // Use the windib SDL backend, which is closest to + // the behaviour of idq3 with in_mouse set to -1 + _putenv( "SDL_VIDEODRIVER=windib" ); + } + else + { + // Use the DirectX SDL backend + _putenv( "SDL_VIDEODRIVER=directx" ); + } + } +#endif +} + +/* +============== +Sys_PlatformInit + +Windows specific initialisation +============== +*/ +void Sys_PlatformInit( void ) +{ +#ifndef DEDICATED + TIMECAPS ptc; + + const char *SDL_VIDEODRIVER = getenv( "SDL_VIDEODRIVER" ); + + if( SDL_VIDEODRIVER ) + { + Com_Printf( "SDL_VIDEODRIVER is externally set to \"%s\", " + "in_mouse -1 will have no effect\n", SDL_VIDEODRIVER ); + SDL_VIDEODRIVER_externallySet = qtrue; + } + else + SDL_VIDEODRIVER_externallySet = qfalse; + + if(timeGetDevCaps(&ptc, sizeof(ptc)) == MMSYSERR_NOERROR) + { + timerResolution = ptc.wPeriodMin; + + if(timerResolution > 1) + { + Com_Printf("Warning: Minimum supported timer resolution is %ums " + "on this system, recommended resolution 1ms\n", timerResolution); + } + + timeBeginPeriod(timerResolution); + } + else + timerResolution = 0; +#endif +} + +/* +============== +Sys_PlatformExit + +Windows specific initialisation +============== +*/ +void Sys_PlatformExit( void ) +{ +#ifndef DEDICATED + if(timerResolution) + timeEndPeriod(timerResolution); +#endif +} + +/* +============== +Sys_SetEnv + +set/unset environment variables (empty value removes it) +============== +*/ +void Sys_SetEnv(const char *name, const char *value) +{ + if(value) + _putenv(va("%s=%s", name, value)); + else + _putenv(va("%s=", name)); +} + +/* +============== +Sys_PID +============== +*/ +int Sys_PID( void ) +{ + return GetCurrentProcessId( ); +} + +/* +============== +Sys_PIDIsRunning +============== +*/ +qboolean Sys_PIDIsRunning( int pid ) +{ + DWORD processes[ 1024 ]; + DWORD numBytes, numProcesses; + int i; + + if( !EnumProcesses( processes, sizeof( processes ), &numBytes ) ) + return qfalse; // Assume it's not running + + numProcesses = numBytes / sizeof( DWORD ); + + // Search for the pid + for( i = 0; i < numProcesses; i++ ) + { + if( processes[ i ] == pid ) + return qtrue; + } + + return qfalse; +} diff --git a/src/sys/win_resource.h b/src/sys/win_resource.h new file mode 100644 index 0000000..8e658d7 --- /dev/null +++ b/src/sys/win_resource.h @@ -0,0 +1,45 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous 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. + +Tremulous 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 Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by winquake.rc +// +#define IDS_STRING1 1 +#define IDI_ICON1 1 +#define IDB_BITMAP1 1 +#define IDB_BITMAP2 128 +#define IDC_CURSOR1 129 +#define IDC_CURSOR2 130 +#define IDC_CURSOR3 131 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_NEXT_RESOURCE_VALUE 132 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1005 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/sys/win_resource.rc b/src/sys/win_resource.rc new file mode 100644 index 0000000..6f5d82a --- /dev/null +++ b/src/sys/win_resource.rc @@ -0,0 +1,72 @@ +//Microsoft Developer Studio generated resource script. +// +#include "win_resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ICON1 ICON DISCARDABLE "misc/tremulous.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_STRING1 "Tremulous" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + -- cgit