ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/rclock.C
Revision: 1.13
Committed: Sat Dec 24 00:15:31 2022 UTC (16 months, 4 weeks ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.12: +2 -2 lines
Log Message:
*** empty log message ***

File Contents

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