main.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  1. /*
  2. ===========================================================================
  3. Doom 3 GPL Source Code
  4. Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
  6. Doom 3 Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
  17. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  18. ===========================================================================
  19. */
  20. #include "../../idlib/precompiled.h"
  21. #include "../posix/posix_public.h"
  22. #include "../sys_local.h"
  23. #include "local.h"
  24. #include <pthread.h>
  25. #include <errno.h>
  26. #include <unistd.h>
  27. #include <sys/stat.h>
  28. #include <sys/types.h>
  29. #include <fcntl.h>
  30. #ifdef ID_MCHECK
  31. #include <mcheck.h>
  32. #endif
  33. static idStr basepath;
  34. static idStr savepath;
  35. #ifndef USE_SDL
  36. /*
  37. ===========
  38. Sys_InitScanTable
  39. ===========
  40. */
  41. void Sys_InitScanTable( void ) {
  42. common->DPrintf( "TODO: Sys_InitScanTable\n" );
  43. }
  44. #endif
  45. /*
  46. =================
  47. Sys_AsyncThread
  48. =================
  49. */
  50. void Sys_AsyncThread( void ) {
  51. int now;
  52. int next;
  53. int want_sleep;
  54. // multi tick compensate for poor schedulers (Linux 2.4)
  55. int ticked, to_ticked;
  56. now = Sys_Milliseconds();
  57. ticked = now >> 4;
  58. while (1) {
  59. // sleep
  60. now = Sys_Milliseconds();
  61. next = ( now & 0xFFFFFFF0 ) + 0x10;
  62. want_sleep = ( next-now-1 ) * 1000;
  63. if ( want_sleep > 0 ) {
  64. usleep( want_sleep ); // sleep 1ms less than true target
  65. }
  66. // compensate if we slept too long
  67. now = Sys_Milliseconds();
  68. to_ticked = now >> 4;
  69. // show ticking statistics - every 100 ticks, print a summary
  70. #if 0
  71. #define STAT_BUF 100
  72. static int stats[STAT_BUF];
  73. static int counter = 0;
  74. // how many ticks to play
  75. stats[counter] = to_ticked - ticked;
  76. counter++;
  77. if (counter == STAT_BUF) {
  78. Sys_DebugPrintf("\n");
  79. for( int i = 0; i < STAT_BUF; i++) {
  80. if ( ! (i & 0xf) ) {
  81. Sys_DebugPrintf("\n");
  82. }
  83. Sys_DebugPrintf( "%d ", stats[i] );
  84. }
  85. Sys_DebugPrintf("\n");
  86. counter = 0;
  87. }
  88. #endif
  89. while ( ticked < to_ticked ) {
  90. common->Async();
  91. ticked++;
  92. Sys_TriggerEvent( TRIGGER_EVENT_ONE );
  93. }
  94. // thread exit
  95. pthread_testcancel();
  96. }
  97. }
  98. /*
  99. ==============
  100. Sys_DefaultSavePath
  101. ==============
  102. */
  103. const char *Sys_DefaultSavePath(void) {
  104. // TODO: use sdl2 path methods
  105. #if defined( ID_DEMO_BUILD )
  106. sprintf( savepath, "%s/.local/share/BlendoGames/QuadrilateralCowboy-demo", getenv( "HOME" ) );
  107. #else
  108. sprintf( savepath, "%s/.local/share/BlendoGames/QuadrilateralCowboy", getenv( "HOME" ) );
  109. #endif
  110. return savepath.c_str();
  111. }
  112. /*
  113. ==============
  114. Sys_EXEPath
  115. ==============
  116. */
  117. const char *Sys_EXEPath( void ) {
  118. static char buf[ 1024 ];
  119. idStr linkpath;
  120. int len;
  121. buf[ 0 ] = '\0';
  122. sprintf( linkpath, "/proc/%d/exe", getpid() );
  123. len = readlink( linkpath.c_str(), buf, sizeof( buf ) );
  124. if ( len == -1 ) {
  125. Sys_Printf("couldn't stat exe path link %s\n", linkpath.c_str());
  126. buf[ len ] = '\0';
  127. }
  128. return buf;
  129. }
  130. /*
  131. ================
  132. Sys_DefaultBasePath
  133. Get the default base path
  134. - binary image path
  135. - current directory
  136. - hardcoded
  137. Try to be intelligent: if there is no BASE_GAMEDIR, try the next path
  138. ================
  139. */
  140. const char *Sys_DefaultBasePath(void) {
  141. struct stat st;
  142. idStr testbase;
  143. basepath = Sys_EXEPath();
  144. if ( basepath.Length() ) {
  145. basepath.StripFilename();
  146. testbase = basepath; testbase += "/"; testbase += BASE_GAMEDIR;
  147. if ( stat( testbase.c_str(), &st ) != -1 && S_ISDIR( st.st_mode ) ) {
  148. return basepath.c_str();
  149. } else {
  150. common->Printf( "no '%s' directory in exe path %s, skipping\n", BASE_GAMEDIR, basepath.c_str() );
  151. }
  152. }
  153. if ( basepath != Posix_Cwd() ) {
  154. basepath = Posix_Cwd();
  155. testbase = basepath; testbase += "/"; testbase += BASE_GAMEDIR;
  156. if ( stat( testbase.c_str(), &st ) != -1 && S_ISDIR( st.st_mode ) ) {
  157. return basepath.c_str();
  158. } else {
  159. common->Printf("no '%s' directory in cwd path %s, skipping\n", BASE_GAMEDIR, basepath.c_str());
  160. }
  161. }
  162. common->Printf( "WARNING: using hardcoded default base path\n" );
  163. return LINUX_DEFAULT_PATH;
  164. }
  165. #ifndef USE_SDL
  166. /*
  167. ===============
  168. Sys_GetConsoleKey
  169. ===============
  170. */
  171. unsigned char Sys_GetConsoleKey( bool shifted ) {
  172. return shifted ? '~' : '`';
  173. }
  174. #endif
  175. /*
  176. ===============
  177. Sys_Shutdown
  178. ===============
  179. */
  180. void Sys_Shutdown( void ) {
  181. basepath.Clear();
  182. savepath.Clear();
  183. Posix_Shutdown();
  184. }
  185. /*
  186. ===============
  187. Sys_GetProcessorId
  188. ===============
  189. */
  190. cpuid_t Sys_GetProcessorId( void ) {
  191. return CPUID_GENERIC;
  192. }
  193. /*
  194. ===============
  195. Sys_GetProcessorString
  196. ===============
  197. */
  198. const char *Sys_GetProcessorString( void ) {
  199. return "generic";
  200. }
  201. /*
  202. ===============
  203. Sys_FPU_EnableExceptions
  204. ===============
  205. */
  206. void Sys_FPU_EnableExceptions( int exceptions ) {
  207. }
  208. /*
  209. ===============
  210. Sys_FPE_handler
  211. ===============
  212. */
  213. void Sys_FPE_handler( int signum, siginfo_t *info, void *context ) {
  214. assert( signum == SIGFPE );
  215. Sys_Printf( "FPE\n" );
  216. }
  217. /*
  218. ===============
  219. Sys_GetClockticks
  220. ===============
  221. */
  222. double Sys_GetClockTicks( void ) {
  223. #if defined( __i386__ )
  224. unsigned long lo, hi;
  225. __asm__ __volatile__ (
  226. "push %%ebx\n" \
  227. "xor %%eax,%%eax\n" \
  228. "cpuid\n" \
  229. "rdtsc\n" \
  230. "mov %%eax,%0\n" \
  231. "mov %%edx,%1\n" \
  232. "pop %%ebx\n"
  233. : "=r" (lo), "=r" (hi) );
  234. return (double) lo + (double) 0xFFFFFFFF * hi;
  235. #else
  236. #error unsupported CPU
  237. #endif
  238. }
  239. /*
  240. ===============
  241. MeasureClockTicks
  242. ===============
  243. */
  244. double MeasureClockTicks( void ) {
  245. double t0, t1;
  246. t0 = Sys_GetClockTicks( );
  247. Sys_Sleep( 1000 );
  248. t1 = Sys_GetClockTicks( );
  249. return t1 - t0;
  250. }
  251. /*
  252. ===============
  253. Sys_ClockTicksPerSecond
  254. ===============
  255. */
  256. double Sys_ClockTicksPerSecond(void) {
  257. static bool init = false;
  258. static double ret;
  259. int fd, len, pos, end;
  260. char buf[ 4096 ];
  261. if ( init ) {
  262. return ret;
  263. }
  264. fd = open( "/proc/cpuinfo", O_RDONLY );
  265. if ( fd == -1 ) {
  266. common->Printf( "couldn't read /proc/cpuinfo\n" );
  267. ret = MeasureClockTicks();
  268. init = true;
  269. common->Printf( "measured CPU frequency: %g MHz\n", ret / 1000000.0 );
  270. return ret;
  271. }
  272. len = read( fd, buf, 4096 );
  273. close( fd );
  274. pos = 0;
  275. while ( pos < len ) {
  276. if ( !idStr::Cmpn( buf + pos, "cpu MHz", 7 ) ) {
  277. pos = strchr( buf + pos, ':' ) - buf + 2;
  278. end = strchr( buf + pos, '\n' ) - buf;
  279. if ( pos < len && end < len ) {
  280. buf[end] = '\0';
  281. ret = atof( buf + pos );
  282. } else {
  283. common->Printf( "failed parsing /proc/cpuinfo\n" );
  284. ret = MeasureClockTicks();
  285. init = true;
  286. common->Printf( "measured CPU frequency: %g MHz\n", ret / 1000000.0 );
  287. return ret;
  288. }
  289. common->Printf( "/proc/cpuinfo CPU frequency: %g MHz\n", ret );
  290. ret *= 1000000;
  291. init = true;
  292. return ret;
  293. }
  294. pos = strchr( buf + pos, '\n' ) - buf + 1;
  295. }
  296. common->Printf( "failed parsing /proc/cpuinfo\n" );
  297. ret = MeasureClockTicks();
  298. init = true;
  299. common->Printf( "measured CPU frequency: %g MHz\n", ret / 1000000.0 );
  300. return ret;
  301. }
  302. /*
  303. ================
  304. Sys_GetSystemRam
  305. returns in megabytes
  306. ================
  307. */
  308. int Sys_GetSystemRam( void ) {
  309. long count, page_size;
  310. int mb;
  311. count = sysconf( _SC_PHYS_PAGES );
  312. if ( count == -1 ) {
  313. common->Printf( "GetSystemRam: sysconf _SC_PHYS_PAGES failed\n" );
  314. return 512;
  315. }
  316. page_size = sysconf( _SC_PAGE_SIZE );
  317. if ( page_size == -1 ) {
  318. common->Printf( "GetSystemRam: sysconf _SC_PAGE_SIZE failed\n" );
  319. return 512;
  320. }
  321. mb= (int)( (double)count * (double)page_size / ( 1024 * 1024 ) );
  322. // round to the nearest 16Mb
  323. mb = ( mb + 8 ) & ~15;
  324. return mb;
  325. }
  326. /*
  327. ==================
  328. Sys_DoStartProcess
  329. if we don't fork, this function never returns
  330. the no-fork lets you keep the terminal when you're about to spawn an installer
  331. if the command contains spaces, system() is used. Otherwise the more straightforward execl ( system() blows though )
  332. ==================
  333. */
  334. void Sys_DoStartProcess( const char *exeName, bool dofork ) {
  335. bool use_system = false;
  336. if ( strchr( exeName, ' ' ) ) {
  337. use_system = true;
  338. } else {
  339. // set exec rights when it's about a single file to execute
  340. struct stat buf;
  341. if ( stat( exeName, &buf ) == -1 ) {
  342. printf( "stat %s failed: %s\n", exeName, strerror( errno ) );
  343. } else {
  344. if ( chmod( exeName, buf.st_mode | S_IXUSR ) == -1 ) {
  345. printf( "cmod +x %s failed: %s\n", exeName, strerror( errno ) );
  346. }
  347. }
  348. }
  349. if ( dofork ) {
  350. switch ( fork() ) {
  351. case -1:
  352. // main thread
  353. break;
  354. case 0:
  355. if ( use_system ) {
  356. printf( "system %s\n", exeName );
  357. system( exeName );
  358. _exit( 0 );
  359. } else {
  360. printf( "execl %s\n", exeName );
  361. execl( exeName, exeName, NULL );
  362. printf( "execl failed: %s\n", strerror( errno ) );
  363. _exit( -1 );
  364. }
  365. break;
  366. }
  367. } else {
  368. if ( use_system ) {
  369. printf( "system %s\n", exeName );
  370. system( exeName );
  371. sleep( 1 ); // on some systems I've seen that starting the new process and exiting this one should not be too close
  372. } else {
  373. printf( "execl %s\n", exeName );
  374. execl( exeName, exeName, NULL );
  375. printf( "execl failed: %s\n", strerror( errno ) );
  376. }
  377. // terminate
  378. _exit( 0 );
  379. }
  380. }
  381. /*
  382. =================
  383. Sys_OpenURL
  384. =================
  385. */
  386. void idSysLocal::OpenURL( const char *url, bool quit ) {
  387. const char *script_path;
  388. idFile *script_file;
  389. char cmdline[ 1024 ];
  390. static bool quit_spamguard = false;
  391. if ( quit_spamguard ) {
  392. common->DPrintf( "Sys_OpenURL: already in a doexit sequence, ignoring %s\n", url );
  393. return;
  394. }
  395. common->Printf( "Open URL: %s\n", url );
  396. // opening an URL on *nix can mean a lot of things ..
  397. // just spawn a script instead of deciding for the user :-)
  398. // look in the savepath first, then in the basepath
  399. script_path = fileSystem->BuildOSPath( cvarSystem->GetCVarString( "fs_savepath" ), "", "openurl.sh" );
  400. script_file = fileSystem->OpenExplicitFileRead( script_path );
  401. if ( !script_file ) {
  402. script_path = fileSystem->BuildOSPath( cvarSystem->GetCVarString( "fs_basepath" ), "", "openurl.sh" );
  403. script_file = fileSystem->OpenExplicitFileRead( script_path );
  404. }
  405. if ( !script_file ) {
  406. common->Printf( "Can't find URL script 'openurl.sh' in either savepath or basepath\n" );
  407. common->Printf( "OpenURL '%s' failed\n", url );
  408. return;
  409. }
  410. fileSystem->CloseFile( script_file );
  411. // if we are going to quit, only accept a single URL before quitting and spawning the script
  412. if ( quit ) {
  413. quit_spamguard = true;
  414. }
  415. common->Printf( "URL script: %s\n", script_path );
  416. // StartProcess is going to execute a system() call with that - hence the &
  417. idStr::snPrintf( cmdline, 1024, "%s '%s' &", script_path, url );
  418. sys->StartProcess( cmdline, quit );
  419. }
  420. /*
  421. ==================
  422. Sys_DoPreferences
  423. ==================
  424. */
  425. void Sys_DoPreferences( void ) { }
  426. /*
  427. ================
  428. Sys_FPU_SetDAZ
  429. ================
  430. */
  431. void Sys_FPU_SetDAZ( bool enable ) {
  432. /*
  433. DWORD dwData;
  434. _asm {
  435. movzx ecx, byte ptr enable
  436. and ecx, 1
  437. shl ecx, 6
  438. STMXCSR dword ptr dwData
  439. mov eax, dwData
  440. and eax, ~(1<<6) // clear DAX bit
  441. or eax, ecx // set the DAZ bit
  442. mov dwData, eax
  443. LDMXCSR dword ptr dwData
  444. }
  445. */
  446. }
  447. /*
  448. ================
  449. Sys_FPU_SetFTZ
  450. ================
  451. */
  452. void Sys_FPU_SetFTZ( bool enable ) {
  453. /*
  454. DWORD dwData;
  455. _asm {
  456. movzx ecx, byte ptr enable
  457. and ecx, 1
  458. shl ecx, 15
  459. STMXCSR dword ptr dwData
  460. mov eax, dwData
  461. and eax, ~(1<<15) // clear FTZ bit
  462. or eax, ecx // set the FTZ bit
  463. mov dwData, eax
  464. LDMXCSR dword ptr dwData
  465. }
  466. */
  467. }
  468. /*
  469. ===============
  470. mem consistency stuff
  471. ===============
  472. */
  473. #ifdef ID_MCHECK
  474. const char *mcheckstrings[] = {
  475. "MCHECK_DISABLED",
  476. "MCHECK_OK",
  477. "MCHECK_FREE", // block freed twice
  478. "MCHECK_HEAD", // memory before the block was clobbered
  479. "MCHECK_TAIL" // memory after the block was clobbered
  480. };
  481. void abrt_func( mcheck_status status ) {
  482. Sys_Printf( "memory consistency failure: %s\n", mcheckstrings[ status + 1 ] );
  483. Posix_SetExit( EXIT_FAILURE );
  484. common->Quit();
  485. }
  486. #endif
  487. /*
  488. ===============
  489. main
  490. ===============
  491. */
  492. int main(int argc, const char **argv) {
  493. #ifdef ID_MCHECK
  494. // must have -lmcheck linkage
  495. mcheck( abrt_func );
  496. Sys_Printf( "memory consistency checking enabled\n" );
  497. #endif
  498. Posix_EarlyInit( );
  499. if ( argc > 1 ) {
  500. common->Init( argc-1, &argv[1], NULL );
  501. } else {
  502. common->Init( 0, NULL, NULL );
  503. }
  504. Posix_LateInit( );
  505. while (1) {
  506. common->Frame();
  507. }
  508. }