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