ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/rclock.C
Revision: 1.4
Committed: Mon May 10 00:40:41 2021 UTC (3 years ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rxvt-unicode-rel-9_26, rxvt-unicode-rel-9_25
Changes since 1.3: +7 -8 lines
Log Message:
more fixes, more c++

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 ();
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 ();
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 ()
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 ()
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 #ifdef MAIL
749 static int
750 MailAvailable ()
751 {
752 struct stat st;
753
754 if (is_maildir)
755 {
756 DIR *dirp;
757 struct dirent *d;
758
759 if ((dirp = opendir (mail_file)))
760 {
761 while ((d = readdir (dirp)))
762 {
763 if (*d->d_name == '.')
764 continue;
765 if (isdigit (*d->d_name))
766 {
767 closedir (dirp);
768 return 1;
769 }
770 }
771 closedir (dirp);
772 }
773 return 0;
774 }
775 else
776 return !stat (mail_file, &st) && (st.st_size > 0) && (st.st_mtime >= st.st_atime);
777 }
778 #endif
779
780 /*----------------------------------------------------------------------*
781 * Redraw the whole window after an exposure or size change.
782 * After a timeout, only redraw the hands.
783 * Provide reminder if needed.
784 *----------------------------------------------------------------------*/
785 static void
786 Draw_Window (mywindow_t * W, int full_redraw)
787 {
788 /* pre-computed values for sin() x1000, to avoid using floats */
789 static const short Sin[720] = {
790 0,
791 9, 17, 26, 35, 44, 52, 61, 70, 78, 87, 96, 105,
792 113, 122, 131, 139, 148, 156, 165, 174, 182, 191, 199, 208,
793 216, 225, 233, 242, 250, 259, 267, 276, 284, 292, 301, 309,
794 317, 326, 334, 342, 350, 358, 367, 375, 383, 391, 399, 407,
795 415, 423, 431, 438, 446, 454, 462, 469, 477, 485, 492, 500,
796 508, 515, 522, 530, 537, 545, 552, 559, 566, 574, 581, 588,
797 595, 602, 609, 616, 623, 629, 636, 643, 649, 656, 663, 669,
798 676, 682, 688, 695, 701, 707, 713, 719, 725, 731, 737, 743,
799 749, 755, 760, 766, 772, 777, 783, 788, 793, 799, 804, 809,
800 814, 819, 824, 829, 834, 839, 843, 848, 853, 857, 862, 866,
801 870, 875, 879, 883, 887, 891, 895, 899, 903, 906, 910, 914,
802 917, 921, 924, 927, 930, 934, 937, 940, 943, 946, 948, 951,
803 954, 956, 959, 961, 964, 966, 968, 970, 972, 974, 976, 978,
804 980, 982, 983, 985, 986, 988, 989, 990, 991, 993, 994, 995,
805 995, 996, 997, 998, 998, 999, 999, 999, 1000, 1000, 1000, 1000,
806 1000, 1000, 1000, 999, 999, 999, 998, 998, 997, 996, 995, 995,
807 994, 993, 991, 990, 989, 988, 986, 985, 983, 982, 980, 978,
808 976, 974, 972, 970, 968, 966, 964, 961, 959, 956, 954, 951,
809 948, 946, 943, 940, 937, 934, 930, 927, 924, 921, 917, 914,
810 910, 906, 903, 899, 895, 891, 887, 883, 879, 875, 870, 866,
811 862, 857, 853, 848, 843, 839, 834, 829, 824, 819, 814, 809,
812 804, 799, 793, 788, 783, 777, 772, 766, 760, 755, 749, 743,
813 737, 731, 725, 719, 713, 707, 701, 695, 688, 682, 676, 669,
814 663, 656, 649, 643, 636, 629, 623, 616, 609, 602, 595, 588,
815 581, 574, 566, 559, 552, 545, 537, 530, 523, 515, 508, 500,
816 492, 485, 477, 469, 462, 454, 446, 438, 431, 423, 415, 407,
817 399, 391, 383, 375, 367, 358, 350, 342, 334, 326, 317, 309,
818 301, 292, 284, 276, 267, 259, 250, 242, 233, 225, 216, 208,
819 199, 191, 182, 174, 165, 156, 148, 139, 131, 122, 113, 105,
820 96, 87, 78, 70, 61, 52, 44, 35, 26, 17, 9, 0,
821 -9, -17, -26, -35, -44, -52, -61, -70, -78, -87, -96, -105,
822 -113, -122, -131, -139, -148, -156, -165, -174, -182, -191, -199, -208,
823 -216, -225, -233, -242, -250, -259, -267, -276, -284, -292, -301, -309,
824 -317, -326, -334, -342, -350, -358, -366, -375, -383, -391, -399, -407,
825 -415, -423, -431, -438, -446, -454, -462, -469, -477, -485, -492, -500,
826 -508, -515, -522, -530, -537, -545, -552, -559, -566, -574, -581, -588,
827 -595, -602, -609, -616, -623, -629, -636, -643, -649, -656, -663, -669,
828 -676, -682, -688, -695, -701, -707, -713, -719, -725, -731, -737, -743,
829 -749, -755, -760, -766, -772, -777, -783, -788, -793, -799, -804, -809,
830 -814, -819, -824, -829, -834, -839, -843, -848, -853, -857, -862, -866,
831 -870, -875, -879, -883, -887, -891, -895, -899, -903, -906, -910, -914,
832 -917, -921, -924, -927, -930, -934, -937, -940, -943, -946, -948, -951,
833 -954, -956, -959, -961, -964, -966, -968, -970, -972, -974, -976, -978,
834 -980, -982, -983, -985, -986, -988, -989, -990, -991, -993, -994, -995,
835 -995, -996, -997, -998, -998, -999, -999, -999, -1000, -1000, -1000, -1000,
836 -1000, -1000, -1000, -999, -999, -999, -998, -998, -997, -996, -995, -995,
837 -994, -993, -991, -990, -989, -988, -986, -985, -983, -982, -980, -978,
838 -976, -974, -972, -970, -968, -966, -964, -961, -959, -956, -954, -951,
839 -948, -946, -943, -940, -937, -934, -930, -927, -924, -921, -917, -914,
840 -910, -906, -903, -899, -895, -891, -887, -883, -879, -875, -870, -866,
841 -862, -857, -853, -848, -843, -839, -834, -829, -824, -819, -814, -809,
842 -804, -799, -793, -788, -783, -777, -772, -766, -760, -755, -749, -743,
843 -737, -731, -725, -719, -713, -707, -701, -695, -688, -682, -676, -669,
844 -663, -656, -649, -643, -636, -629, -623, -616, -609, -602, -595, -588,
845 -581, -574, -566, -559, -552, -545, -537, -530, -523, -515, -508, -500,
846 -492, -485, -477, -469, -462, -454, -446, -438, -431, -423, -415, -407,
847 -399, -391, -383, -375, -367, -358, -350, -342, -334, -326, -317, -309,
848 -301, -292, -284, -276, -267, -259, -250, -242, -233, -225, -216, -208,
849 -199, -191, -182, -174, -165, -156, -148, -139, -131, -122, -113, -105,
850 -96, -87, -78, -70, -61, -52, -44, -35, -26, -17, -9,
851 };
852
853 static int savedDay = -1;
854
855 time_t currentTime;
856 struct tm *tmval;
857 int ctr_x, ctr_y;
858
859 typedef struct
860 {
861 int h_x, h_y; /* hour */
862 int m_x, m_y; /* minute */
863 int s_x, s_y; /* second */
864 } hands_t; /* hand positions (x,y) */
865
866 hands_t HandsNow, *pHandsOld;
867
868 GC X_gc, X_rvgc;
869
870 static hands_t HandsOld = { -1 };
871 #ifdef ICONWIN
872 static hands_t HandsOld_icon = { -1 };
873 #endif
874 #ifdef REMINDERS
875 static int lastUpdateTime = -10;
876 #endif
877
878 #ifdef MAIL
879 static time_t mailTime = 0;
880 static int MailUp = 0, MailUp_rvideo = 0;
881
882 # ifdef ICONWIN
883 static int MailUp_icon = 0;
884 # endif
885 #endif /* MAIL */
886
887 currentTime = time (NULL) + adjustTime; /* get the current time */
888 tmval = localtime (&currentTime);
889
890 #ifdef MAIL
891 # ifdef REMINDERS
892 if (W->win != Msg.win)
893 # endif
894 {
895 int *pMailUp = (
896 # ifdef ICONWIN
897 W->win == Icon.win ? &MailUp_icon :
898 # endif
899 &MailUp);
900
901 if ((currentTime - mailTime) >= mailUpdate)
902 {
903 if (
904 # ifdef ICONWIN
905 MailUp != MailUp_icon ? MailUp :
906 # endif
907 ((mail_file != NULL) && MailAvailable ()))
908 {
909 if (!*pMailUp)
910 {
911 *pMailUp = 1;
912 full_redraw = 1;
913 XSetWindowBackground (Xdisplay, W->win, PixColors[fgColor]);
914 # ifdef MAIL_BELL
915 XBell (Xdisplay, 0);
916 # endif
917 }
918 }
919 else
920 {
921 if (*pMailUp)
922 {
923 *pMailUp = 0;
924 full_redraw = 1;
925 XSetWindowBackground (Xdisplay, W->win, PixColors[bgColor]);
926 }
927 }
928 # ifdef ICONWIN
929 if (MailUp == MailUp_icon)
930 # endif
931 mailTime = currentTime;
932
933 MailUp_rvideo = *pMailUp;
934 }
935 }
936 #endif /* MAIL */
937
938 /* once every day, update the window and icon name */
939 if (tmval->tm_yday != savedDay)
940 {
941 char str[20];
942
943 savedDay = tmval->tm_yday;
944 strftime (str, sizeof (str), "%a %h %d", tmval);
945 XStoreName (Xdisplay, Clock.win, str);
946 XSetIconName (Xdisplay, Clock.win, str);
947 }
948
949 if (full_redraw)
950 XClearWindow (Xdisplay, W->win);
951
952 #ifdef REMINDERS
953 /* for a message window, just re-draw the message */
954 if (W->win == Msg.win)
955 {
956 char *beg, *next;
957 int lines;
958
959 for (beg = message, lines = 0; beg; beg = next, lines++)
960 {
961 char *end;
962
963 if ((end = strstr (beg, "\\n")) == NULL)
964 {
965 end = beg + strlen (beg);
966 next = NULL;
967 }
968 else
969 {
970 next = end + 2;
971 }
972
973 XDrawString (Xdisplay, Msg.win,
974 Xgc,
975 (Msg.width -
976 XTextWidth (Xfont, beg, (end - beg))) / 2, 10 + Xfont->ascent + FontHeight () * lines, beg, (end - beg));
977 }
978
979 XDrawString (Xdisplay, msgButton.Dismiss, Xrvgc, (msgButton.width - XTextWidth (Xfont, "Done", 4)) / 2, Xfont->ascent + 2, "Done", 4);
980
981 XDrawString (Xdisplay, msgButton.Defer, Xrvgc, (msgButton.width - XTextWidth (Xfont, "Defer", 5)) / 2, Xfont->ascent + 2, "Defer", 5);
982
983 # ifndef NO_REMINDER_EXEC
984 XDrawString (Xdisplay, msgButton.Start, Xrvgc, (msgButton.width - XTextWidth (Xfont, "Start", 5)) / 2, Xfont->ascent + 2, "Start", 5);
985
986 if (strlen (execPrgm) > 1)
987 XMapWindow (Xdisplay, msgButton.Start);
988 else
989 XUnmapWindow (Xdisplay, msgButton.Start);
990 # endif /* NO_REMINDER_EXEC */
991 return;
992 }
993
994 /*
995 * Convert multi-field time info to a single integer with a resolution
996 * in minutes.
997 */
998 currentTime = mk_time (tmval);
999
1000 /* is there a reminder pending? */
1001 if (reminderTime >= 0 && currentTime >= reminderTime)
1002 Reminder ();
1003
1004 /* every 10 minutes, or at start of day, check for revised entries */
1005 if (!Msg_Mapped &&
1006 (currentTime > lastUpdateTime + REMINDERS_TIME || (currentTime != lastUpdateTime && tmval->tm_hour == 0 && tmval->tm_min == 0)))
1007 {
1008 Next_Reminder (UPDATE);
1009 lastUpdateTime = currentTime;
1010 }
1011 #endif
1012
1013 /*
1014 * draw clock
1015 */
1016
1017 ctr_x = (W->width / 2);
1018 ctr_y = (W->height / 2);
1019
1020 #define XPOS(i,val) (ctr_x + (W->width * Sin[i%720] * (val) + 100000) / 200000)
1021 #define YPOS(i,val) (ctr_y - (W->height * Sin[(i+180)%720] * (val) + 100000) / 200000)
1022 /*
1023 * how to draw the clock face
1024 */
1025
1026 /* calculate the positions of the hands */
1027 {
1028 int angle = (tmval->tm_hour % 12) * 60 + tmval->tm_min;
1029
1030 HandsNow.h_x = XPOS (angle, 60);
1031 HandsNow.h_y = YPOS (angle, 60);
1032 }
1033 {
1034 int angle = (tmval->tm_min * 12);
1035
1036 HandsNow.m_x = XPOS (angle, 80);
1037 HandsNow.m_y = YPOS (angle, 80);
1038 }
1039 if (clockUpdate == 1)
1040 {
1041 int angle = (tmval->tm_sec * 12);
1042
1043 HandsNow.s_x = XPOS (angle, 85);
1044 HandsNow.s_y = YPOS (angle, 85);
1045 }
1046
1047 pHandsOld = (
1048 #ifdef ICONWIN
1049 W->win == Icon.win ? &HandsOld_icon :
1050 #endif
1051 &HandsOld);
1052
1053 #ifdef MAIL
1054 if (MailUp_rvideo)
1055 {
1056 X_gc = Xrvgc;
1057 X_rvgc = Xgc;
1058 }
1059 else
1060 #endif
1061 {
1062 X_gc = Xgc;
1063 X_rvgc = Xrvgc;
1064 }
1065
1066 /*
1067 * Draw the date in the lower half of the clock window.
1068 * The code is enclosed in REMINDERS because it uses the same
1069 * font as the reminders code.
1070 * I believe this should be drawn always so it does not get
1071 * "swept away" by the minute hand.
1072 */
1073 #if defined(REMINDERS) && defined(DATE_ON_CLOCK_FACE)
1074 if (show_date)
1075 {
1076 char date[10];
1077
1078 currentTime = time (NULL) + adjustTime; /* get the current time */
1079 tmval = localtime (&currentTime);
1080 strftime (date, sizeof (date), "%d", tmval);
1081 XDrawString (Xdisplay, W->win, X_gc,
1082 ctr_x - XTextWidth (Xfont, date, strlen (date)) / 2,
1083 ctr_y + FontHeight () + (ctr_y - FontHeight ()) / 2, date, strlen (date));
1084 }
1085 #endif
1086
1087 if (full_redraw)
1088 {
1089 int mintick;
1090
1091 /*
1092 * draw clock face
1093 */
1094 #ifdef SUBTICKS
1095 for (mintick = 0; mintick < 60; mintick++)
1096 XDrawPoint (Xdisplay, W->win, X_gc, XPOS ((mintick * 12), 95), YPOS ((mintick * 12), 95));
1097 #endif
1098 for (mintick = 0; mintick < 60; mintick += 5)
1099 XDrawLine (Xdisplay, W->win, X_gc,
1100 XPOS ((mintick * 12), 90), YPOS ((mintick * 12), 90), XPOS ((mintick * 12), 100), YPOS ((mintick * 12), 100));
1101 }
1102 else if (memcmp (pHandsOld, &HandsNow, sizeof (hands_t)))
1103 {
1104 int i, j;
1105
1106 /*
1107 * erase old hands
1108 */
1109 for (i = -1; i < 2; i++)
1110 for (j = -1; j < 2; j++)
1111 {
1112 /* hour/minute hands */
1113 XDrawLine (Xdisplay, W->win, X_rvgc, ctr_x + i, ctr_y + j, pHandsOld->h_x, pHandsOld->h_y);
1114 XDrawLine (Xdisplay, W->win, X_rvgc, ctr_x + i, ctr_y + j, pHandsOld->m_x, pHandsOld->m_y);
1115 }
1116
1117 if (clockUpdate == 1) /* seconds hand */
1118 XDrawLine (Xdisplay, W->win, X_rvgc, ctr_x, ctr_y, pHandsOld->s_x, pHandsOld->s_y);
1119 }
1120
1121 if (full_redraw || memcmp (pHandsOld, &HandsNow, sizeof (hands_t)))
1122 {
1123 int i, j;
1124
1125 /*
1126 * draw new hands
1127 */
1128 for (i = -1; i < 2; i++)
1129 for (j = -1; j < 2; j++)
1130 {
1131 /* hour/minute hands */
1132 XDrawLine (Xdisplay, W->win, X_gc, ctr_x + i, ctr_y + j, HandsNow.h_x, HandsNow.h_y);
1133
1134 XDrawLine (Xdisplay, W->win, X_gc, ctr_x + i, ctr_y + j, HandsNow.m_x, HandsNow.m_y);
1135 }
1136 if (clockUpdate == 1) /* seconds hand */
1137 XDrawLine (Xdisplay, W->win, X_gc, ctr_x, ctr_y, HandsNow.s_x, HandsNow.s_y);
1138
1139 *pHandsOld = HandsNow;
1140 }
1141 }
1142
1143 #ifdef REMINDERS
1144
1145 /*
1146 * Read a single integer from *pstr, returns default value if it finds "*"
1147 * DELIM = trailing delimiter to skip
1148 */
1149 static int
1150 GetOneNum (char **pstr, int def)
1151 {
1152 int num, hit = 0;
1153
1154 for (num = 0; isdigit (**pstr); (*pstr)++)
1155 {
1156 num = num * 10 + (**pstr - '0');
1157 hit = 1;
1158 }
1159 if (!hit)
1160 {
1161 num = def;
1162 while (**pstr == '*')
1163 (*pstr)++;
1164 }
1165 return num;
1166 }
1167
1168 /*
1169 * find if TODAY is found in PSTR
1170 */
1171 static int
1172 isToday (char **pstr, int wday)
1173 {
1174 const char *dayNames = DAY_NAMES;
1175 int rval, today;
1176
1177 today = dayNames[wday];
1178 /* no day specified is same as wildcard */
1179 if (!strchr (dayNames, tolower (**pstr)))
1180 return 1;
1181
1182 for (rval = 0; strchr (dayNames, tolower (**pstr)); (*pstr)++)
1183 {
1184 if (today == tolower (**pstr) || **pstr == '*')
1185 rval = 1; /* found it */
1186 }
1187 return rval;
1188 }
1189
1190 static char *
1191 trim_string (char *str)
1192 {
1193 if (str && *str)
1194 {
1195 int n;
1196
1197 while (*str && isspace (*str))
1198 str++;
1199
1200 n = strlen (str) - 1;
1201 while (n > 0 && isspace (str[n]))
1202 n--;
1203 str[n + 1] = '\0';
1204 }
1205 return str;
1206 }
1207
1208 # ifndef NO_REMINDER_EXEC
1209 static char *
1210 extract_program (char *text)
1211 {
1212 char *prgm = text;
1213
1214 while ((prgm = strchr (prgm, ';')) != NULL)
1215 {
1216 if (*(prgm - 1) == '\\') /* backslash escaped */
1217 {
1218 /* remove backslash - avoid memmove() */
1219 int i, n = strlen (prgm);
1220
1221 for (i = 0; i <= n; i++)
1222 prgm[i - 1] = prgm[i];
1223 }
1224 else
1225 {
1226 *prgm++ = '\0';
1227 /* remove leading/trailing space */
1228 prgm = trim_string (prgm);
1229 break;
1230 }
1231 }
1232 return prgm;
1233 }
1234 # endif /* NO_REMINDER_EXEC */
1235
1236 /*
1237 * Read the ~/.rclock file and find the next reminder
1238 *
1239 * update_only = 1
1240 * look for a reminder whose time is greater than the current time,
1241 * but less than the currently set reminder time
1242 *
1243 * update_only = 0
1244 * look for a reminder whose time is greater than the reminder that
1245 * just went off
1246 */
1247 static void
1248 Next_Reminder (int update_only)
1249 {
1250 struct tm *tmval;
1251 char buffer[256];
1252
1253 # ifndef INT_MAX
1254 # define INT_MAX 1e8
1255 # endif
1256 time_t currentTime;
1257 int savedTime = INT_MAX;
1258 FILE *fd;
1259
1260 if (reminders_file == NULL || (fd = fopen (reminders_file, "r")) == NULL)
1261 {
1262 reminderTime = -1; /* no config file, no reminders */
1263 return;
1264 }
1265
1266 currentTime = time (NULL) + adjustTime; /* get the current time */
1267 tmval = localtime (&currentTime);
1268 currentTime = mk_time (tmval);
1269
1270 /* initial startup */
1271 if (reminderTime < 0)
1272 {
1273 /* ignore reminders that have already occurred */
1274 reminderTime = currentTime;
1275 # ifndef NO_REMINDER_EXEC
1276 /* scan for programs run on start-up */
1277 while (fgets (buffer, sizeof (buffer), fd))
1278 {
1279 char *prgm, *text;
1280
1281 text = trim_string (buffer);
1282 if (*text != ';')
1283 continue;
1284
1285 prgm = extract_program (text);
1286 if (prgm != NULL && strlen (prgm) > 1)
1287 system (prgm);
1288 }
1289 rewind (fd);
1290 # endif /* NO_REMINDER_EXEC */
1291 }
1292
1293 /* now scan for next reminder */
1294 while (fgets (buffer, sizeof (buffer), fd))
1295 {
1296 int testTime, hh, mm, mo, dd, yy;
1297 char *text;
1298
1299 text = trim_string (buffer);
1300 if (*text == '#')
1301 continue; /* comment */
1302 if (*text == ';')
1303 continue; /* program run on startup */
1304 /*
1305 * parse the line, format is hh:mm mo/dd/yr message; program
1306 * any of hh, mm, mo, dd, yr could be a wildcard `*'
1307 */
1308 hh = GetOneNum (&text, tmval->tm_hour);
1309 if (*text == ':')
1310 text++;
1311 mm = GetOneNum (&text, 0);
1312
1313 while (isspace (*text))
1314 text++;
1315 if (!isToday (&text, tmval->tm_wday))
1316 continue;
1317 while (isspace (*text))
1318 text++;
1319
1320 mo = GetOneNum (&text, tmval->tm_mon + 1);
1321 if (*text == '/')
1322 text++;
1323 dd = GetOneNum (&text, tmval->tm_mday);
1324 if (*text == '/')
1325 text++;
1326 yy = GetOneNum (&text, tmval->tm_year);
1327
1328 /* handle 20th/21st centuries */
1329 if (yy > CENTURY)
1330 yy -= 1900;
1331 else if (yy < CENTURY)
1332 yy += (CENTURY - 1900);
1333
1334 while (isspace (*text))
1335 text++;
1336 if (!*text)
1337 continue;
1338
1339 testTime = (mm + 60 * (hh + 24 * (dd + 31 * (mo + 12 * yy))));
1340
1341 if (testTime > (update_only ? currentTime : reminderTime))
1342 {
1343 # ifndef NO_REMINDER_EXEC
1344 char *prgm = extract_program (text);
1345 # endif/* NO_REMINDER_EXEC */
1346 /* trim leading/trailing space */
1347 text = trim_string (text);
1348
1349 /*
1350 * have a reminder whose time is greater than the last
1351 * reminder, now make sure it is the smallest available
1352 */
1353 if (testTime < savedTime)
1354 {
1355 savedTime = testTime;
1356 strncpy (message, text, sizeof (message));
1357 # ifndef NO_REMINDER_EXEC
1358 strncpy (execPrgm, (prgm ? prgm : ""), sizeof (execPrgm));
1359 # endif
1360 }
1361 else if (testTime == savedTime)
1362 {
1363 if (strlen (text))
1364 {
1365 int n = (sizeof (message) - strlen (message) - 3);
1366
1367 if (n > 0)
1368 {
1369 /* for co-occurring events */
1370 strcat (message, "\\n");
1371 strncat (message, text, n);
1372 }
1373 }
1374 # ifndef NO_REMINDER_EXEC
1375 if (prgm != NULL)
1376 {
1377 int n = (sizeof (execPrgm) - strlen (execPrgm) - 2);
1378
1379 if ((n > 0) && (n >= strlen (prgm)))
1380 {
1381 /* for co-occurring programs */
1382 switch (execPrgm[strlen (execPrgm) - 1])
1383 {
1384 case '&':
1385 case ';':
1386 break;
1387 default:
1388 strcat (execPrgm, ";");
1389 break;
1390 }
1391 strncat (execPrgm, prgm, n);
1392 }
1393 }
1394 # endif/* NO_REMINDER_EXEC */
1395 }
1396 }
1397 }
1398
1399 reminderTime = (savedTime < INT_MAX) ? savedTime : -1;
1400 fclose (fd);
1401 }
1402
1403 /*
1404 * Provide reminder by mapping the message window
1405 */
1406 static void
1407 Reminder ()
1408 {
1409 char *beg, *next;
1410 int lines;
1411
1412 if (Msg_Mapped)
1413 return;
1414
1415 # ifndef NO_REMINDER_EXEC
1416 if (strlen (message) == 0)
1417 {
1418 if (strlen (execPrgm) > 1)
1419 {
1420 system (execPrgm);
1421 Next_Reminder (REPLACE);
1422 }
1423 return; /* some sort of error */
1424 }
1425 # endif
1426
1427 /* compute the window size */
1428 # ifdef NO_REMINDER_EXEC
1429 Msg.width = 10 * XTextWidth (Xfont, "M", 1);
1430 # else
1431 Msg.width = 18 * XTextWidth (Xfont, "M", 1);
1432 # endif
1433
1434 for (beg = message, lines = 1; beg; beg = next, lines++)
1435 {
1436 int width;
1437 char *end;
1438
1439 if ((end = strstr (beg, "\\n")) == NULL)
1440 {
1441 end = beg + strlen (beg);
1442 next = NULL;
1443 }
1444 else
1445 {
1446 next = end + 2;
1447 }
1448
1449 width = XTextWidth (Xfont, beg, (end - beg));
1450 if (Msg.width < width)
1451 Msg.width = width;
1452 }
1453
1454 Msg.width += 30;
1455 Msg.height = (lines + 1) * FontHeight () + 30;
1456
1457 /* resize and centre the window */
1458 XMoveResizeWindow (Xdisplay, Msg.win,
1459 (DisplayWidth (Xdisplay, Xscreen) - Msg.width) / 2,
1460 (DisplayHeight (Xdisplay, Xscreen) - Msg.height) / 2, Msg.width, Msg.height);
1461
1462 # define BUTTON_MARGIN 8
1463
1464 XMoveWindow (Xdisplay, msgButton.Dismiss, BUTTON_MARGIN, (Msg.height - msgButton.height - BUTTON_MARGIN));
1465 XMoveWindow (Xdisplay, msgButton.Defer, (Msg.width - msgButton.width - BUTTON_MARGIN), (Msg.height - msgButton.height - BUTTON_MARGIN));
1466 # ifndef NO_REMINDER_EXEC
1467 XMoveWindow (Xdisplay, msgButton.Start, (Msg.width - msgButton.width) / 2, (Msg.height - msgButton.height - BUTTON_MARGIN));
1468 # endif
1469
1470 XMapRaised (Xdisplay, Msg.win);
1471 XBell (Xdisplay, 0);
1472 Msg_Mapped = 1;
1473 }
1474 #endif /* REMINDERS */
1475
1476 /*
1477 * Loops forever, looking for stuff to do. Sleeps 1 minute if nothing to do
1478 */
1479 static void
1480 getXevent ()
1481 {
1482 XEvent ev;
1483 int num_fds; /* number of file descriptors being used */
1484 struct timeval tm;
1485 struct tm *tmval;
1486 Atom wmDeleteWindow;
1487 fd_set in_fdset;
1488
1489 /* Enable delete window protocol */
1490 wmDeleteWindow = XInternAtom (Xdisplay, "WM_DELETE_WINDOW", False);
1491 XSetWMProtocols (Xdisplay, Clock.win, &wmDeleteWindow, 1);
1492 #ifdef ICONWIN
1493 XSetWMProtocols (Xdisplay, Icon.win, &wmDeleteWindow, 1);
1494 #endif
1495 #ifdef REMINDERS
1496 XSetWMProtocols (Xdisplay, Msg.win, &wmDeleteWindow, 1);
1497 #endif
1498
1499 num_fds = sysconf (_SC_OPEN_MAX);
1500 #ifdef FD_SETSIZE
1501 if (num_fds > FD_SETSIZE)
1502 num_fds = FD_SETSIZE;
1503 #endif
1504
1505 while (1)
1506 {
1507 /* take care of all pending X events */
1508 while (XPending (Xdisplay))
1509 {
1510 XNextEvent (Xdisplay, &ev);
1511 switch (ev.type)
1512 {
1513 case ClientMessage:
1514 /* check for delete window requests */
1515 if ((ev.xclient.format == 32) && (ev.xclient.data.l[0] == (long)wmDeleteWindow))
1516 {
1517 #ifdef REMINDERS
1518 if (ev.xany.window == Msg.win)
1519 {
1520 XUnmapWindow (Xdisplay, Msg.win);
1521 Msg_Mapped = 0;
1522 Next_Reminder (REPLACE);
1523 }
1524 else
1525 #endif
1526 return; /* delete window is how this terminates */
1527 }
1528 break;
1529
1530 case Expose:
1531 case GraphicsExpose:
1532 /* need to re-draw a window */
1533 if (ev.xany.window == Clock.win)
1534 Draw_Window (&Clock, 1);
1535 #ifdef ICONWIN
1536 else if (ev.xany.window == Icon.win)
1537 Draw_Window (&Icon, 1);
1538 #endif
1539 #ifdef REMINDERS
1540 else
1541 Draw_Window (&Msg, 1);
1542 #endif
1543 break;
1544
1545 case ConfigureNotify:
1546 /* window has been re-sized */
1547 if (ev.xany.window == Clock.win)
1548 {
1549 Clock.width = ev.xconfigure.width;
1550 Clock.height = ev.xconfigure.height;
1551 }
1552 break;
1553
1554 #ifdef REMINDERS
1555 case KeyPress:
1556 /* any key press to dismiss message window */
1557 if (ev.xany.window == Msg.win)
1558 {
1559 Next_Reminder (REPLACE);
1560 Msg_Mapped = 0;
1561 XUnmapWindow (Xdisplay, Msg.win);
1562 }
1563 break;
1564 #endif
1565
1566 case ButtonPress:
1567 #ifdef REMINDERS
1568 /* button press to dismiss message window */
1569 if (ev.xany.window == Msg.win)
1570 {
1571 if (ev.xbutton.subwindow == msgButton.Dismiss)
1572 {
1573 Next_Reminder (REPLACE);
1574 Msg_Mapped = 0;
1575 XUnmapWindow (Xdisplay, Msg.win);
1576 }
1577 else if (ev.xbutton.subwindow == msgButton.Defer)
1578 {
1579 time_t t = time (NULL) + adjustTime;
1580
1581 tmval = localtime (&t);
1582 reminderTime = mk_time (tmval) + DEFER_TIME;
1583 Msg_Mapped = 0;
1584 XUnmapWindow (Xdisplay, Msg.win);
1585 }
1586 # ifndef NO_REMINDER_EXEC
1587 else if (ev.xbutton.subwindow == msgButton.Start)
1588 {
1589 system (execPrgm);
1590 Next_Reminder (REPLACE);
1591 Msg_Mapped = 0;
1592 XUnmapWindow (Xdisplay, Msg.win);
1593 }
1594 # endif/* NO_REMINDER_EXEC */
1595 }
1596 #endif
1597 #ifdef MAIL
1598 if (ev.xany.window == Clock.win)
1599 {
1600 # ifdef MAIL_SPAWN
1601 /* left button action - spawn a mail reader */
1602 if (ev.xbutton.button == Button1)
1603 system (MAIL_SPAWN);
1604 # else
1605 if ((ev.xbutton.button == Button1) && (mail_spawn != NULL))
1606 system (mail_spawn);
1607 # endif
1608 /* redraw the window */
1609 Draw_Window (&Clock, 1);
1610 }
1611 #endif
1612 break;
1613 }
1614 }
1615
1616 /* Now wait for time out or new X event */
1617 FD_ZERO (&in_fdset);
1618 FD_SET (Xfd, &in_fdset);
1619 tm.tv_sec = clockUpdate;
1620 tm.tv_usec = 0;
1621 select (num_fds, &in_fdset, NULL, NULL, &tm);
1622
1623 Draw_Window (&Clock, 0);
1624 #ifdef ICONWIN
1625 Draw_Window (&Icon, 0);
1626 #endif
1627 }
1628 }
1629
1630 /*
1631 * Print an error message.
1632 */
1633 static void
1634 print_error (const char *fmt, ...)
1635 {
1636 va_list arg_ptr;
1637
1638 va_start (arg_ptr, fmt);
1639 fprintf (stderr, APL_NAME ": ");
1640 vfprintf (stderr, fmt, arg_ptr);
1641 fprintf (stderr, "\n");
1642 va_end (arg_ptr);
1643 }
1644
1645 /*----------------------- end-of-file (C source) -----------------------*/