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

File Contents

# 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/Intrinsic.h> /* Xlib, Xutil, Xresource, Xfuncproto */
40
41 #define APL_CLASS "Clock"
42 #define APL_NAME "rclock"
43 #define MSG_CLASS "Appointment"
44 #define MSG_NAME "Appointment"
45 #define CONFIG_FILE ".rclock"
46
47 #ifndef EXIT_SUCCESS /* missed from <stdlib.h> ? */
48 # define EXIT_SUCCESS 0
49 # define EXIT_FAILURE 1
50 #endif
51
52 #define VERSION "TODO: fetch from urxvt somehow"
53
54 /*--------------------------------*-C-*---------------------------------*
55 * Compile-time configuration.
56 *----------------------------------------------------------------------*
57 * Copyright (C) 1997 1998 mj olesen <olesen@me.QueensU.CA>
58 *
59 * This program is free software; you can redistribute it and/or modify
60 * it under the terms of the GNU General Public License as published by
61 * the Free Software Foundation; either version 2 of the License, or
62 * (at your option) any later version.
63 *
64 * This program is distributed in the hope that it will be useful,
65 * but WITHOUT ANY WARRANTY; without even the implied warranty of
66 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
67 * GNU General Public License for more details.
68 *
69 * You should have received a copy of the GNU General Public License
70 * along with this program; if not, write to the Free Software
71 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
72 *----------------------------------------------------------------------*/
73
74 /*----------------------------------------------------------------------*
75 * #define ICONWIN
76 * to enable fancy (active) icon
77 *
78 * #define REMINDERS
79 * to enable the appointment reminder functions
80 *
81 * #define NO_REMINDER_EXEC
82 * to disable the execution of a program on an appointment
83 *
84 * #define DATE_ON_CLOCK_FACE
85 * to display today's date on the face of the clock
86 * Note: this requires REMINDERS since it uses the same font
87 *
88 * #define MAIL
89 * to enable xbiff-type mail reminders
90 *
91 * #define MAIL_BELL
92 * to enable xbiff-type mail reminders with a beep
93 *
94 * #define MAIL_SPAWN "xmh\ -font\ 7x14\&"
95 * to define a mail program to run
96 *
97 * #define MAIL_SPOOL "/var/spool/mail/"
98 * to define the mail spool when the $MAIL variable isn't set
99 *
100 * program size approximately doubles from no options to all options
101 *----------------------------------------------------------------------*/
102 #define ICONWIN
103 #define REMINDERS
104
105 /* #define NO_REMINDER_EXEC */
106 #define DATE_ON_CLOCK_FACE
107 #define MAIL
108
109 /* #define MAIL_BELL */ /* TODO: command line switch */
110
111 /* #define MAIL_SPAWN "xmh\ -font\ 7x14\&" */ /* TODO: command line switch */
112 #define MAIL_SPOOL "/var/spool/mail/"
113
114 /*----------------------------------------------------------------------*
115 * #define CLOCKUPDATE 30
116 * to define the frequency (seconds) to update the clock
117 *
118 * #define MAILUPDATE 60
119 * to define the frequency (seconds) to check for new mail
120 *
121 * #define REMINDERS_TIME 10
122 * to define the frequency (minutes) to check ~/.rclock
123 *
124 * #define DEFER_TIME 3
125 * to define the amount (minutes) to defer a message
126 *
127 * #define ADJUST_TIME
128 * to add -adjust command-line option
129 *
130 * #define CENTURY 2000
131 * to set the base century for 2 digit year short-hand
132 *----------------------------------------------------------------------*/
133 #define CLOCKUPDATE 30
134 #define MAILUPDATE 60
135 #define REMINDERS_TIME 10
136 #define DEFER_TIME 3
137 #define ADJUST_TIME
138
139 #define CENTURY 2000 / TODO: verify */
140
141 /*----------------------------------------------------------------------*
142 * #define FONT_NAME "7x14"
143 * to define the font to be used for appointment reminders
144 *
145 * #define FG_COLOR_NAME "black"
146 * #define BG_COLOR_NAME "white"
147 * to define the foreground/background colors to use
148 *----------------------------------------------------------------------*/
149 #define FONT_NAME "7x14"
150 #define FG_COLOR_NAME "black"
151 #define BG_COLOR_NAME "white"
152
153 /*----------------------------------------------------------------------*
154 * #define DAY_NAMES "umtwrfs*"
155 * define this string appropriate for any language.
156 *
157 * It starts with a symbol for Sunday, ends with Saturday, then '*'
158 * NOTE: 8 characters total - 7 days of the week plus '*'
159 *----------------------------------------------------------------------*/
160 #define DAY_NAMES "umtwrfs*"
161
162 /*----------------------------------------------------------------------*
163 * #define SUBTICKS
164 * to show additional minute/second markings
165 *----------------------------------------------------------------------*/
166 #define SUBTICKS
167
168 /*----------------------------------------------------------------------*
169 * sort out conflicts
170 *----------------------------------------------------------------------*/
171 #if defined (MAIL_BELL) || defined (MAIL_SPAWN) || defined (MAIL_SPOOL)
172 # ifndef MAIL
173 # define MAIL
174 # endif
175 #endif
176
177 /*----------------------------------------------------------------------*/
178
179 static Display *Xdisplay; /* X display */
180 static int Xfd; /* file descriptor of server connection */
181 static GC Xgc, Xrvgc; /* normal, reverse video GC */
182
183 #define Xscreen DefaultScreen (Xdisplay)
184 #define Xcmap DefaultColormap (Xdisplay, Xscreen)
185 #define Xroot DefaultRootWindow (Xdisplay)
186
187 /* windows and their sizes */
188 typedef struct
189 {
190 Window win;
191 int width, height;
192 } mywindow_t;
193
194 static mywindow_t Clock = { None, 80, 80 }; /* parent window */
195
196 #define fgColor 0
197 #define bgColor 1
198 static const char *rs_color[2] = { FG_COLOR_NAME, BG_COLOR_NAME };
199
200 static Pixel PixColors[2];
201 static const char *rs_geometry = NULL;
202
203 #ifdef ICONWIN
204 static const char *rs_iconGeometry = NULL;
205 static mywindow_t Icon = { None, 65, 65 }; /* icon window */
206
207 static int iconic_state = NormalState; /* iconic startup? */
208 #endif
209
210 #ifdef REMINDERS
211 static mywindow_t Msg = { 0, 0, 0 }; /* message window */
212
213 static struct
214 {
215 Window
216 # ifndef NO_REMINDER_EXEC
217 Start,
218 # endif
219 Dismiss, Defer;
220 int width, height;
221 } msgButton;
222
223 static XFontStruct *Xfont;
224
225 # define FontHeight() (Xfont->ascent + Xfont->descent)
226 static int Msg_Mapped = 0; /* message window mapped? */
227 static int reminderTime = -1;
228 static char message[256] = "";
229
230 # ifndef NO_REMINDER_EXEC
231 static char execPrgm[256] = "";
232 # endif
233 static const char *reminders_file = NULL; /* name of ~/.rclock file */
234
235 # ifdef DATE_ON_CLOCK_FACE
236 static int show_date = 1; /* show date on face of clock */
237 # endif
238 #endif
239
240 #ifdef ADJUST_TIME
241 static int adjustTime = 0;
242 #else
243 # define adjustTime 0
244 #endif
245
246 #ifdef CENTURY
247 # if (CENTURY < 1900)
248 Error, Century incorrectly set.
249 # endif
250 #else
251 # define CENTURY 1900
252 #endif
253
254 static int clockUpdate = CLOCKUPDATE;
255
256 #ifdef MAIL
257 static int mailUpdate = MAILUPDATE;
258 static char *mail_file = NULL;
259
260 # ifndef MAIL_SPAWN
261 static char *mail_spawn = NULL;
262 # endif
263 static int is_maildir = 0;
264 #endif
265
266 static XSizeHints szHint = {
267 PMinSize | PResizeInc | PBaseSize | PWinGravity,
268 0, 0, 80, 80, /* x, y, width and height */
269 1, 1, /* Min width and height */
270 0, 0, /* Max width and height */
271 1, 1, /* Width and height increments */
272 {1, 1}, /* x, y increments */
273 {1, 1}, /* Aspect ratio - not used */
274 0, 0, /* base size */
275 NorthWestGravity /* gravity */
276 };
277
278 /* subroutine declarations */
279 static void geometry2sizehint (mywindow_t * /* win */ ,
280 const char * /* geom */ );
281 static void Create_Windows (int /* argc */ ,
282 char * /* argv */ []);
283 static void getXevent (void);
284 static void print_error (const char * /* fmt */ , ...);
285
286 static void Draw_Window (mywindow_t * /* this_win */ ,
287 int /* full_redraw */ );
288 static void Reminder (void);
289 static void Next_Reminder (int /* update_only */ );
290
291 /* Arguments for Next_Reminder() */
292 #define REPLACE 0
293 #define UPDATE 1
294
295 /*----------------------------------------------------------------------*/
296
297 static void
298 usage (void)
299 {
300 int i;
301 struct
302 {
303 const char *const opt;
304 const char *const desc;
305 } optList[] = {
306 #define optList_size() (sizeof (optList) / sizeof (optList[0]))
307 {"-display displayname", "X server to contact"},
308 {"-geometry geom", "size (in pixels) and position"},
309 {"-bg color", "background color"},
310 {"-fg color", "foreground color"},
311 #ifdef REMINDERS
312 {"-fn fontname", "normal font for messages"},
313 # ifdef DATE_ON_CLOCK_FACE
314 {"-nodate", "do not display date on the clock face"},
315 # endif
316 #endif
317 #ifdef ICONWIN
318 {"-iconic", "start iconic"},
319 #endif
320 #ifdef ADJUST_TIME
321 {"-adjust +/-ddhhmm", "adjust clock time"},
322 #endif
323 {"-update seconds", "clock update interval"},
324 #ifdef MAIL
325 {"-mail seconds", "check $MAIL interval"},
326 {"-mailfile file", "file to use for mail checking"},
327 # ifndef MAIL_SPAWN
328 {"-mailspawn cmd", "execute `cmd` when clock is clicked"},
329 # endif
330 #endif
331 {"#geom", "icon window geometry"}
332 };
333
334 fprintf (stderr, "\nUsage v" VERSION ":\n " APL_NAME " [options]\n\n" "where options include:\n");
335
336 for (i = 0; i < (int)optList_size (); i++)
337 fprintf (stderr, " %-29s%s\n", optList[i].opt, optList[i].desc);
338 }
339
340
341 /****************
342 * Check out if we are using a maildir drop (qmail)
343 * Note: this changes mail_dir to hold the "new" diretory
344 */
345 #ifdef MAIL
346 static void
347 CheckMaildir (void)
348 {
349 struct stat st;
350 char *buf, *p;
351
352 if (!*mail_file || stat (mail_file, &st) || !S_ISDIR (st.st_mode))
353 return; /* no */
354
355 if (!(buf = (char *) malloc (strlen (mail_file) + 5)))
356 {
357 print_error ("malloc error");
358 exit (EXIT_FAILURE);
359 }
360 strcpy (buf, mail_file);
361 p = buf + strlen (buf);
362 if (p[-1] != '/')
363 *p++ = '/';
364
365 strcpy (p, "tmp");
366 if (stat (buf, &st) || !S_ISDIR (st.st_mode))
367 goto leave;
368 strcpy (p, "cur");
369 if (stat (buf, &st) || !S_ISDIR (st.st_mode))
370 goto leave;
371 strcpy (p, "new");
372 if (stat (buf, &st) || !S_ISDIR (st.st_mode))
373 goto leave;
374
375 mail_file = buf;
376 is_maildir = 1;
377 return;
378 leave:
379 free (buf);
380 }
381 #endif
382
383 /*----------------------------------------------------------------------*
384 * rclock - Rob's clock
385 * simple X windows clock with appointment reminder
386 *----------------------------------------------------------------------*/
387 int
388 main (int argc, char *argv[])
389 {
390 int i;
391 char *opt, *val;
392 const char *display_name = NULL;
393 XGCValues gcv;
394
395 #ifdef REMINDERS
396 const char *rs_font = FONT_NAME;
397
398 /* find the ~/.rclock file */
399 if ((val = getenv ("HOME")) != NULL)
400 {
401 char *p = (char *) malloc (strlen (CONFIG_FILE) + strlen (val) + 2);
402
403 if (p == NULL)
404 goto Malloc_Error;
405
406 strcpy (p, val);
407 strcat (p, "/" CONFIG_FILE);
408
409 reminders_file = p;
410 }
411 #endif
412 #ifdef MAIL
413 val = getenv ("MAIL"); /* get the mail spool file name */
414 # ifdef MAIL_SPOOL
415 if (val == NULL) /* csh doesn't set $MAIL */
416 {
417 const char *spool = MAIL_SPOOL;
418 char *user = getenv ("USER"); /* assume this works */
419
420 if (user != NULL)
421 {
422 val = (char *) malloc (strlen (spool) + strlen (user) + 1);
423 if (val == NULL)
424 goto Malloc_Error;
425 strcpy (val, spool);
426 strcat (val, user);
427 }
428 }
429 # endif
430 mail_file = val;
431 if (mail_file)
432 CheckMaildir ();
433 #endif
434
435 /* parse the command line */
436 for (i = 1; i < argc; i += 2)
437 {
438 opt = argv[i];
439 val = argv[i + 1];
440
441 switch (*opt++)
442 {
443 case '-':
444 break;
445
446 case '#':
447 #ifdef ICONWIN
448 rs_iconGeometry = opt; /* drop */
449 #endif
450 default:
451 continue;
452 break;
453 }
454
455 if (*opt == 'd' && val)
456 display_name = val; /* "d", "display" */
457 else if (*opt == 'g' && val)
458 rs_geometry = val; /* "g", "geometry" */
459 #ifdef ICONWIN
460 else if (*opt == 'i') /* "ic", "iconic" */
461 {
462 iconic_state = IconicState;
463 i--; /* no argument */
464 }
465 #endif
466 else if (!strcmp (opt, "fg") && val)
467 rs_color[fgColor] = val;
468 else if (!strcmp (opt, "bg") && val)
469 rs_color[bgColor] = val;
470 #ifdef REMINDERS
471 else if (!strcmp (opt, "fn") && val)
472 rs_font = val;
473 # ifdef DATE_ON_CLOCK_FACE
474 else if (!strcmp (opt, "nodate"))
475 {
476 show_date = 0;
477 i--; /* no argument */
478 }
479 # endif
480 #endif
481 else if (!strcmp (opt, "update") && val)
482 {
483 int x = atoi (val);
484
485 if (x < 1 || x > 60)
486 print_error ("update: %d sec", clockUpdate);
487 else
488 clockUpdate = x;
489 }
490 #ifdef MAIL
491 else if (!strcmp (opt, "mail") && val)
492 {
493 int x = atoi (val);
494
495 if (x < 1)
496 print_error ("mail update: %d sec", mailUpdate);
497 else
498 mailUpdate = x;
499 }
500 else if (!strcmp (opt, "mailfile") && val)
501 {
502 /* If the mail environment is not set, then mail_file was created
503 * with a malloc. We need to free it.
504 */
505 if (getenv ("MAIL") == NULL)
506 free (mail_file);
507 /* assume user knows what he's doing, don't check that file is valid... */
508 mail_file = val;
509 }
510 # ifndef MAIL_SPAWN
511 else if (!strcmp (opt, "mailspawn") && val)
512 {
513 mail_spawn = val;
514 }
515 # endif
516 #endif /* MAIL */
517 #ifdef ADJUST_TIME
518 else if (!strcmp (opt, "adjust") && val)
519 {
520 /* convert ddhhmm to seconds, minimal error checking */
521 int x = atoi (val);
522
523 adjustTime = ((((abs (x) / 10000) % 100) * 24 /* days */
524 + ((abs (x) / 100) % 100)) * 60 /* hours */
525 + (abs (x) % 100)) * 60; /* minutes */
526 if (x < 0)
527 adjustTime = -adjustTime;
528 }
529 #endif /* ADJUST_TIME */
530 else
531 {
532 usage ();
533 goto Abort;
534 }
535 }
536
537 /* open display */
538 Xdisplay = XOpenDisplay (display_name);
539 if (!Xdisplay)
540 {
541 print_error ("can't open display %s", display_name ? display_name :
542 getenv ("DISPLAY") ? getenv ("DISPLAY") : "as no -d given and DISPLAY not set");
543 goto Abort;
544 }
545
546 /* get display info */
547 Xfd = XConnectionNumber (Xdisplay);
548 {
549 const char *const color_msg = "can't load color \"%s\"";
550 XColor xcol;
551
552 /* allocate foreground/background colors */
553 if (!XParseColor (Xdisplay, Xcmap, rs_color[fgColor], &xcol) || !XAllocColor (Xdisplay, Xcmap, &xcol))
554 {
555 print_error (color_msg, rs_color[fgColor]);
556 goto Abort;
557 }
558 PixColors[fgColor] = xcol.pixel;
559
560 if (!XParseColor (Xdisplay, Xcmap, rs_color[bgColor], &xcol) || !XAllocColor (Xdisplay, Xcmap, &xcol))
561 {
562 print_error (color_msg, rs_color[bgColor]);
563 goto Abort;
564 }
565 PixColors[bgColor] = xcol.pixel;
566 }
567
568 #ifdef REMINDERS
569 /* load the font for messages */
570 if ((Xfont = XLoadQueryFont (Xdisplay, rs_font)) == NULL)
571 {
572 print_error ("can't load font \"%s\"", rs_font);
573 goto Abort;
574 }
575 gcv.font = Xfont->fid;
576 #endif
577
578 Create_Windows (argc, argv);
579 /* Create the graphics contexts */
580 gcv.foreground = PixColors[fgColor];
581 gcv.background = PixColors[bgColor];
582
583 Xgc = XCreateGC (Xdisplay, Clock.win,
584 #ifdef REMINDERS
585 GCFont |
586 #endif
587 GCForeground | GCBackground, &gcv);
588
589 gcv.foreground = PixColors[bgColor];
590 gcv.background = PixColors[fgColor];
591 Xrvgc = XCreateGC (Xdisplay, Clock.win,
592 #ifdef REMINDERS
593 GCFont |
594 #endif
595 GCForeground | GCBackground, &gcv);
596
597 getXevent ();
598 return EXIT_SUCCESS;
599
600 Malloc_Error:
601 print_error ("malloc error");
602 Abort:
603 print_error ("aborting");
604 return EXIT_FAILURE;
605 }
606
607 /*
608 * translate geometry string to appropriate sizehint
609 */
610 static void
611 geometry2sizehint (mywindow_t * win, const char *geom)
612 {
613 int x, y, flags;
614 unsigned int width, height;
615
616 /* copy in values */
617 szHint.width = win->width;
618 szHint.height = win->height;
619
620 if (geom == NULL)
621 return;
622
623 flags = XParseGeometry (geom, &x, &y, &width, &height);
624
625 if (flags & WidthValue)
626 {
627 szHint.width = width + szHint.base_width;
628 szHint.flags |= USSize;
629 }
630 if (flags & HeightValue)
631 {
632 szHint.height = height + szHint.base_height;
633 szHint.flags |= USSize;
634 }
635
636 if (flags & XValue)
637 {
638 if (flags & XNegative)
639 {
640 x += (DisplayWidth (Xdisplay, Xscreen) - szHint.width);
641 szHint.win_gravity = NorthEastGravity;
642 }
643 szHint.x = x;
644 szHint.flags |= USPosition;
645 }
646 if (flags & YValue)
647 {
648 if (flags & YNegative)
649 {
650 y += (DisplayHeight (Xdisplay, Xscreen) - szHint.height);
651 szHint.win_gravity = (szHint.win_gravity == NorthEastGravity ? SouthEastGravity : SouthWestGravity);
652 }
653 szHint.y = y;
654 szHint.flags |= USPosition;
655 }
656
657 /* copy out values */
658 win->width = szHint.width;
659 win->height = szHint.height;
660 }
661
662 /*
663 * Open and map the windows
664 */
665 static void
666 Create_Windows (int argc, char *argv[])
667 {
668 XClassHint classHint;
669 XWMHints wmHint;
670
671 geometry2sizehint (&Clock, rs_geometry);
672 Clock.win = XCreateSimpleWindow (Xdisplay, Xroot,
673 szHint.x, szHint.y, Clock.width, Clock.height, 0, PixColors[fgColor], PixColors[bgColor]);
674
675 #ifdef ICONWIN
676 geometry2sizehint (&Icon, rs_iconGeometry);
677 Icon.win = XCreateSimpleWindow (Xdisplay, Xroot, szHint.x, szHint.y, Icon.width, Icon.height, 0, PixColors[fgColor], PixColors[bgColor]);
678 wmHint.initial_state = iconic_state;
679 wmHint.icon_window = Icon.win;
680 wmHint.flags = InputHint | StateHint | IconWindowHint;
681 #else
682 wmHint.flags = InputHint;
683 #endif
684 wmHint.input = True;
685
686 classHint.res_name = (char *)APL_NAME;
687 classHint.res_class = (char *)APL_CLASS;
688 XSetWMProperties (Xdisplay, Clock.win, NULL, NULL, argv, argc, &szHint, &wmHint, &classHint);
689
690 XSelectInput (Xdisplay, Clock.win, (ExposureMask | StructureNotifyMask | ButtonPressMask));
691
692 #ifdef ICONWIN
693 XSelectInput (Xdisplay, Icon.win, (ExposureMask | ButtonPressMask));
694 #endif
695 XMapWindow (Xdisplay, Clock.win);
696
697 /* create, but don't map a window for appointment reminders */
698 #ifdef REMINDERS
699 Msg.win = XCreateSimpleWindow (Xdisplay, Xroot,
700 szHint.x, szHint.y, szHint.width, szHint.height, 0, PixColors[fgColor], PixColors[bgColor]);
701
702 szHint.flags |= USPosition;
703 /* ignore warning about discarded `const' */
704 classHint.res_name = (char *)MSG_NAME;
705 classHint.res_class = (char *)MSG_CLASS;
706 wmHint.input = True;
707 wmHint.flags = InputHint;
708
709 XSetWMProperties (Xdisplay, Msg.win, NULL, NULL, argv, argc, &szHint, &wmHint, &classHint);
710
711 {
712 const char *str = MSG_NAME;
713
714 XStoreName (Xdisplay, Msg.win, str);
715 XSetIconName (Xdisplay, Msg.win, str);
716 }
717
718 XSelectInput (Xdisplay, Msg.win, (ExposureMask | ButtonPressMask | KeyPressMask));
719
720 /* font already loaded */
721
722 msgButton.width = 4 + 5 * XTextWidth (Xfont, "M", 1);
723 msgButton.height = 4 + FontHeight ();
724
725 msgButton.Dismiss = XCreateSimpleWindow (Xdisplay, Msg.win,
726 0, 0, msgButton.width, msgButton.height, 0, PixColors[bgColor], PixColors[fgColor]);
727
728 XMapWindow (Xdisplay, msgButton.Dismiss);
729
730 msgButton.Defer = XCreateSimpleWindow (Xdisplay, Msg.win,
731 0, 0, msgButton.width, msgButton.height, 0, PixColors[bgColor], PixColors[fgColor]);
732 XMapWindow (Xdisplay, msgButton.Defer);
733
734 # ifndef NO_REMINDER_EXEC
735 msgButton.Start = XCreateSimpleWindow (Xdisplay, Msg.win,
736 0, 0, msgButton.width, msgButton.height, 0, PixColors[bgColor], PixColors[fgColor]);
737 XMapWindow (Xdisplay, msgButton.Start);
738 # endif/* NO_REMINDER_EXEC */
739 #endif
740 }
741
742 static time_t
743 mk_time (struct tm *tmval)
744 {
745 return (tmval->tm_min + 60 * (tmval->tm_hour + 24 * (tmval->tm_mday + 31 * ((tmval->tm_mon + 1) + 12 * tmval->tm_year))));
746 }
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 (void)
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 (void)
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) -----------------------*/