ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/rclock.C
Revision: 1.5
Committed: Wed Jul 14 18:50:37 2021 UTC (2 years, 10 months ago) by sf-exg
Content type: text/plain
Branch: MAIN
Changes since 1.4: +3 -2 lines
Log Message:
rclock: remove dependency on libXt header

File Contents

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