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