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