1 /* $OpenBSD: lib_twait.c,v 1.10 2023/10/17 09:52:09 nicm Exp $ */ 2 3 /**************************************************************************** 4 * Copyright 2018-2020,2023 Thomas E. Dickey * 5 * Copyright 1998-2015,2016 Free Software Foundation, Inc. * 6 * * 7 * Permission is hereby granted, free of charge, to any person obtaining a * 8 * copy of this software and associated documentation files (the * 9 * "Software"), to deal in the Software without restriction, including * 10 * without limitation the rights to use, copy, modify, merge, publish, * 11 * distribute, distribute with modifications, sublicense, and/or sell * 12 * copies of the Software, and to permit persons to whom the Software is * 13 * furnished to do so, subject to the following conditions: * 14 * * 15 * The above copyright notice and this permission notice shall be included * 16 * in all copies or substantial portions of the Software. * 17 * * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 21 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 22 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 23 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 24 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 25 * * 26 * Except as contained in this notice, the name(s) of the above copyright * 27 * holders shall not be used in advertising or otherwise to promote the * 28 * sale, use or other dealings in this Software without prior written * 29 * authorization. * 30 ****************************************************************************/ 31 32 /**************************************************************************** 33 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 34 * and: Eric S. Raymond <esr@snark.thyrsus.com> * 35 * and: Thomas E. Dickey 1996-on * 36 ****************************************************************************/ 37 38 /* 39 ** lib_twait.c 40 ** 41 ** The routine _nc_timed_wait(). 42 ** 43 ** (This file was originally written by Eric Raymond; however except for 44 ** comments, none of the original code remains - T.Dickey). 45 */ 46 47 #include <curses.priv.h> 48 49 #if defined __HAIKU__ && defined __BEOS__ 50 #undef __BEOS__ 51 #endif 52 53 #ifdef __BEOS__ 54 #undef false 55 #undef true 56 #include <OS.h> 57 #endif 58 59 #if USE_KLIBC_KBD 60 #define INCL_KBD 61 #include <os2.h> 62 #endif 63 64 #if USE_FUNC_POLL 65 # if HAVE_SYS_TIME_H 66 # include <sys/time.h> 67 # endif 68 #elif HAVE_SELECT 69 # if HAVE_SYS_TIME_H && HAVE_SYS_TIME_SELECT 70 # include <sys/time.h> 71 # endif 72 # if HAVE_SYS_SELECT_H 73 # include <sys/select.h> 74 # endif 75 #endif 76 #if HAVE_SYS_TIME_H 77 # include <sys/time.h> 78 #endif 79 #undef CUR 80 81 MODULE_ID("$Id: lib_twait.c,v 1.10 2023/10/17 09:52:09 nicm Exp $") 82 83 /* 84 * Returns an elapsed time, in milliseconds (if possible). 85 */ 86 static long 87 _nc_gettime(TimeType * t0, int first) 88 { 89 long res; 90 91 #if PRECISE_GETTIME 92 TimeType t1; 93 if (GetClockTime(&t1) == -1) { 94 *t0 = t1; 95 res = first ? 0 : 1; 96 } else if (first) { 97 *t0 = t1; 98 res = 0; 99 } else { 100 /* .tv_sec and .tv_usec are unsigned, be careful when subtracting */ 101 if (t0->sub_secs > t1.sub_secs) { 102 t1.sub_secs += TimeScale; 103 t1.tv_sec--; 104 } 105 res = (long) ((t1.tv_sec - t0->tv_sec) * 1000L 106 + (t1.sub_secs - t0->sub_secs) / (TimeScale / 1000L)); 107 } 108 #else 109 time_t t1 = time((time_t *) 0); 110 if (first) { 111 *t0 = t1; 112 } 113 res = (long) ((t1 - *t0) * 1000); 114 #endif 115 TR(TRACE_IEVENT, ("%s time: %ld msec", first ? "get" : "elapsed", res)); 116 return res; 117 } 118 119 #ifdef NCURSES_WGETCH_EVENTS 120 NCURSES_EXPORT(int) 121 _nc_eventlist_timeout(_nc_eventlist * evl) 122 { 123 int event_delay = -1; 124 125 if (evl != 0) { 126 int n; 127 128 for (n = 0; n < evl->count; ++n) { 129 _nc_event *ev = evl->events[n]; 130 131 if (ev->type == _NC_EVENT_TIMEOUT_MSEC) { 132 event_delay = (int) ev->data.timeout_msec; 133 if (event_delay < 0) 134 event_delay = INT_MAX; /* FIXME Is this defined? */ 135 } 136 } 137 } 138 return event_delay; 139 } 140 #endif /* NCURSES_WGETCH_EVENTS */ 141 142 #if (USE_FUNC_POLL || HAVE_SELECT) 143 # define MAYBE_UNUSED 144 #else 145 # define MAYBE_UNUSED GCC_UNUSED 146 #endif 147 148 #if (USE_FUNC_POLL || HAVE_SELECT) 149 # define MAYBE_UNUSED 150 #else 151 # define MAYBE_UNUSED GCC_UNUSED 152 #endif 153 154 /* 155 * Wait a specified number of milliseconds, returning nonzero if the timer 156 * didn't expire before there is activity on the specified file descriptors. 157 * The file-descriptors are specified by the mode: 158 * TW_NONE 0 - none (absolute time) 159 * TW_INPUT 1 - ncurses' normal input-descriptor 160 * TW_MOUSE 2 - mouse descriptor, if any 161 * TW_ANY 3 - either input or mouse. 162 * TW_EVENT 4 - 163 * Experimental: if NCURSES_WGETCH_EVENTS is defined, (mode & 4) determines 164 * whether to pay attention to evl argument. If set, the smallest of 165 * millisecond and of timeout of evl is taken. 166 * 167 * We return a mask that corresponds to the mode (e.g., 2 for mouse activity). 168 * 169 * If the milliseconds given are -1, the wait blocks until activity on the 170 * descriptors. 171 */ 172 NCURSES_EXPORT(int) 173 _nc_timed_wait(SCREEN *sp MAYBE_UNUSED, 174 int mode MAYBE_UNUSED, 175 int milliseconds, 176 int *timeleft 177 EVENTLIST_2nd(_nc_eventlist * evl)) 178 { 179 int count; 180 int result = TW_NONE; 181 TimeType t0; 182 #if (USE_FUNC_POLL || HAVE_SELECT) 183 int fd; 184 #endif 185 186 #ifdef NCURSES_WGETCH_EVENTS 187 int timeout_is_event = 0; 188 int n; 189 #endif 190 191 #if USE_FUNC_POLL 192 #define MIN_FDS 2 193 struct pollfd fd_list[MIN_FDS]; 194 struct pollfd *fds = fd_list; 195 #elif defined(__BEOS__) 196 #elif HAVE_SELECT 197 fd_set set; 198 #endif 199 200 #if USE_KLIBC_KBD 201 fd_set saved_set; 202 KBDKEYINFO ki; 203 struct timeval tv; 204 #endif 205 206 long starttime, returntime; 207 208 #ifdef NCURSES_WGETCH_EVENTS 209 (void) timeout_is_event; 210 #endif 211 212 TR(TRACE_IEVENT, ("start twait: %d milliseconds, mode: %d", 213 milliseconds, mode)); 214 215 #ifdef NCURSES_WGETCH_EVENTS 216 if (mode & TW_EVENT) { 217 int event_delay = _nc_eventlist_timeout(evl); 218 219 if (event_delay >= 0 220 && (milliseconds >= event_delay || milliseconds < 0)) { 221 milliseconds = event_delay; 222 timeout_is_event = 1; 223 } 224 } 225 #endif 226 227 #if PRECISE_GETTIME && HAVE_NANOSLEEP 228 retry: 229 #endif 230 starttime = _nc_gettime(&t0, TRUE); 231 232 count = 0; 233 (void) count; 234 235 #ifdef NCURSES_WGETCH_EVENTS 236 if ((mode & TW_EVENT) && evl) 237 evl->result_flags = 0; 238 #endif 239 240 #if USE_FUNC_POLL 241 memset(fd_list, 0, sizeof(fd_list)); 242 243 #ifdef NCURSES_WGETCH_EVENTS 244 if ((mode & TW_EVENT) && evl) { 245 if (fds == fd_list) 246 fds = typeMalloc(struct pollfd, MIN_FDS + evl->count); 247 if (fds == 0) 248 return TW_NONE; 249 } 250 #endif 251 252 if (mode & TW_INPUT) { 253 fds[count].fd = sp->_ifd; 254 fds[count].events = POLLIN; 255 count++; 256 } 257 if ((mode & TW_MOUSE) 258 && (fd = sp->_mouse_fd) >= 0) { 259 fds[count].fd = fd; 260 fds[count].events = POLLIN; 261 count++; 262 } 263 #ifdef NCURSES_WGETCH_EVENTS 264 if ((mode & TW_EVENT) && evl) { 265 for (n = 0; n < evl->count; ++n) { 266 _nc_event *ev = evl->events[n]; 267 268 if (ev->type == _NC_EVENT_FILE 269 && (ev->data.fev.flags & _NC_EVENT_FILE_READABLE)) { 270 fds[count].fd = ev->data.fev.fd; 271 fds[count].events = POLLIN; 272 count++; 273 } 274 } 275 } 276 #endif 277 278 result = poll(fds, (size_t) count, milliseconds); 279 280 #ifdef NCURSES_WGETCH_EVENTS 281 if ((mode & TW_EVENT) && evl) { 282 int c; 283 284 if (!result) 285 count = 0; 286 287 for (n = 0; n < evl->count; ++n) { 288 _nc_event *ev = evl->events[n]; 289 290 if (ev->type == _NC_EVENT_FILE 291 && (ev->data.fev.flags & _NC_EVENT_FILE_READABLE)) { 292 ev->data.fev.result = 0; 293 for (c = 0; c < count; c++) 294 if (fds[c].fd == ev->data.fev.fd 295 && fds[c].revents & POLLIN) { 296 ev->data.fev.result |= _NC_EVENT_FILE_READABLE; 297 evl->result_flags |= _NC_EVENT_FILE_READABLE; 298 } 299 } else if (ev->type == _NC_EVENT_TIMEOUT_MSEC 300 && !result && timeout_is_event) { 301 evl->result_flags |= _NC_EVENT_TIMEOUT_MSEC; 302 } 303 } 304 } 305 #endif 306 307 #elif defined(__BEOS__) 308 /* 309 * BeOS's select() is declared in socket.h, so the configure script does 310 * not see it. That's just as well, since that function works only for 311 * sockets. This (using snooze and ioctl) was distilled from Be's patch 312 * for ncurses which uses a separate thread to simulate select(). 313 * 314 * FIXME: the return values from the ioctl aren't very clear if we get 315 * interrupted. 316 * 317 * FIXME: this assumes mode&1 if milliseconds < 0 (see lib_getch.c). 318 */ 319 result = TW_NONE; 320 if (mode & TW_INPUT) { 321 int step = (milliseconds < 0) ? 0 : 5000; 322 bigtime_t d; 323 bigtime_t useconds = milliseconds * 1000; 324 int n, howmany; 325 326 if (useconds <= 0) /* we're here to go _through_ the loop */ 327 useconds = 1; 328 329 for (d = 0; d < useconds; d += step) { 330 n = 0; 331 howmany = ioctl(0, 'ichr', &n); 332 if (howmany >= 0 && n > 0) { 333 result = 1; 334 break; 335 } 336 if (useconds > 1 && step > 0) { 337 snooze(step); 338 milliseconds -= (step / 1000); 339 if (milliseconds <= 0) { 340 milliseconds = 0; 341 break; 342 } 343 } 344 } 345 } else if (milliseconds > 0) { 346 snooze(milliseconds * 1000); 347 milliseconds = 0; 348 } 349 #elif HAVE_SELECT 350 /* 351 * select() modifies the fd_set arguments; do this in the 352 * loop. 353 */ 354 FD_ZERO(&set); 355 356 #if !USE_KLIBC_KBD 357 if (mode & TW_INPUT) { 358 FD_SET(sp->_ifd, &set); 359 count = sp->_ifd + 1; 360 } 361 #endif 362 if ((mode & TW_MOUSE) 363 && (fd = sp->_mouse_fd) >= 0) { 364 FD_SET(fd, &set); 365 count = max(fd, count) + 1; 366 } 367 #ifdef NCURSES_WGETCH_EVENTS 368 if ((mode & TW_EVENT) && evl) { 369 for (n = 0; n < evl->count; ++n) { 370 _nc_event *ev = evl->events[n]; 371 372 if (ev->type == _NC_EVENT_FILE 373 && (ev->data.fev.flags & _NC_EVENT_FILE_READABLE)) { 374 FD_SET(ev->data.fev.fd, &set); 375 count = max(ev->data.fev.fd + 1, count); 376 } 377 } 378 } 379 #endif 380 381 #if USE_KLIBC_KBD 382 for (saved_set = set;; set = saved_set) { 383 if ((mode & TW_INPUT) 384 && (sp->_extended_key 385 || (KbdPeek(&ki, 0) == 0 386 && (ki.fbStatus & KBDTRF_FINAL_CHAR_IN)))) { 387 FD_ZERO(&set); 388 FD_SET(sp->_ifd, &set); 389 result = 1; 390 break; 391 } 392 393 tv.tv_sec = 0; 394 tv.tv_usec = (milliseconds == 0) ? 0 : (10 * 1000); 395 396 if ((result = select(count, &set, NULL, NULL, &tv)) != 0) 397 break; 398 399 /* Time out ? */ 400 if (milliseconds >= 0 && _nc_gettime(&t0, FALSE) >= milliseconds) { 401 result = 0; 402 break; 403 } 404 } 405 #else 406 if (milliseconds >= 0) { 407 struct timeval ntimeout; 408 ntimeout.tv_sec = milliseconds / 1000; 409 ntimeout.tv_usec = (milliseconds % 1000) * 1000; 410 result = select(count, &set, NULL, NULL, &ntimeout); 411 } else { 412 result = select(count, &set, NULL, NULL, NULL); 413 } 414 #endif 415 416 #ifdef NCURSES_WGETCH_EVENTS 417 if ((mode & TW_EVENT) && evl) { 418 evl->result_flags = 0; 419 for (n = 0; n < evl->count; ++n) { 420 _nc_event *ev = evl->events[n]; 421 422 if (ev->type == _NC_EVENT_FILE 423 && (ev->data.fev.flags & _NC_EVENT_FILE_READABLE)) { 424 ev->data.fev.result = 0; 425 if (FD_ISSET(ev->data.fev.fd, &set)) { 426 ev->data.fev.result |= _NC_EVENT_FILE_READABLE; 427 evl->result_flags |= _NC_EVENT_FILE_READABLE; 428 } 429 } else if (ev->type == _NC_EVENT_TIMEOUT_MSEC 430 && !result && timeout_is_event) 431 evl->result_flags |= _NC_EVENT_TIMEOUT_MSEC; 432 } 433 } 434 #endif 435 436 #endif /* USE_FUNC_POLL, etc */ 437 438 returntime = _nc_gettime(&t0, FALSE); 439 440 if (milliseconds >= 0) 441 milliseconds -= (int) (returntime - starttime); 442 443 #ifdef NCURSES_WGETCH_EVENTS 444 if (evl) { 445 evl->result_flags = 0; 446 for (n = 0; n < evl->count; ++n) { 447 _nc_event *ev = evl->events[n]; 448 449 if (ev->type == _NC_EVENT_TIMEOUT_MSEC) { 450 long diff = (returntime - starttime); 451 if (ev->data.timeout_msec <= diff) 452 ev->data.timeout_msec = 0; 453 else 454 ev->data.timeout_msec -= diff; 455 } 456 457 } 458 } 459 #endif 460 461 #if PRECISE_GETTIME && HAVE_NANOSLEEP 462 /* 463 * If the timeout hasn't expired, and we've gotten no data, 464 * this is probably a system where 'select()' needs to be left 465 * alone so that it can complete. Make this process sleep, 466 * then come back for more. 467 */ 468 if (result == 0 && milliseconds > 100) { 469 napms(100); /* FIXME: this won't be right if I recur! */ 470 milliseconds -= 100; 471 goto retry; 472 } 473 #endif 474 475 /* return approximate time left in milliseconds */ 476 if (timeleft) 477 *timeleft = milliseconds; 478 479 TR(TRACE_IEVENT, ("end twait: returned %d (%d), remaining time %d msec", 480 result, errno, milliseconds)); 481 482 /* 483 * Both 'poll()' and 'select()' return the number of file descriptors 484 * that are active. Translate this back to the mask that denotes which 485 * file-descriptors, so that we don't need all of this system-specific 486 * code everywhere. 487 */ 488 if (result != 0) { 489 if (result > 0) { 490 result = 0; 491 #if USE_FUNC_POLL 492 for (count = 0; count < MIN_FDS; count++) { 493 if ((mode & (1 << count)) 494 && (fds[count].revents & POLLIN)) { 495 result |= (1 << count); 496 } 497 } 498 #elif defined(__BEOS__) 499 result = TW_INPUT; /* redundant, but simple */ 500 #elif HAVE_SELECT 501 if ((mode & TW_MOUSE) 502 && (fd = sp->_mouse_fd) >= 0 503 && FD_ISSET(fd, &set)) 504 result |= TW_MOUSE; 505 if ((mode & TW_INPUT) 506 && FD_ISSET(sp->_ifd, &set)) 507 result |= TW_INPUT; 508 #endif 509 } else 510 result = 0; 511 } 512 #ifdef NCURSES_WGETCH_EVENTS 513 if ((mode & TW_EVENT) && evl && evl->result_flags) 514 result |= TW_EVENT; 515 #endif 516 517 #if USE_FUNC_POLL 518 #ifdef NCURSES_WGETCH_EVENTS 519 if (fds != fd_list) 520 free((char *) fds); 521 #endif 522 #endif 523 524 return (result); 525 } 526