/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef RTCTIMER_LINUX #include #include #include #endif #if defined(__FreeBSD__) #include #endif #include "../qcommon/qcommon.h" #include "glob.h" cvar_t *nostdout; unsigned sys_frame_time; uid_t saved_euid; qboolean stdin_active = qtrue; #ifdef RTCTIMER_LINUX /* needed for RTC timer */ static int rtc_rate = -1; static int rtc_fd = 0; /* file descriptor for rtc device */ #endif // ======================================================================= // General routines // ======================================================================= void Sys_ConsoleOutput (char *string) { if (nostdout && nostdout->integer) return; fputs (string, stdout); } /* ================= Sys_Quit ================= */ void Sys_Quit (void) { fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY); Qcommon_Shutdown (); #ifdef RTCTIMER_LINUX if (rtc_fd) { close(rtc_fd); rtc_fd = 0; } #endif _exit(0); } /* ================= Sys_Init ================= */ void Sys_Init(void) { } /* ================= Sys_Error ================= */ void Sys_Error (char *error, ...) { va_list argptr; char string[1024]; // change stdin to non blocking fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY); CL_Shutdown (); va_start (argptr, error); vsnprintf (string, 1024, error, argptr); va_end (argptr); fprintf (stderr, "Error: %s\n", string); Qcommon_Shutdown (); #ifdef RTCTIMER_LINUX if (rtc_fd) { close(rtc_fd); rtc_fd = 0; } #endif _exit (1); } /* ================ Sys_Milliseconds ================ */ unsigned int curtime; #ifdef RTCTIMER_LINUX /* RTC timer code is adapted from EzQuake */ static void Milliseconds_RTC (void) { /* rtc timer vars */ unsigned long curticks = 0; struct pollfd pfd; static unsigned long totalticks; pfd.fd = rtc_fd; pfd.events = POLLIN | POLLERR; while (poll(&pfd, 1, 100000) < 0) { if ((errno = EINTR)) continue; /* happens with gdb or signal exiting */ else Sys_Error("Poll call on RTC timer failed!\n"); } read(rtc_fd, &curticks, sizeof(curticks)); curticks = curticks >> 8; /* knock out info byte */ totalticks += curticks; // TODO handle the wrap curtime = totalticks / (rtc_rate/1000.0); } #endif #ifdef RTCTIMER_LINUX static void Milliseconds_GTOD (void) { #else unsigned int Sys_Milliseconds (void) { #endif struct timeval tp; struct timezone tzp; static unsigned int secbase; gettimeofday(&tp, &tzp); if (!secbase) { secbase = tp.tv_sec; curtime = tp.tv_usec/1000; return curtime; } // TODO handle the wrap curtime = (tp.tv_sec - secbase)*1000 + tp.tv_usec/1000; #ifndef RTCTIMER_LINUX return curtime; #endif } #ifdef RTCTIMER_LINUX unsigned int Sys_Milliseconds (void) { if (rtc_fd) Milliseconds_RTC(); else Milliseconds_GTOD(); return curtime; } #endif void Sys_Mkdir (const char *path) { mkdir (path, 0777); } void floating_point_exception_handler (int whatever) { signal (SIGFPE, floating_point_exception_handler); } char *Sys_ConsoleInput(void) { static char text[256]; int len; fd_set fdset; struct timeval timeout; if (!dedicated || !dedicated->integer) return NULL; if (!stdin_active) return NULL; FD_ZERO (&fdset); FD_SET (0, &fdset); // stdin timeout.tv_sec = 0; timeout.tv_usec = 0; if (select (1, &fdset, NULL, NULL, &timeout) == -1 || !FD_ISSET(0, &fdset)) return NULL; len = read (0, 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; } /* ======================================================================== DLL ======================================================================== */ /* ================= Sys_UnloadLibrary ================= */ void Sys_UnloadLibrary( void **lib ) { if( lib && *lib ) { if( dlclose( *lib ) ) Com_Error( ERR_FATAL, "dlclose failed" ); *lib = NULL; } } /* ================= Sys_LoadLibrary ================= */ void *Sys_LoadLibrary( char *name, dllfunc_t *funcs ) { void *lib; dllfunc_t *func; if( !name || !name[0] || !funcs ) return NULL; Com_DPrintf( "LoadLibrary (%s)\n", name ); lib = dlopen( name, RTLD_NOW ); if( !lib ) { // wsw : pb : error handling Com_Printf( "LoadLibrary (%s):(%s)\n", name, dlerror() ); return NULL; } for( func = funcs; func->name; func++ ) { *(func->funcPointer) = ( void * )dlsym( lib, func->name ); if( !(*(func->funcPointer)) ) { Sys_UnloadLibrary( &lib ); Com_Error( ERR_FATAL, "%s: dlsym failed for %s", name, func->name ); } } return lib; } /*****************************************************************************/ static void *game_library = NULL; static void *cgame_library = NULL; static void *ui_library = NULL; #ifdef __cplusplus # define EXTERN_API_FUNC extern "C" #else # define EXTERN_API_FUNC extern #endif /* ================= Sys_UnloadGameLibrary ================= */ void Sys_UnloadGameLibrary (gamelib_t gamelib) { // Prevent compiler warning void **lib = NULL; switch( gamelib ) { case LIB_GAME: lib = &game_library; #ifdef GAME_HARD_LINKED *lib = NULL; #endif break; case LIB_CGAME: lib = &cgame_library; #ifdef CGAME_HARD_LINKED *lib = NULL; #endif break; case LIB_UI: lib = &ui_library; #ifdef UI_HARD_LINKED *lib = NULL; #endif break; default: assert( 0 ); } if( *lib ) { if( dlclose (*lib) ) Com_Error (ERR_FATAL, "dlclose failed"); *lib = NULL; } } /* ================= Sys_LoadGameLibrary ================= */ void *Sys_LoadGameLibrary (gamelib_t gamelib, void *parms) { char name[MAX_OSPATH]; char cwd[MAX_OSPATH]; char *path; void *(*APIfunc) (void *); void **lib = NULL; char *libname = NULL; char *apifuncname = NULL; #if defined __FreeBSD__ #if defined __i386__ #define ARCH "i386" #ifdef NDEBUG const char *debugdir = "freebsd_releasei386"; #else const char *debugdir = "freebsd_debugi386"; #endif #elif defined __x86_64__ #define ARCH "x86_64" #ifdef NDEBUG const char *debugdir = "releasex86_64"; #else const char *debugdir = "debugx86_64"; #endif #endif /*__x86_64__ */ #elif defined __i386__ #define ARCH "i386" #ifdef NDEBUG const char *debugdir = "releasei386"; #else const char *debugdir = "debugi386"; #endif #elif defined __x86_64__ #define ARCH "x86_64" #ifdef NDEBUG const char *debugdir = "releasex86_64"; #else const char *debugdir = "debugx86_64"; #endif #elif defined __alpha__ #define ARCH "axp" #ifdef NDEBUG const char *debugdir = "releaseaxp"; #else const char *debugdir = "debugaxp"; #endif #elif defined __powerpc__ #define ARCH "ppc" #ifdef NDEBUG const char *debugdir = "releaseppc"; #else const char *debugdir = "debugppc"; #endif #elif defined __sparc__ #define ARCH "sparc" #ifdef NDEBUG const char *debugdir = "releasepsparc"; #else const char *debugdir = "debugpsparc"; #endif #else #define ARCH "UNKNOW" #ifdef NDEBUG const char *debugdir = "release"; #else const char *debugdir = "debug"; #endif #endif APIfunc = NULL; switch( gamelib ) { case LIB_GAME: { #ifdef GAME_HARD_LINKED EXTERN_API_FUNC void *GetGameAPI( void * ); APIfunc = GetGameAPI; #endif lib = &game_library; libname = "game_" ARCH ".so"; apifuncname = "GetGameAPI"; break; } case LIB_CGAME: { #ifdef CGAME_HARD_LINKED EXTERN_API_FUNC void *GetCGameAPI( void * ); APIfunc = GetCGameAPI; #endif lib = &cgame_library; libname = "cgame_" ARCH ".so"; apifuncname = "GetCGameAPI"; break; } case LIB_UI: { #ifdef UI_HARD_LINKED EXTERN_API_FUNC void *GetUIAPI( void * ); APIfunc = GetUIAPI; #endif lib = &ui_library; libname = "ui_" ARCH ".so"; apifuncname = "GetUIAPI"; break; } default: assert( 0 ); } if (*lib) Com_Error (ERR_FATAL, "Sys_LoadGameLibrary without Sys_UnloadGameLibrary"); if (APIfunc) { *lib = ( void * )1; return APIfunc (parms); } // check the current debug directory first for development purposes getcwd (cwd, sizeof(cwd)); Q_snprintfz (name, sizeof(name), "%s/%s/%s", cwd, debugdir, libname); *lib = dlopen (name, RTLD_NOW); if (!*lib) { // now run through the search paths path = NULL; while (1) { path = FS_NextPath (path); if (!path) return NULL; // couldn't find one anywhere Q_snprintfz (name, sizeof(name), "%s/%s", path, libname); *lib = dlopen (name, RTLD_NOW); if (*lib) { break; } // wsw : pb : error handling Com_Printf ("LoadLibrary (%s):(%s)\n", name, dlerror() ); } } APIfunc = (void *)dlsym (*lib, apifuncname); if (!APIfunc) { Sys_UnloadGameLibrary (gamelib); return NULL; } return APIfunc(parms); } //=============================================================================== static char findbase[MAX_OSPATH]; static char findpath[MAX_OSPATH]; static char findpattern[MAX_OSPATH]; static DIR *fdir; static qboolean CompareAttributes(char *path, char *name, unsigned musthave, unsigned canthave ) { struct stat st; char fn[MAX_OSPATH]; // . and .. never match if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) return qfalse; Q_snprintfz( fn, sizeof(fn), "%s/%s", path, name ); if (stat(fn, &st) == -1) { Com_Printf( "Warning, stat failed: %s\n", fn ); return qfalse; // shouldn't happen } if ( ( st.st_mode & S_IFDIR ) && ( canthave & SFF_SUBDIR ) ) return qfalse; if ( ( musthave & SFF_SUBDIR ) && !( st.st_mode & S_IFDIR ) ) return qfalse; return qtrue; } char *Sys_FindFirst (char *path, unsigned musthave, unsigned canhave) { struct dirent *d; char *p; if (fdir) Sys_Error ("Sys_BeginFind without close"); // COM_FilePath (path, findbase); Q_strncpyz(findbase, path, sizeof(findbase)); if ((p = strrchr(findbase, '/')) != NULL) { *p = 0; Q_strncpyz(findpattern, p + 1, sizeof(findpattern)); } else Q_strncpyz(findpattern, "*", sizeof(findpattern)); if (strcmp(findpattern, "*.*") == 0) Q_strncpyz(findpattern, "*", sizeof(findpattern)); if ((fdir = opendir(findbase)) == NULL) return NULL; while ((d = readdir(fdir)) != NULL) { if (!*findpattern || glob_match(findpattern, d->d_name)) { // if (*findpattern) // printf("%s matched %s\n", findpattern, d->d_name); if (CompareAttributes(findbase, d->d_name, musthave, canhave)) { sprintf (findpath, "%s/%s", findbase, d->d_name); return findpath; } } } return NULL; } char *Sys_FindNext (unsigned musthave, unsigned canhave) { struct dirent *d; if (fdir == NULL) return NULL; while ((d = readdir(fdir)) != NULL) { if (!*findpattern || glob_match(findpattern, d->d_name)) { // if (*findpattern) // printf("%s matched %s\n", findpattern, d->d_name); if (CompareAttributes(findbase, d->d_name, musthave, canhave)) { sprintf (findpath, "%s/%s", findbase, d->d_name); return findpath; } } } return NULL; } void Sys_FindClose (void) { if (fdir != NULL) closedir(fdir); fdir = NULL; } /* ================= Sys_GetHomeDirectory ================= */ char *Sys_GetHomeDirectory (void) { return getenv( "HOME" ); } //=============================================================================== /* ================= Sys_GetClipboardData ================= */ char *Sys_GetClipboardData(void) { #if 0 Window sowner; Atom type, property; unsigned long len, bytes_left, tmp; unsigned char *data; int format, result; char *ret = NULL; if (!x11display.dpy && x11display.win) return NULL; sowner = XGetSelectionOwner (x11display.dpy, XA_PRIMARY); if (sowner != None) { property = XInternAtom(x11display.dpy, "GETCLIPBOARDDATA_PROP", False); XConvertSelection (x11display.dpy, XA_PRIMARY, XA_STRING, property, x11display.win, myxtime); /* myxtime == time of last X event */ XFlush (x11display.dpy); XGetWindowProperty (x11display.dpy, x11display.win, property, 0, 0, False, AnyPropertyType, &type, &format, &len, &bytes_left, &data); if (bytes_left > 0) { result = XGetWindowProperty(x11display.dpy, x11display.win, property, 0, bytes_left, True, AnyPropertyType, &type, &format, &len, &tmp, &data); if (result == Success){ ret = strdup((char *) data); } XFree(data); } } return ret; #endif return NULL; } /* ================= Sys_AppActivate ================= */ void Sys_AppActivate (void) { } /* ================= Sys_SendKeyEvents ================= */ void Sys_SendKeyEvents (void) { // grab frame time sys_frame_time = Sys_Milliseconds(); } #ifdef RTCTIMER_LINUX void rtcinit() { int retval; unsigned long tmpread; /* try accessing rtc */ rtc_fd = open("/dev/rtc", O_RDONLY); if (rtc_fd < 0) { Com_Printf("Could not open /dev/rtc\n"); rtc_fd = 0; return; } /* make sure RTC is set to high enough rate */ retval = ioctl(rtc_fd, RTC_IRQP_READ, &tmpread); if (retval < 0) { Com_Printf("Could not check RTC rate\n"); close(rtc_fd); rtc_fd = 0; return; } if (tmpread < 1000) { Com_Printf("RTC rate is not set to high enough value\n"); close(rtc_fd); rtc_fd = 0; return; } rtc_rate = tmpread; /* take ownership of rtc */ retval = fcntl(rtc_fd, F_SETOWN, getpid()); if (retval < 0) { Com_Printf("Couldn't set ownership of /dev/rtc\n"); close(rtc_fd); rtc_fd = 0; return; } /* everything is nice - now turn on the RTC's periodic timer */ retval = ioctl(rtc_fd, RTC_PIE_ON, 0); if (retval == -1) { Com_Printf("Error activating RTC timer\n"); close(rtc_fd); rtc_fd = 0; return; } Com_Printf("RTC timer enabled, running at %iHz\n", rtc_rate); } #endif /*****************************************************************************/ int main (int argc, char **argv) { unsigned int oldtime, newtime, time; #ifdef RTCTIMER_LINUX rtcinit(); #endif Qcommon_Init(argc, argv); fcntl(0, F_SETFL, fcntl (0, F_GETFL, 0) | FNDELAY); nostdout = Cvar_Get ("nostdout", "0", 0); if (!nostdout->integer) { fcntl(0, F_SETFL, fcntl (0, F_GETFL, 0) | FNDELAY); } oldtime = Sys_Milliseconds (); while (1) { // find time spent rendering last frame do { newtime = Sys_Milliseconds (); time = newtime - oldtime; // no warp test needed as unsigned } while (time < 1); Qcommon_Frame (time); oldtime = newtime; } }