WaitFor.c revision 9ace9065
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 25 26Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. 27 28 All Rights Reserved 29 30Permission to use, copy, modify, and distribute this software and its 31documentation for any purpose and without fee is hereby granted, 32provided that the above copyright notice appear in all copies and that 33both that copyright notice and this permission notice appear in 34supporting documentation, and that the name of Digital not be 35used in advertising or publicity pertaining to distribution of the 36software without specific, written prior permission. 37 38DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 39ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 40DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 41ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 42WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 43ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 44SOFTWARE. 45 46******************************************************************/ 47 48 49/***************************************************************** 50 * OS Dependent input routines: 51 * 52 * WaitForSomething 53 * TimerForce, TimerSet, TimerCheck, TimerFree 54 * 55 *****************************************************************/ 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 <X11/Xpoll.h> 72#include "dixstruct.h" 73#include "opaque.h" 74#ifdef DPMSExtension 75#include "dpmsproc.h" 76#endif 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/* like ffs, but uses fd_mask instead of int as argument, so it works 96 when fd_mask is longer than an int, such as common 64-bit platforms */ 97/* modifications by raphael */ 98int 99mffs(fd_mask mask) 100{ 101 int i; 102 103 if (!mask) return 0; 104 i = 1; 105 while (!(mask & 1)) 106 { 107 i++; 108 mask >>= 1; 109 } 110 return i; 111} 112 113#ifdef DPMSExtension 114#include <X11/extensions/dpmsconst.h> 115#endif 116 117struct _OsTimerRec { 118 OsTimerPtr next; 119 CARD32 expires; 120 CARD32 delta; 121 OsTimerCallback callback; 122 pointer arg; 123}; 124 125static void DoTimer(OsTimerPtr timer, CARD32 now, OsTimerPtr *prev); 126static void CheckAllTimers(void); 127static OsTimerPtr timers = NULL; 128 129/***************** 130 * WaitForSomething: 131 * Make the server suspend until there is 132 * 1. data from clients or 133 * 2. input events available or 134 * 3. ddx notices something of interest (graphics 135 * queue ready, etc.) or 136 * 4. clients that have buffered replies/events are ready 137 * 138 * If the time between INPUT events is 139 * greater than ScreenSaverTime, the display is turned off (or 140 * saved, depending on the hardware). So, WaitForSomething() 141 * has to handle this also (that's why the select() has a timeout. 142 * For more info on ClientsWithInput, see ReadRequestFromClient(). 143 * pClientsReady is an array to store ready client->index values into. 144 *****************/ 145 146int 147WaitForSomething(int *pClientsReady) 148{ 149 int i; 150 struct timeval waittime, *wt; 151 INT32 timeout = 0; 152 fd_set clientsReadable; 153 fd_set clientsWritable; 154 int curclient; 155 int selecterr; 156 static int nready; 157 fd_set devicesReadable; 158 CARD32 now = 0; 159 Bool someReady = FALSE; 160 161 FD_ZERO(&clientsReadable); 162 163 if (nready) 164 SmartScheduleStopTimer(); 165 nready = 0; 166 167 /* We need a while loop here to handle 168 crashed connections and the screen saver timeout */ 169 while (1) 170 { 171 /* deal with any blocked jobs */ 172 if (workQueue) 173 ProcessWorkQueue(); 174 if (XFD_ANYSET (&ClientsWithInput)) 175 { 176 if (!SmartScheduleDisable) 177 { 178 someReady = TRUE; 179 waittime.tv_sec = 0; 180 waittime.tv_usec = 0; 181 wt = &waittime; 182 } 183 else 184 { 185 XFD_COPYSET (&ClientsWithInput, &clientsReadable); 186 break; 187 } 188 } 189 if (someReady) 190 { 191 XFD_COPYSET(&AllSockets, &LastSelectMask); 192 XFD_UNSET(&LastSelectMask, &ClientsWithInput); 193 } 194 else 195 { 196 wt = NULL; 197 if (timers) 198 { 199 now = GetTimeInMillis(); 200 timeout = timers->expires - now; 201 if (timeout > 0 && timeout > timers->delta + 250) { 202 /* time has rewound. reset the timers. */ 203 CheckAllTimers(); 204 } 205 206 if (timers) { 207 timeout = timers->expires - now; 208 if (timeout < 0) 209 timeout = 0; 210 waittime.tv_sec = timeout / MILLI_PER_SECOND; 211 waittime.tv_usec = (timeout % MILLI_PER_SECOND) * 212 (1000000 / MILLI_PER_SECOND); 213 wt = &waittime; 214 } 215 } 216 XFD_COPYSET(&AllSockets, &LastSelectMask); 217 } 218 219 BlockHandler((pointer)&wt, (pointer)&LastSelectMask); 220 if (NewOutputPending) 221 FlushAllOutput(); 222 /* keep this check close to select() call to minimize race */ 223 if (dispatchException) 224 i = -1; 225 else if (AnyClientsWriteBlocked) 226 { 227 XFD_COPYSET(&ClientsWriteBlocked, &clientsWritable); 228 i = Select (MaxClients, &LastSelectMask, &clientsWritable, NULL, wt); 229 } 230 else 231 { 232 i = Select (MaxClients, &LastSelectMask, NULL, NULL, wt); 233 } 234 selecterr = GetErrno(); 235 WakeupHandler(i, (pointer)&LastSelectMask); 236 if (i <= 0) /* An error or timeout occurred */ 237 { 238 if (dispatchException) 239 return 0; 240 if (i < 0) 241 { 242 if (selecterr == EBADF) /* Some client disconnected */ 243 { 244 CheckConnections (); 245 if (! XFD_ANYSET (&AllClients)) 246 return 0; 247 } 248 else if (selecterr == EINVAL) 249 { 250 FatalError("WaitForSomething(): select: %s\n", 251 strerror(selecterr)); 252 } 253 else if (selecterr != EINTR && selecterr != EAGAIN) 254 { 255 ErrorF("WaitForSomething(): select: %s\n", 256 strerror(selecterr)); 257 } 258 } 259 else if (someReady) 260 { 261 /* 262 * If no-one else is home, bail quickly 263 */ 264 XFD_COPYSET(&ClientsWithInput, &LastSelectMask); 265 XFD_COPYSET(&ClientsWithInput, &clientsReadable); 266 break; 267 } 268 if (*checkForInput[0] != *checkForInput[1]) 269 return 0; 270 271 if (timers) 272 { 273 int expired = 0; 274 now = GetTimeInMillis(); 275 if ((int) (timers->expires - now) <= 0) 276 expired = 1; 277 278 while (timers && (int) (timers->expires - now) <= 0) 279 DoTimer(timers, now, &timers); 280 281 if (expired) 282 return 0; 283 } 284 } 285 else 286 { 287 fd_set tmp_set; 288 289 if (*checkForInput[0] == *checkForInput[1]) { 290 if (timers) 291 { 292 int expired = 0; 293 now = GetTimeInMillis(); 294 if ((int) (timers->expires - now) <= 0) 295 expired = 1; 296 297 while (timers && (int) (timers->expires - now) <= 0) 298 DoTimer(timers, now, &timers); 299 300 if (expired) 301 return 0; 302 } 303 } 304 if (someReady) 305 XFD_ORSET(&LastSelectMask, &ClientsWithInput, &LastSelectMask); 306 if (AnyClientsWriteBlocked && XFD_ANYSET (&clientsWritable)) 307 { 308 NewOutputPending = TRUE; 309 XFD_ORSET(&OutputPending, &clientsWritable, &OutputPending); 310 XFD_UNSET(&ClientsWriteBlocked, &clientsWritable); 311 if (! XFD_ANYSET(&ClientsWriteBlocked)) 312 AnyClientsWriteBlocked = FALSE; 313 } 314 315 XFD_ANDSET(&devicesReadable, &LastSelectMask, &EnabledDevices); 316 XFD_ANDSET(&clientsReadable, &LastSelectMask, &AllClients); 317 XFD_ANDSET(&tmp_set, &LastSelectMask, &WellKnownConnections); 318 if (XFD_ANYSET(&tmp_set)) 319 QueueWorkProc(EstablishNewConnections, NULL, 320 (pointer)&LastSelectMask); 321 322 if (XFD_ANYSET (&devicesReadable) || XFD_ANYSET (&clientsReadable)) 323 break; 324 /* check here for DDXes that queue events during Block/Wakeup */ 325 if (*checkForInput[0] != *checkForInput[1]) 326 return 0; 327 } 328 } 329 330 nready = 0; 331 if (XFD_ANYSET (&clientsReadable)) 332 { 333#ifndef WIN32 334 for (i=0; i<howmany(XFD_SETSIZE, NFDBITS); i++) 335 { 336 int highest_priority = 0; 337 338 while (clientsReadable.fds_bits[i]) 339 { 340 int client_priority, client_index; 341 342 curclient = mffs (clientsReadable.fds_bits[i]) - 1; 343 client_index = /* raphael: modified */ 344 ConnectionTranslation[curclient + (i * (sizeof(fd_mask) * 8))]; 345#else 346 int highest_priority = 0; 347 fd_set savedClientsReadable; 348 XFD_COPYSET(&clientsReadable, &savedClientsReadable); 349 for (i = 0; i < XFD_SETCOUNT(&savedClientsReadable); i++) 350 { 351 int client_priority, client_index; 352 353 curclient = XFD_FD(&savedClientsReadable, i); 354 client_index = GetConnectionTranslation(curclient); 355#endif 356 /* We implement "strict" priorities. 357 * Only the highest priority client is returned to 358 * dix. If multiple clients at the same priority are 359 * ready, they are all returned. This means that an 360 * aggressive client could take over the server. 361 * This was not considered a big problem because 362 * aggressive clients can hose the server in so many 363 * other ways :) 364 */ 365 client_priority = clients[client_index]->priority; 366 if (nready == 0 || client_priority > highest_priority) 367 { 368 /* Either we found the first client, or we found 369 * a client whose priority is greater than all others 370 * that have been found so far. Either way, we want 371 * to initialize the list of clients to contain just 372 * this client. 373 */ 374 pClientsReady[0] = client_index; 375 highest_priority = client_priority; 376 nready = 1; 377 } 378 /* the following if makes sure that multiple same-priority 379 * clients get batched together 380 */ 381 else if (client_priority == highest_priority) 382 { 383 pClientsReady[nready++] = client_index; 384 } 385#ifndef WIN32 386 clientsReadable.fds_bits[i] &= ~(((fd_mask)1L) << curclient); 387 } 388#else 389 FD_CLR(curclient, &clientsReadable); 390#endif 391 } 392 } 393 394 if (nready) 395 SmartScheduleStartTimer(); 396 397 return nready; 398} 399 400/* If time has rewound, re-run every affected timer. 401 * Timers might drop out of the list, so we have to restart every time. */ 402static void 403CheckAllTimers(void) 404{ 405 OsTimerPtr timer; 406 CARD32 now; 407 408start: 409 now = GetTimeInMillis(); 410 411 for (timer = timers; timer; timer = timer->next) { 412 if (timer->expires - now > timer->delta + 250) { 413 TimerForce(timer); 414 goto start; 415 } 416 } 417} 418 419static void 420DoTimer(OsTimerPtr timer, CARD32 now, OsTimerPtr *prev) 421{ 422 CARD32 newTime; 423 424 *prev = timer->next; 425 timer->next = NULL; 426 newTime = (*timer->callback)(timer, now, timer->arg); 427 if (newTime) 428 TimerSet(timer, 0, newTime, timer->callback, timer->arg); 429} 430 431OsTimerPtr 432TimerSet(OsTimerPtr timer, int flags, CARD32 millis, 433 OsTimerCallback func, pointer arg) 434{ 435 register OsTimerPtr *prev; 436 CARD32 now = GetTimeInMillis(); 437 438 if (!timer) 439 { 440 timer = malloc(sizeof(struct _OsTimerRec)); 441 if (!timer) 442 return NULL; 443 } 444 else 445 { 446 for (prev = &timers; *prev; prev = &(*prev)->next) 447 { 448 if (*prev == timer) 449 { 450 *prev = timer->next; 451 if (flags & TimerForceOld) 452 (void)(*timer->callback)(timer, now, timer->arg); 453 break; 454 } 455 } 456 } 457 if (!millis) 458 return timer; 459 if (flags & TimerAbsolute) { 460 timer->delta = millis - now; 461 } 462 else { 463 timer->delta = millis; 464 millis += now; 465 } 466 timer->expires = millis; 467 timer->callback = func; 468 timer->arg = arg; 469 if ((int) (millis - now) <= 0) 470 { 471 timer->next = NULL; 472 millis = (*timer->callback)(timer, now, timer->arg); 473 if (!millis) 474 return timer; 475 } 476 for (prev = &timers; 477 *prev && (int) ((*prev)->expires - millis) <= 0; 478 prev = &(*prev)->next) 479 ; 480 timer->next = *prev; 481 *prev = timer; 482 return timer; 483} 484 485Bool 486TimerForce(OsTimerPtr timer) 487{ 488 OsTimerPtr *prev; 489 490 for (prev = &timers; *prev; prev = &(*prev)->next) 491 { 492 if (*prev == timer) 493 { 494 DoTimer(timer, GetTimeInMillis(), prev); 495 return TRUE; 496 } 497 } 498 return FALSE; 499} 500 501 502void 503TimerCancel(OsTimerPtr timer) 504{ 505 OsTimerPtr *prev; 506 507 if (!timer) 508 return; 509 for (prev = &timers; *prev; prev = &(*prev)->next) 510 { 511 if (*prev == timer) 512 { 513 *prev = timer->next; 514 break; 515 } 516 } 517} 518 519void 520TimerFree(OsTimerPtr timer) 521{ 522 if (!timer) 523 return; 524 TimerCancel(timer); 525 free(timer); 526} 527 528void 529TimerCheck(void) 530{ 531 CARD32 now = GetTimeInMillis(); 532 533 while (timers && (int) (timers->expires - now) <= 0) 534 DoTimer(timers, now, &timers); 535} 536 537void 538TimerInit(void) 539{ 540 OsTimerPtr timer; 541 542 while ((timer = timers)) 543 { 544 timers = timer->next; 545 free(timer); 546 } 547} 548 549#ifdef DPMSExtension 550 551#define DPMS_CHECK_MODE(mode,time)\ 552 if (time > 0 && DPMSPowerLevel < mode && timeout >= time)\ 553 DPMSSet(serverClient, mode); 554 555#define DPMS_CHECK_TIMEOUT(time)\ 556 if (time > 0 && (time - timeout) > 0)\ 557 return time - timeout; 558 559static CARD32 560NextDPMSTimeout(INT32 timeout) 561{ 562 /* 563 * Return the amount of time remaining until we should set 564 * the next power level. Fallthroughs are intentional. 565 */ 566 switch (DPMSPowerLevel) 567 { 568 case DPMSModeOn: 569 DPMS_CHECK_TIMEOUT(DPMSStandbyTime) 570 571 case DPMSModeStandby: 572 DPMS_CHECK_TIMEOUT(DPMSSuspendTime) 573 574 case DPMSModeSuspend: 575 DPMS_CHECK_TIMEOUT(DPMSOffTime) 576 577 default: /* DPMSModeOff */ 578 return 0; 579 } 580} 581#endif /* DPMSExtension */ 582 583static CARD32 584ScreenSaverTimeoutExpire(OsTimerPtr timer,CARD32 now,pointer arg) 585{ 586 INT32 timeout = now - lastDeviceEventTime.milliseconds; 587 CARD32 nextTimeout = 0; 588 589#ifdef DPMSExtension 590 /* 591 * Check each mode lowest to highest, since a lower mode can 592 * have the same timeout as a higher one. 593 */ 594 if (DPMSEnabled) 595 { 596 DPMS_CHECK_MODE(DPMSModeOff, DPMSOffTime) 597 DPMS_CHECK_MODE(DPMSModeSuspend, DPMSSuspendTime) 598 DPMS_CHECK_MODE(DPMSModeStandby, DPMSStandbyTime) 599 600 nextTimeout = NextDPMSTimeout(timeout); 601 } 602 603 /* 604 * Only do the screensaver checks if we're not in a DPMS 605 * power saving mode 606 */ 607 if (DPMSPowerLevel != DPMSModeOn) 608 return nextTimeout; 609#endif /* DPMSExtension */ 610 611 if (!ScreenSaverTime) 612 return nextTimeout; 613 614 if (timeout < ScreenSaverTime) 615 { 616 return nextTimeout > 0 ? 617 min(ScreenSaverTime - timeout, nextTimeout) : 618 ScreenSaverTime - timeout; 619 } 620 621 ResetOsBuffers(); /* not ideal, but better than nothing */ 622 dixSaveScreens(serverClient, SCREEN_SAVER_ON, ScreenSaverActive); 623 624 if (ScreenSaverInterval > 0) 625 { 626 nextTimeout = nextTimeout > 0 ? 627 min(ScreenSaverInterval, nextTimeout) : 628 ScreenSaverInterval; 629 } 630 631 return nextTimeout; 632} 633 634static OsTimerPtr ScreenSaverTimer = NULL; 635 636void 637FreeScreenSaverTimer(void) 638{ 639 if (ScreenSaverTimer) { 640 TimerFree(ScreenSaverTimer); 641 ScreenSaverTimer = NULL; 642 } 643} 644 645void 646SetScreenSaverTimer(void) 647{ 648 CARD32 timeout = 0; 649 650#ifdef DPMSExtension 651 if (DPMSEnabled) 652 { 653 /* 654 * A higher DPMS level has a timeout that's either less 655 * than or equal to that of a lower DPMS level. 656 */ 657 if (DPMSStandbyTime > 0) 658 timeout = DPMSStandbyTime; 659 660 else if (DPMSSuspendTime > 0) 661 timeout = DPMSSuspendTime; 662 663 else if (DPMSOffTime > 0) 664 timeout = DPMSOffTime; 665 } 666#endif 667 668 if (ScreenSaverTime > 0) 669 { 670 timeout = timeout > 0 ? 671 min(ScreenSaverTime, timeout) : 672 ScreenSaverTime; 673 } 674 675#ifdef SCREENSAVER 676 if (timeout && !screenSaverSuspended) { 677#else 678 if (timeout) { 679#endif 680 ScreenSaverTimer = TimerSet(ScreenSaverTimer, 0, timeout, 681 ScreenSaverTimeoutExpire, NULL); 682 } 683 else if (ScreenSaverTimer) { 684 FreeScreenSaverTimer(); 685 } 686} 687 688