wmclockmon: import version 0.8.1
[dockapps.git] / wmclockmon / src / main.c
blob1bd7e629b9551bd87be8135757b660a897136b9e
1 /*
2 * WMClockMon - A dockapp to display time and date
3 * Copyright (C) 2002 Thomas Nemeth <tnemeth@free.fr>
5 * Based on work by Seiichi SATO <ssato@sh.rim.or.jp>
6 * Copyright (C) 2001,2002 Seiichi SATO <ssato@sh.rim.or.jp>
7 * And on work by Mark Staggs <me@markstaggs.net>
8 * Copyright (C) 2002 Mark Staggs <me@markstaggs.net>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
30 #include <time.h>
31 #include <sys/time.h>
32 #include <unistd.h>
33 #include <pwd.h>
34 #include <signal.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <dirent.h>
38 #include <locale.h>
39 #include <ctype.h>
40 #include "dockapp.h"
41 #include "backlightB_on.xpm"
42 #include "backlightB_off.xpm"
43 #include "backlightI_on.xpm"
44 #include "backlightI_off.xpm"
45 #include "backlight0_on.xpm"
46 #include "backlight0_off.xpm"
47 #include "parts.xpm"
48 #include "letters.xpm"
51 #define FREE(data) {if (data) free(data); data = NULL;}
52 #define SET_STRING(str, val) {if (str) free(str); str = xstrdup(val);}
54 #if defined(netbsd) || defined(openbsd) || defined(freebsd) || defined(darwin)
55 # define BSDTIMEZONE
56 #endif
59 #define DEFAULT_CFGFILE ".wmclockmonrc"
60 #define DEFAULT_CONFIGDIR ".wmclockmoncal"
61 #define SIZE 58
62 #define MAXSTRLEN 512
63 #define WINDOWED_BG ". c #AEAAAE"
64 #define MAX_HISTORY 16
65 #define CPUNUM_NONE -1
67 #define MTIME 1
68 #define ATIME 2
71 typedef enum { LIGHTON, LIGHTOFF } Light;
73 typedef enum { CLOCK, INTERNET, BINARY } TMode;
75 typedef enum {T_INT, T_BOOL, T_STRING, T_FILE} KType;
77 typedef struct {
78 char *parts_s, *letters_s, *itime_s, *btime_s; /* styles files */
79 char *parts, *letters; /* parts and letters pixmap files */
80 char *backl, *backu; /* main pixmap files */
81 char *ibackl, *ibacku; /* internet time pixmap files */
82 char *bbackl, *bbacku; /* binary time pixmap files */
83 int ncolors; /* number of shadow colors */
84 int bdigith, bdigitw; /* big digits size */
85 int sdigith, sdigitw; /* small digits size */
86 int letterh, letterw; /* letters size */
87 int bsep, ssep, lsep; /* big, small digits and letters separations */
88 int hposx, hposy; /* hours */
89 Bool hbig; /* big digits for hours */
90 int mposx, mposy; /* minutes */
91 Bool mbig; /* big digits for minutes */
92 int sposx, sposy; /* seconds */
93 Bool sbig, csec; /* big digits for seconds, colon seconds */
94 int aposx, aposy; /* AM display */
95 int pposx, pposy; /* PM display */
96 int lposx, lposy; /* ALRM display */
97 int wposx, wposy; /* week day */
98 int dposx, dposy; /* day */
99 int oposx, oposy; /* month */
100 int bposx, bposy; /* beats */
101 Bool bbig; /* big digits for beats */
102 int tposx, tposy; /* 10th of beat */
103 Bool tbig, tdisp; /* big digits for 10th of beats, display them */
104 int gposx, gposy; /* beats graph bar */
105 Bool gdisp; /* display it */
106 int binhx, binhy; /* Binary hour X / Y */
107 int binmx, binmy; /* Binary minutes X / Y */
108 int binsx, binsy; /* Binary seconds X / Y */
109 int binzx, binzy; /* Binary time size X / size Y */
110 int binwx, binwy; /* Binary weekday X /Y */
111 int bindx, bindy; /* Binary day X / Y */
112 int binox, binoy; /* Binary month X / Y */
113 int binix, biniy; /* Binary date size X / Y */
114 int bind1x, bind1y; /* Binary delta X / delta Y, time same number */
115 int bind2x, bind2y; /* Binary delta X / delta Y, time tens->units */
116 int bind3x, bind3y; /* Binary delta X / delta Y, date same number */
117 int bind4x, bind4y; /* Binary delta X / delta Y, date tens->units */
118 } Style;
121 typedef struct {
122 char *key;
123 KType type;
124 void *var;
125 void *defval;
126 } StyleDef;
129 typedef struct Alarm {
130 char *entry;
131 char *alarm_time;
132 char *alarm_date;
133 char *message;
134 Bool on;
135 Bool cal;
136 struct Alarm *next;
137 } Alarm;
140 static Style default_style = {
141 /* FILES */
142 NULL, NULL, NULL, NULL, /* styles files */
143 NULL, NULL, /* parts and letters pixmap files */
144 NULL, NULL, /* main pixmap files */
145 NULL, NULL, /* internet time pixmap files */
146 NULL, NULL, /* binary time pixmap files */
147 /* DEFAULT SIZES */
148 2, /* number of shadow colors */
149 20, 10, /* big digits size */
150 9, 5, /* small digits size */
151 7, 5, /* letters size */
152 /* DEFAULT THEME - MAIN PART */
153 2, 1, 1, /* big, small digits and letters separations */
154 5, 6, True, /* hours pos and size */
155 32, 6, True, /* minutes pos and size */
156 43, 28, False, False, /* seconds pos, size and colon */
157 5, 28, /* AM */
158 5, 36, /* PM */
159 18, 28, /* alarm */
160 5, 47, /* day of week */
161 24, 45, /* day */
162 37, 47, /* month */
163 20, 6, True, /* beats */
164 /* DEFAULT THEME - BEATS FOR INTERNET TIME */
165 49, 28, False, True, /* 10th of beat pos, size and display */
166 6, 45, True, /* graph pos and display */
167 /* DEFAULT THEME - BINARY PART */
168 5, 5, /* Binary hour X / Y */
169 23, 5, /* Binary minutes X / Y */
170 41, 5, /* Binary seconds X / Y */
171 6, 6, /* Binary time size X / size Y */
172 14, 38, /* Binary weekday X /Y */
173 25, 38, /* Binary day X / Y */
174 43, 38, /* Binary month X / Y */
175 4, 3, /* Binary date size X / Y */
176 0, 2, /* Binary delta X / delta Y, time same number */
177 2, 0, /* Binary delta X / delta Y, time tens->units */
178 0, 1, /* Binary delta X / delta Y, date same number */
179 2, 0 /* Binary delta X / delta Y, date tens->units */
183 static char *upcases[] = {
184 "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
185 "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "\250",
186 NULL
190 Pixmap pixmap;
191 Pixmap backdrop_on;
192 Pixmap backdrop_off;
193 Pixmap backdropI_on;
194 Pixmap backdropI_off;
195 Pixmap backdropB_on;
196 Pixmap backdropB_off;
197 Pixmap parts;
198 Pixmap letters;
199 Pixmap mask;
200 Style style;
201 static char *display_name = "";
202 static char *light_color = NULL; /* led or backlight color */
203 static unsigned update_interval = 1;
204 static char *style_name = NULL;
205 static char *style_dir = NULL;
206 static Light backlight = LIGHTOFF;
207 static Bool switch_authorized = True;
208 static char *command = NULL;
209 static char *msgcmd = NULL;
210 static char *message = NULL;
211 static Alarm *alarms = NULL;
212 static char *config_file = NULL;
213 static time_t config_mtime = 0;
214 static Bool h12 = False;
215 static TMode time_mode = CLOCK;
216 static Bool showcal = False;
217 static Bool calalrms = False;
218 static Bool use_locale = True;
219 static struct tm *timeinfos;
220 static double swtime;
221 static char* label = NULL;
224 /* prototypes */
225 static void rotate_style();
226 static Bool stringdiff(const char *s1, const char *s2);
227 static char *set_filename(const char *file_name, const char *ext);
228 static Bool set_style_value(StyleDef opt, char *value);
229 static void set_style_default(StyleDef opt);
230 static void load_stylepart(const char *filename, StyleDef *opts);
231 static void load_style(const char *stylename);
232 static void init_pixmap(char **src_pix, Pixmap *dst_pix, const char *text,
233 XpmColorSymbol *c, int n, int keep_mask);
234 static void init_pixfile(char *src_name, Pixmap *dst_pix, const char *text,
235 XpmColorSymbol *c, int n, int keep_mask);
236 static void graphics_init();
237 static void control(unsigned int btn, int x, int y, unsigned int state);
238 static void draw_dockapp();
239 static void update();
240 static void switch_light();
241 static void draw_background(Light back);
242 static void draw_bigdigit(int num, int x, int y);
243 static void draw_smalldigit(int num, int x, int y);
244 static char equiv(char letter);
245 static void draw_textdigit(const char *text, int x, int y);
246 static void draw_timedigit();
247 static void draw_datedigit();
248 static void draw_itimedigit();
249 static void draw_binarytime();
250 static void parse_arguments(int argc, char **argv);
251 static void print_help(char *prog);
252 static void time_update();
253 static Bool raise_alarm();
254 static Bool fexist(const char *filename);
255 static Bool filestat(const char *filename, time_t *time, int mode);
256 static int my_system(char *cmd, char *opt);
257 void *xmalloc(size_t size);
258 char *xstrdup(const char *string);
259 static void alrm_add(Alarm **list, const char *value);
260 static void free_alrm(Alarm **list);
261 static int nb_alrm(Alarm *list);
262 static Bool alarms_on(Alarm *list);
263 static void switch_alarms(Alarm *list);
264 static Bool getbool(char *value);
265 static Bool load_cfgfile();
266 static char *get_calend_file(int type);
267 static int cal_alrms_chg();
268 static void load_cal_file(int type);
269 static void load_calalrms();
270 static void reload_alarms();
271 static void show_cal_file(int type);
272 static void show_cal();
273 static char *robust_home();
274 static void signal_reload();
278 int main(int argc, char **argv) {
279 XEvent event;
280 struct sigaction sa;
282 sa.sa_handler = SIG_IGN;
283 #ifdef SA_NOCLDWAIT
284 sa.sa_flags = SA_NOCLDWAIT;
285 #else
286 sa.sa_flags = 0;
287 #endif
288 sigemptyset(&sa.sa_mask);
289 sigaction(SIGCHLD, &sa, NULL);
291 /* Set default for style dir : */
292 SET_STRING(style_dir, DATADIR);
294 /* Init time */
295 time_update();
297 /* Load default configuration file */
298 if (! config_file) {
299 char *Home = robust_home();
300 config_file = xmalloc(strlen(Home) + strlen(DEFAULT_CFGFILE) + 2);
301 sprintf(config_file, "%s/%s", Home, DEFAULT_CFGFILE);
303 load_cfgfile();
304 FREE(config_file);
306 /* Parse CommandLine */
307 parse_arguments(argc, argv);
309 if (! config_file) {
310 char *Home = robust_home();
311 config_file = xmalloc(strlen(Home) + strlen(DEFAULT_CFGFILE) + 2);
312 sprintf(config_file, "%s/%s", Home, DEFAULT_CFGFILE);
313 } else {
314 load_cfgfile();
316 style = default_style;
317 load_style(style_name);
319 if (use_locale) {
320 setlocale(LC_TIME, "");
321 setlocale(LC_CTYPE, "");
322 setlocale(LC_COLLATE, "");
325 /* Initialize Application */
326 dockapp_open_window(display_name, argv[0], SIZE, SIZE, argc, argv);
327 dockapp_set_eventmask(ButtonPressMask);
329 graphics_init();
331 if (showcal) show_cal();
333 /* Main loop */
334 while (1) {
335 if (dockapp_nextevent_or_timeout(&event, update_interval * 1000 - 10)) {
336 /* Next Event */
337 switch (event.type) {
338 case ButtonPress:
339 control(event.xbutton.button,
340 event.xbutton.x,
341 event.xbutton.y,
342 event.xbutton.state);
343 break;
344 default: break;
346 } else {
347 /* Time Out */
348 update();
352 return 0;
356 static void rotate_style() {
357 DIR *dir;
358 struct dirent *dir_ent;
359 char *next = NULL;
360 Bool is_next = False;
362 if (! style_dir) return;
363 if ((dir = opendir(style_dir)) == NULL) return;
364 while ((dir_ent = readdir(dir)) != NULL) {
365 if (strstr(dir_ent->d_name, ".mwcs") != NULL) {
366 if (is_next || (! style_name)) {
367 next = xstrdup(dir_ent->d_name);
368 is_next = False;
369 break;
371 if (style_name && (strstr(style_name, dir_ent->d_name) != NULL))
372 is_next = True;
375 closedir(dir);
376 if (next) {
377 FREE(style_name);
378 style_name = xmalloc(strlen(next)+strlen(style_dir)+2);
379 sprintf(style_name, "%s/%s", style_dir, next);
380 } else {
381 FREE(style_name);
382 FREE(style.parts_s);
383 FREE(style.letters_s);
384 FREE(style.itime_s);
385 FREE(style.btime_s);
386 FREE(style.parts);
387 FREE(style.letters);
388 FREE(style.backl);
389 FREE(style.backu);
390 FREE(style.ibackl);
391 FREE(style.ibacku);
392 FREE(style.bbackl);
393 FREE(style.bbacku);
394 style = default_style;
396 FREE(next);
400 static Bool stringdiff(const char *s1, const char *s2) {
401 if (!s1 && !s2) return False;
402 if (!s1 && s2) return True;
403 if (s1 && !s2) return True;
404 if (strcmp(s1, s2) == 0) return False;
405 return True;
409 static char *set_filename(const char *file_name, const char *ext) {
410 int length = style_dir ? strlen(style_dir) + 1 : 0;
411 char *filename = NULL;
412 if (! file_name) return NULL;
413 if ((file_name[0] == '.') || (file_name[0] == '/') ||
414 (ext && (strstr(file_name, ext) != NULL))) { /* local file */
415 char *e;
416 filename = xstrdup(file_name);
417 e = strrchr(filename, '/');
418 if ((!style_dir) && (e != NULL)) {
419 char *p = filename;
420 int i = 0, style_len = strlen(filename) - strlen(e);
421 style_dir = xmalloc(style_len + 2);
422 while (p != e) {
423 style_dir[i++] = p[0];
424 p++;
426 style_dir[i] = 0;
428 } else {
429 length += strlen(file_name);
430 length += ext ? strlen(ext) + 1 : 1;
431 filename = xmalloc(length);
432 sprintf(filename, "%s%s%s%s",
433 style_dir ? style_dir : "",
434 style_dir ? "/" : "",
435 file_name,
436 ext ? ext : "");
438 return filename;
442 static Bool set_style_value(StyleDef opt, char *value) {
443 Bool res = True;
444 char *string1 = NULL;
445 char *string2 = NULL;
447 switch (opt.type) {
448 case T_INT:
449 *(int *)(opt.var) = atoi(value);
450 break;
451 case T_BOOL:
452 *(Bool *)(opt.var) = getbool(value);
453 break;
454 case T_STRING:
455 string1 = *(char **)(opt.var);
456 SET_STRING(string1, value);
457 *(char **)(opt.var) = string1;
458 break;
459 case T_FILE:
460 string1 = *(char **)(opt.var);
461 string2 = set_filename(value, NULL);
462 SET_STRING(string1, string2);
463 *(char **)(opt.var) = string1;
464 FREE(string2);
465 break;
466 default: res = False;
468 return res;
472 static void set_style_default(StyleDef opt) {
473 char *string1 = NULL;
474 char *string2 = NULL;
476 switch (opt.type) {
477 case T_INT:
478 *(int *)(opt.var) = *(int *)(opt.defval);
479 break;
480 case T_BOOL:
481 *(Bool *)(opt.var) = *(Bool *)(opt.defval);
482 break;
483 case T_STRING:
484 string1 = *(char **)(opt.var);
485 string2 = *(char **)(opt.defval);
486 SET_STRING(string1, string2);
487 *(char **)(opt.var) = string1;
488 break;
489 case T_FILE:
490 string1 = *(char **)(opt.var);
491 string2 = set_filename(*(char **)(opt.defval), NULL);
492 SET_STRING(string1, string2);
493 *(char **)(opt.var) = string1;
494 FREE(string2);
495 break;
500 static void load_stylepart(const char *filename, StyleDef *opts) {
501 FILE *file;
502 int i = 0;
504 if ((!filename) || (filename[0] == '-')) {
505 for (i = 0 ; opts[i].key ; i++)
506 set_style_default(opts[i]);
507 return;
509 if ((file = fopen(filename, "r")) == NULL) return;
510 while (!feof(file)) {
511 char line[MAXSTRLEN + 1], *value;
512 int j, set = False;
513 bzero(line, MAXSTRLEN + 1);
514 fgets(line, MAXSTRLEN, file);
516 i++;
517 if (line[strlen(line) - 1] == '\n') line[strlen(line) - 1] = 0;
518 if ((line[0] == '#') || (line[0] == 0)) continue;
519 value = strchr(line, '=') + 1;
520 while ((value[0] == ' ') && (value[0] != 0)) value++;
521 if (value[0] == 0) continue;
523 for (j = 0 ; opts[j].key ; j++) {
524 int length = strlen(opts[j].key);
525 if (strlen(line) <= length) continue;
526 if (strncmp(line, opts[j].key, length) == 0) {
527 set_style_value(opts[j], value);
528 set = True;
529 break;
532 if (!set)
533 fprintf(stderr, "Error in %s at line %d :\n[%s].\n",
534 filename, i, line);
539 static void load_style(const char *stylename) {
540 StyleDef parts_style_opts[] = {
541 {"Parts", T_FILE, &style.parts, &default_style.parts},
542 {"BDigitHeight", T_INT, &style.bdigith, &default_style.bdigith},
543 {"BDigitWidth", T_INT, &style.bdigitw, &default_style.bdigitw},
544 {"SDigitHeight", T_INT, &style.sdigith, &default_style.sdigith},
545 {"SDigitWidth", T_INT, &style.sdigitw, &default_style.sdigitw},
546 {"BDigitSep", T_INT, &style.bsep, &default_style.bsep},
547 {"SDigitSep", T_INT, &style.ssep, &default_style.ssep},
548 {NULL, T_STRING, NULL}
550 StyleDef letters_style_opts[] = {
551 {"Letters", T_FILE, &style.letters, &default_style.letters},
552 {"LetterHeight", T_INT, &style.letterh, &default_style.letterh},
553 {"LetterWidth", T_INT, &style.letterw, &default_style.letterw},
554 {"LetterSep", T_INT, &style.lsep, &default_style.lsep},
555 {NULL, T_STRING, NULL}
557 StyleDef itime_style_opts[] = {
558 {"IBacklightOn", T_FILE, &style.ibackl, &default_style.ibackl},
559 {"IBacklightOff", T_FILE, &style.ibacku, &default_style.ibacku},
560 {"Beats_PosX", T_INT, &style.bposx, &default_style.bposx},
561 {"Beats_PosY", T_INT, &style.bposy, &default_style.bposy},
562 {"Beats_Big", T_BOOL, &style.bbig, &default_style.bbig},
563 {"10thOB_PosX", T_INT, &style.tposx, &default_style.tposx},
564 {"10thOB_PosY", T_INT, &style.tposy, &default_style.tposy},
565 {"10thOB_Big", T_BOOL, &style.tbig, &default_style.tbig},
566 {"10thOB_Display", T_BOOL, &style.tdisp, &default_style.tdisp},
567 {"Graph_PosX", T_INT, &style.gposx, &default_style.gposx},
568 {"Graph_PosY", T_INT, &style.gposy, &default_style.gposy},
569 {"Graph_Display", T_BOOL, &style.gdisp, &default_style.gdisp},
570 {NULL, T_STRING, NULL}
572 StyleDef btime_style_opts[] = {
573 {"BBacklightOn", T_FILE, &style.bbackl, &default_style.bbackl},
574 {"BBacklightOff", T_FILE, &style.bbacku, &default_style.bbacku},
575 {"Bin_HX", T_INT, &style.binhx, &default_style.binhx},
576 {"Bin_HY", T_INT, &style.binhy, &default_style.binhy},
577 {"Bin_MX", T_INT, &style.binmx, &default_style.binmx},
578 {"Bin_MY", T_INT, &style.binmy, &default_style.binmy},
579 {"Bin_SX", T_INT, &style.binsx, &default_style.binsx},
580 {"Bin_SY", T_INT, &style.binsy, &default_style.binsy},
581 {"Bin_ZX", T_INT, &style.binzx, &default_style.binzx},
582 {"Bin_ZY", T_INT, &style.binzy, &default_style.binzy},
583 {"Bin_WX", T_INT, &style.binwx, &default_style.binwx},
584 {"Bin_WY", T_INT, &style.binwy, &default_style.binwy},
585 {"Bin_DX", T_INT, &style.bindx, &default_style.bindx},
586 {"Bin_DY", T_INT, &style.bindy, &default_style.bindy},
587 {"Bin_OX", T_INT, &style.binox, &default_style.binox},
588 {"Bin_OY", T_INT, &style.binoy, &default_style.binoy},
589 {"Bin_IX", T_INT, &style.binix, &default_style.binix},
590 {"Bin_IY", T_INT, &style.biniy, &default_style.biniy},
591 {"Bin_d1X", T_INT, &style.bind1x, &default_style.bind1x},
592 {"Bin_d1Y", T_INT, &style.bind1y, &default_style.bind1y},
593 {"Bin_d2X", T_INT, &style.bind2x, &default_style.bind2x},
594 {"Bin_d2Y", T_INT, &style.bind2y, &default_style.bind2y},
595 {"Bin_d3X", T_INT, &style.bind3x, &default_style.bind3x},
596 {"Bin_d3Y", T_INT, &style.bind3y, &default_style.bind3y},
597 {"Bin_d4X", T_INT, &style.bind4x, &default_style.bind4x},
598 {"Bin_d4Y", T_INT, &style.bind4y, &default_style.bind4y},
599 {NULL, T_STRING, NULL}
601 StyleDef main_style_opts[] = {
602 {"PartsStyle", T_FILE, &style.parts_s, &default_style.parts_s},
603 {"LettersStyle", T_FILE, &style.letters_s, &default_style.letters_s},
604 {"ITimeStyle", T_FILE, &style.itime_s, &default_style.itime_s},
605 {"BTimeStyle", T_FILE, &style.btime_s, &default_style.btime_s},
606 {"BacklightOn", T_FILE, &style.backl, &default_style.backl},
607 {"BacklightOff", T_FILE, &style.backu, &default_style.backu},
608 {"NbColors", T_INT, &style.ncolors, &default_style.ncolors},
609 {"Hours_PosX", T_INT, &style.hposx, &default_style.hposx},
610 {"Hours_PosY", T_INT, &style.hposy, &default_style.hposy},
611 {"Hours_Big", T_BOOL, &style.hbig, &default_style.hbig},
612 {"Minutes_PosX", T_INT, &style.mposx, &default_style.mposx},
613 {"Minutes_PosY", T_INT, &style.mposy, &default_style.mposy},
614 {"Minutes_Big", T_BOOL, &style.mbig, &default_style.mbig},
615 {"Seconds_PosX", T_INT, &style.sposx, &default_style.sposx},
616 {"Seconds_PosY", T_INT, &style.sposy, &default_style.sposy},
617 {"Seconds_Big", T_BOOL, &style.sbig, &default_style.sbig},
618 {"Seconds_Colon", T_BOOL, &style.csec, &default_style.csec},
619 {"AM_PosX", T_INT, &style.aposx, &default_style.aposx},
620 {"AM_PosY", T_INT, &style.aposy, &default_style.aposy},
621 {"PM_PosX", T_INT, &style.pposx, &default_style.pposx},
622 {"PM_PosY", T_INT, &style.pposy, &default_style.pposy},
623 {"ALRM_PosX", T_INT, &style.lposx, &default_style.lposx},
624 {"ALRM_PosY", T_INT, &style.lposy, &default_style.lposy},
625 {"Weekday_PosX", T_INT, &style.wposx, &default_style.wposx},
626 {"Weekday_PosY", T_INT, &style.wposy, &default_style.wposy},
627 {"Day_PosX", T_INT, &style.dposx, &default_style.dposx},
628 {"Day_PosY", T_INT, &style.dposy, &default_style.dposy},
629 {"Month_PosX", T_INT, &style.oposx, &default_style.oposx},
630 {"Month_PosY", T_INT, &style.oposy, &default_style.oposy},
631 {NULL, T_STRING, NULL}
633 char *partsfile = xstrdup(style.parts_s);
634 char *lettersfile = xstrdup(style.letters_s);
635 char *itimefile = xstrdup(style.itime_s);
636 char *btimefile = xstrdup(style.btime_s);
637 char *filename;
639 if (! stylename) return;
640 filename = set_filename(stylename, ".mwcs");
642 /* main style */
643 load_stylepart(filename, main_style_opts);
645 /* parts */
646 if (stringdiff(partsfile, style.parts_s) == True)
647 load_stylepart(style.parts_s, parts_style_opts);
649 /* letters */
650 if (stringdiff(lettersfile, style.letters_s) == True)
651 load_stylepart(style.letters_s, letters_style_opts);
653 /* internet time */
654 if (stringdiff(itimefile, style.itime_s) == True)
655 load_stylepart(style.itime_s, itime_style_opts);
657 /* binary clock */
658 if (stringdiff(btimefile, style.btime_s) == True)
659 load_stylepart(style.btime_s, btime_style_opts);
661 FREE(partsfile);
662 FREE(lettersfile);
663 FREE(itimefile);
664 FREE(btimefile);
665 FREE(filename);
669 static void init_pixmap(char **src_pix, Pixmap *dst_pix, const char *text,
670 XpmColorSymbol *c, int n, int keep_mask) {
671 if (*dst_pix) XFreePixmap(display, *dst_pix);
672 if (!dockapp_xpm2pixmap(src_pix, dst_pix, &mask, c, n)) {
673 fprintf(stderr, "Error initializing %s image.\n", text);
674 exit(1);
676 if (!keep_mask && mask) XFreePixmap(display, mask);
680 static void init_pixfile(char *src_name, Pixmap *dst_pix, const char *text,
681 XpmColorSymbol *c, int n, int keep_mask) {
682 if (*dst_pix) XFreePixmap(display, *dst_pix);
683 if (!dockapp_file2pixmap(src_name, dst_pix, &mask, c, n)) {
684 fprintf(stderr, "Error initializing %s image.\n", text);
685 exit(1);
687 if (!keep_mask && mask) XFreePixmap(display, mask);
691 static void graphics_init() {
692 XpmColorSymbol colors[3] = { {"Back0", NULL, 0}, {"Back1", NULL, 0}, {"Back2", NULL, 0} };
693 int ncolor = 0;
695 if (light_color) {
696 ncolor = style.ncolors;
697 if (ncolor == 2) {
698 colors[0].pixel = dockapp_getcolor(light_color);
699 colors[1].pixel = dockapp_blendedcolor(light_color, -24, -24, -24, 1.0);
700 } else {
701 colors[0].pixel = dockapp_getcolor(light_color);
702 colors[1].pixel = dockapp_dividecolor(light_color, 3);
703 colors[2].pixel = dockapp_blendedcolor(light_color, -50, -50, -50, 1.0);
707 /* change raw xpm data to pixmap */
709 if (dockapp_iswindowed)
710 backlit_pix[1] = backgrd_pix[1] = WINDOWED_BG;
713 if (style.parts)
714 init_pixfile(style.parts, &parts, "parts",
715 colors, ncolor, False);
716 else
717 init_pixmap(parts_xpm, &parts, "parts",
718 colors, ncolor, False);
720 if (style.letters)
721 init_pixfile(style.letters, &letters, "letters",
722 colors, ncolor, False);
723 else
724 init_pixmap(letters_xpm, &letters, "letters",
725 colors, ncolor, False);
727 if (style.ibackl)
728 init_pixfile(style.ibackl, &backdropI_on, "backlit background 2",
729 colors, ncolor, False);
730 else
731 init_pixmap(backlightI_on_xpm, &backdropI_on, "backlit background 2",
732 colors, ncolor, False);
734 if (style.ibacku)
735 init_pixfile(style.ibacku, &backdropI_off, "background 2",
736 colors, ncolor, False);
737 else
738 init_pixmap(backlightI_off_xpm, &backdropI_off, "background 2",
739 colors, ncolor, False);
741 if (style.bbackl)
742 init_pixfile(style.bbackl, &backdropB_on, "backlit background 3",
743 colors, ncolor, False);
744 else
745 init_pixmap(backlightB_on_xpm, &backdropB_on, "backlit background 3",
746 colors, ncolor, False);
748 if (style.bbacku)
749 init_pixfile(style.bbacku, &backdropB_off, "background 3",
750 colors, ncolor, False);
751 else
752 init_pixmap(backlightB_off_xpm, &backdropB_off, "background 3",
753 colors, ncolor, False);
755 if (style.backl)
756 init_pixfile(style.backl, &backdrop_on, "backlit background",
757 colors, ncolor, False);
758 else
759 init_pixmap(backlight0_on_xpm, &backdrop_on, "backlit background",
760 colors, ncolor, False);
762 if (style.backu)
763 init_pixfile(style.backu, &backdrop_off, "background",
764 colors, ncolor, True);
765 else
766 init_pixmap(backlight0_off_xpm, &backdrop_off, "background",
767 colors, ncolor, True);
769 /* shape window */
770 if (!dockapp_iswindowed) dockapp_setshape(mask, 0, 0);
771 if (mask) XFreePixmap(display, mask);
773 /* pixmap : draw area */
774 if (pixmap) XFreePixmap(display, pixmap);
775 pixmap = dockapp_XCreatePixmap(SIZE, SIZE);
777 /* Initialize pixmap */
778 draw_background(backlight);
780 dockapp_set_background(pixmap);
781 update();
782 dockapp_show();
786 static void control(unsigned int btn, int x, int y, unsigned int state) {
787 switch (btn) {
788 case 1:
789 if (time_mode == CLOCK) {
790 if ( (x >= style.aposx) && (x <= style.aposx + 12) &&
791 (y >= style.aposy) && (y <= style.aposy + 7) ) {
792 h12 = !h12;
793 break;
794 } else if ( (x >= style.pposx) && (x <= style.pposx + 12) &&
795 (y >= style.pposy) && (y <= style.pposy + 7) ) {
796 h12 = !h12;
797 break;
798 } else if ( (x >= style.lposx) && (x <= style.lposx + 22) &&
799 (y >= style.lposy) && (y <= style.lposy + 7) ) {
800 if (alarms) switch_alarms(alarms);
801 break;
804 if (state & ControlMask) {
805 time_mode += 1;
806 if (time_mode == 3) time_mode = CLOCK;
807 time_update();
808 draw_dockapp();
809 } else {
810 switch_light();
812 break;
813 case 2:
814 if (state & ControlMask) {
815 my_system("wmclockmon-config -f", config_file);
816 } else {
817 rotate_style();
818 load_style(style_name);
819 graphics_init();
820 draw_dockapp();
822 break;
823 case 3:
824 if (state & ControlMask) {
825 my_system("wmclockmon-cal", NULL);
826 } else {
827 switch_authorized = !switch_authorized;
829 break;
830 default: break;
835 static void draw_dockapp() {
836 /* all clear */
837 draw_background(backlight);
839 /* draw digit */
840 if (time_mode == INTERNET) {
841 draw_itimedigit();
842 } else if (time_mode == BINARY) {
843 draw_binarytime();
844 } else {
845 draw_timedigit();
846 draw_datedigit();
849 /* show */
850 dockapp_copy2window(pixmap);
854 /* called by timer */
855 static void update() {
856 static Light pre_backlight;
857 static Bool in_alarm_mode = False;
859 /* check config_file modifications */
860 if (load_cfgfile()) graphics_init();
862 /* get current time */
863 time_update();
865 /* alarm mode */
866 if (raise_alarm()) {
867 if (!in_alarm_mode) {
868 in_alarm_mode = True;
869 my_system(command, NULL);
870 if (message) my_system(msgcmd, message);
871 pre_backlight = backlight;
873 if ( (switch_authorized) ||
874 ( (switch_authorized) && (backlight != pre_backlight) ) ) {
875 switch_light();
876 return;
878 } else {
879 if (in_alarm_mode) {
880 in_alarm_mode = False;
881 if (backlight != pre_backlight) {
882 switch_light();
883 return;
887 draw_dockapp();
891 /* called when mouse button pressed */
892 static void switch_light() {
893 switch (backlight) {
894 case LIGHTOFF: backlight = LIGHTON; break;
895 case LIGHTON: backlight = LIGHTOFF; break;
897 /* redraw digit */
898 time_update();
899 draw_dockapp();
903 static void draw_background(Light back) {
904 if (back == LIGHTON) {
905 if (time_mode == INTERNET)
906 dockapp_copyarea(backdropI_on, pixmap, 0, 0, 58, 58, 0, 0);
907 else if (time_mode == BINARY)
908 dockapp_copyarea(backdropB_on, pixmap, 0, 0, 58, 58, 0, 0);
909 else
910 dockapp_copyarea(backdrop_on, pixmap, 0, 0, 58, 58, 0, 0);
911 } else {
912 if (time_mode == INTERNET)
913 dockapp_copyarea(backdropI_off, pixmap, 0, 0, 58, 58, 0, 0);
914 else if (time_mode == BINARY)
915 dockapp_copyarea(backdropB_off, pixmap, 0, 0, 58, 58, 0, 0);
916 else
917 dockapp_copyarea(backdrop_off, pixmap, 0, 0, 58, 58, 0, 0);
922 static void draw_bigdigit(int num, int x, int y) {
923 int dy = 0, w = style.bdigitw, h = style.bdigith;
924 int incr = style.bdigitw + style.bsep;
926 if (num < 0) num = 0;
927 if (backlight == LIGHTON) dy = style.bdigith;
929 /* draw digit */
930 dockapp_copyarea(parts, pixmap, (num % 10) * w, dy, w, h, x + incr, y);
931 dockapp_copyarea(parts, pixmap, (num / 10) * w, dy, w, h, x, y);
935 static void draw_smalldigit(int num, int x, int y) {
936 int dx = 0, w = style.sdigitw, h = style.sdigith;
937 int incr = style.sdigitw + style.bsep;
938 int dy = 2 * style.bdigith;
940 if (num < 0) num = 0;
941 if (backlight == LIGHTON) dx = style.sdigitw * 10;
943 /* draw digit */
944 dockapp_copyarea(parts, pixmap, (num % 10) * w + dx, dy, w, h, x + incr, y);
945 dockapp_copyarea(parts, pixmap, (num / 10) * w + dx, dy, w, h, x, y);
949 static char equiv(char letter) {
950 int i, sign, oldsign = 0;
951 char letr[2], upcase, ret = 0;
953 upcase = toupper(letter);
954 letr[0] = upcase; letr[1] = 0;
955 if (! isupper(upcase)) return letter;
956 for (i = 0 ; upcases[i] ; i++) {
957 sign = strcoll(letr, upcases[i]);
958 if ((oldsign > 0) && (sign < 0)) ret = upcases[i-1][0];
959 oldsign = sign;
961 if (ret == 0) ret = upcase;
962 return ret;
966 static void draw_textdigit(const char *text, int x, int y) {
967 int i, dy = style.letterh, incr = style.letterw + style.lsep;
968 int w = style.letterw, h = style.letterh;
970 if (backlight == LIGHTON) dy = style.letterh * 2;
971 for (i = 0 ; text[i] ; i ++) {
972 int pos = equiv(text[i]) - 'A';
973 dockapp_copyarea(letters, pixmap, pos * w, dy, w, h, x + i * incr, y);
978 static void draw_timedigit() {
979 int hour = timeinfos->tm_hour, dx = 0, dy = 0;
981 if (backlight == LIGHTON) {
982 dx = style.sdigitw * 10; /* AM/PM/ALRM parts positions */
983 dy = style.bdigith; /* colon seconds */
986 if (h12) {
987 if (hour == 0)
988 hour = 12;
989 else
990 hour = (hour > 12) ? hour - 12 : hour;
993 if (style.hbig)
994 draw_bigdigit(hour, style.hposx, style.hposy);
995 else
996 draw_smalldigit(hour, style.hposx, style.hposy);
998 if (style.mbig)
999 draw_bigdigit(timeinfos->tm_min, style.mposx, style.mposy);
1000 else
1001 draw_smalldigit(timeinfos->tm_min, style.mposx, style.mposy);
1003 if (style.csec) {
1004 if (timeinfos->tm_sec % 2 == 1) {
1005 if (style.sbig) {
1006 dockapp_copyarea(parts, pixmap, 4, dy, 2, 2, style.sposx, style.sposy);
1007 dockapp_copyarea(parts, pixmap, 4, dy, 2, 2, style.sposx, style.sposy + 9);
1008 } else {
1009 dockapp_copyarea(parts, pixmap, 4, dy, 1, 1, style.sposx, style.sposy);
1010 dockapp_copyarea(parts, pixmap, 4, dy, 1, 1, style.sposx, style.sposy + 4);
1013 } else {
1014 if (style.sbig)
1015 draw_bigdigit(timeinfos->tm_sec, style.sposx, style.sposy);
1016 else
1017 draw_smalldigit(timeinfos->tm_sec, style.sposx, style.sposy);
1020 if (h12) {
1021 /* Some say pm is when h>12 or h==0 but others (and my watch) told me
1022 * that the good way to handle am/pm is what is below
1024 if (timeinfos->tm_hour >= 12) /* PM */
1025 dockapp_copyarea(parts, pixmap, 36 + dx, 49, 12, 7, style.pposx, style.pposy);
1026 else
1027 dockapp_copyarea(parts, pixmap, 23 + dx, 49, 12, 7, style.aposx, style.aposy);
1029 if (alarms_on(alarms))
1030 dockapp_copyarea(parts, pixmap, dx, 49, 22, 7, style.lposx, style.lposy);
1034 static void draw_datedigit() {
1035 char text[5];
1037 if (label) {
1038 draw_textdigit(label, style.wposx, style.wposy);
1039 } else {
1040 strftime(text, 4, "%a", timeinfos);
1041 draw_textdigit(text, style.wposx, style.wposy);
1042 draw_smalldigit(timeinfos->tm_mday, style.dposx, style.dposy);
1043 strftime(text, 4, "%b", timeinfos);
1044 draw_textdigit(text, style.oposx, style.oposy);
1049 static void draw_itimedigit() {
1050 int dx = 0, dy = 0, v1, v10, v100, v0, v00, nb, pc;
1051 int bw = style.bdigitw, bh = style.bdigith;
1052 int binc = style.bdigitw + 2;
1053 int tw = style.sdigitw, th = style.bdigith;
1054 int xd = 100;
1056 if (backlight == LIGHTON) {
1057 dx = 50;
1058 dy = 20;
1059 xd = 102;
1062 /* use floor(3) */
1063 v100 = swtime / 100.0;
1064 v10 = (swtime - v100 * 100) / 10.0;
1065 v1 = (swtime - v100 * 100.0 - v10 * 10.0);
1066 v0 = (swtime - v100 * 100.0 - v10 * 10.0 - v1) * 10;
1067 v00 = (swtime - v100 * 100.0 - v10 * 10.0 - v1) * 1000;
1068 pc = v00 - v0 * 100;
1070 /* draw main beats digit */
1071 if (!style.bbig) {
1072 bw = tw;
1073 bh = th;
1075 dockapp_copyarea(parts, pixmap, v1 * 10, dy, bw, bh, style.bposx + 2 * binc, style.bposy);
1076 dockapp_copyarea(parts, pixmap, v10 * 10, dy, bw, bh, style.bposx + binc, style.bposy);
1077 dockapp_copyarea(parts, pixmap, v100 * 10, dy, bw, bh, style.bposx, style.bposy);
1079 /* draw 10th of beats */
1080 dockapp_copyarea(parts, pixmap, v0 * 5 + dx, 40, 5, 9, 49, 28);
1082 /* graph */
1083 for (nb = 0 ; nb < pc / 6.25 ; nb++)
1084 dockapp_copyarea(parts, pixmap, xd, 0, 2, 9, 6 + nb * 3, 45);
1088 static void draw_bits(int num, int x, int y, int u, int d, int sx, int sy, int d1x, int d1y, int d2x, int d2y) {
1089 int v = num;
1090 int dx = u * (sx + d2x) + d1x;
1091 int dy = sy + d1y;
1092 int b[4], i = 0;
1093 int bx = 0;
1094 int by = 2 * style.bdigith + style.sdigith + 7 + d;
1096 if (num == 0) return;
1097 if (backlight == LIGHTON) bx = 50;
1099 b[3] = 0; b[2] = 0; b[1] = 0; b[0] = 0;
1100 while (v != 0) {
1101 if (i == 4) {
1102 fprintf(stderr, "Error : num is too big (%d)\n", num);
1103 exit(1);
1105 b[i] = v % 2;
1106 v = v / 2;
1107 i++;
1109 for (i = 0 ; i < 4 ; i++) {
1110 if (b[i]) {
1111 int px = x + dx;
1112 int py = y + (3 - i) * dy + d2y;
1113 dockapp_copyarea(parts, pixmap, bx, by, sx, sy, px, py);
1119 static void draw_binarytime() {
1120 int sx = style.binzx;
1121 int sy = style.binzy;
1122 int d1x = style.bind1x;
1123 int d1y = style.bind1y;
1124 int d2x = style.bind2x;
1125 int d2y = style.bind2y;
1127 draw_bits(timeinfos->tm_hour / 10, style.binhx, style.binhy, 0, 0,
1128 sx, sy, d1x, d1y, d2x, d2y);
1129 draw_bits(timeinfos->tm_hour % 10, style.binhx, style.binhy, 1, 0,
1130 sx, sy, d1x, d1y, d2x, d2y);
1132 draw_bits(timeinfos->tm_min / 10, style.binmx, style.binmy, 0, 0,
1133 sx, sy, d1x, d1y, d2x, d2y);
1134 draw_bits(timeinfos->tm_min % 10, style.binmx, style.binmy, 1, 0,
1135 sx, sy, d1x, d1y, d2x, d2y);
1137 draw_bits(timeinfos->tm_sec / 10, style.binsx, style.binsy, 0, 0,
1138 sx, sy, d1x, d1y, d2x, d2y);
1139 draw_bits(timeinfos->tm_sec % 10, style.binsx, style.binsy, 1, 0,
1140 sx, sy, d1x, d1y, d2x, d2y);
1142 if (style.binix != -1) {
1143 int wd = timeinfos->tm_wday == 0 ? 7 : timeinfos->tm_wday;
1145 sx = style.binix;
1146 sy = style.biniy;
1147 d1x = style.bind3x;
1148 d1y = style.bind3y;
1149 d2x = style.bind4x;
1150 d2y = style.bind4y;
1152 draw_bits(wd, style.binwx, style.binwy, 0, 1,
1153 sx, sy, d1x, d1y, d2x, d2y);
1155 draw_bits(timeinfos->tm_mday / 10, style.bindx, style.bindy, 0, 1,
1156 sx, sy, d1x, d1y, d2x, d2y);
1157 draw_bits(timeinfos->tm_mday % 10, style.bindx, style.bindy, 1, 1,
1158 sx, sy, d1x, d1y, d2x, d2y);
1160 draw_bits((timeinfos->tm_mon + 1) / 10, style.binox, style.binoy, 0, 1,
1161 sx, sy, d1x, d1y, d2x, d2y);
1162 draw_bits((timeinfos->tm_mon + 1) % 10, style.binox, style.binoy, 1, 1,
1163 sx, sy, d1x, d1y, d2x, d2y);
1168 static void parse_arguments(int argc, char **argv) {
1169 int i, integer;
1170 for (i = 1; i < argc; i++) {
1171 if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "-h")) {
1172 print_help(argv[0]), exit(0);
1173 } else if (!strcmp(argv[i], "--version") || !strcmp(argv[i], "-v")) {
1174 printf("%s version %s\n", PACKAGE, VERSION), exit(0);
1175 } else if (!strcmp(argv[i], "--display") || !strcmp(argv[i], "-d")) {
1176 display_name = argv[i + 1];
1177 i++;
1178 } else if (!strcmp(argv[i], "--backlight") || !strcmp(argv[i], "-bl")) {
1179 backlight = LIGHTON;
1180 } else if (!strcmp(argv[i], "--light-color") || !strcmp(argv[i], "-lc")) {
1181 if (argc == i + 1)
1182 fprintf(stderr, "%s: argument \"%s\" needs an option.\n", argv[0], argv[i]), exit(1);
1183 SET_STRING(light_color, argv[i + 1]);
1184 i++;
1185 } else if (!strcmp(argv[i], "--windowed") || !strcmp(argv[i], "-w")) {
1186 dockapp_iswindowed = True;
1187 } else if (!strcmp(argv[i], "--broken-wm") || !strcmp(argv[i], "-bw")) {
1188 dockapp_isbrokenwm = True;
1189 } else if (!strcmp(argv[i], "--no-blink") || !strcmp(argv[i], "-nb")) {
1190 switch_authorized = False;
1191 } else if (!strcmp(argv[i], "--h12") || !strcmp(argv[i], "-12")) {
1192 h12 = True;
1193 } else if (!strcmp(argv[i], "--time-mode") || !strcmp(argv[i], "-tm")) {
1194 if (argc == i + 1)
1195 fprintf(stderr, "%s: error parsing argument for option %s\n",
1196 argv[0], argv[i]), exit(1);
1197 if (sscanf(argv[i + 1], "%i", &integer) != 1)
1198 fprintf(stderr, "%s: error parsing argument for option %s\n",
1199 argv[0], argv[i]), exit(1);
1200 if ((integer < 0) || (integer > 2))
1201 fprintf(stderr, "%s: argument %s must be in [0,2]\n",
1202 argv[0], argv[i]), exit(1);
1203 time_mode = integer;
1204 i++;
1205 } else if (!strcmp(argv[i], "--alarm-cmd") || !strcmp(argv[i], "-c")) {
1206 SET_STRING(command, argv[i + 1]);
1207 i++;
1208 } else if (!strcmp(argv[i], "--message-cmd") || !strcmp(argv[i], "-mc")) {
1209 SET_STRING(msgcmd, argv[i + 1]);
1210 i++;
1211 } else if (!strcmp(argv[i], "--alarm") || !strcmp(argv[i], "-a")) {
1212 alrm_add(&alarms, argv[i + 1]);
1213 i++;
1214 } else if (!strcmp(argv[i], "--cfg-file") || !strcmp(argv[i], "-f")) {
1215 SET_STRING(config_file, argv[i + 1]);
1216 i++;
1217 } else if (!strcmp(argv[i], "--no-locale") || !strcmp(argv[i], "-nl")) {
1218 use_locale = False;
1219 } else if (!strcmp(argv[i], "--style") || !strcmp(argv[i], "-s")) {
1220 SET_STRING(style_name, argv[i + 1]);
1221 i++;
1222 } else if (!strcmp(argv[i], "--style-dir") || !strcmp(argv[i], "-sd")) {
1223 SET_STRING(style_dir, argv[i + 1]);
1224 i++;
1225 } else if (!strcmp(argv[i], "--label") || !strcmp(argv[i], "-l")) {
1226 SET_STRING(label, argv[i + 1]);
1227 i++;
1228 } else if (!strcmp(argv[i], "--show-cal") || !strcmp(argv[i], "-sc")) {
1229 showcal = True;
1230 } else if (!strcmp(argv[i], "--cal-alrm") || !strcmp(argv[i], "-ca")) {
1231 calalrms = True;
1232 load_calalrms();
1233 } else {
1234 fprintf(stderr, "%s: unrecognized option '%s'\n", argv[0], argv[i]);
1235 print_help(argv[0]), exit(1);
1241 static void print_help(char *prog)
1243 printf("Usage : %s [OPTIONS]\n"
1244 "%s - Window Maker digital clock dockapp\n"
1245 " -h, --help show this help text and exit\n"
1246 " -v, --version show program version and exit\n"
1247 " -d, --display <string> display to use\n"
1248 " -bl, --backlight turn on back-light\n"
1249 " -lc, --light-color <color> backlight/led colour\n"
1250 " -tm, --time-mode <mode> start with time mode (0: clock, 1: internet time, 2: binary clock)\n"
1251 " -w, --windowed run the application in windowed mode\n"
1252 " -bw, --broken-wm activate broken window manager fix\n"
1253 " -a, --alarm <HH:MM> set alarm time to HH:MM (24h clock mode)\n"
1254 " -c, --alarm-cmd <string> command to launch when alarm raises\n"
1255 " -mc, --message-cmd <string> command to launch when alarm raises and a message is associated\n"
1256 " -12, --h12 12 hours clock mode (default is 24)\n"
1257 " -s, --style <file> style to use for display\n"
1258 " -sd, --style-dir <dir> directory where styles are stored\n"
1259 " -nb, --no-blink disable blinking when alarm is raised\n"
1260 " -f, --cfgfile <filename> use 'filename' as configuration file\n"
1261 " -nl, --no-locale don't use current locale\n"
1262 " -l, --label <string> use a label instead of date\n"
1263 " -ca, --cal-alrm load calendar alarms for today\n"
1264 " -sc, --show-cal show calendar at startup/00:00\n",
1265 prog, prog);
1269 static void time_update() {
1270 time_t tnow;
1272 time(&tnow);
1273 timeinfos = localtime(&tnow);
1275 if (time_mode == INTERNET) {
1276 long localtmzone;
1277 swtime = timeinfos->tm_hour * 3600
1278 + timeinfos->tm_min * 60
1279 + timeinfos->tm_sec;
1280 #ifdef BSDTIMEZONE
1281 localtmzone = timeinfos->tm_gmtoff;
1282 #else
1283 localtmzone = timezone;
1284 #endif
1285 swtime += localtmzone+3600;
1286 if (timeinfos->tm_isdst) swtime -= 3600;
1287 swtime *= 1000;
1288 swtime /= 86400;
1289 if (swtime >= 1000)
1290 swtime -= 1000;
1291 else
1292 if (swtime < 0) swtime += 1000;
1297 static Bool raise_alarm() {
1298 if ((timeinfos->tm_hour == 0) &&
1299 (timeinfos->tm_min == 0) &&
1300 (timeinfos->tm_sec == 0)) {
1301 if (showcal) show_cal();
1302 if (calalrms) reload_alarms();
1304 if (alarms) {
1305 Alarm *alrm = alarms;
1306 char thistime[MAXSTRLEN + 1];
1307 char thisdate[MAXSTRLEN + 1];
1309 strftime(thistime, MAXSTRLEN, "%H:%M", timeinfos);
1310 strftime(thisdate, MAXSTRLEN, "%u", timeinfos);
1311 while (alrm) {
1312 if (alrm->on && (strcmp(thistime, alrm->alarm_time) == 0)) {
1313 message = alrm->message;
1314 if (alrm->alarm_date) {
1315 if (strcmp(thisdate, alrm->alarm_date) == 0)
1316 return True;
1317 else
1318 message = NULL;
1319 } else {
1320 return True;
1323 alrm = alrm->next;
1326 return False;
1330 static Bool fexist(const char *filename) {
1331 FILE *file;
1333 if ((file = fopen(filename, "r")) == NULL) return False;
1334 fclose(file);
1336 return True;
1340 static Bool filestat(const char *filename, time_t *time, int mode) {
1341 struct stat s;
1342 time_t t = *time;
1344 if (stat(filename, &s) == -1) {
1345 if (*time == 0) return False;
1346 return True;
1348 switch (mode) {
1349 case MTIME: t = s.st_mtime; break;
1350 case ATIME: t = s.st_atime; break;
1351 default: break;
1353 if (t == *time) {
1354 return False;
1355 } else {
1356 *time = t;
1357 return True;
1362 static int my_system(char *cmd, char *opt) {
1363 int pid;
1364 extern char **environ;
1366 if (cmd == NULL) return 1;
1367 pid = fork();
1368 if (pid == -1) return -1;
1369 if (pid == 0) {
1370 pid = fork();
1371 if (pid == 0) {
1372 char *argv[3];
1373 char *thiscommand = xmalloc(strlen(cmd)
1374 + (opt ? strlen(opt) + 4 : 1));
1375 sprintf(thiscommand, "%s %s%s%s", cmd,
1376 opt ? "\"" : "",
1377 opt ? opt : "",
1378 opt ? "\"" : "");
1379 argv[0] = "sh";
1380 argv[1] = "-c";
1381 argv[2] = thiscommand;
1382 argv[3] = 0;
1383 execve("/bin/sh", argv, environ);
1384 FREE(thiscommand);
1385 exit(0);
1387 exit(0);
1389 return 0;
1393 void *xmalloc(size_t size) {
1394 void *ret = malloc(size);
1395 if (ret == NULL) {
1396 perror("malloc() ");
1397 exit(-1);
1398 } else
1399 return ret;
1403 char *xstrdup(const char *string) {
1404 char *ret = string ? strdup(string) : NULL;
1405 if (string && (ret == NULL)) {
1406 perror("strdup() ");
1407 exit(-1);
1408 } else
1409 return ret;
1413 static void put_alrm(Alarm **list, const char *entry,
1414 char *time, char *date, char *ison, char *mesg, Bool cal) {
1415 Alarm *lst = *list;
1416 Bool ok = True;
1418 if (! lst) {
1419 lst = xmalloc(sizeof(Alarm));
1420 *list = lst;
1421 } else {
1422 if (strcmp(entry, lst->entry) == 0) ok = False;
1423 while ( (lst->next) && ok) {
1424 lst = lst->next;
1425 if (strcmp(entry, lst->entry) == 0) ok = False;
1427 if (! ok) return;
1428 lst->next = xmalloc(sizeof(Alarm));
1429 lst = lst->next;
1431 lst->entry = xstrdup(entry);
1432 lst->alarm_time = time ? xstrdup(time) : NULL;
1433 lst->alarm_date = date ? xstrdup(date) : NULL;
1434 lst->on = ison ? getbool(ison) : True;
1435 lst->message = mesg ? xstrdup(mesg) : NULL;
1436 lst->cal = cal;
1437 lst->next = NULL;
1441 static void alrm_add(Alarm **list, const char *value) {
1442 char *time = NULL, *date = NULL, *ison = NULL, *mesg = NULL, *at;
1443 char *tokstr = xstrdup(value);
1444 char *toksav = tokstr;
1446 if (! value) return;
1447 at = strchr(value, '@');
1448 if (at) ison = strtok(tokstr, "@");
1449 time = strtok(at ? NULL : tokstr, "-.");
1450 if (strchr(value, '-')) date = strtok(NULL, ".");
1451 mesg = strtok(NULL, "\n\0");
1453 put_alrm(list, value, time, date, ison, mesg, False);
1455 FREE(toksav);
1459 static void free_alrm(Alarm **list) {
1460 Alarm *lst = *list, *next;
1461 while (lst) {
1462 next = lst->next;
1463 FREE(lst->entry);
1464 FREE(lst->alarm_time);
1465 FREE(lst->alarm_date);
1466 FREE(lst->message);
1467 free(lst);
1468 lst = next;
1470 *list = NULL;
1474 static int nb_alrm(Alarm *list) {
1475 Alarm *alrm = list;
1476 int n = 0;
1477 while (alrm) {
1478 n++;
1479 alrm = alrm->next;
1481 return n;
1485 static Bool alarms_on(Alarm *list) {
1486 Alarm *alrm = list;
1488 while (alrm) {
1489 if (alrm->on) return True;
1490 alrm = alrm->next;
1492 return False;
1496 static void switch_alarms(Alarm *list) {
1497 Alarm *alrm = list;
1498 Bool set_to = True;
1500 if (alarms_on(list)) set_to = False;
1501 while (alrm) {
1502 char *tokstr = xstrdup(alrm->entry);
1503 char *toksav = tokstr;
1504 Bool is_on = alrm->cal || getbool(strtok(tokstr, "@"));
1506 if ((is_on && set_to) || (!set_to)) {
1507 alrm->on = set_to;
1509 alrm = alrm->next;
1510 FREE(toksav);
1515 static Bool getbool(char *value) {
1516 int i;
1517 for (i = 0 ; value[i] ; i++) value[i] = tolower(value[i]);
1518 if (strcmp(value, "0") == 0) return False;
1519 if (strcmp(value, "1") == 0) return True;
1520 if (strcmp(value, "true") == 0) return True;
1521 if (strcmp(value, "false") == 0) return False;
1522 if (strcmp(value, "yes") == 0) return True;
1523 if (strcmp(value, "no") == 0) return False;
1524 if (strcmp(value, "on") == 0) return True;
1525 if (strcmp(value, "off") == 0) return False;
1526 printf("Error in converting \"%s\" to boolean value.\n", value);
1527 return False;
1531 static Bool load_cfgfile() {
1532 FILE *file;
1533 int i = 0, ok = True;
1534 char line[MAXSTRLEN + 1];
1535 char *value;
1537 if ((file = fopen(config_file, "r")) == NULL) {
1538 if (strstr(config_file, "/"DEFAULT_CFGFILE) == NULL)
1539 printf("Unable to open configuration file \"%s\".\n", config_file);
1540 ok = False;
1542 if (ok && (! filestat(config_file, &config_mtime, MTIME))) {
1543 fclose(file);
1544 ok = False;
1546 if (ok) {
1547 if (alarms) free_alrm(&alarms);
1548 while (! feof(file)) {
1549 bzero(line, MAXSTRLEN + 1);
1550 fgets(line, MAXSTRLEN, file);
1551 i++;
1552 if (line[strlen(line) - 1] == '\n') line[strlen(line) - 1] = 0;
1553 if ((line[0] == '#') || (line[0] == 0)) continue;
1554 value = strchr(line, '=');
1555 if (! value) continue;
1556 value++;
1557 while ((value[0] != 0) && (value[0] == ' ')) value++;
1558 if (value[0] == 0) continue;
1560 if (strncmp(line, "Backlight", 9) == 0) {
1561 backlight = getbool(value) ? LIGHTON : LIGHTOFF;
1562 } else if (strncmp(line, "Color", 5) == 0) {
1563 SET_STRING(light_color, value);
1564 } else if (strncmp(line, "Alarm", 5) == 0) {
1565 alrm_add(&alarms, value);
1566 } else if (strncmp(line, "Command", 7) == 0) {
1567 SET_STRING(command, value);
1568 } else if (strncmp(line, "MessageCmd", 10) == 0) {
1569 SET_STRING(msgcmd, value);
1570 } else if (strncmp(line, "Blink", 5) == 0) {
1571 switch_authorized = getbool(value);
1572 } else if (strncmp(line, "H12", 3) == 0) {
1573 h12 = getbool(value);
1574 } else if (strncmp(line, "Locale", 6) == 0) {
1575 use_locale = getbool(value);
1576 } else if (strncmp(line, "StyleDir", 8) == 0) {
1577 SET_STRING(style_dir, value);
1578 } else if (strncmp(line, "Style", 5) == 0) {
1579 SET_STRING(style_name, value);
1580 } else if (strncmp(line, "TimeMode", 5) == 0) {
1581 time_mode = atoi(value);
1582 } else if (strncmp(line, "ShowCal", 7) == 0) {
1583 showcal = getbool(value);
1584 } else if (strncmp(line, "CalAlrms", 8) == 0) {
1585 calalrms = getbool(value);
1586 } else {
1587 printf("Error in %s at line %d :\n[%s].\n", config_file, i, line);
1590 if (calalrms) load_calalrms();
1591 fclose(file);
1592 } else if (calalrms && cal_alrms_chg()) {
1593 reload_alarms();
1595 return ok;
1599 static char *get_calend_file(int type) {
1600 char *Home = robust_home();
1601 char *filename = xmalloc(
1602 strlen(Home) +
1603 strlen(DEFAULT_CONFIGDIR) +
1604 18);
1606 switch (type) {
1607 case 1:
1608 sprintf(filename, "%s/%s/%04d-%02d-%02d",
1609 Home,
1610 DEFAULT_CONFIGDIR,
1611 timeinfos->tm_year + 1900,
1612 timeinfos->tm_mon + 1,
1613 timeinfos->tm_mday);
1614 break;
1615 case 2:
1616 sprintf(filename, "%s/%s/XXXX-%02d-%02d",
1617 Home,
1618 DEFAULT_CONFIGDIR,
1619 timeinfos->tm_mon + 1,
1620 timeinfos->tm_mday);
1621 break;
1622 case 3:
1623 sprintf(filename, "%s/%s/XXXX-XX-%02d",
1624 Home,
1625 DEFAULT_CONFIGDIR,
1626 timeinfos->tm_mday);
1627 break;
1628 default: exit(1);
1630 return filename;
1634 static int cal_alrms_chg() {
1635 static time_t cal_u_mtime = 0;
1636 static time_t cal_y_mtime = 0;
1637 static time_t cal_m_mtime = 0;
1638 char *cal_u_fname = NULL;
1639 char *cal_y_fname = NULL;
1640 char *cal_m_fname = NULL;
1641 int chg_u, chg_y, chg_m;
1643 if (! calalrms) return False;
1644 cal_u_fname = get_calend_file(1);
1645 cal_y_fname = get_calend_file(2);
1646 cal_m_fname = get_calend_file(3);
1647 chg_u = filestat(cal_u_fname, &cal_u_mtime, MTIME);
1648 chg_y = filestat(cal_y_fname, &cal_y_mtime, MTIME);
1649 chg_m = filestat(cal_m_fname, &cal_m_mtime, MTIME);
1650 free(cal_u_fname);
1651 free(cal_y_fname);
1652 free(cal_m_fname);
1653 if (chg_u || chg_y || chg_m) return True;
1654 return False;
1658 static void load_cal_file(int type) {
1659 FILE *file;
1660 char *calend_file = get_calend_file(type);
1663 if ((file = fopen(calend_file, "r")) != NULL) {
1664 while (! feof(file)) {
1665 char line[MAXSTRLEN + 1];
1666 bzero(line, MAXSTRLEN + 1);
1667 fgets(line, MAXSTRLEN, file);
1668 if ( (line[0] != 0) && (strncmp(line, "@ ", 2) == 0) ) {
1669 char time[MAXSTRLEN + 1];
1670 sscanf(line, "@ %s ", time);
1671 put_alrm(&alarms, line, time, NULL, NULL, line + 8, True);
1674 fclose(file);
1676 free(calend_file);
1680 static void load_calalrms() {
1681 int i;
1682 for (i = 1 ; i < 4 ; i++) load_cal_file(i);
1686 static void reload_alarms() {
1687 FILE *file;
1688 int i = 0;
1689 char line[MAXSTRLEN + 1];
1690 char *value;
1692 if (alarms) free_alrm(&alarms);
1693 if ((file = fopen(config_file, "r")) == NULL) {
1694 if (strstr(config_file, "/"DEFAULT_CFGFILE) == NULL)
1695 printf("Unable to open configuration file \"%s\".\n", config_file);
1696 } else {
1697 while (! feof(file)) {
1698 bzero(line, MAXSTRLEN + 1);
1699 fgets(line, MAXSTRLEN, file);
1700 i++;
1701 if (line[strlen(line) - 1] == '\n') line[strlen(line) - 1] = 0;
1702 if ((line[0] == '#') || (line[0] == 0)) continue;
1703 value = strchr (line, '=') + 1;
1704 while ((value[0] == ' ') && (value[0] != 0)) value++;
1705 if (value[0] == 0) continue;
1707 if (strncmp(line, "Alarm", 5) == 0) alrm_add(&alarms, value);
1710 if (calalrms) load_calalrms();
1714 static void show_cal_file(int type) {
1715 FILE *file;
1716 char *data = NULL;
1717 char *tmp = NULL;
1718 char *calend_file = get_calend_file(type);
1720 if ((file = fopen(calend_file, "r")) != NULL) {
1721 while (! feof(file)) {
1722 char line[MAXSTRLEN + 1];
1723 bzero(line, MAXSTRLEN + 1);
1724 fgets(line, MAXSTRLEN, file);
1725 if (line[0] != 0) {
1726 int len = data ? strlen(data) : 0;
1727 tmp = xmalloc(len + strlen(line) + 1);
1728 sprintf(tmp, "%s%s", data ? data : "", line);
1729 FREE(data);
1730 data = tmp;
1731 tmp = NULL;
1734 fclose(file);
1735 my_system(msgcmd, data);
1736 FREE(data);
1738 free(calend_file);
1742 static void show_cal() {
1743 int i;
1744 for (i = 1 ; i < 4 ; i++) show_cal_file(i);
1748 static char *robust_home() {
1749 if (getenv("HOME"))
1750 return getenv("HOME");
1751 else if (getenv("USER") && getpwnam(getenv("USER")))
1752 return getpwnam(getenv("USER"))->pw_dir;
1753 else if (getenv("LOGNAME") && getpwnam(getenv("LOGNAME")))
1754 return getpwnam (getenv ("LOGNAME") )->pw_dir;
1755 else if ((getuid() != -1) && (getpwuid(getuid())))
1756 return getpwuid(getuid())->pw_dir;
1757 else
1758 return "/";