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

File Contents

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