* Sys_Dialog for more user friendly error reporting

* (bug #3932) Recovery from bad video settings
This commit is contained in:
Tim Angus 2010-02-15 16:20:33 +00:00
parent 4876413217
commit 005f870ebe
13 changed files with 564 additions and 120 deletions

View file

@ -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 <Cocoa/Cocoa.h>
void Cocoa_MsgBox( const char *text )
{
NSRunInformationalAlertPanel(@"ioquake3",
[NSString stringWithUTF8String:text],
@"OK", nil, nil);
}
// end of sys_cocoa.m ...

View file

@ -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 );

View file

@ -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

131
code/sys/sys_osx.m Normal file
View file

@ -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 <Carbon/Carbon.h>
#import <Cocoa/Cocoa.h>
/*
================
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;
}

View file

@ -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;
}

View file

@ -36,6 +36,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include <conio.h>
#include <wincrypt.h>
#include <shlobj.h>
#include <psapi.h>
// 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;
}