WaitFor.c (13604B)
1 /*********************************************************** 2 3 Copyright 1987, 1998 The Open Group 4 5 Permission to use, copy, modify, distribute, and sell this software and its 6 documentation for any purpose is hereby granted without fee, provided that 7 the above copyright notice appear in all copies and that both that 8 copyright notice and this permission notice appear in supporting 9 documentation. 10 11 The above copyright notice and this permission notice shall be included in 12 all copies or substantial portions of the Software. 13 14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 18 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 21 Except as contained in this notice, the name of The Open Group shall not be 22 used in advertising or otherwise to promote the sale, use or other dealings 23 in this Software without prior written authorization from The Open Group. 24 25 Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. 26 27 All Rights Reserved 28 29 Permission to use, copy, modify, and distribute this software and its 30 documentation for any purpose and without fee is hereby granted, 31 provided that the above copyright notice appear in all copies and that 32 both that copyright notice and this permission notice appear in 33 supporting documentation, and that the name of Digital not be 34 used in advertising or publicity pertaining to distribution of the 35 software without specific, written prior permission. 36 37 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 38 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 39 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 40 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 41 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 42 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 43 SOFTWARE. 44 45 ******************************************************************/ 46 47 /***************************************************************** 48 * OS Dependent input routines: 49 * 50 * WaitForSomething 51 * TimerForce, TimerSet, TimerCheck, TimerFree 52 * 53 *****************************************************************/ 54 55 #ifdef HAVE_DIX_CONFIG_H 56 #include <dix-config.h> 57 #endif 58 59 #ifdef WIN32 60 #include <X11/Xwinsock.h> 61 #endif 62 #include <X11/Xos.h> /* for strings, fcntl, time */ 63 #include <errno.h> 64 #include <stdio.h> 65 #include <X11/X.h> 66 #include "misc.h" 67 68 #include "osdep.h" 69 #include "dixstruct.h" 70 #include "opaque.h" 71 #ifdef DPMSExtension 72 #include "dpmsproc.h" 73 #endif 74 #include "busfault.h" 75 76 #ifdef WIN32 77 /* Error codes from windows sockets differ from fileio error codes */ 78 #undef EINTR 79 #define EINTR WSAEINTR 80 #undef EINVAL 81 #define EINVAL WSAEINVAL 82 #undef EBADF 83 #define EBADF WSAENOTSOCK 84 /* Windows select does not set errno. Use GetErrno as wrapper for 85 WSAGetLastError */ 86 #define GetErrno WSAGetLastError 87 #else 88 /* This is just a fallback to errno to hide the differences between unix and 89 Windows in the code */ 90 #define GetErrno() errno 91 #endif 92 93 #ifdef DPMSExtension 94 #include <X11/extensions/dpmsconst.h> 95 #endif 96 97 struct _OsTimerRec { 98 struct xorg_list list; 99 CARD32 expires; 100 CARD32 delta; 101 OsTimerCallback callback; 102 void *arg; 103 }; 104 105 static void DoTimer(OsTimerPtr timer, CARD32 now); 106 static void DoTimers(CARD32 now); 107 static void CheckAllTimers(void); 108 static volatile struct xorg_list timers; 109 110 static inline OsTimerPtr 111 first_timer(void) 112 { 113 /* inline xorg_list_is_empty which can't handle volatile */ 114 if (timers.next == &timers) 115 return NULL; 116 return xorg_list_first_entry(&timers, struct _OsTimerRec, list); 117 } 118 119 /* 120 * Compute timeout until next timer, running 121 * any expired timers 122 */ 123 static int 124 check_timers(void) 125 { 126 OsTimerPtr timer; 127 128 if ((timer = first_timer()) != NULL) { 129 CARD32 now = GetTimeInMillis(); 130 int timeout = timer->expires - now; 131 132 if (timeout <= 0) { 133 DoTimers(now); 134 } else { 135 /* Make sure the timeout is sane */ 136 if (timeout < timer->delta + 250) 137 return timeout; 138 139 /* time has rewound. reset the timers. */ 140 CheckAllTimers(); 141 } 142 143 return 0; 144 } 145 return -1; 146 } 147 148 /***************** 149 * WaitForSomething: 150 * Make the server suspend until there is 151 * 1. data from clients or 152 * 2. input events available or 153 * 3. ddx notices something of interest (graphics 154 * queue ready, etc.) or 155 * 4. clients that have buffered replies/events are ready 156 * 157 * If the time between INPUT events is 158 * greater than ScreenSaverTime, the display is turned off (or 159 * saved, depending on the hardware). So, WaitForSomething() 160 * has to handle this also (that's why the select() has a timeout. 161 * For more info on ClientsWithInput, see ReadRequestFromClient(). 162 * pClientsReady is an array to store ready client->index values into. 163 *****************/ 164 165 Bool 166 WaitForSomething(Bool are_ready) 167 { 168 int i; 169 int timeout; 170 int pollerr; 171 static Bool were_ready; 172 Bool timer_is_running; 173 174 timer_is_running = were_ready; 175 176 if (were_ready && !are_ready) { 177 timer_is_running = FALSE; 178 SmartScheduleStopTimer(); 179 } 180 181 were_ready = FALSE; 182 183 #ifdef BUSFAULT 184 busfault_check(); 185 #endif 186 187 /* We need a while loop here to handle 188 crashed connections and the screen saver timeout */ 189 while (1) { 190 /* deal with any blocked jobs */ 191 if (workQueue) { 192 ProcessWorkQueue(); 193 } 194 195 timeout = check_timers(); 196 are_ready = clients_are_ready(); 197 198 if (are_ready) 199 timeout = 0; 200 201 BlockHandler(&timeout); 202 if (NewOutputPending) 203 FlushAllOutput(); 204 /* keep this check close to select() call to minimize race */ 205 if (dispatchException) 206 i = -1; 207 else 208 i = ospoll_wait(server_poll, timeout); 209 pollerr = GetErrno(); 210 WakeupHandler(i); 211 if (i <= 0) { /* An error or timeout occurred */ 212 if (dispatchException) 213 return FALSE; 214 if (i < 0) { 215 if (pollerr != EINTR && !ETEST(pollerr)) { 216 ErrorF("WaitForSomething(): poll: %s\n", 217 strerror(pollerr)); 218 } 219 } 220 } else 221 are_ready = clients_are_ready(); 222 223 if (InputCheckPending()) 224 return FALSE; 225 226 if (are_ready) { 227 were_ready = TRUE; 228 if (!timer_is_running) 229 SmartScheduleStartTimer(); 230 return TRUE; 231 } 232 } 233 } 234 235 void 236 AdjustWaitForDelay(void *waitTime, int newdelay) 237 { 238 int *timeoutp = waitTime; 239 int timeout = *timeoutp; 240 241 if (timeout < 0 || newdelay < timeout) 242 *timeoutp = newdelay; 243 } 244 245 static inline Bool timer_pending(OsTimerPtr timer) { 246 return !xorg_list_is_empty(&timer->list); 247 } 248 249 /* If time has rewound, re-run every affected timer. 250 * Timers might drop out of the list, so we have to restart every time. */ 251 static void 252 CheckAllTimers(void) 253 { 254 OsTimerPtr timer; 255 CARD32 now; 256 257 input_lock(); 258 start: 259 now = GetTimeInMillis(); 260 261 xorg_list_for_each_entry(timer, &timers, list) { 262 if (timer->expires - now > timer->delta + 250) { 263 DoTimer(timer, now); 264 goto start; 265 } 266 } 267 input_unlock(); 268 } 269 270 static void 271 DoTimer(OsTimerPtr timer, CARD32 now) 272 { 273 CARD32 newTime; 274 275 xorg_list_del(&timer->list); 276 newTime = (*timer->callback) (timer, now, timer->arg); 277 if (newTime) 278 TimerSet(timer, 0, newTime, timer->callback, timer->arg); 279 } 280 281 static void 282 DoTimers(CARD32 now) 283 { 284 OsTimerPtr timer; 285 286 input_lock(); 287 while ((timer = first_timer())) { 288 if ((int) (timer->expires - now) > 0) 289 break; 290 DoTimer(timer, now); 291 } 292 input_unlock(); 293 } 294 295 OsTimerPtr 296 TimerSet(OsTimerPtr timer, int flags, CARD32 millis, 297 OsTimerCallback func, void *arg) 298 { 299 OsTimerPtr existing; 300 CARD32 now = GetTimeInMillis(); 301 302 if (!timer) { 303 timer = calloc(1, sizeof(struct _OsTimerRec)); 304 if (!timer) 305 return NULL; 306 xorg_list_init(&timer->list); 307 } 308 else { 309 input_lock(); 310 if (timer_pending(timer)) { 311 xorg_list_del(&timer->list); 312 if (flags & TimerForceOld) 313 (void) (*timer->callback) (timer, now, timer->arg); 314 } 315 input_unlock(); 316 } 317 if (!millis) 318 return timer; 319 if (flags & TimerAbsolute) { 320 timer->delta = millis - now; 321 } 322 else { 323 timer->delta = millis; 324 millis += now; 325 } 326 timer->expires = millis; 327 timer->callback = func; 328 timer->arg = arg; 329 input_lock(); 330 331 /* Sort into list */ 332 xorg_list_for_each_entry(existing, &timers, list) 333 if ((int) (existing->expires - millis) > 0) 334 break; 335 /* This even works at the end of the list -- existing->list will be timers */ 336 xorg_list_append(&timer->list, &existing->list); 337 338 /* Check to see if the timer is ready to run now */ 339 if ((int) (millis - now) <= 0) 340 DoTimer(timer, now); 341 342 input_unlock(); 343 return timer; 344 } 345 346 Bool 347 TimerForce(OsTimerPtr timer) 348 { 349 int pending; 350 351 input_lock(); 352 pending = timer_pending(timer); 353 if (pending) 354 DoTimer(timer, GetTimeInMillis()); 355 input_unlock(); 356 return pending; 357 } 358 359 void 360 TimerCancel(OsTimerPtr timer) 361 { 362 if (!timer) 363 return; 364 input_lock(); 365 xorg_list_del(&timer->list); 366 input_unlock(); 367 } 368 369 void 370 TimerFree(OsTimerPtr timer) 371 { 372 if (!timer) 373 return; 374 TimerCancel(timer); 375 free(timer); 376 } 377 378 void 379 TimerCheck(void) 380 { 381 DoTimers(GetTimeInMillis()); 382 } 383 384 void 385 TimerInit(void) 386 { 387 static Bool been_here; 388 OsTimerPtr timer, tmp; 389 390 if (!been_here) { 391 been_here = TRUE; 392 xorg_list_init((struct xorg_list*) &timers); 393 } 394 395 xorg_list_for_each_entry_safe(timer, tmp, &timers, list) { 396 xorg_list_del(&timer->list); 397 free(timer); 398 } 399 } 400 401 #ifdef DPMSExtension 402 403 #define DPMS_CHECK_MODE(mode,time)\ 404 if (time > 0 && DPMSPowerLevel < mode && timeout >= time)\ 405 DPMSSet(serverClient, mode); 406 407 #define DPMS_CHECK_TIMEOUT(time)\ 408 if (time > 0 && (time - timeout) > 0)\ 409 return time - timeout; 410 411 static CARD32 412 NextDPMSTimeout(INT32 timeout) 413 { 414 /* 415 * Return the amount of time remaining until we should set 416 * the next power level. Fallthroughs are intentional. 417 */ 418 switch (DPMSPowerLevel) { 419 case DPMSModeOn: 420 DPMS_CHECK_TIMEOUT(DPMSStandbyTime) 421 422 case DPMSModeStandby: 423 DPMS_CHECK_TIMEOUT(DPMSSuspendTime) 424 425 case DPMSModeSuspend: 426 DPMS_CHECK_TIMEOUT(DPMSOffTime) 427 428 default: /* DPMSModeOff */ 429 return 0; 430 } 431 } 432 #endif /* DPMSExtension */ 433 434 static CARD32 435 ScreenSaverTimeoutExpire(OsTimerPtr timer, CARD32 now, void *arg) 436 { 437 INT32 timeout = now - LastEventTime(XIAllDevices).milliseconds; 438 CARD32 nextTimeout = 0; 439 440 #ifdef DPMSExtension 441 /* 442 * Check each mode lowest to highest, since a lower mode can 443 * have the same timeout as a higher one. 444 */ 445 if (DPMSEnabled) { 446 DPMS_CHECK_MODE(DPMSModeOff, DPMSOffTime) 447 DPMS_CHECK_MODE(DPMSModeSuspend, DPMSSuspendTime) 448 DPMS_CHECK_MODE(DPMSModeStandby, DPMSStandbyTime) 449 450 nextTimeout = NextDPMSTimeout(timeout); 451 } 452 453 /* 454 * Only do the screensaver checks if we're not in a DPMS 455 * power saving mode 456 */ 457 if (DPMSPowerLevel != DPMSModeOn) 458 return nextTimeout; 459 #endif /* DPMSExtension */ 460 461 if (!ScreenSaverTime) 462 return nextTimeout; 463 464 if (timeout < ScreenSaverTime) { 465 return nextTimeout > 0 ? 466 min(ScreenSaverTime - timeout, nextTimeout) : 467 ScreenSaverTime - timeout; 468 } 469 470 ResetOsBuffers(); /* not ideal, but better than nothing */ 471 dixSaveScreens(serverClient, SCREEN_SAVER_ON, ScreenSaverActive); 472 473 if (ScreenSaverInterval > 0) { 474 nextTimeout = nextTimeout > 0 ? 475 min(ScreenSaverInterval, nextTimeout) : ScreenSaverInterval; 476 } 477 478 return nextTimeout; 479 } 480 481 static OsTimerPtr ScreenSaverTimer = NULL; 482 483 void 484 FreeScreenSaverTimer(void) 485 { 486 if (ScreenSaverTimer) { 487 TimerFree(ScreenSaverTimer); 488 ScreenSaverTimer = NULL; 489 } 490 } 491 492 void 493 SetScreenSaverTimer(void) 494 { 495 CARD32 timeout = 0; 496 497 #ifdef DPMSExtension 498 if (DPMSEnabled) { 499 /* 500 * A higher DPMS level has a timeout that's either less 501 * than or equal to that of a lower DPMS level. 502 */ 503 if (DPMSStandbyTime > 0) 504 timeout = DPMSStandbyTime; 505 506 else if (DPMSSuspendTime > 0) 507 timeout = DPMSSuspendTime; 508 509 else if (DPMSOffTime > 0) 510 timeout = DPMSOffTime; 511 } 512 #endif 513 514 if (ScreenSaverTime > 0) { 515 timeout = timeout > 0 ? min(ScreenSaverTime, timeout) : ScreenSaverTime; 516 } 517 518 #ifdef SCREENSAVER 519 if (timeout && !screenSaverSuspended) { 520 #else 521 if (timeout) { 522 #endif 523 ScreenSaverTimer = TimerSet(ScreenSaverTimer, 0, timeout, 524 ScreenSaverTimeoutExpire, NULL); 525 } 526 else if (ScreenSaverTimer) { 527 FreeScreenSaverTimer(); 528 } 529 }