No more busy-waiting when framerate cap is reached (in both Linux and win versions)
[divverent/darkplaces.git] / sys_linux.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <stdarg.h>
4
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include <unistd.h>
8 #include <fcntl.h>
9
10 #include <signal.h>
11 #include <limits.h>
12 #include <sys/ipc.h>
13 #include <sys/shm.h>
14 #include <sys/time.h>
15 #include <sys/wait.h>
16 #include <sys/mman.h>
17 #include <string.h>
18 #include <ctype.h>
19 #include <errno.h>
20 #include <time.h>
21
22 #include "quakedef.h"
23
24 qboolean                        isDedicated;
25
26 int nostdout = 0;
27
28 char *basedir = ".";
29 char *cachedir = "/tmp";
30
31 cvar_t  sys_linerefresh = {"sys_linerefresh","0"};// set for entity display
32
33 extern cvar_t   timestamps;
34 extern cvar_t   timeformat;
35
36 /* The translation table between the graphical font and plain ASCII  --KB */
37 static char qfont_table[256] = {
38         '\0', '#',  '#',  '#',  '#',  '.',  '#',  '#',
39         '#',  9,    10,   '#',  ' ',  13,   '.',  '.',
40         '[',  ']',  '0',  '1',  '2',  '3',  '4',  '5',
41         '6',  '7',  '8',  '9',  '.',  '<',  '=',  '>',
42         ' ',  '!',  '"',  '#',  '$',  '%',  '&',  '\'',
43         '(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',
44         '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',
45         '8',  '9',  ':',  ';',  '<',  '=',  '>',  '?',
46         '@',  'A',  'B',  'C',  'D',  'E',  'F',  'G',
47         'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',
48         'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',
49         'X',  'Y',  'Z',  '[',  '\\', ']',  '^',  '_',
50         '`',  'a',  'b',  'c',  'd',  'e',  'f',  'g',
51         'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
52         'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
53         'x',  'y',  'z',  '{',  '|',  '}',  '~',  '<',
54
55         '<',  '=',  '>',  '#',  '#',  '.',  '#',  '#',
56         '#',  '#',  ' ',  '#',  ' ',  '>',  '.',  '.',
57         '[',  ']',  '0',  '1',  '2',  '3',  '4',  '5',
58         '6',  '7',  '8',  '9',  '.',  '<',  '=',  '>',
59         ' ',  '!',  '"',  '#',  '$',  '%',  '&',  '\'',
60         '(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',
61         '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',
62         '8',  '9',  ':',  ';',  '<',  '=',  '>',  '?',
63         '@',  'A',  'B',  'C',  'D',  'E',  'F',  'G',
64         'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',
65         'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',
66         'X',  'Y',  'Z',  '[',  '\\', ']',  '^',  '_',
67         '`',  'a',  'b',  'c',  'd',  'e',  'f',  'g',
68         'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o', 
69         'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
70         'x',  'y',  'z',  '{',  '|',  '}',  '~',  '<'
71 };
72
73 // =======================================================================
74 // General routines
75 // =======================================================================
76
77 void Sys_DebugNumber(int y, int val)
78 {
79 }
80
81 /*
82 void Sys_Printf (char *fmt, ...)
83 {
84         va_list         argptr;
85         char            text[1024];
86         
87         va_start (argptr,fmt);
88         vsprintf (text,fmt,argptr);
89         va_end (argptr);
90         fprintf(stderr, "%s", text);
91         
92         Con_Print (text);
93 }
94
95 void Sys_Printf (char *fmt, ...)
96 {
97
98     va_list     argptr;
99     char        text[1024], *t_p;
100     int         l, r;
101
102         if (nostdout)
103                 return;
104
105     va_start (argptr,fmt);
106     vsprintf (text,fmt,argptr);
107     va_end (argptr);
108
109     l = strlen(text);
110     t_p = text;
111
112 // make sure everything goes through, even though we are non-blocking
113     while (l)
114     {
115         r = write (1, text, l);
116         if (r != l)
117             sleep (0);
118         if (r > 0)
119         {
120             t_p += r;
121             l -= r;
122         }
123     }
124
125 }
126 */
127
128 #define MAX_PRINT_MSG   4096
129 void Sys_Printf (char *fmt, ...)
130 {
131         va_list         argptr;
132         char            start[MAX_PRINT_MSG];   // String we started with
133         char            stamp[MAX_PRINT_MSG];   // Time stamp
134         char            final[MAX_PRINT_MSG];   // String we print
135
136         time_t          mytime = 0;
137         struct tm       *local = NULL;
138
139         unsigned char           *p;
140
141         va_start (argptr, fmt);
142         vsnprintf (start, sizeof(start), fmt, argptr);
143         va_end (argptr);
144
145         if (nostdout)
146                 return;
147
148         if (timestamps.value) {
149                 mytime = time (NULL);
150                 local = localtime (&mytime);
151                 strftime (stamp, sizeof (stamp), timeformat.string, local);
152                 
153                 snprintf (final, sizeof (final), "%s%s", stamp, start);
154         } else {
155                 snprintf (final, sizeof (final), "%s", start);
156         }
157
158         for (p = (unsigned char *) final; *p; p++) {
159                 putc (qfont_table[*p], stdout);
160         }
161         fflush (stdout);
162 }
163
164 #if 0
165 static char end1[] =
166 "\x1b[?7h\x1b[40m\x1b[2J\x1b[0;1;41m\x1b[1;1H                QUAKE: The Doomed Dimension \x1b[33mby \x1b[44mid\x1b[41m Software                      \x1b[2;1H  ----------------------------------------------------------------------------  \x1b[3;1H           CALL 1-800-IDGAMES TO ORDER OR FOR TECHNICAL SUPPORT                 \x1b[4;1H             PRICE: $45.00 (PRICES MAY VARY OUTSIDE THE US.)                    \x1b[5;1H                                                                                \x1b[6;1H  \x1b[37mYes! You only have one fourth of this incredible epic. That is because most   \x1b[7;1H   of you have paid us nothing or at most, very little. You could steal the     \x1b[8;1H   game from a friend. But we both know you'll be punished by God if you do.    \x1b[9;1H        \x1b[33mWHY RISK ETERNAL DAMNATION? CALL 1-800-IDGAMES AND BUY NOW!             \x1b[10;1H             \x1b[37mRemember, we love you almost as much as He does.                   \x1b[11;1H                                                                                \x1b[12;1H            \x1b[33mProgramming: \x1b[37mJohn Carmack, Michael Abrash, John Cash                \x1b[13;1H       \x1b[33mDesign: \x1b[37mJohn Romero, Sandy Petersen, American McGee, Tim Willits         \x1b[14;1H                     \x1b[33mArt: \x1b[37mAdrian Carmack, Kevin Cloud                           \x1b[15;1H               \x1b[33mBiz: \x1b[37mJay Wilbur, Mike Wilson, Donna Jackson                      \x1b[16;1H            \x1b[33mProjects: \x1b[37mShawn Green   \x1b[33mSupport: \x1b[37mBarrett Alexander                  \x1b[17;1H              \x1b[33mSound Effects: \x1b[37mTrent Reznor and Nine Inch Nails                   \x1b[18;1H  For other information or details on ordering outside the US, check out the    \x1b[19;1H     files accompanying QUAKE or our website at http://www.idsoftware.com.      \x1b[20;1H    \x1b[0;41mQuake is a trademark of Id Software, inc., (c)1996 Id Software, inc.        \x1b[21;1H     All rights reserved. NIN logo is a r
167 egistered trademark licensed to        \x1b[22;1H                 Nothing Interactive, Inc. All rights reserved.                 \x1b[40m\x1b[23;1H\x1b[0m";
168 static char end2[] =
169 "\x1b[?7h\x1b[40m\x1b[2J\x1b[0;1;41m\x1b[1;1H        QUAKE \x1b[33mby \x1b[44mid\x1b[41m Software                                                    \x1b[2;1H -----------------------------------------------------------------------------  \x1b[3;1H        \x1b[37mWhy did you quit from the registered version of QUAKE? Did the          \x1b[4;1H        scary monsters frighten you? Or did Mr. Sandman tug at your             \x1b[5;1H        little lids? No matter! What is important is you love our               \x1b[6;1H        game, and gave us your money. Congratulations, you are probably         \x1b[7;1H        not a thief.                                                            \x1b[8;1H                                                           Thank You.           \x1b[9;1H        \x1b[33;44mid\x1b[41m Software is:                                                         \x1b[10;1H        PROGRAMMING: \x1b[37mJohn Carmack, Michael Abrash, John Cash                    \x1b[11;1H        \x1b[33mDESIGN: \x1b[37mJohn Romero, Sandy Petersen, American McGee, Tim Willits        \x1b[12;1H        \x1b[33mART: \x1b[37mAdrian Carmack, Kevin Cloud                                        \x1b[13;1H        \x1b[33mBIZ: \x1b[37mJay Wilbur, Mike Wilson     \x1b[33mPROJECTS MAN: \x1b[37mShawn Green              \x1b[14;1H        \x1b[33mBIZ ASSIST: \x1b[37mDonna Jackson        \x1b[33mSUPPORT: \x1b[37mBarrett Alexander             \x1b[15;1H        \x1b[33mSOUND EFFECTS AND MUSIC: \x1b[37mTrent Reznor and Nine Inch Nails               \x1b[16;1H                                                                                \x1b[17;1H        If you need help running QUAKE refer to the text files in the           \x1b[18;1H        QUAKE directory, or our website at http://www.idsoftware.com.           \x1b[19;1H        If all else fails, call our technical support at 1-800-IDGAMES.         \x1b[20;1H      \x1b[0;41mQuake is a trademark of Id Software, inc., (c)1996 Id Software, inc.      \x1b[21;1H        All rights reserved. N
170 IN logo is a registered trademark licensed        \x1b[22;1H             to Nothing Interactive, Inc. All rights reserved.                  \x1b[23;1H\x1b[40m\x1b[0m";
171
172 #endif
173 void Sys_Quit (void)
174 {
175         Host_Shutdown();
176     fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY);
177 #if 0
178         if (registered.value)
179                 printf("%s", end2);
180         else
181                 printf("%s", end1);
182 #endif
183         fflush(stdout);
184         exit(0);
185 }
186
187 void Sys_Init(void)
188 {
189 }
190
191 void Sys_Error (char *error, ...)
192
193     va_list     argptr;
194     char        string[1024];
195
196 // change stdin to non blocking
197     fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY);
198     
199     va_start (argptr,error);
200     vsprintf (string,error,argptr);
201     va_end (argptr);
202         fprintf(stderr, "Error: %s\n", string);
203
204         Host_Shutdown ();
205         exit (1);
206
207
208
209 void Sys_Warn (char *warning, ...)
210
211     va_list     argptr;
212     char        string[1024];
213     
214     va_start (argptr,warning);
215     vsprintf (string,warning,argptr);
216     va_end (argptr);
217         fprintf(stderr, "Warning: %s", string);
218
219
220 /*
221 ============
222 Sys_FileTime
223
224 returns -1 if not present
225 ============
226 */
227 int     Sys_FileTime (char *path)
228 {
229         struct  stat    buf;
230         
231         if (stat (path,&buf) == -1)
232                 return -1;
233         
234         return buf.st_mtime;
235 }
236
237
238 void Sys_mkdir (char *path)
239 {
240     mkdir (path, 0777);
241 }
242
243 int Sys_FileOpenRead (char *path, int *handle)
244 {
245         int     h;
246         struct stat     fileinfo;
247     
248         
249         h = open (path, O_RDONLY, 0666);
250         *handle = h;
251         if (h == -1)
252                 return -1;
253         
254         if (fstat (h,&fileinfo) == -1)
255                 Sys_Error ("Error fstating %s", path);
256
257         return fileinfo.st_size;
258 }
259
260 int Sys_FileOpenWrite (char *path)
261 {
262         int     handle;
263
264         umask (0);
265         
266         handle = open(path,O_RDWR | O_CREAT | O_TRUNC
267         , 0666);
268
269         if (handle == -1)
270                 Sys_Error ("Error opening %s: %s", path,strerror(errno));
271
272         return handle;
273 }
274
275 int Sys_FileWrite (int handle, void *src, int count)
276 {
277         return write (handle, src, count);
278 }
279
280 void Sys_FileClose (int handle)
281 {
282         close (handle);
283 }
284
285 void Sys_FileSeek (int handle, int position)
286 {
287         lseek (handle, position, SEEK_SET);
288 }
289
290 int Sys_FileRead (int handle, void *dest, int count)
291 {
292     return read (handle, dest, count);
293 }
294
295 void Sys_DebugLog(char *file, char *fmt, ...)
296 {
297     va_list argptr; 
298     static char data[1024];
299     int fd;
300     
301     va_start(argptr, fmt);
302     vsprintf(data, fmt, argptr);
303     va_end(argptr);
304 //    fd = open(file, O_WRONLY | O_BINARY | O_CREAT | O_APPEND, 0666);
305     fd = open(file, O_WRONLY | O_CREAT | O_APPEND, 0666);
306     write(fd, data, strlen(data));
307     close(fd);
308 }
309
310 void Sys_EditFile(char *filename)
311 {
312
313         char cmd[256];
314         char *term;
315         char *editor;
316
317         term = getenv("TERM");
318         if (term && !strcmp(term, "xterm"))
319         {
320                 editor = getenv("VISUAL");
321                 if (!editor)
322                         editor = getenv("EDITOR");
323                 if (!editor)
324                         editor = getenv("EDIT");
325                 if (!editor)
326                         editor = "vi";
327                 sprintf(cmd, "xterm -e %s %s", editor, filename);
328                 system(cmd);
329         }
330
331 }
332
333 double Sys_FloatTime (void)
334 {
335     struct timeval tp;
336     struct timezone tzp; 
337     static int      secbase; 
338     
339     gettimeofday(&tp, &tzp);  
340
341     if (!secbase)
342     {
343         secbase = tp.tv_sec;
344         return tp.tv_usec/1000000.0;
345     }
346
347     return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0;
348 }
349
350 // =======================================================================
351 // Sleeps for microseconds
352 // =======================================================================
353
354 static volatile int oktogo;
355
356 void alarm_handler(int x)
357 {
358         oktogo=1;
359 }
360
361 void Sys_LineRefresh(void)
362 {
363 }
364
365 void floating_point_exception_handler(int whatever)
366 {
367 //      Sys_Warn("floating point exception\n");
368         signal(SIGFPE, floating_point_exception_handler);
369 }
370
371 char *Sys_ConsoleInput(void)
372 {
373     static char text[256];
374     int     len;
375         fd_set  fdset;
376     struct timeval timeout;
377
378         if (cls.state == ca_dedicated) {
379                 FD_ZERO(&fdset);
380                 FD_SET(0, &fdset); // stdin
381                 timeout.tv_sec = 0;
382                 timeout.tv_usec = 0;
383                 if (select (1, &fdset, NULL, NULL, &timeout) == -1 || !FD_ISSET(0, &fdset))
384                         return NULL;
385
386                 len = read (0, text, sizeof(text));
387                 if (len < 1)
388                         return NULL;
389                 text[len-1] = 0;    // rip off the /n and terminate
390
391                 return text;
392         }
393         return NULL;
394 }
395
396 void Sys_Sleep(void)
397 {
398         usleep(1);
399 }
400
401 int main (int c, char **v)
402 {
403
404         double          time, oldtime, newtime;
405
406 //      static char cwd[1024];
407
408 //      signal(SIGFPE, floating_point_exception_handler);
409         signal(SIGFPE, SIG_IGN);
410
411         memset(&host_parms, 0, sizeof(host_parms));
412
413         COM_InitArgv(c, v);
414         host_parms.argc = com_argc;
415         host_parms.argv = com_argv;
416
417         host_parms.memsize = DEFAULTMEM * 1024*1024;
418
419         host_parms.basedir = basedir;
420 // caching is disabled by default, use -cachedir to enable
421 //      host_parms.cachedir = cachedir;
422
423         fcntl(0, F_SETFL, fcntl (0, F_GETFL, 0) | FNDELAY);
424
425         Host_Init();
426
427         Sys_Init();
428
429         if (COM_CheckParm("-nostdout"))
430                 nostdout = 1;
431         else
432         {
433                 fcntl(0, F_SETFL, fcntl (0, F_GETFL, 0) | FNDELAY);
434                 printf ("Linux DarkPlaces -- Version %0.3f (build %i)\n", VERSION, buildnumber);
435         }
436
437         oldtime = Sys_FloatTime () - 0.1;
438         while (1)
439         {
440                 // find time spent rendering last frame
441                 newtime = Sys_FloatTime ();
442                 time = newtime - oldtime;
443
444                 if (cls.state == ca_dedicated)
445                 {
446                         if (time < sys_ticrate.value)
447                         {
448                                 usleep(1);
449                                 continue;       // not time to run a server only tic yet
450                         }
451                         time = sys_ticrate.value;
452                 }
453
454                 if (time > sys_ticrate.value*2)
455                         oldtime = newtime;
456                 else
457                         oldtime += time;
458
459                 Host_Frame (time);
460
461                 // graphic debugging aids
462                 if (sys_linerefresh.value)
463                         Sys_LineRefresh ();
464         }
465         return 0;
466 }
467
468
469 /*
470 ================
471 Sys_MakeCodeWriteable
472 ================
473 */
474 void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length)
475 {
476
477         int r;
478         unsigned long addr;
479         int psize = getpagesize();
480
481         addr = (startaddr & ~(psize-1)) - psize;
482
483 //      fprintf(stderr, "writable code %lx(%lx)-%lx, length=%lx\n", startaddr,
484 //                      addr, startaddr+length, length);
485
486         r = mprotect((char*)addr, length + startaddr - addr + psize, 7);
487
488         if (r < 0)
489                 Sys_Error("Protection change failed\n");
490
491 }
492