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

File Contents

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