ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/rclock.C
Revision: 1.3
Committed: Mon May 10 00:37:29 2021 UTC (3 years ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.2: +1302 -1250 lines
Log Message:
indent, c++ize, cleanup, make independent of feature.h

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     #define CENTURY 2000 / TODO: verify */
140    
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     static void getXevent (void);
284     static void print_error (const char * /* fmt */ , ...);
285    
286     static void Draw_Window (mywindow_t * /* this_win */ ,
287     int /* full_redraw */ );
288     static void Reminder (void);
289     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     usage (void)
299     {
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.3 CheckMaildir (void)
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    
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     Reminder (void)
1409     {
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     getXevent (void)
1482     {
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) -----------------------*/