posix_main.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059
  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 "../sys_local.h"
  22. #include <sys/types.h>
  23. #include <sys/stat.h>
  24. #include <errno.h>
  25. #include <dirent.h>
  26. #include <unistd.h>
  27. #include <sys/mman.h>
  28. #include <sys/time.h>
  29. #include <pwd.h>
  30. #include <pthread.h>
  31. #include <dlfcn.h>
  32. #include <termios.h>
  33. #include <signal.h>
  34. #include <fcntl.h>
  35. #include "posix_public.h"
  36. #define MAX_OSPATH 256
  37. #define COMMAND_HISTORY 64
  38. static int input_hide = 0;
  39. idEditField input_field;
  40. static char input_ret[256];
  41. static idStr history[ COMMAND_HISTORY ]; // cycle buffer
  42. static int history_count = 0; // buffer fill up
  43. static int history_start = 0; // current history start
  44. static int history_current = 0; // goes back in history
  45. idEditField history_backup; // the base edit line
  46. // terminal support
  47. idCVar in_tty( "in_tty", "1", CVAR_BOOL | CVAR_INIT | CVAR_SYSTEM, "terminal tab-completion and history" );
  48. static bool tty_enabled = false;
  49. static struct termios tty_tc;
  50. // pid - useful when you attach to gdb..
  51. idCVar com_pid( "com_pid", "0", CVAR_INTEGER | CVAR_INIT | CVAR_SYSTEM, "process id" );
  52. // exit - quit - error --------------------------------------------------------
  53. static int set_exit = 0;
  54. static char exit_spawn[ 1024 ];
  55. /*
  56. ================
  57. Posix_Exit
  58. ================
  59. */
  60. void Posix_Exit(int ret) {
  61. if ( tty_enabled ) {
  62. Sys_Printf( "shutdown terminal support\n" );
  63. if ( tcsetattr( 0, TCSADRAIN, &tty_tc ) == -1 ) {
  64. Sys_Printf( "tcsetattr failed: %s\n", strerror( errno ) );
  65. }
  66. }
  67. // at this point, too late to catch signals
  68. Posix_ClearSigs();
  69. if ( asyncThread.threadHandle ) {
  70. Sys_DestroyThread( asyncThread );
  71. }
  72. // process spawning. it's best when it happens after everything has shut down
  73. if ( exit_spawn[0] ) {
  74. Sys_DoStartProcess( exit_spawn, false );
  75. }
  76. // in case of signal, handler tries a common->Quit
  77. // we use set_exit to maintain a correct exit code
  78. if ( set_exit ) {
  79. exit( set_exit );
  80. }
  81. exit( ret );
  82. }
  83. /*
  84. ================
  85. Posix_SetExit
  86. ================
  87. */
  88. void Posix_SetExit(int ret) {
  89. set_exit = 0;
  90. }
  91. /*
  92. ===============
  93. Posix_SetExitSpawn
  94. set the process to be spawned when we quit
  95. ===============
  96. */
  97. void Posix_SetExitSpawn( const char *exeName ) {
  98. idStr::Copynz( exit_spawn, exeName, 1024 );
  99. }
  100. /*
  101. ==================
  102. idSysLocal::StartProcess
  103. if !quit, start the process asap
  104. otherwise, push it for execution at exit
  105. (i.e. let complete shutdown of the game and freeing of resources happen)
  106. NOTE: might even want to add a small delay?
  107. ==================
  108. */
  109. void idSysLocal::StartProcess( const char *exeName, bool quit ) {
  110. if ( quit ) {
  111. common->DPrintf( "Sys_StartProcess %s (delaying until final exit)\n", exeName );
  112. Posix_SetExitSpawn( exeName );
  113. cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "quit\n" );
  114. return;
  115. }
  116. common->DPrintf( "Sys_StartProcess %s\n", exeName );
  117. Sys_DoStartProcess( exeName );
  118. }
  119. /*
  120. ================
  121. Sys_Quit
  122. ================
  123. */
  124. void Sys_Quit(void) {
  125. Posix_Exit( EXIT_SUCCESS );
  126. }
  127. /*
  128. ================
  129. Sys_Milliseconds
  130. ================
  131. */
  132. /* base time in seconds, that's our origin
  133. timeval:tv_sec is an int:
  134. assuming this wraps every 0x7fffffff - ~68 years since the Epoch (1970) - we're safe till 2038
  135. using unsigned long data type to work right with Sys_XTimeToSysTime */
  136. unsigned long sys_timeBase = 0;
  137. /* current time in ms, using sys_timeBase as origin
  138. NOTE: sys_timeBase*1000 + curtime -> ms since the Epoch
  139. 0x7fffffff ms - ~24 days
  140. or is it 48 days? the specs say int, but maybe it's casted from unsigned int?
  141. */
  142. int Sys_Milliseconds( void ) {
  143. int curtime;
  144. struct timeval tp;
  145. gettimeofday(&tp, NULL);
  146. if (!sys_timeBase) {
  147. sys_timeBase = tp.tv_sec;
  148. return tp.tv_usec / 1000;
  149. }
  150. curtime = (tp.tv_sec - sys_timeBase) * 1000 + tp.tv_usec / 1000;
  151. return curtime;
  152. }
  153. /*
  154. ================
  155. Sys_Mkdir
  156. ================
  157. */
  158. void Sys_Mkdir( const char *path ) {
  159. mkdir(path, 0777);
  160. }
  161. /*
  162. ================
  163. Sys_ListFiles
  164. ================
  165. */
  166. int Sys_ListFiles( const char *directory, const char *extension, idStrList &list ) {
  167. struct dirent *d;
  168. DIR *fdir;
  169. bool dironly = false;
  170. char search[MAX_OSPATH];
  171. struct stat st;
  172. bool debug;
  173. list.Clear();
  174. debug = cvarSystem->GetCVarBool( "fs_debug" );
  175. if (!extension)
  176. extension = "";
  177. // passing a slash as extension will find directories
  178. if (extension[0] == '/' && extension[1] == 0) {
  179. extension = "";
  180. dironly = true;
  181. }
  182. // search
  183. // NOTE: case sensitivity of directory path can screw us up here
  184. if ((fdir = opendir(directory)) == NULL) {
  185. if (debug) {
  186. common->Printf("Sys_ListFiles: opendir %s failed\n", directory);
  187. }
  188. return -1;
  189. }
  190. while ((d = readdir(fdir)) != NULL) {
  191. idStr::snPrintf(search, sizeof(search), "%s/%s", directory, d->d_name);
  192. if (stat(search, &st) == -1)
  193. continue;
  194. if (!dironly) {
  195. idStr look(search);
  196. idStr ext;
  197. look.ExtractFileExtension(ext);
  198. if (extension[0] != '\0' && ext.Icmp(&extension[1]) != 0) {
  199. continue;
  200. }
  201. }
  202. if ((dironly && !(st.st_mode & S_IFDIR)) ||
  203. (!dironly && (st.st_mode & S_IFDIR)))
  204. continue;
  205. list.Append(d->d_name);
  206. }
  207. closedir(fdir);
  208. if ( debug ) {
  209. common->Printf( "Sys_ListFiles: %d entries in %s\n", list.Num(), directory );
  210. }
  211. return list.Num();
  212. }
  213. /*
  214. ============================================================================
  215. EVENT LOOP
  216. ============================================================================
  217. */
  218. #ifndef USE_SDL
  219. #define MAX_QUED_EVENTS 256
  220. #define MASK_QUED_EVENTS ( MAX_QUED_EVENTS - 1 )
  221. static sysEvent_t eventQue[MAX_QUED_EVENTS];
  222. static int eventHead, eventTail;
  223. /*
  224. ================
  225. Posix_QueEvent
  226. ptr should either be null, or point to a block of data that can be freed later
  227. ================
  228. */
  229. void Posix_QueEvent( sysEventType_t type, int value, int value2,
  230. int ptrLength, void *ptr ) {
  231. sysEvent_t *ev;
  232. ev = &eventQue[eventHead & MASK_QUED_EVENTS];
  233. if (eventHead - eventTail >= MAX_QUED_EVENTS) {
  234. common->Printf( "Posix_QueEvent: overflow\n" );
  235. // we are discarding an event, but don't leak memory
  236. // TTimo: verbose dropped event types?
  237. if (ev->evPtr) {
  238. Mem_Free(ev->evPtr);
  239. ev->evPtr = NULL;
  240. }
  241. eventTail++;
  242. }
  243. eventHead++;
  244. ev->evType = type;
  245. ev->evValue = value;
  246. ev->evValue2 = value2;
  247. ev->evPtrLength = ptrLength;
  248. ev->evPtr = ptr;
  249. #if 0
  250. common->Printf( "Event %d: %d %d\n", ev->evType, ev->evValue, ev->evValue2 );
  251. #endif
  252. }
  253. /*
  254. ================
  255. Sys_GetEvent
  256. ================
  257. */
  258. sysEvent_t Sys_GetEvent(void) {
  259. static sysEvent_t ev;
  260. // return if we have data
  261. if (eventHead > eventTail) {
  262. eventTail++;
  263. return eventQue[(eventTail - 1) & MASK_QUED_EVENTS];
  264. }
  265. // return the empty event with the current time
  266. memset(&ev, 0, sizeof(ev));
  267. return ev;
  268. }
  269. /*
  270. ================
  271. Sys_ClearEvents
  272. ================
  273. */
  274. void Sys_ClearEvents( void ) {
  275. eventHead = eventTail = 0;
  276. }
  277. #endif
  278. /*
  279. ================
  280. Posix_Cwd
  281. ================
  282. */
  283. const char *Posix_Cwd( void ) {
  284. static char cwd[MAX_OSPATH];
  285. getcwd( cwd, sizeof( cwd ) - 1 );
  286. cwd[MAX_OSPATH-1] = 0;
  287. return cwd;
  288. }
  289. /*
  290. =================
  291. Sys_GetMemoryStatus
  292. =================
  293. */
  294. void Sys_GetMemoryStatus( sysMemoryStats_t &stats ) {
  295. common->Printf( "FIXME: Sys_GetMemoryStatus stub\n" );
  296. }
  297. void Sys_GetCurrentMemoryStatus( sysMemoryStats_t &stats ) {
  298. common->Printf( "FIXME: Sys_GetCurrentMemoryStatus\n" );
  299. }
  300. void Sys_GetExeLaunchMemoryStatus( sysMemoryStats_t &stats ) {
  301. common->Printf( "FIXME: Sys_GetExeLaunchMemoryStatus\n" );
  302. }
  303. /*
  304. =================
  305. Sys_Init
  306. Posix_EarlyInit/Posix_LateInit is better
  307. =================
  308. */
  309. void Sys_Init( void ) { }
  310. /*
  311. =================
  312. Posix_Shutdown
  313. =================
  314. */
  315. void Posix_Shutdown( void ) {
  316. for ( int i = 0; i < COMMAND_HISTORY; i++ ) {
  317. history[ i ].Clear();
  318. }
  319. }
  320. /*
  321. =================
  322. Sys_DLL_Load
  323. TODO: OSX - use the native API instead? NSModule
  324. =================
  325. */
  326. int Sys_DLL_Load( const char *path ) {
  327. void *handle = dlopen( path, RTLD_NOW );
  328. if ( !handle ) {
  329. Sys_Printf( "dlopen '%s' failed: %s\n", path, dlerror() );
  330. }
  331. return (int)handle;
  332. }
  333. /*
  334. =================
  335. Sys_DLL_GetProcAddress
  336. =================
  337. */
  338. void* Sys_DLL_GetProcAddress( int handle, const char *sym ) {
  339. const char *error;
  340. void *ret = dlsym( (void *)handle, sym );
  341. if ((error = dlerror()) != NULL) {
  342. Sys_Printf( "dlsym '%s' failed: %s\n", sym, error );
  343. }
  344. return ret;
  345. }
  346. /*
  347. =================
  348. Sys_DLL_Unload
  349. =================
  350. */
  351. void Sys_DLL_Unload( int handle ) {
  352. dlclose( (void *)handle );
  353. }
  354. /*
  355. ================
  356. Sys_ShowConsole
  357. ================
  358. */
  359. void Sys_ShowConsole( int visLevel, bool quitOnClose ) { }
  360. // ---------------------------------------------------------------------------
  361. // only relevant when specified on command line
  362. const char *Sys_DefaultCDPath( void ) {
  363. return "";
  364. }
  365. long Sys_FileTimeStamp(FILE * fp) {
  366. struct stat st;
  367. fstat(fileno(fp), &st);
  368. return st.st_mtime;
  369. }
  370. void Sys_Sleep(int msec) {
  371. if ( msec < 20 ) {
  372. static int last = 0;
  373. int now = Sys_Milliseconds();
  374. if ( now - last > 1000 ) {
  375. Sys_Printf("WARNING: Sys_Sleep - %d < 20 msec is not portable\n", msec);
  376. last = now;
  377. }
  378. // ignore that sleep call, keep going
  379. return;
  380. }
  381. // use nanosleep? keep sleeping if signal interrupt?
  382. if (usleep(msec * 1000) == -1)
  383. Sys_Printf("usleep: %s\n", strerror(errno));
  384. }
  385. char *Sys_GetClipboardData(void) {
  386. Sys_Printf( "TODO: Sys_GetClipboardData\n" );
  387. return NULL;
  388. }
  389. void Sys_SetClipboardData( const char *string ) {
  390. Sys_Printf( "TODO: Sys_SetClipboardData\n" );
  391. }
  392. // stub pretty much everywhere - heavy calling
  393. void Sys_FlushCacheMemory(void *base, int bytes)
  394. {
  395. // Sys_Printf("Sys_FlushCacheMemory stub\n");
  396. }
  397. bool Sys_FPU_StackIsEmpty( void ) {
  398. return true;
  399. }
  400. void Sys_FPU_ClearStack( void ) {
  401. }
  402. const char *Sys_FPU_GetState( void ) {
  403. return "";
  404. }
  405. void Sys_FPU_SetPrecision( int precision ) {
  406. }
  407. /*
  408. ================
  409. Sys_LockMemory
  410. ================
  411. */
  412. bool Sys_LockMemory( void *ptr, int bytes ) {
  413. return true;
  414. }
  415. /*
  416. ================
  417. Sys_UnlockMemory
  418. ================
  419. */
  420. bool Sys_UnlockMemory( void *ptr, int bytes ) {
  421. return true;
  422. }
  423. /*
  424. ================
  425. Sys_SetPhysicalWorkMemory
  426. ================
  427. */
  428. void Sys_SetPhysicalWorkMemory( int minBytes, int maxBytes ) {
  429. common->DPrintf( "TODO: Sys_SetPhysicalWorkMemory\n" );
  430. }
  431. /*
  432. ===========
  433. Sys_GetDriveFreeSpace
  434. return in MegaBytes
  435. ===========
  436. */
  437. int Sys_GetDriveFreeSpace( const char *path ) {
  438. common->DPrintf( "TODO: Sys_GetDriveFreeSpace\n" );
  439. return 1000 * 1024;
  440. }
  441. /*
  442. ================
  443. Sys_AlreadyRunning
  444. return true if there is a copy of D3 running already
  445. ================
  446. */
  447. bool Sys_AlreadyRunning( void ) {
  448. return false;
  449. }
  450. /*
  451. ===============
  452. Posix_EarlyInit
  453. ===============
  454. */
  455. void Posix_EarlyInit( void ) {
  456. memset( &asyncThread, 0, sizeof( asyncThread ) );
  457. exit_spawn[0] = '\0';
  458. Posix_InitSigs();
  459. // set the base time
  460. Sys_Milliseconds();
  461. Posix_InitPThreads();
  462. }
  463. /*
  464. ===============
  465. Posix_LateInit
  466. ===============
  467. */
  468. void Posix_LateInit( void ) {
  469. Posix_InitConsoleInput();
  470. com_pid.SetInteger( getpid() );
  471. common->Printf( "pid: %d\n", com_pid.GetInteger() );
  472. common->Printf( "%d MB System Memory\n", Sys_GetSystemRam() );
  473. #ifndef ID_DEDICATED
  474. common->Printf( "%d MB Video Memory\n", Sys_GetVideoRam() );
  475. #endif
  476. Posix_StartAsyncThread( );
  477. }
  478. /*
  479. ===============
  480. Posix_InitConsoleInput
  481. ===============
  482. */
  483. void Posix_InitConsoleInput( void ) {
  484. struct termios tc;
  485. if ( in_tty.GetBool() ) {
  486. if ( isatty( STDIN_FILENO ) != 1 ) {
  487. Sys_Printf( "terminal support disabled: stdin is not a tty\n" );
  488. in_tty.SetBool( false );
  489. return;
  490. }
  491. if ( tcgetattr( 0, &tty_tc ) == -1 ) {
  492. Sys_Printf( "tcgetattr failed. disabling terminal support: %s\n", strerror( errno ) );
  493. in_tty.SetBool( false );
  494. return;
  495. }
  496. // make the input non blocking
  497. if ( fcntl( STDIN_FILENO, F_SETFL, fcntl( STDIN_FILENO, F_GETFL, 0 ) | O_NONBLOCK ) == -1 ) {
  498. Sys_Printf( "fcntl STDIN non blocking failed. disabling terminal support: %s\n", strerror( errno ) );
  499. in_tty.SetBool( false );
  500. return;
  501. }
  502. tc = tty_tc;
  503. /*
  504. ECHO: don't echo input characters
  505. ICANON: enable canonical mode. This enables the special
  506. characters EOF, EOL, EOL2, ERASE, KILL, REPRINT,
  507. STATUS, and WERASE, and buffers by lines.
  508. ISIG: when any of the characters INTR, QUIT, SUSP, or
  509. DSUSP are received, generate the corresponding signal
  510. */
  511. tc.c_lflag &= ~(ECHO | ICANON);
  512. /*
  513. ISTRIP strip off bit 8
  514. INPCK enable input parity checking
  515. */
  516. tc.c_iflag &= ~(ISTRIP | INPCK);
  517. tc.c_cc[VMIN] = 1;
  518. tc.c_cc[VTIME] = 0;
  519. if ( tcsetattr( 0, TCSADRAIN, &tc ) == -1 ) {
  520. Sys_Printf( "tcsetattr failed: %s\n", strerror( errno ) );
  521. Sys_Printf( "terminal support may not work correctly. Use +set in_tty 0 to disable it\n" );
  522. }
  523. #if 0
  524. // make the output non blocking
  525. if ( fcntl( STDOUT_FILENO, F_SETFL, fcntl( STDOUT_FILENO, F_GETFL, 0 ) | O_NONBLOCK ) == -1 ) {
  526. Sys_Printf( "fcntl STDOUT non blocking failed: %s\n", strerror( errno ) );
  527. }
  528. #endif
  529. tty_enabled = true;
  530. // check the terminal type for the supported ones
  531. char *term = getenv( "TERM" );
  532. if ( term ) {
  533. if ( strcmp( term, "linux" ) && strcmp( term, "xterm" ) && strcmp( term, "xterm-color" ) && strcmp( term, "screen" ) ) {
  534. Sys_Printf( "WARNING: terminal type '%s' is unknown. terminal support may not work correctly\n", term );
  535. }
  536. }
  537. Sys_Printf( "terminal support enabled ( use +set in_tty 0 to disabled )\n" );
  538. } else {
  539. Sys_Printf( "terminal support disabled\n" );
  540. }
  541. }
  542. /*
  543. ================
  544. terminal support utilities
  545. ================
  546. */
  547. void tty_Del() {
  548. char key;
  549. key = '\b';
  550. write( STDOUT_FILENO, &key, 1 );
  551. key = ' ';
  552. write( STDOUT_FILENO, &key, 1 );
  553. key = '\b';
  554. write( STDOUT_FILENO, &key, 1 );
  555. }
  556. void tty_Left() {
  557. char key = '\b';
  558. write( STDOUT_FILENO, &key, 1 );
  559. }
  560. void tty_Right() {
  561. char key = 27;
  562. write( STDOUT_FILENO, &key, 1 );
  563. write( STDOUT_FILENO, "[C", 2 );
  564. }
  565. // clear the display of the line currently edited
  566. // bring cursor back to beginning of line
  567. void tty_Hide() {
  568. int len, buf_len;
  569. if ( !tty_enabled ) {
  570. return;
  571. }
  572. if ( input_hide ) {
  573. input_hide++;
  574. return;
  575. }
  576. // clear after cursor
  577. len = strlen( input_field.GetBuffer() ) - input_field.GetCursor();
  578. while ( len > 0 ) {
  579. tty_Right();
  580. len--;
  581. }
  582. buf_len = strlen( input_field.GetBuffer() );
  583. while ( buf_len > 0 ) {
  584. tty_Del();
  585. buf_len--;
  586. }
  587. input_hide++;
  588. }
  589. // show the current line
  590. void tty_Show() {
  591. // int i;
  592. if ( !tty_enabled ) {
  593. return;
  594. }
  595. assert( input_hide > 0 );
  596. input_hide--;
  597. if ( input_hide == 0 ) {
  598. char *buf = input_field.GetBuffer();
  599. if ( buf[0] ) {
  600. write( STDOUT_FILENO, buf, strlen( buf ) );
  601. int back = strlen( buf ) - input_field.GetCursor();
  602. while ( back > 0 ) {
  603. tty_Left();
  604. back--;
  605. }
  606. }
  607. }
  608. }
  609. void tty_FlushIn() {
  610. char key;
  611. while ( read(0, &key, 1) != -1 ) {
  612. Sys_Printf( "'%d' ", key );
  613. }
  614. Sys_Printf( "\n" );
  615. }
  616. /*
  617. ================
  618. Sys_ConsoleInput
  619. Checks for a complete line of text typed in at the console.
  620. Return NULL if a complete line is not ready.
  621. ================
  622. */
  623. char *Sys_ConsoleInput( void ) {
  624. if ( tty_enabled ) {
  625. int ret;
  626. char key;
  627. bool hidden = false;
  628. while ( ( ret = read( STDIN_FILENO, &key, 1 ) ) > 0 ) {
  629. if ( !hidden ) {
  630. tty_Hide();
  631. hidden = true;
  632. }
  633. switch ( key ) {
  634. case 1:
  635. input_field.SetCursor( 0 );
  636. break;
  637. case 5:
  638. input_field.SetCursor( strlen( input_field.GetBuffer() ) );
  639. break;
  640. case 127:
  641. case 8:
  642. input_field.CharEvent( K_BACKSPACE );
  643. break;
  644. case '\n':
  645. idStr::Copynz( input_ret, input_field.GetBuffer(), sizeof( input_ret ) );
  646. assert( hidden );
  647. tty_Show();
  648. write( STDOUT_FILENO, &key, 1 );
  649. input_field.Clear();
  650. if ( history_count < COMMAND_HISTORY ) {
  651. history[ history_count ] = input_ret;
  652. history_count++;
  653. } else {
  654. history[ history_start ] = input_ret;
  655. history_start++;
  656. history_start %= COMMAND_HISTORY;
  657. }
  658. history_current = 0;
  659. return input_ret;
  660. case '\t':
  661. input_field.AutoComplete();
  662. break;
  663. case 27: {
  664. // enter escape sequence mode
  665. ret = read( STDIN_FILENO, &key, 1 );
  666. if ( ret <= 0 ) {
  667. Sys_Printf( "dropping sequence: '27' " );
  668. tty_FlushIn();
  669. assert( hidden );
  670. tty_Show();
  671. return NULL;
  672. }
  673. switch ( key ) {
  674. case 79:
  675. ret = read( STDIN_FILENO, &key, 1 );
  676. if ( ret <= 0 ) {
  677. Sys_Printf( "dropping sequence: '27' '79' " );
  678. tty_FlushIn();
  679. assert( hidden );
  680. tty_Show();
  681. return NULL;
  682. }
  683. switch ( key ) {
  684. case 72:
  685. // xterm only
  686. input_field.SetCursor( 0 );
  687. break;
  688. case 70:
  689. // xterm only
  690. input_field.SetCursor( strlen( input_field.GetBuffer() ) );
  691. break;
  692. default:
  693. Sys_Printf( "dropping sequence: '27' '79' '%d' ", key );
  694. tty_FlushIn();
  695. assert( hidden );
  696. tty_Show();
  697. return NULL;
  698. }
  699. break;
  700. case 91: {
  701. ret = read( STDIN_FILENO, &key, 1 );
  702. if ( ret <= 0 ) {
  703. Sys_Printf( "dropping sequence: '27' '91' " );
  704. tty_FlushIn();
  705. assert( hidden );
  706. tty_Show();
  707. return NULL;
  708. }
  709. switch ( key ) {
  710. case 49: {
  711. ret = read( STDIN_FILENO, &key, 1 );
  712. if ( ret <= 0 || key != 126 ) {
  713. Sys_Printf( "dropping sequence: '27' '91' '49' '%d' ", key );
  714. tty_FlushIn();
  715. assert( hidden );
  716. tty_Show();
  717. return NULL;
  718. }
  719. // only screen and linux terms
  720. input_field.SetCursor( 0 );
  721. break;
  722. }
  723. case 50: {
  724. ret = read( STDIN_FILENO, &key, 1 );
  725. if ( ret <= 0 || key != 126 ) {
  726. Sys_Printf( "dropping sequence: '27' '91' '50' '%d' ", key );
  727. tty_FlushIn();
  728. assert( hidden );
  729. tty_Show();
  730. return NULL;
  731. }
  732. // all terms
  733. input_field.KeyDownEvent( K_INS );
  734. break;
  735. }
  736. case 52: {
  737. ret = read( STDIN_FILENO, &key, 1 );
  738. if ( ret <= 0 || key != 126 ) {
  739. Sys_Printf( "dropping sequence: '27' '91' '52' '%d' ", key );
  740. tty_FlushIn();
  741. assert( hidden );
  742. tty_Show();
  743. return NULL;
  744. }
  745. // only screen and linux terms
  746. input_field.SetCursor( strlen( input_field.GetBuffer() ) );
  747. break;
  748. }
  749. case 51: {
  750. ret = read( STDIN_FILENO, &key, 1 );
  751. if ( ret <= 0 ) {
  752. Sys_Printf( "dropping sequence: '27' '91' '51' " );
  753. tty_FlushIn();
  754. assert( hidden );
  755. tty_Show();
  756. return NULL;
  757. }
  758. if ( key == 126 ) {
  759. input_field.KeyDownEvent( K_DEL );
  760. break;
  761. }
  762. Sys_Printf( "dropping sequence: '27' '91' '51' '%d'", key );
  763. tty_FlushIn();
  764. assert( hidden );
  765. tty_Show();
  766. return NULL;
  767. }
  768. case 65:
  769. case 66: {
  770. // history
  771. if ( history_current == 0 ) {
  772. history_backup = input_field;
  773. }
  774. if ( key == 65 ) {
  775. // up
  776. history_current++;
  777. } else {
  778. // down
  779. history_current--;
  780. }
  781. // history_current cycle:
  782. // 0: current edit
  783. // 1 .. Min( COMMAND_HISTORY, history_count ): back in history
  784. if ( history_current < 0 ) {
  785. history_current = Min( COMMAND_HISTORY, history_count );
  786. } else {
  787. history_current %= Min( COMMAND_HISTORY, history_count ) + 1;
  788. }
  789. int index = -1;
  790. if ( history_current == 0 ) {
  791. input_field = history_backup;
  792. } else {
  793. index = history_start + Min( COMMAND_HISTORY, history_count ) - history_current;
  794. index %= COMMAND_HISTORY;
  795. assert( index >= 0 && index < COMMAND_HISTORY );
  796. input_field.SetBuffer( history[ index ] );
  797. }
  798. assert( hidden );
  799. tty_Show();
  800. return NULL;
  801. }
  802. case 67:
  803. input_field.KeyDownEvent( K_RIGHTARROW );
  804. break;
  805. case 68:
  806. input_field.KeyDownEvent( K_LEFTARROW );
  807. break;
  808. default:
  809. Sys_Printf( "dropping sequence: '27' '91' '%d' ", key );
  810. tty_FlushIn();
  811. assert( hidden );
  812. tty_Show();
  813. return NULL;
  814. }
  815. break;
  816. }
  817. default:
  818. Sys_Printf( "dropping sequence: '27' '%d' ", key );
  819. tty_FlushIn();
  820. assert( hidden );
  821. tty_Show();
  822. return NULL;
  823. }
  824. break;
  825. }
  826. default:
  827. if ( key >= ' ' ) {
  828. input_field.CharEvent( key );
  829. break;
  830. }
  831. Sys_Printf( "dropping sequence: '%d' ", key );
  832. tty_FlushIn();
  833. assert( hidden );
  834. tty_Show();
  835. return NULL;
  836. }
  837. }
  838. if ( hidden ) {
  839. tty_Show();
  840. }
  841. return NULL;
  842. } else {
  843. // disabled on OSX. works fine from a terminal, but launching from Finder is causing trouble
  844. // I'm pretty sure it could be re-enabled if needed, and just handling the Finder failure case right (TTimo)
  845. #ifndef MACOS_X
  846. // no terminal support - read only complete lines
  847. int len;
  848. fd_set fdset;
  849. struct timeval timeout;
  850. FD_ZERO( &fdset );
  851. FD_SET( STDIN_FILENO, &fdset );
  852. timeout.tv_sec = 0;
  853. timeout.tv_usec = 0;
  854. if ( select( 1, &fdset, NULL, NULL, &timeout ) == -1 || !FD_ISSET( 0, &fdset ) ) {
  855. return NULL;
  856. }
  857. len = read( 0, input_ret, sizeof( input_ret ) );
  858. if ( len == 0 ) {
  859. // EOF
  860. return NULL;
  861. }
  862. if ( len < 1 ) {
  863. Sys_Printf( "read failed: %s\n", strerror( errno ) ); // something bad happened, cancel this line and print an error
  864. return NULL;
  865. }
  866. if ( len == sizeof( input_ret ) ) {
  867. Sys_Printf( "read overflow\n" ); // things are likely to break, as input will be cut into pieces
  868. }
  869. input_ret[ len-1 ] = '\0'; // rip off the \n and terminate
  870. return input_ret;
  871. #endif
  872. }
  873. return NULL;
  874. }
  875. #ifndef USE_SDL
  876. /*
  877. called during frame loops, pacifier updates etc.
  878. this is only for console input polling and misc mouse grab tasks
  879. the actual mouse and keyboard input is in the Sys_Poll logic
  880. */
  881. void Sys_GenerateEvents( void ) {
  882. char *s;
  883. if ( ( s = Sys_ConsoleInput() ) ) {
  884. char *b;
  885. int len;
  886. len = strlen( s ) + 1;
  887. b = (char *)Mem_Alloc( len );
  888. strcpy( b, s );
  889. Posix_QueEvent( SE_CONSOLE, 0, 0, len, b );
  890. }
  891. }
  892. #endif
  893. /*
  894. ===============
  895. low level output
  896. ===============
  897. */
  898. void Sys_DebugPrintf( const char *fmt, ... ) {
  899. va_list argptr;
  900. tty_Hide();
  901. va_start( argptr, fmt );
  902. vprintf( fmt, argptr );
  903. va_end( argptr );
  904. tty_Show();
  905. }
  906. void Sys_DebugVPrintf( const char *fmt, va_list arg ) {
  907. tty_Hide();
  908. vprintf( fmt, arg );
  909. tty_Show();
  910. }
  911. void Sys_Printf(const char *msg, ...) {
  912. va_list argptr;
  913. tty_Hide();
  914. va_start( argptr, msg );
  915. vprintf( msg, argptr );
  916. va_end( argptr );
  917. tty_Show();
  918. }
  919. void Sys_VPrintf(const char *msg, va_list arg) {
  920. tty_Hide();
  921. vprintf(msg, arg);
  922. tty_Show();
  923. }
  924. /*
  925. ================
  926. Sys_Error
  927. ================
  928. */
  929. void Sys_Error(const char *error, ...) {
  930. va_list argptr;
  931. Sys_Printf( "Sys_Error: " );
  932. va_start( argptr, error );
  933. Sys_DebugVPrintf( error, argptr );
  934. va_end( argptr );
  935. Sys_Printf( "\n" );
  936. Posix_Exit( EXIT_FAILURE );
  937. }
  938. /*
  939. ===============
  940. Sys_FreeOpenAL
  941. ===============
  942. */
  943. void Sys_FreeOpenAL( void ) { }