ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/rclock.C
Revision: 1.1
Committed: Mon May 10 00:36:04 2021 UTC (3 years ago) by root
Content type: text/plain
Branch: MAIN
Log Message:
original 2.7.10 rclock.c

File Contents

# User Rev Content
1 root 1.1 /*--------------------------------*-C-*---------------------------------*
2     * Copyright 1997 1998 Oezguer Kesim <kesim@math.fu-berlin.de>
3     * Copyright 1992, 1993 Robert Nation <nation@rocket.sanders.lockheed.com>
4     *
5     * This program is free software; you can redistribute it and/or modify
6     * it under the terms of the GNU General Public License as published by
7     * the Free Software Foundation; either version 2 of the License, or
8     * (at your option) any later version.
9     *
10     * This program is distributed in the hope that it will be useful,
11     * but WITHOUT ANY WARRANTY; without even the implied warranty of
12     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13     * GNU General Public License for more details.
14     *
15     * You should have received a copy of the GNU General Public License
16     * along with this program; if not, write to the Free Software
17     * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18     *----------------------------------------------------------------------*/
19     #ifdef HAVE_CONFIG_H
20     # include <config.h>
21     #else
22     /* # define STDC_HEADERS */
23     # define HAVE_UNISTD_H
24     # define TIME_WITH_SYS_TIME
25     # define HAVE_SYS_TIME_H
26     # ifdef _AIX
27     # define HAVE_SYS_SELECT_H
28     # endif
29     #endif
30    
31     #include "../src/version.h"
32     #include "feature.h"
33    
34     #include <ctype.h>
35     /* #ifdef STDC_HEADERS */
36     # include <stdarg.h>
37     # include <stdio.h>
38     # include <stdlib.h>
39     # include <string.h>
40     /* #endif */
41    
42     #ifdef HAVE_UNISTD_H
43     # include <unistd.h>
44     #endif
45    
46     #ifdef TIME_WITH_SYS_TIME
47     # include <sys/time.h>
48     # include <time.h>
49     #else
50     # ifdef HAVE_SYS_TIME_H
51     # include <sys/time.h>
52     # else
53     # include <time.h>
54     # endif
55     #endif
56    
57     #if defined (__svr4__)
58     # include <sys/resource.h> /* for struct rlimit */
59     #endif
60    
61     #ifdef HAVE_SYS_SELECT_H
62     # include <sys/select.h>
63     #endif
64     #include <sys/stat.h>
65    
66     #ifdef MAIL
67     #include <dirent.h>
68     #endif
69    
70     #include <X11/Intrinsic.h> /* Xlib, Xutil, Xresource, Xfuncproto */
71    
72     #define APL_CLASS "Clock"
73     #define APL_NAME "rclock"
74     #define MSG_CLASS "Appointment"
75     #define MSG_NAME "Appointment"
76     #define CONFIG_FILE ".rclock"
77    
78     #ifndef EXIT_SUCCESS /* missed from <stdlib.h> ? */
79     # define EXIT_SUCCESS 0
80     # define EXIT_FAILURE 1
81     #endif
82    
83     /*----------------------------------------------------------------------*/
84    
85     static Display* Xdisplay; /* X display */
86     static int Xfd; /* file descriptor of server connection */
87     static GC Xgc, Xrvgc; /* normal, reverse video GC */
88    
89     #define Xscreen DefaultScreen (Xdisplay)
90     #define Xcmap DefaultColormap (Xdisplay, Xscreen)
91     #define Xroot DefaultRootWindow (Xdisplay)
92    
93     /* windows and their sizes */
94     typedef struct {
95     Window win;
96     int width, height;
97     } mywindow_t;
98    
99     static mywindow_t Clock = {None, 80, 80}; /* parent window */
100    
101     #define fgColor 0
102     #define bgColor 1
103     static const char * rs_color [2] = { FG_COLOR_NAME, BG_COLOR_NAME };
104     static Pixel PixColors [2];
105     static const char * rs_geometry = NULL;
106    
107     #ifdef ICONWIN
108     static const char * rs_iconGeometry = NULL;
109     static mywindow_t Icon = {None, 65, 65}; /* icon window */
110     static int iconic_state = NormalState; /* iconic startup? */
111     #endif
112    
113     #ifdef REMINDERS
114     static mywindow_t Msg = {0, 0, 0}; /* message window */
115     static struct {
116     Window
117     # ifndef NO_REMINDER_EXEC
118     Start,
119     # endif
120     Dismiss,
121     Defer;
122     int width, height;
123     } msgButton;
124    
125     static XFontStruct * Xfont;
126     #define FontHeight() (Xfont->ascent + Xfont->descent)
127     static int Msg_Mapped = 0; /* message window mapped? */
128     static int reminderTime = -1;
129     static char message [256] = "";
130     #ifndef NO_REMINDER_EXEC
131     static char execPrgm [256] = "";
132     #endif
133     static const char * reminders_file = NULL; /* name of ~/.rclock file */
134     #ifdef DATE_ON_CLOCK_FACE
135     static int show_date = 1; /* show date on face of clock */
136     #endif
137     #endif
138    
139     #ifdef ADJUST_TIME
140     static int adjustTime = 0;
141     #else
142     # define adjustTime 0
143     #endif
144    
145     #ifdef CENTURY
146     # if (CENTURY < 1900)
147     Error, Cenury incorrectly set.
148     # endif
149     #else
150     # define CENTURY 1900
151     #endif
152    
153     static int clockUpdate = CLOCKUPDATE;
154    
155     #ifdef MAIL
156     static int mailUpdate = MAILUPDATE;
157     static char * mail_file = NULL;
158     #ifndef MAIL_SPAWN
159     static char * mail_spawn = NULL;
160     #endif
161     static int is_maildir = 0;
162     #endif
163    
164     static XSizeHints szHint = {
165     PMinSize | PResizeInc | PBaseSize | PWinGravity,
166     0, 0, 80, 80, /* x, y, width and height */
167     1, 1, /* Min width and height */
168     0, 0, /* Max width and height */
169     1, 1, /* Width and height increments */
170     {1, 1}, /* x, y increments */
171     {1, 1}, /* Aspect ratio - not used */
172     0, 0, /* base size */
173     NorthWestGravity /* gravity */
174     };
175    
176     /* subroutine declarations */
177     static void geometry2sizehint (mywindow_t * /* win */,
178     const char * /* geom */);
179     static void Create_Windows (int /* argc */,
180     char * /* argv */ []);
181     static void getXevent (void);
182     static void print_error (const char * /* fmt */, ...);
183    
184     static void Draw_Window (mywindow_t * /* this_win */,
185     int /* full_redraw */);
186     static void Reminder (void);
187     static void Next_Reminder (int /* update_only */);
188    
189     /* Arguments for Next_Reminder() */
190     #define REPLACE 0
191     #define UPDATE 1
192    
193     /*----------------------------------------------------------------------*/
194    
195     static void
196     usage (void)
197     {
198     int i;
199     struct {
200     const char * const opt;
201     const char * const desc;
202     } optList[] = {
203     #define optList_size() (sizeof(optList)/sizeof(optList[0]))
204     { "-display displayname", "X server to contact" },
205     { "-geometry geom", "size (in pixels) and position" },
206     { "-bg color", "background color" },
207     { "-fg color", "foreground color" },
208     #ifdef REMINDERS
209     { "-fn fontname", "normal font for messages" },
210     #ifdef DATE_ON_CLOCK_FACE
211     { "-nodate", "do not display date on the clock face" },
212     #endif
213     #endif
214     #ifdef ICONWIN
215     { "-iconic", "start iconic" },
216     #endif
217     #ifdef ADJUST_TIME
218     { "-adjust +/-ddhhmm", "adjust clock time" },
219     #endif
220     { "-update seconds", "clock update interval" },
221     #ifdef MAIL
222     { "-mail seconds", "check $MAIL interval" },
223     { "-mailfile file", "file to use for mail checking" },
224     #ifndef MAIL_SPAWN
225     { "-mailspawn cmd", "execute `cmd` when clock is clicked" },
226     #endif
227     #endif
228     { "#geom", "icon window geometry" }
229     };
230    
231     fprintf (stderr, "\nUsage v" VERSION ":\n " APL_NAME " [options]\n\n"
232     "where options include:\n");
233    
234     for (i = 0; i < optList_size(); i++)
235     fprintf (stderr, " %-29s%s\n", optList[i].opt, optList[i].desc);
236     }
237    
238    
239     /****************
240     * Check out if we are using a maildir drop (qmail)
241     * Note: this changes mail_dir to hold the "new" diretory
242     */
243     #ifdef MAIL
244     static void
245     CheckMaildir()
246     {
247     struct stat st;
248     char *buf, *p;
249    
250     if( !*mail_file || stat(mail_file, &st) || !S_ISDIR(st.st_mode) )
251     return; /* no */
252    
253     if( !(buf = malloc(strlen(mail_file)+5)) ) {
254     print_error ("malloc error");
255     exit( EXIT_FAILURE );
256     }
257     strcpy(buf,mail_file);
258     p = buf+strlen(buf);
259     if( p[-1] != '/' )
260     *p++ = '/';
261    
262     strcpy(p, "tmp" );
263     if( stat(buf, &st) || !S_ISDIR(st.st_mode ) )
264     goto leave;
265     strcpy(p, "cur" );
266     if( stat(buf, &st) || !S_ISDIR(st.st_mode ) )
267     goto leave;
268     strcpy(p, "new" );
269     if( stat(buf, &st) || !S_ISDIR(st.st_mode ) )
270     goto leave;
271    
272     mail_file = buf;
273     is_maildir = 1;
274     return;
275     leave:
276     free(buf);
277     }
278     #endif
279    
280     /*----------------------------------------------------------------------*
281     * rclock - Rob's clock
282     * simple X windows clock with appointment reminder
283     *----------------------------------------------------------------------*/
284     int
285     main (int argc, char * argv [])
286     {
287     int i;
288     char * opt, * val;
289     const char * display_name = NULL;
290     XGCValues gcv;
291    
292     #ifdef REMINDERS
293     const char * rs_font = FONT_NAME;
294    
295     /* find the ~/.rclock file */
296     if ((val = getenv ("HOME")) != NULL)
297     {
298     char * p = malloc (strlen (CONFIG_FILE) + strlen (val) + 2);
299     if (p == NULL)
300     goto Malloc_Error;
301    
302     strcpy (p, val);
303     strcat (p, "/" CONFIG_FILE);
304    
305     reminders_file = p;
306     }
307     #endif
308     #ifdef MAIL
309     val = getenv ("MAIL"); /* get the mail spool file name */
310     #ifdef MAIL_SPOOL
311     if (val == NULL) /* csh doesn't set $MAIL */
312     {
313     const char * spool = MAIL_SPOOL;
314     char * user = getenv ("USER"); /* assume this works */
315     val = malloc (strlen (spool) + strlen (user) + 1);
316     if (val == NULL)
317     goto Malloc_Error;
318     strcpy (val, spool);
319     strcat (val, user);
320     }
321     #endif
322     mail_file = val;
323     if( mail_file )
324     CheckMaildir();
325     #endif
326    
327     if ((display_name = getenv ("DISPLAY")) == NULL)
328     display_name = ":0";
329    
330     /* parse the command line */
331     for (i = 1; i < argc; i += 2)
332     {
333     opt = argv [i];
334     val = argv [i+1];
335    
336     switch (*opt++) {
337     case '-':
338     break;
339    
340     case '#':
341     #ifdef ICONWIN
342     rs_iconGeometry = opt; /* drop */
343     #endif
344     default:
345     continue;
346     break;
347     }
348    
349     if (*opt == 'd' && val) display_name = val; /* "d", "display" */
350     else if (*opt == 'g' && val) rs_geometry = val; /* "g", "geometry" */
351     #ifdef ICONWIN
352     else if (*opt == 'i') /* "ic", "iconic" */
353     {
354     iconic_state = IconicState;
355     i--; /* no argument */
356     }
357     #endif
358     else if (!strcmp (opt, "fg") && val) rs_color [fgColor] = val;
359     else if (!strcmp (opt, "bg") && val) rs_color [bgColor] = val;
360     #ifdef REMINDERS
361     else if (!strcmp (opt, "fn") && val) rs_font = val;
362     #ifdef DATE_ON_CLOCK_FACE
363     else if (!strcmp (opt, "nodate"))
364     {
365     show_date = 0;
366     i--; /* no argument */
367     }
368     #endif
369     #endif
370     else if (!strcmp (opt, "update") && val)
371     {
372     int x = atoi (val);
373     if (x < 1 || x > 60)
374     print_error ("update: %d sec", clockUpdate);
375     else
376     clockUpdate = x;
377     }
378     #ifdef MAIL
379     else if (!strcmp (opt, "mail") && val)
380     {
381     int x = atoi (val);
382     if (x < 1)
383     print_error ("mail update: %d sec", mailUpdate);
384     else
385     mailUpdate = x;
386     }
387     else if (!strcmp (opt, "mailfile") && val)
388     {
389     /* If the mail environment is not set, then mail_file was created
390     * with a malloc. We need to free it.
391     */
392     if( getenv ("MAIL") == NULL)
393     free( mail_file);
394     /* assume user knows what he's doing, don't check that file is valid...*/
395     mail_file = val;
396     }
397     #ifndef MAIL_SPAWN
398     else if (!strcmp (opt, "mailspawn") && val)
399     {
400     mail_spawn = val;
401     }
402     #endif
403     #endif /* MAIL */
404     #ifdef ADJUST_TIME
405     else if (!strcmp (opt, "adjust") && val)
406     {
407     /* convert ddhhmm to seconds, minimal error checking */
408     int x = atoi (val);
409     adjustTime = ((((abs (x) / 10000) % 100) * 24 /* days */
410     + ((abs (x) / 100) % 100)) * 60 /* hours */
411     + (abs (x) % 100)) * 60; /* minutes */
412     if (x < 0)
413     adjustTime = -adjustTime;
414     }
415     #endif /* ADJUST_TIME */
416     else
417     {
418     usage ();
419     goto Abort;
420     }
421     }
422    
423     /* open display */
424     Xdisplay = XOpenDisplay (display_name);
425     if (!Xdisplay)
426     {
427     print_error ("can't open display %s", display_name);
428     goto Abort;
429     }
430    
431     /* get display info */
432     Xfd = XConnectionNumber (Xdisplay);
433     {
434     const char * const color_msg = "can't load color \"%s\"";
435     XColor xcol;
436    
437     /* allocate foreground/background colors */
438     if (!XParseColor (Xdisplay, Xcmap, rs_color [fgColor], &xcol) ||
439     !XAllocColor (Xdisplay, Xcmap, &xcol))
440     {
441     print_error (color_msg, rs_color [fgColor]);
442     goto Abort;
443     }
444     PixColors [fgColor] = xcol.pixel;
445    
446     if (!XParseColor (Xdisplay, Xcmap, rs_color [bgColor], &xcol) ||
447     !XAllocColor (Xdisplay, Xcmap, &xcol))
448     {
449     print_error (color_msg, rs_color [bgColor]);
450     goto Abort;
451     }
452     PixColors [bgColor] = xcol.pixel;
453     }
454    
455     #ifdef REMINDERS
456     /* load the font for messages */
457     if ((Xfont = XLoadQueryFont (Xdisplay, rs_font)) == NULL)
458     {
459     print_error ("can't load font \"%s\"", rs_font);
460     goto Abort;
461     }
462     gcv.font = Xfont->fid;
463     #endif
464    
465     Create_Windows (argc, argv);
466     /* Create the graphics contexts */
467     gcv.foreground = PixColors [fgColor];
468     gcv.background = PixColors [bgColor];
469    
470     Xgc = XCreateGC (Xdisplay, Clock.win,
471     #ifdef REMINDERS
472     GCFont |
473     #endif
474     GCForeground | GCBackground, &gcv);
475    
476     gcv.foreground = PixColors [bgColor];
477     gcv.background = PixColors [fgColor];
478     Xrvgc = XCreateGC (Xdisplay, Clock.win,
479     #ifdef REMINDERS
480     GCFont |
481     #endif
482     GCForeground | GCBackground, &gcv);
483    
484     getXevent ();
485     return EXIT_SUCCESS;
486    
487     Malloc_Error:
488     print_error ("malloc error");
489     Abort:
490     print_error ("aborting");
491     return EXIT_FAILURE;
492     }
493    
494     /*
495     * translate geometry string to appropriate sizehint
496     */
497     static void
498     geometry2sizehint (mywindow_t * win, const char * geom)
499     {
500     int x, y, flags;
501     unsigned int width, height;
502    
503     /* copy in values */
504     szHint.width = win->width;
505     szHint.height = win->height;
506    
507     if (geom == NULL)
508     return;
509    
510     flags = XParseGeometry (geom, &x, &y, &width, &height);
511    
512     if (flags & WidthValue)
513     {
514     szHint.width = width + szHint.base_width;
515     szHint.flags |= USSize;
516     }
517     if (flags & HeightValue)
518     {
519     szHint.height = height + szHint.base_height;
520     szHint.flags |= USSize;
521     }
522    
523     if (flags & XValue)
524     {
525     if (flags & XNegative)
526     {
527     x += (DisplayWidth (Xdisplay, Xscreen) - szHint.width);
528     szHint.win_gravity = NorthEastGravity;
529     }
530     szHint.x = x;
531     szHint.flags |= USPosition;
532     }
533     if (flags & YValue)
534     {
535     if (flags & YNegative)
536     {
537     y += (DisplayHeight (Xdisplay, Xscreen) - szHint.height);
538     szHint.win_gravity = (szHint.win_gravity == NorthEastGravity ?
539     SouthEastGravity : SouthWestGravity);
540     }
541     szHint.y = y;
542     szHint.flags |= USPosition;
543     }
544    
545     /* copy out values */
546     win->width = szHint.width;
547     win->height = szHint.height;
548     }
549    
550     /*
551     * Open and map the windows
552     */
553     static void
554     Create_Windows (int argc, char * argv [])
555     {
556     XClassHint classHint;
557     XWMHints wmHint;
558    
559     geometry2sizehint (&Clock, rs_geometry);
560     Clock.win = XCreateSimpleWindow (Xdisplay, Xroot,
561     szHint.x, szHint.y,
562     Clock.width, Clock.height,
563     0,
564     PixColors [fgColor],
565     PixColors [bgColor]);
566    
567     #ifdef ICONWIN
568     geometry2sizehint (&Icon, rs_iconGeometry);
569     Icon.win = XCreateSimpleWindow (Xdisplay, Xroot,
570     szHint.x, szHint.y,
571     Icon.width, Icon.height,
572     0,
573     PixColors [fgColor],
574     PixColors [bgColor]);
575     wmHint.initial_state = iconic_state;
576     wmHint.icon_window = Icon.win;
577     wmHint.flags = InputHint | StateHint | IconWindowHint;
578     #else
579     wmHint.flags = InputHint;
580     #endif
581     wmHint.input = True;
582    
583     /* ignore warning about discarded `const' */
584     classHint.res_name = APL_NAME;
585     classHint.res_class = APL_CLASS;
586     XSetWMProperties (Xdisplay, Clock.win, NULL, NULL, argv, argc,
587     &szHint, &wmHint, &classHint);
588    
589     XSelectInput (Xdisplay, Clock.win,
590     (ExposureMask|StructureNotifyMask|ButtonPressMask));
591    
592     #ifdef ICONWIN
593     XSelectInput (Xdisplay, Icon.win,
594     (ExposureMask|ButtonPressMask));
595     #endif
596     XMapWindow (Xdisplay, Clock.win);
597    
598     /* create, but don't map a window for appointment reminders */
599     #ifdef REMINDERS
600     Msg.win = XCreateSimpleWindow (Xdisplay, Xroot,
601     szHint.x, szHint.y,
602     szHint.width, szHint.height,
603     0,
604     PixColors [fgColor],
605     PixColors [bgColor]);
606    
607     szHint.flags |= USPosition;
608     /* ignore warning about discarded `const' */
609     classHint.res_name = MSG_NAME;
610     classHint.res_class = MSG_CLASS;
611     wmHint.input = True;
612     wmHint.flags = InputHint;
613    
614     XSetWMProperties (Xdisplay, Msg.win, NULL, NULL, argv, argc,
615     &szHint, &wmHint, &classHint);
616     {
617     char * str = MSG_NAME;
618     XStoreName (Xdisplay, Msg.win, str);
619     XSetIconName (Xdisplay, Msg.win, str);
620     }
621    
622     XSelectInput (Xdisplay, Msg.win,
623     (ExposureMask|ButtonPressMask|KeyPressMask));
624    
625     /* font already loaded */
626    
627     msgButton.width = 4 + 5 * XTextWidth (Xfont, "M", 1);
628     msgButton.height = 4 + FontHeight ();
629    
630     msgButton.Dismiss = XCreateSimpleWindow (Xdisplay, Msg.win,
631     0, 0,
632     msgButton.width, msgButton.height,
633     0,
634     PixColors [bgColor],
635     PixColors [fgColor]);
636    
637     XMapWindow (Xdisplay, msgButton.Dismiss);
638    
639     msgButton.Defer = XCreateSimpleWindow (Xdisplay, Msg.win,
640     0, 0,
641     msgButton.width, msgButton.height,
642     0,
643     PixColors [bgColor],
644     PixColors [fgColor]);
645     XMapWindow (Xdisplay, msgButton.Defer);
646    
647     #ifndef NO_REMINDER_EXEC
648     msgButton.Start = XCreateSimpleWindow (Xdisplay, Msg.win,
649     0, 0,
650     msgButton.width, msgButton.height,
651     0,
652     PixColors [bgColor],
653     PixColors [fgColor]);
654     XMapWindow (Xdisplay, msgButton.Start);
655     #endif /* NO_REMINDER_EXEC */
656     #endif
657     }
658    
659     static time_t
660     mk_time (struct tm * tmval)
661     {
662     return (tmval->tm_min
663     + 60 * (tmval->tm_hour
664     + 24 * (tmval->tm_mday
665     + 31 * ((tmval->tm_mon+1)
666     + 12 * tmval->tm_year))));
667     }
668    
669    
670     #ifdef MAIL
671     static int
672     MailAvailable()
673     {
674     struct stat st;
675    
676     if( is_maildir ) {
677     DIR *dirp;
678     struct dirent *d;
679    
680     if( (dirp=opendir( mail_file )) ) {
681     while( (d=readdir(dirp)) ) {
682     if( *d->d_name == '.' )
683     continue;
684     if( isdigit(*d->d_name) ) {
685     closedir(dirp);
686     return 1;
687     }
688     }
689     closedir(dirp);
690     }
691     return 0;
692     }
693     else
694     return !stat(mail_file, &st) &&
695     (st.st_size > 0) && (st.st_mtime >= st.st_atime);
696     }
697     #endif
698    
699     /*----------------------------------------------------------------------*
700     * Redraw the whole window after an exposure or size change.
701     * After a timeout, only redraw the hands.
702     * Provide reminder if needed.
703     *----------------------------------------------------------------------*/
704     static void
705     Draw_Window (mywindow_t * W, int full_redraw)
706     {
707     /* pre-computed values for sin() x1000, to avoid using floats */
708     static const short Sin [60] = {
709     0,
710     105, 208, 309, 407, 500, 588, 669,
711     743, 809, 866, 914, 951, 978, 995,
712     1000,
713     995, 978, 951, 914, 866, 809, 743,
714     669, 588, 500, 407, 309, 208, 105,
715     0,
716     -105, -208, -309, -407, -500, -588, -669,
717     -743, -809, -866, -914, -951, -978, -995,
718     -1000,
719     -995, -978, -951, -914, -866, -809, -743,
720     -669, -588, -500, -407, -309, -208, -105
721     };
722    
723     static int savedDay = -1;
724    
725     time_t currentTime;
726     struct tm * tmval;
727     int ctr_x, ctr_y;
728    
729     typedef struct {
730     int h_x, h_y; /* hour */
731     int m_x, m_y; /* minute */
732     int s_x, s_y; /* second */
733     } hands_t; /* hand positions (x,y) */
734    
735     hands_t HandsNow, * pHandsOld;
736    
737     GC X_gc, X_rvgc;
738    
739     static hands_t HandsOld = { -1 };
740     #ifdef ICONWIN
741     static hands_t HandsOld_icon = { -1 };
742     #endif
743     #ifdef REMINDERS
744     static int lastUpdateTime = -10;
745     #endif
746    
747     #ifdef MAIL
748     static time_t mailTime = 0;
749     static int MailUp = 0, MailUp_rvideo = 0;
750     #ifdef ICONWIN
751     static int MailUp_icon = 0;
752     #endif
753     #endif /* MAIL */
754    
755     currentTime = time (NULL) + adjustTime; /* get the current time */
756     tmval = localtime (&currentTime);
757    
758     #ifdef MAIL
759     #ifdef REMINDERS
760     if (W->win != Msg.win)
761     #endif
762     {
763     int * pMailUp = (
764     #ifdef ICONWIN
765     W->win == Icon.win ? &MailUp_icon :
766     #endif
767     &MailUp);
768    
769     if ((currentTime - mailTime) >= mailUpdate)
770     {
771     struct stat st;
772    
773     if (
774     #ifdef ICONWIN
775     MailUp != MailUp_icon ? MailUp :
776     #endif
777     ((mail_file != NULL) && MailAvailable() ) )
778     {
779     if (!*pMailUp)
780     {
781     *pMailUp = 1;
782     full_redraw = 1;
783     XSetWindowBackground (Xdisplay, W->win,
784     PixColors [fgColor]);
785     #ifdef MAIL_BELL
786     XBell (Xdisplay, 0);
787     #endif
788     }
789     }
790     else
791     {
792     if (*pMailUp)
793     {
794     *pMailUp = 0;
795     full_redraw = 1;
796     XSetWindowBackground (Xdisplay, W->win,
797     PixColors [bgColor]);
798     }
799     }
800     #ifdef ICONWIN
801     if (MailUp == MailUp_icon)
802     #endif
803     mailTime = currentTime;
804    
805     MailUp_rvideo = *pMailUp;
806     }
807     }
808     #endif /* MAIL */
809    
810     /* once every day, update the window and icon name */
811     if (tmval->tm_yday != savedDay)
812     {
813     char str [20];
814    
815     savedDay = tmval->tm_yday;
816     strftime (str, sizeof(str), "%a %h %d", tmval);
817     XStoreName (Xdisplay, Clock.win, str);
818     XSetIconName (Xdisplay, Clock.win, str);
819     }
820    
821     if (full_redraw)
822     XClearWindow (Xdisplay, W->win);
823    
824     #ifdef REMINDERS
825     /* for a message window, just re-draw the message */
826     if (W->win == Msg.win)
827     {
828     char * beg, * next;
829     int lines;
830    
831     for (beg = message, lines = 0; beg; beg = next, lines++)
832     {
833     char * end;
834    
835     if ((end = strstr (beg, "\\n")) == NULL)
836     {
837     end = beg + strlen (beg);
838     next = NULL;
839     }
840     else
841     {
842     next = end + 2;
843     }
844    
845     XDrawString (Xdisplay, Msg.win,
846     Xgc,
847     (Msg.width -
848     XTextWidth (Xfont, beg, (end-beg))) / 2,
849     10 + Xfont->ascent + FontHeight () * lines,
850     beg, (end-beg));
851     }
852    
853     XDrawString (Xdisplay, msgButton.Dismiss,
854     Xrvgc,
855     (msgButton.width - XTextWidth (Xfont, "Done", 4)) / 2,
856     Xfont->ascent + 2,
857     "Done", 4);
858    
859     XDrawString (Xdisplay, msgButton.Defer,
860     Xrvgc,
861     (msgButton.width - XTextWidth (Xfont, "Defer", 5)) / 2,
862     Xfont->ascent + 2,
863     "Defer", 5);
864    
865     # ifndef NO_REMINDER_EXEC
866     XDrawString (Xdisplay, msgButton.Start,
867     Xrvgc,
868     (msgButton.width - XTextWidth (Xfont, "Start", 5)) / 2,
869     Xfont->ascent + 2,
870     "Start", 5);
871    
872     if (strlen (execPrgm) > 1)
873     XMapWindow (Xdisplay, msgButton.Start);
874     else
875     XUnmapWindow (Xdisplay, msgButton.Start);
876     # endif /* NO_REMINDER_EXEC */
877     return;
878     }
879    
880     /*
881     * Convert multi-field time info to a single integer with a resolution
882     * in minutes.
883     */
884     currentTime = mk_time (tmval);
885    
886     /* is there a reminder pending? */
887     if (reminderTime >= 0 && currentTime >= reminderTime)
888     Reminder ();
889    
890     /* every 10 minutes, or at start of day, check for revised entries */
891     if (!Msg_Mapped &&
892     (currentTime > lastUpdateTime + REMINDERS_TIME ||
893     (currentTime != lastUpdateTime &&
894     tmval->tm_hour == 0 && tmval->tm_min == 0)))
895     {
896     Next_Reminder (UPDATE);
897     lastUpdateTime = currentTime;
898     }
899     #endif
900    
901     /*
902     * draw clock
903     */
904    
905     ctr_x = (W->width / 2);
906     ctr_y = (W->height / 2);
907    
908     #define XPOS(i,val) (ctr_x + (W->width * Sin[i%60] * (val) + 100000) / 200000)
909     #define YPOS(i,val) (ctr_y - (W->height * Sin[(i+15)%60] * (val) + 100000) / 200000)
910     /*
911     * how to draw the clock face
912     */
913    
914     /* calculate the positions of the hands */
915     {
916     int angle = (tmval->tm_hour % 12) * 5 + (tmval->tm_min / 12);
917     HandsNow.h_x = XPOS (angle, 60);
918     HandsNow.h_y = YPOS (angle, 60);
919     }
920     {
921     int angle = tmval->tm_min;
922     HandsNow.m_x = XPOS (angle, 80);
923     HandsNow.m_y = YPOS (angle, 80);
924     }
925     if (clockUpdate == 1)
926     {
927     int angle = tmval->tm_sec;
928     HandsNow.s_x = XPOS (angle, 85);
929     HandsNow.s_y = YPOS (angle, 85);
930     }
931    
932     pHandsOld = (
933     #ifdef ICONWIN
934     W->win == Icon.win ? &HandsOld_icon :
935     #endif
936     &HandsOld);
937    
938     #ifdef MAIL
939     if (MailUp_rvideo)
940     {
941     X_gc = Xrvgc;
942     X_rvgc = Xgc;
943     }
944     else
945     #endif
946     {
947     X_gc = Xgc;
948     X_rvgc = Xrvgc;
949     }
950    
951     /*
952     * Draw the date in the lower half of the clock window.
953     * The code is enclosed in REMINDERS because it uses the same
954     * font as the reminders code.
955     * I believe this should be drawn always so it does not get
956     * "swept away" by the minute hand.
957     */
958     #ifdef REMINDERS && DATE_ON_CLOCK_FACE
959     if( show_date)
960     {
961     char date[10];
962     currentTime = time (NULL) + adjustTime; /* get the current time */
963     tmval = localtime (&currentTime);
964     strftime (date, sizeof(date), "%d", tmval);
965     XDrawString (Xdisplay, W->win, X_gc,
966     ctr_x - XTextWidth(Xfont, date, strlen(date))/2,
967     ctr_y + FontHeight() + (ctr_y - FontHeight())/2,
968     date, strlen(date));
969     }
970     #endif
971    
972     if (full_redraw)
973     {
974     int angle;
975     /*
976     * draw clock face
977     */
978     #ifdef SUBTICKS
979     for (angle = 0; angle < 60; angle++)
980     XDrawPoint (Xdisplay, W->win, X_gc,
981     XPOS (angle, 95),
982     YPOS (angle, 95));
983     #endif
984     for (angle = 0; angle < 60; angle += 5)
985     XDrawLine (Xdisplay, W->win, X_gc,
986     XPOS (angle, 90),
987     YPOS (angle, 90),
988     XPOS (angle, 100),
989     YPOS (angle, 100));
990     }
991     else if (memcmp (pHandsOld, &HandsNow, sizeof(hands_t)))
992     {
993     int i, j;
994     /*
995     * erase old hands
996     */
997     for (i = -1; i < 2; i++) for (j = -1; j < 2; j++)
998     {
999     /* hour/minute hands */
1000     XDrawLine (Xdisplay, W->win, X_rvgc,
1001     ctr_x + i,
1002     ctr_y + j,
1003     pHandsOld->h_x, pHandsOld->h_y);
1004     XDrawLine (Xdisplay, W->win, X_rvgc,
1005     ctr_x + i,
1006     ctr_y + j,
1007     pHandsOld->m_x, pHandsOld->m_y);
1008     }
1009    
1010     if (clockUpdate == 1) /* seconds hand */
1011     XDrawLine (Xdisplay,
1012     W->win, X_rvgc,
1013     ctr_x,
1014     ctr_y,
1015     pHandsOld->s_x, pHandsOld->s_y);
1016     }
1017    
1018     if (full_redraw || memcmp (pHandsOld, &HandsNow, sizeof(hands_t)))
1019     {
1020     int i, j;
1021     /*
1022     * draw new hands
1023     */
1024     for (i = -1; i < 2; i++) for (j = -1; j < 2; j++)
1025     {
1026     /* hour/minute hands */
1027     XDrawLine (Xdisplay, W->win, X_gc,
1028     ctr_x + i,
1029     ctr_y + j,
1030     HandsNow.h_x, HandsNow.h_y);
1031    
1032     XDrawLine (Xdisplay, W->win, X_gc,
1033     ctr_x + i,
1034     ctr_y + j,
1035     HandsNow.m_x, HandsNow.m_y);
1036     }
1037     if (clockUpdate == 1) /* seconds hand */
1038     XDrawLine (Xdisplay, W->win, X_gc,
1039     ctr_x,
1040     ctr_y,
1041     HandsNow.s_x, HandsNow.s_y);
1042    
1043     *pHandsOld = HandsNow;
1044     }
1045     }
1046    
1047     #ifdef REMINDERS
1048     /*
1049     * Read a single integer from *pstr, returns default value if it finds "*"
1050     * DELIM = trailing delimiter to skip
1051     */
1052     static int
1053     GetOneNum (char ** pstr, int def)
1054     {
1055     int num, hit = 0;
1056    
1057     for (num = 0; isdigit (**pstr); (*pstr)++)
1058     {
1059     num = num * 10 + (**pstr - '0');
1060     hit = 1;
1061     }
1062     if (!hit)
1063     {
1064     num = def;
1065     while (**pstr == '*') (*pstr)++;
1066     }
1067     return num;
1068     }
1069    
1070     /*
1071     * find if TODAY is found in PSTR
1072     */
1073     static int
1074     isToday (char ** pstr, int wday)
1075     {
1076     const char * dayNames = DAY_NAMES;
1077     int rval, today;
1078    
1079     today = dayNames [wday];
1080     /* no day specified is same as wildcard */
1081     if (!strchr (dayNames, tolower (**pstr)))
1082     return 1;
1083    
1084     for (rval = 0; strchr (dayNames, tolower (**pstr)); (*pstr)++)
1085     {
1086     if (today == tolower (**pstr) || **pstr == '*')
1087     rval = 1; /* found it */
1088     }
1089     return rval;
1090     }
1091    
1092     static char *
1093     trim_string (char * str)
1094     {
1095     if (str && *str)
1096     {
1097     int n;
1098     while (*str && isspace (*str)) str++;
1099    
1100     n = strlen (str) - 1;
1101     while (n > 0 && isspace (str [n])) n--;
1102     str [n+1] = '\0';
1103     }
1104     return str;
1105     }
1106    
1107     # ifndef NO_REMINDER_EXEC
1108     static char *
1109     extract_program (char * text)
1110     {
1111     char * prgm = text;
1112     while ((prgm = strchr (prgm, ';')) != NULL)
1113     {
1114     if (*(prgm-1) == '\\') /* backslash escaped */
1115     {
1116     /* remove backslash - avoid memmove() */
1117     int i, n = strlen (prgm);
1118     for (i = 0; i <= n; i++)
1119     prgm [i - 1] = prgm [i];
1120     }
1121     else
1122     {
1123     *prgm++ = '\0';
1124     /* remove leading/trailing space */
1125     prgm = trim_string (prgm);
1126     break;
1127     }
1128     }
1129     return prgm;
1130     }
1131     # endif /* NO_REMINDER_EXEC */
1132    
1133     /*
1134     * Read the ~/.rclock file and find the next reminder
1135     *
1136     * update_only = 1
1137     * look for a reminder whose time is greater than the current time,
1138     * but less than the currently set reminder time
1139     *
1140     * update_only = 0
1141     * look for a reminder whose time is greater than the reminder that
1142     * just went off
1143     */
1144     static void
1145     Next_Reminder (int update_only)
1146     {
1147     struct tm * tmval;
1148     char buffer [256];
1149     #ifndef INT_MAX
1150     # define INT_MAX 1e8
1151     #endif
1152     time_t currentTime;
1153     int savedTime = INT_MAX;
1154     FILE * fd;
1155    
1156     if (reminders_file == NULL || (fd = fopen (reminders_file, "r")) == NULL)
1157     {
1158     reminderTime = -1; /* no config file, no reminders */
1159     return;
1160     }
1161    
1162     currentTime = time (NULL) + adjustTime; /* get the current time */
1163     tmval = localtime (&currentTime);
1164     currentTime = mk_time (tmval);
1165    
1166     /* initial startup*/
1167     if (reminderTime < 0)
1168     {
1169     /* ignore reminders that have already occurred */
1170     reminderTime = currentTime;
1171     # ifndef NO_REMINDER_EXEC
1172     /* scan for programs run on start-up */
1173     while (fgets (buffer, sizeof(buffer), fd))
1174     {
1175     char * prgm, * text;
1176    
1177     text = trim_string (buffer);
1178     if (*text != ';') continue;
1179    
1180     prgm = extract_program (text);
1181     if (prgm != NULL && strlen (prgm) > 1)
1182     system (prgm);
1183     }
1184     rewind (fd);
1185     # endif /* NO_REMINDER_EXEC */
1186     }
1187    
1188     /* now scan for next reminder */
1189     while (fgets (buffer, sizeof(buffer), fd))
1190     {
1191     int testTime, hh, mm, mo, dd, yy;
1192     char * text;
1193    
1194     text = trim_string (buffer);
1195     if (*text == '#') continue; /* comment */
1196     if (*text == ';') continue; /* program run on startup */
1197     /*
1198     * parse the line, format is hh:mm mo/dd/yr message; program
1199     * any of hh, mm, mo, dd, yr could be a wildcard `*'
1200     */
1201     hh = GetOneNum (&text, tmval->tm_hour); if (*text == ':') text++;
1202     mm = GetOneNum (&text, 0);
1203    
1204     while (isspace (*text)) text++;
1205     if (!isToday (&text, tmval->tm_wday)) continue;
1206     while (isspace (*text)) text++;
1207    
1208     mo = GetOneNum (&text, tmval->tm_mon+1); if (*text == '/') text++;
1209     dd = GetOneNum (&text, tmval->tm_mday); if (*text == '/') text++;
1210     yy = GetOneNum (&text, tmval->tm_year);
1211    
1212     /* handle 20th/21st centuries */
1213     if (yy > CENTURY)
1214     yy -= 1900;
1215     else if (yy < CENTURY)
1216     yy += (CENTURY - 1900);
1217    
1218     while (isspace (*text)) text++;
1219     if (!*text) continue;
1220    
1221     testTime = (mm + 60 * (hh + 24 * (dd + 31 * (mo + 12 * yy))));
1222    
1223     if (testTime > (update_only ? currentTime : reminderTime))
1224     {
1225     #ifndef NO_REMINDER_EXEC
1226     char * prgm = extract_program (text);
1227     #endif /* NO_REMINDER_EXEC */
1228     /* trim leading/trailing space */
1229     text = trim_string (text);
1230    
1231     /*
1232     * have a reminder whose time is greater than the last
1233     * reminder, now make sure it is the smallest available
1234     */
1235     if (testTime < savedTime)
1236     {
1237     savedTime = testTime;
1238     strncpy (message, text, sizeof(message));
1239     #ifndef NO_REMINDER_EXEC
1240     strncpy (execPrgm, (prgm ? prgm : ""), sizeof(execPrgm));
1241     #endif
1242     }
1243     else if (testTime == savedTime)
1244     {
1245     if (strlen (text))
1246     {
1247     int n = (sizeof(message) - strlen (message) - 3);
1248     if (n > 0)
1249     {
1250     /* for co-occurring events */
1251     strcat (message, "\\n");
1252     strncat (message, text, n);
1253     }
1254     }
1255     #ifndef NO_REMINDER_EXEC
1256     if (prgm != NULL)
1257     {
1258     int n = (sizeof(execPrgm) - strlen (execPrgm) - 2);
1259     if ((n > 0) && (n >= strlen (prgm)))
1260     {
1261     /* for co-occurring programs */
1262     strcat (execPrgm, ";");
1263     strncat (execPrgm, prgm, n);
1264     }
1265     }
1266     #endif /* NO_REMINDER_EXEC */
1267     }
1268     }
1269     }
1270    
1271     reminderTime = (savedTime < INT_MAX) ? savedTime : -1;
1272     fclose (fd);
1273     }
1274    
1275     /*
1276     * Provide reminder by mapping the message window
1277     */
1278     static void
1279     Reminder (void)
1280     {
1281     char * beg, * next;
1282     int lines;
1283    
1284     if (Msg_Mapped)
1285     return;
1286    
1287     #ifndef NO_REMINDER_EXEC
1288     if (strlen (message) == 0)
1289     {
1290     if (strlen (execPrgm) > 1)
1291     {
1292     system (execPrgm);
1293     Next_Reminder (REPLACE);
1294     }
1295     return; /* some sort of error */
1296     }
1297     #endif
1298    
1299     /* compute the window size */
1300     #ifdef NO_REMINDER_EXEC
1301     Msg.width = 10 * XTextWidth (Xfont, "M", 1);
1302     #else
1303     Msg.width = 18 * XTextWidth (Xfont, "M", 1);
1304     #endif
1305    
1306     for (beg = message, lines = 1; beg; beg = next, lines++)
1307     {
1308     int width;
1309     char * end;
1310    
1311     if ((end = strstr (beg, "\\n")) == NULL)
1312     {
1313     end = beg + strlen (beg);
1314     next = NULL;
1315     }
1316     else
1317     {
1318     next = end + 2;
1319     }
1320    
1321     width = XTextWidth (Xfont, beg, (end-beg));
1322     if (Msg.width < width)
1323     Msg.width = width;
1324     }
1325    
1326     Msg.width += 30;
1327     Msg.height = (lines+1) * FontHeight () + 30;
1328    
1329     /* resize and centre the window */
1330     XMoveResizeWindow (Xdisplay, Msg.win,
1331     (DisplayWidth (Xdisplay, Xscreen) - Msg.width ) / 2,
1332     (DisplayHeight (Xdisplay, Xscreen) - Msg.height) / 2,
1333     Msg.width, Msg.height);
1334    
1335     #define BUTTON_MARGIN 8
1336    
1337     XMoveWindow (Xdisplay, msgButton.Dismiss,
1338     BUTTON_MARGIN,
1339     (Msg.height - msgButton.height - BUTTON_MARGIN));
1340     XMoveWindow (Xdisplay, msgButton.Defer,
1341     (Msg.width - msgButton.width - BUTTON_MARGIN),
1342     (Msg.height - msgButton.height - BUTTON_MARGIN));
1343     #ifndef NO_REMINDER_EXEC
1344     XMoveWindow (Xdisplay, msgButton.Start,
1345     (Msg.width - msgButton.width) / 2,
1346     (Msg.height - msgButton.height - BUTTON_MARGIN));
1347     #endif
1348    
1349     XMapRaised (Xdisplay, Msg.win);
1350     XBell (Xdisplay, 0);
1351     Msg_Mapped = 1;
1352     }
1353     #endif /* REMINDERS */
1354    
1355     #ifndef _POSIX_VERSION
1356     # if defined (__svr4__)
1357     static int
1358     getdtablesize (void)
1359     {
1360     struct rlimit rlim;
1361     getrlimit (RLIMIT_NOFILE, &rlim);
1362     return rlim.rlim_cur;
1363     }
1364     # endif
1365     #endif
1366    
1367     /*
1368     * Loops forever, looking for stuff to do. Sleeps 1 minute if nothing to do
1369     */
1370     static void
1371     getXevent (void)
1372     {
1373     XEvent ev;
1374     int num_fds; /* number of file descriptors being used */
1375     struct timeval tm;
1376     struct tm * tmval;
1377     Atom wmDeleteWindow;
1378     fd_set in_fdset;
1379    
1380     /* Enable delete window protocol */
1381     wmDeleteWindow = XInternAtom (Xdisplay, "WM_DELETE_WINDOW", False);
1382     XSetWMProtocols (Xdisplay, Clock.win, &wmDeleteWindow, 1);
1383     #ifdef ICONWIN
1384     XSetWMProtocols (Xdisplay, Icon.win, &wmDeleteWindow, 1);
1385     #endif
1386     #ifdef REMINDERS
1387     XSetWMProtocols (Xdisplay, Msg.win, &wmDeleteWindow, 1);
1388     #endif
1389    
1390     #ifdef _POSIX_VERSION
1391     num_fds = sysconf (_SC_OPEN_MAX);
1392     #else
1393     num_fds = getdtablesize ();
1394     #endif
1395     #ifdef FD_SETSIZE
1396     if (num_fds > FD_SETSIZE)
1397     num_fds = FD_SETSIZE;
1398     #endif
1399    
1400     while (1) {
1401     /* take care of all pending X events */
1402     while (XPending (Xdisplay)) {
1403     XNextEvent (Xdisplay, &ev);
1404     switch (ev.type) {
1405     case ClientMessage:
1406     /* check for delete window requests */
1407     if ((ev.xclient.format == 32) &&
1408     (ev.xclient.data.l[0] == wmDeleteWindow))
1409     {
1410     #ifdef REMINDERS
1411     if (ev.xany.window == Msg.win)
1412     {
1413     XUnmapWindow (Xdisplay, Msg.win);
1414     Msg_Mapped = 0;
1415     Next_Reminder (REPLACE);
1416     }
1417     else
1418     #endif
1419     return; /* delete window is how this terminates */
1420     }
1421     break;
1422    
1423     case Expose:
1424     case GraphicsExpose:
1425     /* need to re-draw a window */
1426     if (ev.xany.window == Clock.win)
1427     Draw_Window (&Clock, 1);
1428     #ifdef ICONWIN
1429     else if (ev.xany.window == Icon.win)
1430     Draw_Window (&Icon, 1);
1431     #endif
1432     #ifdef REMINDERS
1433     else
1434     Draw_Window (&Msg, 1);
1435     #endif
1436     break;
1437    
1438     case ConfigureNotify:
1439     /* window has been re-sized */
1440     if (ev.xany.window == Clock.win)
1441     {
1442     Clock.width = ev.xconfigure.width;
1443     Clock.height = ev.xconfigure.height;
1444     }
1445     break;
1446    
1447     #ifdef REMINDERS
1448     case KeyPress:
1449     /* any key press to dismiss message window */
1450     if (ev.xany.window == Msg.win)
1451     {
1452     Next_Reminder (REPLACE);
1453     Msg_Mapped = 0;
1454     XUnmapWindow (Xdisplay, Msg.win);
1455     }
1456     break;
1457     #endif
1458    
1459     case ButtonPress:
1460     #ifdef REMINDERS
1461     /* button press to dismiss message window */
1462     if (ev.xany.window == Msg.win)
1463     {
1464     if (ev.xbutton.subwindow == msgButton.Dismiss)
1465     {
1466     Next_Reminder (REPLACE);
1467     Msg_Mapped = 0;
1468     XUnmapWindow (Xdisplay, Msg.win);
1469     }
1470     else if (ev.xbutton.subwindow == msgButton.Defer)
1471     {
1472     time_t t = time (NULL) + adjustTime;
1473     tmval = localtime (&t);
1474     reminderTime = mk_time (tmval) + DEFER_TIME;
1475     Msg_Mapped = 0;
1476     XUnmapWindow (Xdisplay, Msg.win);
1477     }
1478     #ifndef NO_REMINDER_EXEC
1479     else if (ev.xbutton.subwindow == msgButton.Start)
1480     {
1481     system (execPrgm);
1482     Next_Reminder (REPLACE);
1483     Msg_Mapped = 0;
1484     XUnmapWindow (Xdisplay, Msg.win);
1485     }
1486     #endif /* NO_REMINDER_EXEC */
1487     }
1488     #endif
1489     #ifdef MAIL
1490     if (ev.xany.window == Clock.win)
1491     {
1492     #ifdef MAIL_SPAWN
1493     /* left button action - spawn a mail reader */
1494     if (ev.xbutton.button == Button1)
1495     system (MAIL_SPAWN);
1496     #else
1497     if ( (ev.xbutton.button == Button1) && (mail_spawn != NULL) )
1498     system(mail_spawn);
1499     #endif
1500     /* redraw the window */
1501     Draw_Window (&Clock, 1);
1502     }
1503     #endif
1504     break;
1505     }
1506     }
1507    
1508     /* Now wait for time out or new X event */
1509     FD_ZERO (&in_fdset);
1510     FD_SET (Xfd, &in_fdset);
1511     tm.tv_sec = clockUpdate;
1512     tm.tv_usec = 0;
1513     select (num_fds, &in_fdset, NULL, NULL, &tm);
1514    
1515     Draw_Window (&Clock, 0);
1516     #ifdef ICONWIN
1517     Draw_Window (&Icon, 0);
1518     #endif
1519     }
1520     }
1521    
1522     /*
1523     * Print an error message.
1524     */
1525     static void
1526     print_error (const char * fmt, ...)
1527     {
1528     va_list arg_ptr;
1529    
1530     va_start (arg_ptr, fmt);
1531     fprintf (stderr, APL_NAME ": ");
1532     vfprintf (stderr, fmt, arg_ptr);
1533     fprintf (stderr,"\n");
1534     va_end (arg_ptr);
1535     }
1536     /*----------------------- end-of-file (C source) -----------------------*/