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