]> git.sesse.net Git - rdpsrv/blob - Xserver/programs/Xserver/os/WaitFor.c
Import X server from vnc-3.3.7.
[rdpsrv] / Xserver / programs / Xserver / os / WaitFor.c
1 /***********************************************************
2
3 Copyright (c) 1987  X Consortium
4
5 Permission is hereby granted, free of charge, to any person obtaining a copy
6 of this software and associated documentation files (the "Software"), to deal
7 in the Software without restriction, including without limitation the rights
8 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 copies of the Software, and to permit persons to whom the Software is
10 furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
18 X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
19 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22 Except as contained in this notice, the name of the X Consortium shall not be
23 used in advertising or otherwise to promote the sale, use or other dealings
24 in this Software without prior written authorization from the X Consortium.
25
26
27 Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
28
29                         All Rights Reserved
30
31 Permission to use, copy, modify, and distribute this software and its 
32 documentation for any purpose and without fee is hereby granted, 
33 provided that the above copyright notice appear in all copies and that
34 both that copyright notice and this permission notice appear in 
35 supporting documentation, and that the name of Digital not be
36 used in advertising or publicity pertaining to distribution of the
37 software without specific, written prior permission.  
38
39 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
40 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
41 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
42 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
43 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
44 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
45 SOFTWARE.
46
47 ******************************************************************/
48
49 /* $XConsortium: WaitFor.c /main/55 1996/12/02 10:22:24 lehors $ */
50 /* $XFree86: xc/programs/Xserver/os/WaitFor.c,v 3.11.2.3 1998/01/31 14:23:33 hohndel Exp $ */
51
52 /*****************************************************************
53  * OS Dependent input routines:
54  *
55  *  WaitForSomething
56  *  TimerForce, TimerSet, TimerCheck, TimerFree
57  *
58  *****************************************************************/
59
60 #ifdef WIN32
61 #include <X11/Xwinsock.h>
62 #endif
63 #include "Xos.h"                        /* for strings, fcntl, time */
64
65 #include <errno.h>
66 #ifdef X_NOT_STDC_ENV
67 extern int errno;
68 #endif
69
70 #include <stdio.h>
71 #include "X.h"
72 #include "misc.h"
73
74 #ifdef MINIX
75 #include <sys/nbio.h>
76 #define select(n,r,w,x,t) nbio_select(n,r,w,x,t)
77 #endif
78 #ifdef __EMX__
79 #define select(n,r,w,x,t) os2PseudoSelect(n,r,w,x,t)
80 #endif
81 #include <X11/Xpoll.h>
82 #include "osdep.h"
83 #include "dixstruct.h"
84 #include "opaque.h"
85
86 #ifdef DPMSExtension
87 #include "dpms.h"
88 extern void DPMSSet();
89 #endif
90
91 extern fd_set AllSockets;
92 extern fd_set AllClients;
93 extern fd_set LastSelectMask;
94 extern fd_set WellKnownConnections;
95 extern fd_set EnabledDevices;
96 extern fd_set ClientsWithInput;
97 extern fd_set ClientsWriteBlocked;
98 extern fd_set OutputPending;
99
100 extern int ConnectionTranslation[];
101
102 extern Bool NewOutputPending;
103 extern Bool AnyClientsWriteBlocked;
104
105 extern WorkQueuePtr workQueue;
106
107
108 #ifdef XTESTEXT1
109 /*
110  * defined in xtestext1dd.c
111  */
112 extern int playback_on;
113 #endif /* XTESTEXT1 */
114
115 struct _OsTimerRec {
116     OsTimerPtr          next;
117     CARD32              expires;
118     OsTimerCallback     callback;
119     pointer             arg;
120 };
121
122 static void DoTimer();
123 static OsTimerPtr timers;
124
125 /*****************
126  * WaitForSomething:
127  *     Make the server suspend until there is
128  *      1. data from clients or
129  *      2. input events available or
130  *      3. ddx notices something of interest (graphics
131  *         queue ready, etc.) or
132  *      4. clients that have buffered replies/events are ready
133  *
134  *     If the time between INPUT events is
135  *     greater than ScreenSaverTime, the display is turned off (or
136  *     saved, depending on the hardware).  So, WaitForSomething()
137  *     has to handle this also (that's why the select() has a timeout.
138  *     For more info on ClientsWithInput, see ReadRequestFromClient().
139  *     pClientsReady is an array to store ready client->index values into.
140  *****************/
141
142 static INT32 timeTilFrob = 0;           /* while screen saving */
143
144 #if !defined(AMOEBA)
145
146 int
147 WaitForSomething(pClientsReady)
148     int *pClientsReady;
149 {
150     int i;
151     struct timeval waittime, *wt;
152     INT32 timeout;
153 #ifdef DPMSExtension
154     INT32 standbyTimeout, suspendTimeout, offTimeout;
155 #endif
156     fd_set clientsReadable;
157     fd_set clientsWritable;
158     int curclient;
159     int selecterr;
160     int nready;
161     fd_set devicesReadable;
162     CARD32 now;
163
164     FD_ZERO(&clientsReadable);
165
166     /* We need a while loop here to handle 
167        crashed connections and the screen saver timeout */
168     while (1)
169     {
170         /* deal with any blocked jobs */
171         if (workQueue)
172             ProcessWorkQueue();
173
174         if (XFD_ANYSET (&ClientsWithInput))
175         {
176             XFD_COPYSET (&ClientsWithInput, &clientsReadable);
177             break;
178         }
179 #ifdef DPMSExtension
180         if (ScreenSaverTime > 0 || DPMSEnabled || timers)
181 #else
182         if (ScreenSaverTime > 0 || timers)
183 #endif
184             now = GetTimeInMillis();
185         wt = NULL;
186         if (timers)
187         {
188             while (timers && timers->expires <= now)
189                 DoTimer(timers, now, &timers);
190             if (timers)
191             {
192                 timeout = timers->expires - now;
193                 waittime.tv_sec = timeout / MILLI_PER_SECOND;
194                 waittime.tv_usec = (timeout % MILLI_PER_SECOND) *
195                     (1000000 / MILLI_PER_SECOND);
196                 wt = &waittime;
197             }
198         }
199 #ifdef DPMSExtension
200         if (ScreenSaverTime > 0 ||
201             (DPMSEnabled &&
202              (DPMSStandbyTime > 0 || DPMSSuspendTime > 0 || DPMSOffTime > 0)))
203 #else
204         if (ScreenSaverTime > 0)
205 #endif
206         {
207 #ifdef DPMSExtension
208
209             if (ScreenSaverTime > 0)
210                 timeout = (ScreenSaverTime -
211                            (now - lastDeviceEventTime.milliseconds));
212             if (DPMSStandbyTime > 0)
213                 standbyTimeout = (DPMSStandbyTime -
214                                   (now - lastDeviceEventTime.milliseconds));
215             if (DPMSSuspendTime > 0)
216                 suspendTimeout = (DPMSSuspendTime -
217                                   (now - lastDeviceEventTime.milliseconds));
218             if (DPMSOffTime > 0)
219                 offTimeout = (DPMSOffTime -
220                               (now - lastDeviceEventTime.milliseconds));
221 #else
222             timeout = (ScreenSaverTime -
223                        (now - lastDeviceEventTime.milliseconds));
224 #endif /* DPMSExtension */
225 #ifdef DPMSExtension
226             if (timeout <= 0 && ScreenSaverTime > 0)
227 #else
228             if (timeout <= 0) /* may be forced by AutoResetServer() */
229 #endif /* DPMSExtension */
230             {
231                 INT32 timeSinceSave;
232
233                 timeSinceSave = -timeout;
234                 if (timeSinceSave >= timeTilFrob && timeTilFrob >= 0)
235                 {
236                     ResetOsBuffers(); /* not ideal, but better than nothing */
237                     SaveScreens(SCREEN_SAVER_ON, ScreenSaverActive);
238 #ifdef DPMSExtension
239                     if (ScreenSaverInterval > 0 &&
240                         DPMSPowerLevel == DPMSModeOn)
241 #else
242                     if (ScreenSaverInterval)
243 #endif /* DPMSExtension */
244                         /* round up to the next ScreenSaverInterval */
245                         timeTilFrob = ScreenSaverInterval *
246                                 ((timeSinceSave + ScreenSaverInterval) /
247                                         ScreenSaverInterval);
248                     else
249                         timeTilFrob = -1;
250                 }
251                 timeout = timeTilFrob - timeSinceSave;
252             }
253             else
254             {
255                 if (ScreenSaverTime > 0 && timeout > ScreenSaverTime)
256                     timeout = ScreenSaverTime;
257                 timeTilFrob = 0;
258             }
259 #ifdef DPMSExtension
260             if (DPMSEnabled)
261             {
262                 if (standbyTimeout > 0 
263                     && (timeout <= 0 || timeout > standbyTimeout))
264                     timeout = standbyTimeout;
265                 if (suspendTimeout > 0 
266                     && (timeout <= 0 || timeout > suspendTimeout))
267                     timeout = suspendTimeout;
268                 if (offTimeout > 0 
269                     && (timeout <= 0 || timeout > offTimeout))
270                     timeout = offTimeout;
271             }
272 #endif
273             if (timeout > 0 && (!wt || timeout < (timers->expires - now)))
274             {
275                 waittime.tv_sec = timeout / MILLI_PER_SECOND;
276                 waittime.tv_usec = (timeout % MILLI_PER_SECOND) *
277                                         (1000000 / MILLI_PER_SECOND);
278                 wt = &waittime;
279             }
280 #ifdef DPMSExtension
281             /* don't bother unless it's switched on */
282             if (DPMSEnabled)
283             {
284                 /*
285                  * If this mode's enabled, and if the time's come
286                  * and if we're still at a lesser mode, do it now.
287                  */
288                 if (DPMSStandbyTime > 0) {
289                     if (standbyTimeout <= 0) {
290                         if (DPMSPowerLevel < DPMSModeStandby) {
291                             DPMSSet(DPMSModeStandby);
292                         }
293                     }
294                 }
295                 /*
296                  * and ditto.  Note that since these modes can have the
297                  * same timeouts, they can happen at the same time.
298                  */
299                 if (DPMSSuspendTime > 0) {
300                     if (suspendTimeout <= 0) {
301                         if (DPMSPowerLevel < DPMSModeSuspend) {
302                             DPMSSet(DPMSModeSuspend);
303                         }
304                     }
305                 }
306                 if (DPMSOffTime > 0) {
307                     if (offTimeout <= 0) {
308                         if (DPMSPowerLevel < DPMSModeOff) {
309                             DPMSSet(DPMSModeOff);
310                         }
311                     }
312                 }
313            }
314 #endif
315         }
316         XFD_COPYSET(&AllSockets, &LastSelectMask);
317         BlockHandler((pointer)&wt, (pointer)&LastSelectMask);
318         if (NewOutputPending)
319             FlushAllOutput();
320 #ifdef XTESTEXT1
321         /* XXX how does this interact with new write block handling? */
322         if (playback_on) {
323             wt = &waittime;
324             XTestComputeWaitTime (&waittime);
325         }
326 #endif /* XTESTEXT1 */
327         /* keep this check close to select() call to minimize race */
328         if (dispatchException)
329             i = -1;
330         else if (AnyClientsWriteBlocked)
331         {
332             XFD_COPYSET(&ClientsWriteBlocked, &clientsWritable);
333             i = Select (MAXSOCKS, &LastSelectMask, &clientsWritable, NULL, wt);
334         }
335         else
336             i = Select (MAXSOCKS, &LastSelectMask, NULL, NULL, wt);
337         selecterr = errno;
338         WakeupHandler(i, (pointer)&LastSelectMask);
339 #ifdef XTESTEXT1
340         if (playback_on) {
341             i = XTestProcessInputAction (i, &waittime);
342         }
343 #endif /* XTESTEXT1 */
344         if (i <= 0) /* An error or timeout occurred */
345         {
346
347             if (dispatchException)
348                 return 0;
349             FD_ZERO(&clientsWritable);
350             if (i < 0) 
351                 if (selecterr == EBADF)    /* Some client disconnected */
352                 {
353                     CheckConnections ();
354                     if (! XFD_ANYSET (&AllClients))
355                         return 0;
356                 }
357                 else if (selecterr == EINVAL)
358                 {
359                     FatalError("WaitForSomething(): select: errno=%d\n",
360                         selecterr);
361                 }
362                 else if (selecterr != EINTR)
363                 {
364                     ErrorF("WaitForSomething(): select: errno=%d\n",
365                         selecterr);
366                 }
367             if (timers)
368             {
369                 now = GetTimeInMillis();
370                 while (timers && timers->expires <= now)
371                     DoTimer(timers, now, &timers);
372             }
373             if (*checkForInput[0] != *checkForInput[1])
374                 return 0;
375         }
376         else
377         {
378 #ifdef WIN32
379             fd_set tmp_set;
380 #endif
381             if (AnyClientsWriteBlocked && XFD_ANYSET (&clientsWritable))
382             {
383                 NewOutputPending = TRUE;
384                 XFD_ORSET(&OutputPending, &clientsWritable, &OutputPending);
385                 XFD_UNSET(&ClientsWriteBlocked, &clientsWritable);
386                 if (! XFD_ANYSET(&ClientsWriteBlocked))
387                     AnyClientsWriteBlocked = FALSE;
388             }
389
390             XFD_ANDSET(&devicesReadable, &LastSelectMask, &EnabledDevices);
391             XFD_ANDSET(&clientsReadable, &LastSelectMask, &AllClients); 
392 #ifndef WIN32
393             if (LastSelectMask.fds_bits[0] & WellKnownConnections.fds_bits[0]) 
394 #else
395             XFD_ANDSET(&tmp_set, &LastSelectMask, &WellKnownConnections);
396             if (XFD_ANYSET(&tmp_set))
397 #endif
398                 QueueWorkProc(EstablishNewConnections, NULL,
399                               (pointer)&LastSelectMask);
400 #ifdef DPMSExtension
401             if (XFD_ANYSET (&devicesReadable) && (DPMSPowerLevel != DPMSModeOn))
402                 DPMSSet(DPMSModeOn);
403 #endif
404             if (XFD_ANYSET (&devicesReadable) || XFD_ANYSET (&clientsReadable))
405                 break;
406         }
407     }
408
409     nready = 0;
410     if (XFD_ANYSET (&clientsReadable))
411     {
412 #ifndef WIN32
413         for (i=0; i<howmany(XFD_SETSIZE, NFDBITS); i++)
414         {
415             int highest_priority;
416
417             while (clientsReadable.fds_bits[i])
418             {
419                 int client_priority, client_index;
420
421                 curclient = ffs (clientsReadable.fds_bits[i]) - 1;
422                 client_index = ConnectionTranslation[curclient + (i << 5)];
423 #else
424         int highest_priority;
425         fd_set savedClientsReadable;
426         XFD_COPYSET(&clientsReadable, &savedClientsReadable);
427         for (i = 0; i < XFD_SETCOUNT(&savedClientsReadable); i++)
428         {
429             int client_priority, client_index;
430
431             curclient = XFD_FD(&savedClientsReadable, i);
432             client_index = ConnectionTranslation[curclient];
433 #endif
434 #ifdef XSYNC
435                 /*  We implement "strict" priorities.
436                  *  Only the highest priority client is returned to
437                  *  dix.  If multiple clients at the same priority are
438                  *  ready, they are all returned.  This means that an
439                  *  aggressive client could take over the server.
440                  *  This was not considered a big problem because
441                  *  aggressive clients can hose the server in so many 
442                  *  other ways :)
443                  */
444                 client_priority = clients[client_index]->priority;
445                 if (nready == 0 || client_priority > highest_priority)
446                 {
447                     /*  Either we found the first client, or we found
448                      *  a client whose priority is greater than all others
449                      *  that have been found so far.  Either way, we want 
450                      *  to initialize the list of clients to contain just
451                      *  this client.
452                      */
453                     pClientsReady[0] = client_index;
454                     highest_priority = client_priority;
455                     nready = 1;
456                 }
457                 /*  the following if makes sure that multiple same-priority 
458                  *  clients get batched together
459                  */
460                 else if (client_priority == highest_priority)
461 #endif
462                 {
463                     pClientsReady[nready++] = client_index;
464                 }
465 #ifndef WIN32
466                 clientsReadable.fds_bits[i] &= ~(((fd_mask)1) << curclient);
467             }
468 #else
469             FD_CLR(curclient, &clientsReadable);
470 #endif
471         }
472     }
473     return nready;
474 }
475
476 #if 0
477 /*
478  * This is not always a macro.
479  */
480 ANYSET(src)
481     FdMask      *src;
482 {
483     int i;
484
485     for (i=0; i<mskcnt; i++)
486         if (src[ i ])
487             return (TRUE);
488     return (FALSE);
489 }
490 #endif
491
492 #else /* AMOEBA */
493
494 #define dbprintf(list)  /* printf list */
495
496 int
497 WaitForSomething(pClientsReady)
498     int         *pClientsReady;
499 {
500     register int        i, wt, nt;
501     struct timeval      *wtp;
502     long                alwaysCheckForInput[2];
503     int                 nready;
504     int                 timeout;
505     unsigned long       now;
506
507     WakeupInitWaiters();
508
509     /* Be sure to check for input on every sweep in the dispatcher.
510      * This routine should be in InitInput, but since this is more
511      * or less a device dependent routine, and the semantics of it
512      * are device independent I decided to put it here.
513      */
514     alwaysCheckForInput[0] = 0;
515     alwaysCheckForInput[1] = 1;
516     SetInputCheck(&alwaysCheckForInput[0], &alwaysCheckForInput[1]);
517
518     while (1) {
519         /* deal with any blocked jobs */
520         if (workQueue)
521             ProcessWorkQueue();
522
523         if (ANYSET(ClientsWithInput)) {
524             FdSet clientsReadable;
525             int highest_priority;
526
527             COPYBITS(ClientsWithInput, clientsReadable);
528             dbprintf(("WaitFor: "));
529             nready = 0;
530             for (i=0; i < mskcnt; i++) {
531                 while (clientsReadable[i]) {
532                     int client_priority, curclient, client_index;
533
534                     curclient = ffs (clientsReadable[i]) - 1;
535                     client_index = ConnectionTranslation[curclient + (i << 5)];
536                     dbprintf(("%d has input\n", curclient));
537 #ifdef XSYNC
538                     client_priority = clients[client_index]->priority;
539                     if (nready == 0 || client_priority > highest_priority)
540                     {
541                         pClientsReady[0] = client_index;
542                         highest_priority = client_priority;
543                         nready = 1;
544                     }
545                     else if (client_priority == highest_priority)
546 #endif
547                     {
548                         pClientsReady[nready++] = client_index;
549                     }
550                     clientsReadable[i] &= ~(((FdMask)1) << curclient);
551                 }
552             }
553             break;
554         }       
555
556         wt = -1;
557         now = GetTimeInMillis();
558         if (timers)
559         {
560             while (timers && timers->expires <= now)
561                 DoTimer(timers, now, &timers);
562             if (timers)
563             {
564                 timeout = timers->expires - now;
565                 wt = timeout;
566             }
567         }
568         if (ScreenSaverTime) {
569             timeout = ScreenSaverTime - TimeSinceLastInputEvent();
570             if (timeout <= 0) { /* may be forced by AutoResetServer() */
571                 long timeSinceSave;
572
573                 timeSinceSave = -timeout;
574                 if ((timeSinceSave >= timeTilFrob) && (timeTilFrob >= 0)) {
575                     SaveScreens(SCREEN_SAVER_ON, ScreenSaverActive);
576                     if (ScreenSaverInterval)
577                         /* round up to the next ScreenSaverInterval */
578                         timeTilFrob = ScreenSaverInterval *
579                                 ((timeSinceSave + ScreenSaverInterval) /
580                                         ScreenSaverInterval);
581                     else
582                         timeTilFrob = -1;
583                 }
584                 timeout = timeTilFrob - timeSinceSave;
585             } else {
586                 if (timeout > ScreenSaverTime)
587                     timeout = ScreenSaverTime;
588                 timeTilFrob = 0;
589             }
590             
591             if (wt < 0 || (timeTilFrob >= 0 && wt > timeout)) {
592                 wt = timeout;
593             }
594         }
595
596         /* Check for new clients. We do this here and not in the listener
597          * threads because we cannot be sure that dix is re-entrant, and
598          * we need to call some dix routines during startup.
599          */
600         if (nNewConns) {
601             QueueWorkProc(EstablishNewConnections, NULL,
602                           (pointer) 0);
603         }
604
605         /* Call device dependent block handlers, which may want to
606          * specify a different timeout (e.g. used for key auto-repeat).
607          */
608         wtp = (struct timeval *) NULL;
609         BlockHandler((pointer)&wtp, (pointer)NULL);
610         if (wtp) wt = (wtp->tv_sec * 1000) + (wtp->tv_usec / 1000);
611
612         if (NewOutputPending)
613             FlushAllOutput();
614
615         /* TODO: XTESTEXT1 */
616
617         nready = AmFindReadyClients(pClientsReady, AllSockets);
618
619         /* If we found some work, or the iop server has us informed about
620          * new device events, we return.
621          */
622         if (nready || AmoebaEventsAvailable())
623             break;
624
625         if (dispatchException)
626             return 0;
627
628         /* Nothing interesting is available. Go to sleep with a timeout.
629          * The other threads will wake us when needed.
630          */
631         i = SleepMainThread(wt);
632
633         /* Wake up any of the sleeping handlers */
634         WakeupHandler((unsigned long)0, (pointer)NULL);
635
636         /* TODO: XTESTEXT1 */
637
638         if (dispatchException)
639             return 0;
640
641         if (i == -1) {
642             /* An error or timeout occurred */
643             return 0;
644         }
645     }
646
647     dbprintf(("WaitForSomething: %d clients ready\n", nready));
648     return nready;
649 }
650
651 #endif /* AMOEBA */
652
653
654 static void
655 DoTimer(timer, now, prev)
656     register OsTimerPtr timer;
657     CARD32 now;
658     OsTimerPtr *prev;
659 {
660     CARD32 newTime;
661
662     *prev = timer->next;
663     timer->next = NULL;
664     newTime = (*timer->callback)(timer, now, timer->arg);
665     if (newTime)
666         TimerSet(timer, 0, newTime, timer->callback, timer->arg);
667 }
668
669 OsTimerPtr
670 TimerSet(timer, flags, millis, func, arg)
671     register OsTimerPtr timer;
672     int flags;
673     CARD32 millis;
674     OsTimerCallback func;
675     pointer arg;
676 {
677     register OsTimerPtr *prev;
678     CARD32 now = GetTimeInMillis();
679
680     if (!timer)
681     {
682         timer = (OsTimerPtr)xalloc(sizeof(struct _OsTimerRec));
683         if (!timer)
684             return NULL;
685     }
686     else
687     {
688         for (prev = &timers; *prev; prev = &(*prev)->next)
689         {
690             if (*prev == timer)
691             {
692                 *prev = timer->next;
693                 if (flags & TimerForceOld)
694                     (void)(*timer->callback)(timer, now, timer->arg);
695                 break;
696             }
697         }
698     }
699     if (!millis)
700         return timer;
701     if (!(flags & TimerAbsolute))
702         millis += now;
703     timer->expires = millis;
704     timer->callback = func;
705     timer->arg = arg;
706     if (millis <= now)
707     {
708         timer->next = NULL;
709         millis = (*timer->callback)(timer, now, timer->arg);
710         if (!millis)
711             return timer;
712     }
713     for (prev = &timers;
714          *prev && millis > (*prev)->expires;
715          prev = &(*prev)->next)
716         ;
717     timer->next = *prev;
718     *prev = timer;
719     return timer;
720 }
721
722 Bool
723 TimerForce(timer)
724     register OsTimerPtr timer;
725 {
726     register OsTimerPtr *prev;
727     register CARD32 newTime;
728
729     for (prev = &timers; *prev; prev = &(*prev)->next)
730     {
731         if (*prev == timer)
732         {
733             DoTimer(timer, GetTimeInMillis(), prev);
734             return TRUE;
735         }
736     }
737     return FALSE;
738 }
739
740
741 void
742 TimerCancel(timer)
743     register OsTimerPtr timer;
744 {
745     register OsTimerPtr *prev;
746
747     if (!timer)
748         return;
749     for (prev = &timers; *prev; prev = &(*prev)->next)
750     {
751         if (*prev == timer)
752         {
753             *prev = timer->next;
754             break;
755         }
756     }
757 }
758
759 void
760 TimerFree(timer)
761     register OsTimerPtr timer;
762 {
763     if (!timer)
764         return;
765     TimerCancel(timer);
766     xfree(timer);
767 }
768
769 void
770 TimerCheck()
771 {
772     register CARD32 now = GetTimeInMillis();
773
774     while (timers && timers->expires <= now)
775         DoTimer(timers, now, &timers);
776 }
777
778 void
779 TimerInit()
780 {
781     OsTimerPtr timer;
782
783     while (timer = timers)
784     {
785         timers = timer->next;
786         xfree(timer);
787     }
788 }