ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/rclock.C
Revision: 1.4
Committed: Mon May 10 00:40:41 2021 UTC (3 years ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rxvt-unicode-rel-9_26, rxvt-unicode-rel-9_25
Changes since 1.3: +7 -8 lines
Log Message:
more fixes, more c++

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