1 /*
2 * ============================================================================
3 * Title: Platform Portability "Library"
4 * Author: J. Zbiciak, T. Lindner
5 * ============================================================================
6 * This module fills in missing features on various platforms.
7 * ============================================================================
8 * GETTIMEOFDAY -- Return current time in seconds/microseconds.
9 * STRDUP -- Copy a string into freshly malloc'd storage.
10 * STRICMP -- Case-insensitive string compare.
11 * SNPRINTF -- Like sprintf(), only with bounds checking.
12 * PLAT_DELAY -- Sleep w/ millisecond precision.
13 * GET_EXE_DIR -- Get the directory containing this executable
14 * ============================================================================
15 */
16
17 #include "config.h"
18 #include "plat/plat_lib_config.h"
19
20 #ifdef USE_TERMIO
21 # include <termio.h>
22 #endif
23
24 #ifdef USE_SYS_IOCTL
25 # include <sys/ioctl.h>
26 #endif
27
28 /* ======================================================================== */
29 /* GENERIC PORTABLE VERSIONS... */
30 /* ======================================================================== */
31
32 /* ------------------------------------------------------------------------ */
33 /* GET_TIME -- Return current time in seconds as a double. */
34 /* ------------------------------------------------------------------------ */
35 #if GET_TIME_STRATEGY == GTS_CLOCK_GETTIME
get_time(void)36 double get_time(void)
37 {
38 struct timespec now;
39 double seconds;
40
41 clock_gettime(CLOCK_MONOTONIC, &now);
42
43 seconds = (double)now.tv_sec + (double)now.tv_nsec * 1e-9;
44 return seconds;
45 }
46 #endif
47
48 #if GET_TIME_STRATEGY == GTS_GETTIMEOFDAY
get_time(void)49 double get_time(void)
50 {
51 struct timeval now;
52 double seconds;
53
54 gettimeofday(&now, NULL);
55
56 seconds = (double)now.tv_sec + (double)now.tv_usec * 1e-6;
57
58 return seconds;
59 }
60 #endif
61
62 #if GET_TIME_STRATEGY == GTS_WIN_PERF_COUNTERS
63 # ifndef WIN_HEADERS_INCLUDED
64 # define WIN_HEADERS_INCLUDED
65 # define WIN32_LEAN_AND_MEAN
66 # include <windows.h>
67 # include <math.h>
68 # endif
69
70 LOCAL double perf_rate = -1;
71
72 extern double win32_get_time_fallback(void);
73
get_time(void)74 double get_time(void)
75 {
76 LARGE_INTEGER li;
77
78 if (perf_rate < 0)
79 {
80 if (QueryPerformanceFrequency(&li))
81 {
82 perf_rate = 1.0 / (double)li.QuadPart;
83 } else
84 {
85 perf_rate = 0;
86 }
87 }
88
89 if (perf_rate > 0)
90 {
91 double seconds;
92 QueryPerformanceCounter(&li);
93 seconds = (double)li.QuadPart * perf_rate;
94 return seconds;
95 }
96
97 return win32_get_time_fallback();
98 }
99 #endif
100
101 #if GET_TIME_STRATEGY == GTS_WII
get_time(void)102 double get_time(void)
103 {
104 extern double wii_get_time();
105
106 return wii_get_time();
107 }
108 #endif
109
110 /* ------------------------------------------------------------------------ */
111 /* PLAT_DELAY -- Sleep w/ millisecond precision. */
112 /* PLAT_DELAY_NO_SDL -- Sleep w/ millisecond precision. */
113 /* ------------------------------------------------------------------------ */
114 #if PLAT_DELAY_STRATEGY_NO_SDL == PDS_WIN_WAIT_TIMER
115 # ifndef WIN_HEADERS_INCLUDED
116 # define WIN_HEADERS_INCLUDED
117 # define WIN32_LEAN_AND_MEAN
118 # include <windows.h>
119 # include <math.h>
120 # endif
plat_delay_no_sdl(unsigned msec)121 void plat_delay_no_sdl(unsigned msec)
122 {
123 HANDLE timer;
124 LARGE_INTEGER ft;
125
126 /* Windows wants 100ns intervals */
127 /* Also, -ve to specify _relative_ time */
128 ft.QuadPart = -(10000ll*msec);
129
130 timer = CreateWaitableTimer(NULL, TRUE, NULL);
131 SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);
132 WaitForSingleObject(timer, INFINITE);
133 CloseHandle(timer);
134 }
135 #endif
136
137 #if PLAT_DELAY_STRATEGY_NO_SDL == PDS_NANOSLEEP
plat_delay_no_sdl(unsigned delay)138 void plat_delay_no_sdl(unsigned delay)
139 {
140 struct timespec ts;
141 ts.tv_sec = delay / 1000;
142 ts.tv_nsec = (delay % 1000) * 1000000;
143 nanosleep(&ts, NULL);
144 }
145 #endif
146
147 #if PLAT_DELAY_STRATEGY_NO_SDL == PDS_USLEEP
plat_delay_no_sdl(unsigned delay)148 void plat_delay_no_sdl(unsigned delay)
149 {
150 usleep(delay * 1000);
151 }
152 #endif
153
154 #if PLAT_DELAY_STRATEGY_NO_SDL == PDS_BUSY_LOOP
plat_delay_no_sdl(unsigned delay)155 void plat_delay_no_sdl(unsigned delay)
156 {
157 double soon = get_time() + ((double)delay / 1000.0);
158
159 /* -------------------------------------------------------------------- */
160 /* BAD BAD BAD: Sit in a busy loop until time expires. */
161 /* -------------------------------------------------------------------- */
162 while (get_time() < soon)
163 ;
164
165 return;
166 }
167 #endif
168
169 #if PLAT_DELAY_STRATEGY != PDS_MACINTOSH \
170 && PLAT_DELAY_STRATEGY != PDS_SDL_DELAY
plat_delay(unsigned delay)171 void plat_delay(unsigned delay)
172 {
173 plat_delay_no_sdl(delay);
174 }
175 #endif
176
177
178 /* ======================================================================== */
179 /* Window size functions */
180 /* */
181 /* GET_DISP_WIDTH -- Get the width of the text display. */
182 /* SET_DISP_WIDTH -- Try to set the width of the text display. */
183 /* INIT_DISP_WIDTH -- Initialize the display width. */
184 /* */
185 /* If the display width is unknown, this should return '80', the default */
186 /* width jzIntv historically assumed. Because 'set_disp_width' can fail, */
187 /* it returns the new width, which may be unchanged. */
188 /* */
189 /* On platforms which cannot detect display width, "INIT_DISP_WIDTH" can */
190 /* set the display width. Passing in 0 here just tells it to detect or */
191 /* use the default width. */
192 /* ======================================================================== */
193
194 /* ------------------------------------------------------------------------ */
195 /* Outsourced implementation: Ask GNU Readline! */
196 /* ------------------------------------------------------------------------ */
197 #ifdef USE_GNU_READLINE
198 # define HAVE_WIDTH_IMPL
199 # include <readline/readline.h>
200
get_disp_width(void)201 int get_disp_width(void)
202 {
203 int rows, cols;
204 rl_get_screen_size(&rows, &cols);
205 return cols;
206 }
207
set_disp_width(int new_width)208 int set_disp_width(int new_width)
209 {
210 UNUSED(new_width);
211 return get_disp_width();
212 }
213
init_disp_width(int width)214 void init_disp_width(int width)
215 {
216 UNUSED(width);
217 }
218 #endif
219
220 /* ------------------------------------------------------------------------ */
221 /* Weakest implementation: Just return 80 unless we're told to return */
222 /* something else. */
223 /* ------------------------------------------------------------------------ */
224 #if !defined(HAVE_WIDTH_IMPL) && !defined(WIN32) && !defined(CAN_TIOCGWINSZ)
225 # define HAVE_WIDTH_IMPL
226 LOCAL int disp_width = -1;
227
get_disp_width(void)228 int get_disp_width(void)
229 {
230 return disp_width;
231 }
232
set_disp_width(int new_width)233 int set_disp_width(int new_width)
234 {
235 disp_width = new_width > 0 ? new_width : disp_width;
236 return disp_width;
237 }
238
init_disp_width(int width)239 void init_disp_width(int width)
240 {
241 disp_width = width > 0 ? width : 80;
242 }
243 #endif
244
245 /* ------------------------------------------------------------------------ */
246 /* WIN32 Implementation: Eventually use "mode con:" to change size. */
247 /* ------------------------------------------------------------------------ */
248 #if !defined(HAVE_WIDTH_IMPL) && defined(WIN32)
249 # define HAVE_WIDTH_IMPL
250 LOCAL int disp_width = -1;
251
set_width_from_mode_con(void)252 LOCAL int set_width_from_mode_con(void)
253 {
254 FILE *f;
255 f = popen("mode con:", "r");
256 if (f)
257 {
258 char buf[80];
259 int w = -1;
260
261 while (fgets(buf, sizeof(buf), f) != NULL)
262 {
263 if (sscanf(buf, " Columns: %d", &w) > 0)
264 disp_width = w;
265 }
266
267 pclose(f);
268
269 if ( w == -1 ) // couldn't find it!
270 return -1;
271 else
272 return 0; // success
273 }
274 return 0;
275 }
276
get_disp_width(void)277 int get_disp_width(void)
278 {
279 return disp_width;
280 }
281
set_disp_width(int new_width)282 int set_disp_width(int new_width)
283 {
284 char buf[80];
285
286 if (new_width == disp_width || new_width < 1)
287 return disp_width;
288
289 sprintf(buf, "mode con: cols=%d", new_width);
290
291 if (system(buf) == 0)
292 if ( set_width_from_mode_con() < 0 ) // asking Windows didn't work?
293 disp_width = new_width; // hack: assume it worked.
294
295 return disp_width;
296 }
297
init_disp_width(int width)298 void init_disp_width(int width)
299 {
300 disp_width = width > 0 ? width : 80;
301 }
302
303 #endif
304
305 /* ------------------------------------------------------------------------ */
306 /* TIOCGWINSZ w/out SIGWINCH: Always call the ioctl when asked the size. */
307 /* ------------------------------------------------------------------------ */
308 #if !defined(HAVE_WIDTH_IMPL) && defined(CAN_TIOCGWINSZ) && !defined(CAN_SIGWINCH)
309 # define HAVE_WIDTH_IMPL
310 LOCAL int disp_width = 80;
311
get_disp_width(void)312 int get_disp_width(void)
313 {
314 Dims my_win;
315
316 if (ioctl(0, TIOCGWINSZ, &my_win) != 0)
317 return disp_width;
318
319 return my_win.ws_col;
320 }
321
set_disp_width(int new_width)322 int set_disp_width(int new_width)
323 {
324 disp_width = new_width > 0 ? new_width : disp_width;
325 return get_disp_width();
326 }
327
init_disp_width(int width)328 void init_disp_width(int width)
329 {
330 disp_width = width > 0 ? width : 80;
331 }
332 #endif
333
334 /* ------------------------------------------------------------------------ */
335 /* TIOCGWINSZ with SIGWINCH: Only call the ioctl if a SIGWINCH happened. */
336 /* ------------------------------------------------------------------------ */
337 #if !defined(HAVE_WIDTH_IMPL) && defined(CAN_TIOCGWINSZ) && defined(CAN_SIGWINCH)
338 # define HAVE_WIDTH_IMPL
339 # include <signal.h>
340
341 LOCAL volatile char need_width_ioctl = 1;
342 LOCAL int disp_width = 80;
343
sigwinch_handler(int sig)344 LOCAL void sigwinch_handler(int sig)
345 {
346 UNUSED(sig);
347 need_width_ioctl = 1;
348 }
349
get_disp_width(void)350 int get_disp_width(void)
351 {
352 struct winsize my_win;
353
354 if (need_width_ioctl)
355 {
356 need_width_ioctl = 0;
357 if (ioctl(0, TIOCGWINSZ, &my_win) == 0)
358 disp_width = my_win.ws_col;
359 }
360 signal(SIGWINCH, sigwinch_handler);
361
362 return disp_width;
363 }
364
set_disp_width(int new_width)365 int set_disp_width(int new_width)
366 {
367 disp_width = new_width > 0 ? new_width : disp_width;
368 need_width_ioctl = 1;
369 return get_disp_width();
370 }
371
init_disp_width(int new_width)372 void init_disp_width(int new_width)
373 {
374 disp_width = new_width > 0 ? new_width : disp_width;
375 need_width_ioctl = 1;
376
377 signal(SIGWINCH, sigwinch_handler);
378 }
379 #endif
380
381 /* ======================================================================== */
382 /* This program is free software; you can redistribute it and/or modify */
383 /* it under the terms of the GNU General Public License as published by */
384 /* the Free Software Foundation; either version 2 of the License, or */
385 /* (at your option) any later version. */
386 /* */
387 /* This program is distributed in the hope that it will be useful, */
388 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
389 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
390 /* General Public License for more details. */
391 /* */
392 /* You should have received a copy of the GNU General Public License along */
393 /* with this program; if not, write to the Free Software Foundation, Inc., */
394 /* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
395 /* ======================================================================== */
396 /* Copyright (c) 1998-2020, Joseph Zbiciak, Tim Lindner */
397 /* ======================================================================== */
398