ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/rclock.C
Revision: 1.6
Committed: Sat Jul 17 00:10:15 2021 UTC (2 years, 10 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.5: +33 -69 lines
Log Message:
good riddance

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