diff --git a/Makefile b/Makefile index c734e259..31db8f17 100644 --- a/Makefile +++ b/Makefile @@ -366,6 +366,7 @@ else # ifeq Linux ifeq ($(PLATFORM),darwin) HAVE_VM_COMPILED=true + LIBS = -framework Cocoa CLIENT_LIBS= OPTIMIZEVM= @@ -422,7 +423,7 @@ ifeq ($(PLATFORM),darwin) # the file has been modified by each build. LIBSDLMAIN=$(B)/libSDLmain.a LIBSDLMAINSRC=$(LIBSDIR)/macosx/libSDLmain.a - CLIENT_LIBS += -framework Cocoa -framework IOKit -framework OpenGL \ + CLIENT_LIBS += -framework IOKit -framework OpenGL \ $(LIBSDIR)/macosx/libSDL-1.2.0.dylib OPTIMIZEVM += -falign-loops=16 @@ -1557,7 +1558,7 @@ endif ifeq ($(PLATFORM),darwin) Q3OBJ += \ - $(B)/client/sys_cocoa.o + $(B)/client/sys_osx.o endif ifeq ($(USE_MUMBLE),1) @@ -1725,11 +1726,10 @@ else $(B)/ded/con_tty.o endif -# Not currently referenced in the dedicated server. -#ifeq ($(PLATFORM),darwin) -# Q3DOBJ += \ -# $(B)/ded/sys_cocoa.o -#endif +ifeq ($(PLATFORM),darwin) + Q3DOBJ += \ + $(B)/ded/sys_osx.o +endif $(B)/ioq3ded$(FULLBINEXT): $(Q3DOBJ) $(echo_cmd) "LD $@" diff --git a/code/client/cl_main.c b/code/client/cl_main.c index 55bf41ca..7b19ff0d 100644 --- a/code/client/cl_main.c +++ b/code/client/cl_main.c @@ -3276,14 +3276,14 @@ CL_Shutdown =============== */ -void CL_Shutdown( void ) { +void CL_Shutdown( char *finalmsg ) { static qboolean recursive = qfalse; // check whether the client is running at all. if(!(com_cl_running && com_cl_running->integer)) return; - Com_Printf( "----- CL_Shutdown -----\n" ); + Com_Printf( "----- Client Shutdown (%s) -----\n", finalmsg ); if ( recursive ) { Com_Printf( "WARNING: Recursive shutdown\n" ); diff --git a/code/null/null_client.c b/code/null/null_client.c index e67b95b0..66ead8a2 100644 --- a/code/null/null_client.c +++ b/code/null/null_client.c @@ -25,7 +25,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA cvar_t *cl_shownet; -void CL_Shutdown( void ) { +void CL_Shutdown( char *finalmsg ) { } void CL_Init( void ) { diff --git a/code/qcommon/common.c b/code/qcommon/common.c index a225aa32..64ec51d9 100644 --- a/code/qcommon/common.c +++ b/code/qcommon/common.c @@ -82,6 +82,7 @@ cvar_t *com_unfocused; cvar_t *com_maxfpsUnfocused; cvar_t *com_minimized; cvar_t *com_maxfpsMinimized; +cvar_t *com_abnormalExit; cvar_t *com_standalone; // com_speeds times @@ -237,7 +238,7 @@ void QDECL Com_DPrintf( const char *fmt, ...) { Com_Error Both client and server can use this, and it will -do the apropriate things. +do the appropriate thing. ============= */ void QDECL Com_Error( int code, const char *fmt, ... ) { @@ -322,7 +323,7 @@ void QDECL Com_Error( int code, const char *fmt, ... ) { com_errorEntered = qfalse; longjmp (abortframe, -1); } else { - CL_Shutdown (); + CL_Shutdown (va("Client fatal crashed: %s", com_errorMessage)); SV_Shutdown (va("Server fatal crashed: %s", com_errorMessage)); } @@ -346,7 +347,7 @@ void Com_Quit_f( void ) { char *p = Cmd_Args( ); if ( !com_errorEntered ) { SV_Shutdown (p[0] ? p : "Server quit"); - CL_Shutdown (); + CL_Shutdown (p[0] ? p : "Client quit"); Com_Shutdown (); FS_Shutdown(qtrue); } @@ -2737,6 +2738,7 @@ void Com_Init( char *commandLine ) { com_maxfpsUnfocused = Cvar_Get( "com_maxfpsUnfocused", "0", CVAR_ARCHIVE ); com_minimized = Cvar_Get( "com_minimized", "0", CVAR_ROM ); com_maxfpsMinimized = Cvar_Get( "com_maxfpsMinimized", "0", CVAR_ARCHIVE ); + com_abnormalExit = Cvar_Get( "com_abnormalExit", "0", CVAR_ROM ); com_standalone = Cvar_Get( "com_standalone", "0", CVAR_INIT ); com_introPlayed = Cvar_Get( "com_introplayed", "0", CVAR_ARCHIVE); @@ -2746,6 +2748,18 @@ void Com_Init( char *commandLine ) { Sys_Init(); + if( Sys_WritePIDFile( ) ) { +#ifndef DEDICATED + const char *message = "The last time " CLIENT_WINDOW_TITLE " ran, " + "it didn't exit properly. This may be due to inappropriate video " + "settings. Would you like to start with \"safe\" video settings?"; + + if( Sys_Dialog( DT_YES_NO, message, "Abnormal Exit" ) == DR_YES ) { + Cvar_Set( "com_abnormalExit", "1" ); + } +#endif + } + // Pick a random port value Com_RandomBytes( (byte*)&qport, sizeof(int) ); Netchan_Init( qport & 0xffff ); diff --git a/code/qcommon/qcommon.h b/code/qcommon/qcommon.h index de1dba0f..c6a61e29 100644 --- a/code/qcommon/qcommon.h +++ b/code/qcommon/qcommon.h @@ -181,7 +181,7 @@ qboolean NET_CompareBaseAdrMask(netadr_t a, netadr_t b, int netmask); qboolean NET_CompareBaseAdr (netadr_t a, netadr_t b); qboolean NET_IsLocalAddress (netadr_t adr); const char *NET_AdrToString (netadr_t a); -const char *NET_AdrToStringwPort (netadr_t a); +const char *NET_AdrToStringwPort (netadr_t a); int NET_StringToAdr ( const char *s, netadr_t *a, netadrtype_t family); qboolean NET_GetLoopPacket (netsrc_t sock, netadr_t *net_from, msg_t *net_message); void NET_JoinMulticast6(void); @@ -937,7 +937,7 @@ void CL_InitKeyCommands( void ); void CL_Init( void ); void CL_Disconnect( qboolean showMainMenu ); -void CL_Shutdown( void ); +void CL_Shutdown( char *finalmsg ); void CL_Frame( int msec ); qboolean CL_GameCommand( void ); void CL_KeyEvent (int key, qboolean down, unsigned time); @@ -1087,6 +1087,7 @@ char *Sys_DefaultAppPath(void); void Sys_SetDefaultHomePath(const char *path); char *Sys_DefaultHomePath(void); +const char *Sys_TempPath(void); const char *Sys_Dirname( char *path ); const char *Sys_Basename( char *path ); char *Sys_ConsoleInput(void); @@ -1099,6 +1100,27 @@ qboolean Sys_LowPhysicalMemory( void ); void Sys_SetEnv(const char *name, const char *value); +typedef enum +{ + DR_YES = 0, + DR_NO = 1, + DR_OK = 0, + DR_CANCEL = 1 +} dialogResult_t; + +typedef enum +{ + DT_INFO, + DT_WARNING, + DT_ERROR, + DT_YES_NO, + DT_OK_CANCEL +} dialogType_t; + +dialogResult_t Sys_Dialog( dialogType_t type, const char *message, const char *title ); + +qboolean Sys_WritePIDFile( void ); + /* This is based on the Adaptive Huffman algorithm described in Sayood's Data * Compression book. The ranks are not actually stored, but implicitly defined * by the location of a node within a doubly-linked list */ diff --git a/code/sdl/sdl_glimp.c b/code/sdl/sdl_glimp.c index ea2268fb..dd005da0 100644 --- a/code/sdl/sdl_glimp.c +++ b/code/sdl/sdl_glimp.c @@ -673,6 +673,14 @@ void GLimp_Init( void ) r_allowResize = ri.Cvar_Get( "r_allowResize", "0", CVAR_ARCHIVE ); r_centerWindow = ri.Cvar_Get( "r_centerWindow", "0", CVAR_ARCHIVE ); + if( Cvar_VariableIntegerValue( "com_abnormalExit" ) ) + { + ri.Cvar_Set( "r_mode", va( "%d", R_MODE_FALLBACK ) ); + ri.Cvar_Set( "r_fullscreen", "0" ); + ri.Cvar_Set( "r_centerWindow", "0" ); + ri.Cvar_Set( "com_abnormalExit", "0" ); + } + Sys_SetEnv( "SDL_VIDEO_CENTERED", r_centerWindow->integer ? "1" : "" ); Sys_GLimpInit( ); @@ -693,7 +701,7 @@ void GLimp_Init( void ) ri.Printf( PRINT_ALL, "Setting r_mode %d failed, falling back on r_mode %d\n", r_mode->integer, R_MODE_FALLBACK ); - if(GLimp_StartDriverAndSetMode(R_MODE_FALLBACK, r_fullscreen->integer, qfalse)) + if(GLimp_StartDriverAndSetMode(R_MODE_FALLBACK, qfalse, qfalse)) goto success; } diff --git a/code/sdl/sdl_input.c b/code/sdl/sdl_input.c index 5bf3056a..ac3e7a01 100644 --- a/code/sdl/sdl_input.c +++ b/code/sdl/sdl_input.c @@ -900,7 +900,7 @@ static void IN_ProcessEvents( void ) break; case SDL_QUIT: - Sys_Quit( ); + Cbuf_ExecuteText(EXEC_NOW, "quit Closed window\n"); break; case SDL_VIDEORESIZE: diff --git a/code/sys/sys_cocoa.m b/code/sys/sys_cocoa.m deleted file mode 100644 index cfc754dd..00000000 --- a/code/sys/sys_cocoa.m +++ /dev/null @@ -1,40 +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 -=========================================================================== -*/ - -#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 ... - -#import - -void Cocoa_MsgBox( const char *text ) -{ - NSRunInformationalAlertPanel(@"ioquake3", - [NSString stringWithUTF8String:text], - @"OK", nil, nil); -} - -// end of sys_cocoa.m ... - diff --git a/code/sys/sys_local.h b/code/sys/sys_local.h index ee808d48..71dade7d 100644 --- a/code/sys/sys_local.h +++ b/code/sys/sys_local.h @@ -54,3 +54,6 @@ void Sys_PlatformInit( 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/code/sys/sys_main.c b/code/sys/sys_main.c index 8f97cebd..91c54f12 100644 --- a/code/sys/sys_main.c +++ b/code/sys/sys_main.c @@ -127,6 +127,60 @@ 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; + + fread( pidBuffer, sizeof( char ), sizeof( pidBuffer ) - 1, f ); + fclose( f ); + + pid = atoi( pidBuffer ); + if( !Sys_PIDIsRunning( pid ) ) + 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 @@ -134,7 +188,7 @@ Sys_Exit Single exit point (regular exit or in case of error) ================= */ -void Sys_Exit( int ex ) +static void Sys_Exit( int exitCode ) { CON_Shutdown( ); @@ -142,13 +196,13 @@ void Sys_Exit( int ex ) SDL_Quit( ); #endif -#ifdef NDEBUG - exit( ex ); -#else - // Cause a backtrace on error exits - assert( ex == 0 ); - exit( ex ); -#endif + if( exitCode < 2 ) + { + // Normal exit + remove( Sys_PIDFileName( ) ); + } + + exit( exitCode ); } /* @@ -158,7 +212,6 @@ Sys_Quit */ void Sys_Quit( void ) { - CL_Shutdown( ); Sys_Exit( 0 ); } @@ -287,15 +340,14 @@ void Sys_Error( const char *error, ... ) va_list argptr; char string[1024]; - CL_Shutdown (); - va_start (argptr,error); Q_vsnprintf (string, sizeof(string), error, argptr); va_end (argptr); + CL_Shutdown( string ); Sys_ErrorDialog( string ); - Sys_Exit( 1 ); + Sys_Exit( 3 ); } /* @@ -450,7 +502,7 @@ void Sys_ParseArgs( int argc, char **argv ) #else fprintf( stdout, Q3_VERSION " client (%s)\n", date ); #endif - Sys_Exit(0); + Sys_Exit( 0 ); } } } @@ -480,14 +532,16 @@ void Sys_SigHandler( int signal ) else { signalcaught = qtrue; - fprintf( stderr, "Received signal %d, exiting...\n", signal ); #ifndef DEDICATED - CL_Shutdown(); + CL_Shutdown( va( "Received signal %d", signal ) ); #endif - SV_Shutdown( "Signal caught" ); + SV_Shutdown( va( "Received signal %d", signal ) ); } - Sys_Exit( 0 ); // Exit with 0 to avoid recursive signals + if( signal == SIGTERM || signal == SIGINT ) + Sys_Exit( 1 ); + else + Sys_Exit( 2 ); } /* @@ -519,7 +573,10 @@ int main( int argc, char **argv ) if( SDL_VERSIONNUM( ver->major, ver->minor, ver->patch ) < SDL_VERSIONNUM( MINSDL_MAJOR, MINSDL_MINOR, MINSDL_PATCH ) ) { - Sys_Print( "SDL version " MINSDL_VERSION " or greater required\n" ); + 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 diff --git a/code/sys/sys_osx.m b/code/sys/sys_osx.m new file mode 100644 index 00000000..237a931e --- /dev/null +++ b/code/sys/sys_osx.m @@ -0,0 +1,131 @@ +/* +=========================================================================== +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 +=========================================================================== +*/ + +#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 ) +{ + 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]; + return DR_OK; + + case DT_YES_NO: + [alert addButtonWithTitle: @"Yes"]; + [alert addButtonWithTitle: @"No"]; + switch( [alert runModal] ) + { + default: + case NSAlertFirstButtonReturn: return DR_YES; + case NSAlertSecondButtonReturn: return DR_NO; + } + + case DT_OK_CANCEL: + [alert addButtonWithTitle: @"OK"]; + [alert addButtonWithTitle: @"Cancel"]; + + switch( [alert runModal] ) + { + default: + case NSAlertFirstButtonReturn: return DR_OK; + case NSAlertSecondButtonReturn: return DR_CANCEL; + } + } +} + +/* +================= +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/code/sys/sys_unix.c b/code/sys/sys_unix.c index c0b4ecb6..f09db7b9 100644 --- a/code/sys/sys_unix.c +++ b/code/sys/sys_unix.c @@ -68,6 +68,23 @@ char *Sys_DefaultHomePath(void) 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 @@ -428,35 +445,6 @@ void Sys_FreeFileList( char **list ) Z_Free( list ); } -#ifdef MACOS_X -/* -================= -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; -} -#endif // MACOS_X - - /* ================== Sys_Sleep @@ -517,40 +505,196 @@ void Sys_ErrorDialog( const char *error ) Sys_Print( va( "%s\n", error ) ); -#if defined(MACOS_X) && !DEDICATED - /* This function has to be in a separate file, compiled as Objective-C. */ - extern void Cocoa_MsgBox( const char *text ); - if (!com_dedicated || !com_dedicated->integer) - Cocoa_MsgBox(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... */ + // 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); + // 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. */ + // 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) { + if( write( f, buffer, size ) != size ) { Com_Printf( "ERROR: couldn't fully write to %s\n", fileName ); break; } } - close(f); + 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 @@ -611,3 +755,23 @@ void Sys_SetEnv(const char *name, const char *value) 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/code/sys/sys_win32.c b/code/sys/sys_win32.c index c4285336..74cf1d19 100644 --- a/code/sys/sys_win32.c +++ b/code/sys/sys_win32.c @@ -36,6 +36,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include #include #include +#include // Used to determine where to store user-specific files static char homePath[ MAX_OSPATH ] = { 0 }; @@ -82,6 +83,24 @@ char *Sys_DefaultHomePath( void ) 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 @@ -543,8 +562,8 @@ Display an error message */ void Sys_ErrorDialog( const char *error ) { - if( MessageBox( NULL, va( "%s. Copy console log to clipboard?", error ), - NULL, MB_YESNO|MB_ICONERROR ) == IDYES ) + if( Sys_Dialog( DT_YES_NO, va( "%s. Copy console log to clipboard?", error ), + "Error" ) == DR_YES ) { HGLOBAL memoryHandle; char *clipMemory; @@ -575,6 +594,37 @@ void Sys_ErrorDialog( const char *error ) } } +/* +============== +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 @@ -658,8 +708,43 @@ Sys_SetEnv set/unset environment variables (empty value removes it) ============== */ - void Sys_SetEnv(const char *name, const char *value) { _putenv(va("%s=%s", name, value)); } + +/* +============== +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; +}