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