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