wmclockmon: rename `DFLAGS` `debug_CFLAGS`
[dockapps.git] / wmclockmon / src / main.c
blob94aece7f2894e69949a12906772555f6396ba4c0
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;
634 char *lettersfile;
635 char *itimefile;
636 char *btimefile;
637 char *filename;
639 if (! stylename) return;
641 partsfile = xstrdup(style.parts_s);
642 lettersfile = xstrdup(style.letters_s);
643 itimefile = xstrdup(style.itime_s);
644 btimefile = xstrdup(style.btime_s);
645 filename = set_filename(stylename, ".mwcs");
647 /* main style */
648 load_stylepart(filename, main_style_opts);
650 /* parts */
651 if (stringdiff(partsfile, style.parts_s) == True)
652 load_stylepart(style.parts_s, parts_style_opts);
654 /* letters */
655 if (stringdiff(lettersfile, style.letters_s) == True)
656 load_stylepart(style.letters_s, letters_style_opts);
658 /* internet time */
659 if (stringdiff(itimefile, style.itime_s) == True)
660 load_stylepart(style.itime_s, itime_style_opts);
662 /* binary clock */
663 if (stringdiff(btimefile, style.btime_s) == True)
664 load_stylepart(style.btime_s, btime_style_opts);
666 FREE(partsfile);
667 FREE(lettersfile);
668 FREE(itimefile);
669 FREE(btimefile);
670 FREE(filename);
674 static void init_pixmap(char **src_pix, Pixmap *dst_pix, const char *text,
675 XpmColorSymbol *c, int n, int keep_mask) {
676 if (*dst_pix) XFreePixmap(display, *dst_pix);
677 if (!dockapp_xpm2pixmap(src_pix, dst_pix, &mask, c, n)) {
678 fprintf(stderr, "Error initializing %s image.\n", text);
679 exit(1);
681 if (!keep_mask && mask) XFreePixmap(display, mask);
685 static void init_pixfile(char *src_name, Pixmap *dst_pix, const char *text,
686 XpmColorSymbol *c, int n, int keep_mask) {
687 if (*dst_pix) XFreePixmap(display, *dst_pix);
688 if (!dockapp_file2pixmap(src_name, dst_pix, &mask, c, n)) {
689 fprintf(stderr, "Error initializing %s image.\n", text);
690 exit(1);
692 if (!keep_mask && mask) XFreePixmap(display, mask);
696 static void graphics_init() {
697 XpmColorSymbol colors[3] = { {"Back0", NULL, 0}, {"Back1", NULL, 0}, {"Back2", NULL, 0} };
698 int ncolor = 0;
700 if (light_color) {
701 ncolor = style.ncolors;
702 if (ncolor == 2) {
703 colors[0].pixel = dockapp_getcolor(light_color);
704 colors[1].pixel = dockapp_blendedcolor(light_color, -24, -24, -24, 1.0);
705 } else {
706 colors[0].pixel = dockapp_getcolor(light_color);
707 colors[1].pixel = dockapp_dividecolor(light_color, 3);
708 colors[2].pixel = dockapp_blendedcolor(light_color, -50, -50, -50, 1.0);
712 /* change raw xpm data to pixmap */
714 if (dockapp_iswindowed)
715 backlit_pix[1] = backgrd_pix[1] = WINDOWED_BG;
718 if (style.parts)
719 init_pixfile(style.parts, &parts, "parts",
720 colors, ncolor, False);
721 else
722 init_pixmap(parts_xpm, &parts, "parts",
723 colors, ncolor, False);
725 if (style.letters)
726 init_pixfile(style.letters, &letters, "letters",
727 colors, ncolor, False);
728 else
729 init_pixmap(letters_xpm, &letters, "letters",
730 colors, ncolor, False);
732 if (style.ibackl)
733 init_pixfile(style.ibackl, &backdropI_on, "backlit background 2",
734 colors, ncolor, False);
735 else
736 init_pixmap(backlightI_on_xpm, &backdropI_on, "backlit background 2",
737 colors, ncolor, False);
739 if (style.ibacku)
740 init_pixfile(style.ibacku, &backdropI_off, "background 2",
741 colors, ncolor, False);
742 else
743 init_pixmap(backlightI_off_xpm, &backdropI_off, "background 2",
744 colors, ncolor, False);
746 if (style.bbackl)
747 init_pixfile(style.bbackl, &backdropB_on, "backlit background 3",
748 colors, ncolor, False);
749 else
750 init_pixmap(backlightB_on_xpm, &backdropB_on, "backlit background 3",
751 colors, ncolor, False);
753 if (style.bbacku)
754 init_pixfile(style.bbacku, &backdropB_off, "background 3",
755 colors, ncolor, False);
756 else
757 init_pixmap(backlightB_off_xpm, &backdropB_off, "background 3",
758 colors, ncolor, False);
760 if (style.backl)
761 init_pixfile(style.backl, &backdrop_on, "backlit background",
762 colors, ncolor, False);
763 else
764 init_pixmap(backlight0_on_xpm, &backdrop_on, "backlit background",
765 colors, ncolor, False);
767 if (style.backu)
768 init_pixfile(style.backu, &backdrop_off, "background",
769 colors, ncolor, True);
770 else
771 init_pixmap(backlight0_off_xpm, &backdrop_off, "background",
772 colors, ncolor, True);
774 /* shape window */
775 if (!dockapp_iswindowed) dockapp_setshape(mask, 0, 0);
776 if (mask) XFreePixmap(display, mask);
778 /* pixmap : draw area */
779 if (pixmap) XFreePixmap(display, pixmap);
780 pixmap = dockapp_XCreatePixmap(SIZE, SIZE);
782 /* Initialize pixmap */
783 draw_background(backlight);
785 dockapp_set_background(pixmap);
786 update();
787 dockapp_show();
791 static void control(unsigned int btn, int x, int y, unsigned int state) {
792 switch (btn) {
793 case 1:
794 if (time_mode == CLOCK) {
795 if ( (x >= style.aposx) && (x <= style.aposx + 12) &&
796 (y >= style.aposy) && (y <= style.aposy + 7) ) {
797 h12 = !h12;
798 break;
799 } else if ( (x >= style.pposx) && (x <= style.pposx + 12) &&
800 (y >= style.pposy) && (y <= style.pposy + 7) ) {
801 h12 = !h12;
802 break;
803 } else if ( (x >= style.lposx) && (x <= style.lposx + 22) &&
804 (y >= style.lposy) && (y <= style.lposy + 7) ) {
805 if (alarms) switch_alarms(alarms);
806 break;
809 if (state & ControlMask) {
810 time_mode += 1;
811 if (time_mode == 3) time_mode = CLOCK;
812 time_update();
813 draw_dockapp();
814 } else {
815 switch_light();
817 break;
818 case 2:
819 if (state & ControlMask) {
820 my_system("wmclockmon-config -f", config_file);
821 } else {
822 rotate_style();
823 load_style(style_name);
824 graphics_init();
825 draw_dockapp();
827 break;
828 case 3:
829 if (state & ControlMask) {
830 my_system("wmclockmon-cal", NULL);
831 } else {
832 switch_authorized = !switch_authorized;
834 break;
835 default: break;
840 static void draw_dockapp() {
841 /* all clear */
842 draw_background(backlight);
844 /* draw digit */
845 if (time_mode == INTERNET) {
846 draw_itimedigit();
847 } else if (time_mode == BINARY) {
848 draw_binarytime();
849 } else {
850 draw_timedigit();
851 draw_datedigit();
854 /* show */
855 dockapp_copy2window(pixmap);
859 /* called by timer */
860 static void update() {
861 static Light pre_backlight;
862 static Bool in_alarm_mode = False;
864 /* check config_file modifications */
865 if (load_cfgfile()) graphics_init();
867 /* get current time */
868 time_update();
870 /* alarm mode */
871 if (raise_alarm()) {
872 if (!in_alarm_mode) {
873 in_alarm_mode = True;
874 my_system(command, NULL);
875 if (message) my_system(msgcmd, message);
876 pre_backlight = backlight;
878 if ( (switch_authorized) ||
879 ( (switch_authorized) && (backlight != pre_backlight) ) ) {
880 switch_light();
881 return;
883 } else {
884 if (in_alarm_mode) {
885 in_alarm_mode = False;
886 if (backlight != pre_backlight) {
887 switch_light();
888 return;
892 draw_dockapp();
896 /* called when mouse button pressed */
897 static void switch_light() {
898 switch (backlight) {
899 case LIGHTOFF: backlight = LIGHTON; break;
900 case LIGHTON: backlight = LIGHTOFF; break;
902 /* redraw digit */
903 time_update();
904 draw_dockapp();
908 static void draw_background(Light back) {
909 if (back == LIGHTON) {
910 if (time_mode == INTERNET)
911 dockapp_copyarea(backdropI_on, pixmap, 0, 0, 58, 58, 0, 0);
912 else if (time_mode == BINARY)
913 dockapp_copyarea(backdropB_on, pixmap, 0, 0, 58, 58, 0, 0);
914 else
915 dockapp_copyarea(backdrop_on, pixmap, 0, 0, 58, 58, 0, 0);
916 } else {
917 if (time_mode == INTERNET)
918 dockapp_copyarea(backdropI_off, pixmap, 0, 0, 58, 58, 0, 0);
919 else if (time_mode == BINARY)
920 dockapp_copyarea(backdropB_off, pixmap, 0, 0, 58, 58, 0, 0);
921 else
922 dockapp_copyarea(backdrop_off, pixmap, 0, 0, 58, 58, 0, 0);
927 static void draw_bigdigit(int num, int x, int y) {
928 int dy = 0, w = style.bdigitw, h = style.bdigith;
929 int incr = style.bdigitw + style.bsep;
931 if (num < 0) num = 0;
932 if (backlight == LIGHTON) dy = style.bdigith;
934 /* draw digit */
935 dockapp_copyarea(parts, pixmap, (num % 10) * w, dy, w, h, x + incr, y);
936 dockapp_copyarea(parts, pixmap, (num / 10) * w, dy, w, h, x, y);
940 static void draw_smalldigit(int num, int x, int y) {
941 int dx = 0, w = style.sdigitw, h = style.sdigith;
942 int incr = style.sdigitw + style.bsep;
943 int dy = 2 * style.bdigith;
945 if (num < 0) num = 0;
946 if (backlight == LIGHTON) dx = style.sdigitw * 10;
948 /* draw digit */
949 dockapp_copyarea(parts, pixmap, (num % 10) * w + dx, dy, w, h, x + incr, y);
950 dockapp_copyarea(parts, pixmap, (num / 10) * w + dx, dy, w, h, x, y);
954 static char equiv(char letter) {
955 int i, sign, oldsign = 0;
956 char letr[2], upcase, ret = 0;
958 upcase = toupper(letter);
959 letr[0] = upcase; letr[1] = 0;
960 if (! isupper(upcase)) return letter;
961 for (i = 0 ; upcases[i] ; i++) {
962 sign = strcoll(letr, upcases[i]);
963 if ((oldsign > 0) && (sign < 0)) ret = upcases[i-1][0];
964 oldsign = sign;
966 if (ret == 0) ret = upcase;
967 return ret;
971 static void draw_textdigit(const char *text, int x, int y) {
972 int i, dy = style.letterh, incr = style.letterw + style.lsep;
973 int w = style.letterw, h = style.letterh;
975 if (backlight == LIGHTON) dy = style.letterh * 2;
976 for (i = 0 ; text[i] ; i ++) {
977 int pos = equiv(text[i]) - 'A';
978 dockapp_copyarea(letters, pixmap, pos * w, dy, w, h, x + i * incr, y);
983 static void draw_timedigit() {
984 int hour = timeinfos->tm_hour, dx = 0, dy = 0;
986 if (backlight == LIGHTON) {
987 dx = style.sdigitw * 10; /* AM/PM/ALRM parts positions */
988 dy = style.bdigith; /* colon seconds */
991 if (h12) {
992 if (hour == 0)
993 hour = 12;
994 else
995 hour = (hour > 12) ? hour - 12 : hour;
998 if (style.hbig)
999 draw_bigdigit(hour, style.hposx, style.hposy);
1000 else
1001 draw_smalldigit(hour, style.hposx, style.hposy);
1003 if (style.mbig)
1004 draw_bigdigit(timeinfos->tm_min, style.mposx, style.mposy);
1005 else
1006 draw_smalldigit(timeinfos->tm_min, style.mposx, style.mposy);
1008 if (style.csec) {
1009 if (timeinfos->tm_sec % 2 == 1) {
1010 if (style.sbig) {
1011 dockapp_copyarea(parts, pixmap, 4, dy, 2, 2, style.sposx, style.sposy);
1012 dockapp_copyarea(parts, pixmap, 4, dy, 2, 2, style.sposx, style.sposy + 9);
1013 } else {
1014 dockapp_copyarea(parts, pixmap, 4, dy, 1, 1, style.sposx, style.sposy);
1015 dockapp_copyarea(parts, pixmap, 4, dy, 1, 1, style.sposx, style.sposy + 4);
1018 } else {
1019 if (style.sbig)
1020 draw_bigdigit(timeinfos->tm_sec, style.sposx, style.sposy);
1021 else
1022 draw_smalldigit(timeinfos->tm_sec, style.sposx, style.sposy);
1025 if (h12) {
1026 /* Some say pm is when h>12 or h==0 but others (and my watch) told me
1027 * that the good way to handle am/pm is what is below
1029 if (timeinfos->tm_hour >= 12) /* PM */
1030 dockapp_copyarea(parts, pixmap, 36 + dx, 49, 12, 7, style.pposx, style.pposy);
1031 else
1032 dockapp_copyarea(parts, pixmap, 23 + dx, 49, 12, 7, style.aposx, style.aposy);
1034 if (alarms_on(alarms))
1035 dockapp_copyarea(parts, pixmap, dx, 49, 22, 7, style.lposx, style.lposy);
1039 static void draw_datedigit() {
1040 char text[5];
1042 if (label) {
1043 draw_textdigit(label, style.wposx, style.wposy);
1044 } else {
1045 strftime(text, 4, "%a", timeinfos);
1046 draw_textdigit(text, style.wposx, style.wposy);
1047 draw_smalldigit(timeinfos->tm_mday, style.dposx, style.dposy);
1048 strftime(text, 4, "%b", timeinfos);
1049 draw_textdigit(text, style.oposx, style.oposy);
1054 static void draw_itimedigit() {
1055 int dx = 0, dy = 0, v1, v10, v100, v0, v00, nb, pc;
1056 int bw = style.bdigitw, bh = style.bdigith;
1057 int binc = style.bdigitw + 2;
1058 int tw = style.sdigitw, th = style.bdigith;
1059 int xd = 100;
1061 if (backlight == LIGHTON) {
1062 dx = 50;
1063 dy = 20;
1064 xd = 102;
1067 /* use floor(3) */
1068 v100 = swtime / 100.0;
1069 v10 = (swtime - v100 * 100) / 10.0;
1070 v1 = (swtime - v100 * 100.0 - v10 * 10.0);
1071 v0 = (swtime - v100 * 100.0 - v10 * 10.0 - v1) * 10;
1072 v00 = (swtime - v100 * 100.0 - v10 * 10.0 - v1) * 1000;
1073 pc = v00 - v0 * 100;
1075 /* draw main beats digit */
1076 if (!style.bbig) {
1077 bw = tw;
1078 bh = th;
1080 dockapp_copyarea(parts, pixmap, v1 * 10, dy, bw, bh, style.bposx + 2 * binc, style.bposy);
1081 dockapp_copyarea(parts, pixmap, v10 * 10, dy, bw, bh, style.bposx + binc, style.bposy);
1082 dockapp_copyarea(parts, pixmap, v100 * 10, dy, bw, bh, style.bposx, style.bposy);
1084 /* draw 10th of beats */
1085 dockapp_copyarea(parts, pixmap, v0 * 5 + dx, 40, 5, 9, 49, 28);
1087 /* graph */
1088 for (nb = 0 ; nb < pc / 6.25 ; nb++)
1089 dockapp_copyarea(parts, pixmap, xd, 0, 2, 9, 6 + nb * 3, 45);
1093 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) {
1094 int v = num;
1095 int dx = u * (sx + d2x) + d1x;
1096 int dy = sy + d1y;
1097 int b[4], i = 0;
1098 int bx = 0;
1099 int by = 2 * style.bdigith + style.sdigith + 7 + d;
1101 if (num == 0) return;
1102 if (backlight == LIGHTON) bx = 50;
1104 b[3] = 0; b[2] = 0; b[1] = 0; b[0] = 0;
1105 while (v != 0) {
1106 if (i == 4) {
1107 fprintf(stderr, "Error : num is too big (%d)\n", num);
1108 exit(1);
1110 b[i] = v % 2;
1111 v = v / 2;
1112 i++;
1114 for (i = 0 ; i < 4 ; i++) {
1115 if (b[i]) {
1116 int px = x + dx;
1117 int py = y + (3 - i) * dy + d2y;
1118 dockapp_copyarea(parts, pixmap, bx, by, sx, sy, px, py);
1124 static void draw_binarytime() {
1125 int sx = style.binzx;
1126 int sy = style.binzy;
1127 int d1x = style.bind1x;
1128 int d1y = style.bind1y;
1129 int d2x = style.bind2x;
1130 int d2y = style.bind2y;
1132 draw_bits(timeinfos->tm_hour / 10, style.binhx, style.binhy, 0, 0,
1133 sx, sy, d1x, d1y, d2x, d2y);
1134 draw_bits(timeinfos->tm_hour % 10, style.binhx, style.binhy, 1, 0,
1135 sx, sy, d1x, d1y, d2x, d2y);
1137 draw_bits(timeinfos->tm_min / 10, style.binmx, style.binmy, 0, 0,
1138 sx, sy, d1x, d1y, d2x, d2y);
1139 draw_bits(timeinfos->tm_min % 10, style.binmx, style.binmy, 1, 0,
1140 sx, sy, d1x, d1y, d2x, d2y);
1142 draw_bits(timeinfos->tm_sec / 10, style.binsx, style.binsy, 0, 0,
1143 sx, sy, d1x, d1y, d2x, d2y);
1144 draw_bits(timeinfos->tm_sec % 10, style.binsx, style.binsy, 1, 0,
1145 sx, sy, d1x, d1y, d2x, d2y);
1147 if (style.binix != -1) {
1148 int wd = timeinfos->tm_wday == 0 ? 7 : timeinfos->tm_wday;
1150 sx = style.binix;
1151 sy = style.biniy;
1152 d1x = style.bind3x;
1153 d1y = style.bind3y;
1154 d2x = style.bind4x;
1155 d2y = style.bind4y;
1157 draw_bits(wd, style.binwx, style.binwy, 0, 1,
1158 sx, sy, d1x, d1y, d2x, d2y);
1160 draw_bits(timeinfos->tm_mday / 10, style.bindx, style.bindy, 0, 1,
1161 sx, sy, d1x, d1y, d2x, d2y);
1162 draw_bits(timeinfos->tm_mday % 10, style.bindx, style.bindy, 1, 1,
1163 sx, sy, d1x, d1y, d2x, d2y);
1165 draw_bits((timeinfos->tm_mon + 1) / 10, style.binox, style.binoy, 0, 1,
1166 sx, sy, d1x, d1y, d2x, d2y);
1167 draw_bits((timeinfos->tm_mon + 1) % 10, style.binox, style.binoy, 1, 1,
1168 sx, sy, d1x, d1y, d2x, d2y);
1173 static void parse_arguments(int argc, char **argv) {
1174 int i, integer;
1175 for (i = 1; i < argc; i++) {
1176 if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "-h")) {
1177 print_help(argv[0]), exit(0);
1178 } else if (!strcmp(argv[i], "--version") || !strcmp(argv[i], "-v")) {
1179 printf("%s version %s\n", PACKAGE, VERSION), exit(0);
1180 } else if (!strcmp(argv[i], "--display") || !strcmp(argv[i], "-d")) {
1181 display_name = argv[i + 1];
1182 i++;
1183 } else if (!strcmp(argv[i], "--backlight") || !strcmp(argv[i], "-bl")) {
1184 backlight = LIGHTON;
1185 } else if (!strcmp(argv[i], "--light-color") || !strcmp(argv[i], "-lc")) {
1186 if (argc == i + 1)
1187 fprintf(stderr, "%s: argument \"%s\" needs an option.\n", argv[0], argv[i]), exit(1);
1188 SET_STRING(light_color, argv[i + 1]);
1189 i++;
1190 } else if (!strcmp(argv[i], "--windowed") || !strcmp(argv[i], "-w")) {
1191 dockapp_iswindowed = True;
1192 } else if (!strcmp(argv[i], "--broken-wm") || !strcmp(argv[i], "-bw")) {
1193 dockapp_isbrokenwm = True;
1194 } else if (!strcmp(argv[i], "--no-blink") || !strcmp(argv[i], "-nb")) {
1195 switch_authorized = False;
1196 } else if (!strcmp(argv[i], "--h12") || !strcmp(argv[i], "-12")) {
1197 h12 = True;
1198 } else if (!strcmp(argv[i], "--time-mode") || !strcmp(argv[i], "-tm")) {
1199 if (argc == i + 1)
1200 fprintf(stderr, "%s: error parsing argument for option %s\n",
1201 argv[0], argv[i]), exit(1);
1202 if (sscanf(argv[i + 1], "%i", &integer) != 1)
1203 fprintf(stderr, "%s: error parsing argument for option %s\n",
1204 argv[0], argv[i]), exit(1);
1205 if ((integer < 0) || (integer > 2))
1206 fprintf(stderr, "%s: argument %s must be in [0,2]\n",
1207 argv[0], argv[i]), exit(1);
1208 time_mode = integer;
1209 i++;
1210 } else if (!strcmp(argv[i], "--alarm-cmd") || !strcmp(argv[i], "-c")) {
1211 SET_STRING(command, argv[i + 1]);
1212 i++;
1213 } else if (!strcmp(argv[i], "--message-cmd") || !strcmp(argv[i], "-mc")) {
1214 SET_STRING(msgcmd, argv[i + 1]);
1215 i++;
1216 } else if (!strcmp(argv[i], "--alarm") || !strcmp(argv[i], "-a")) {
1217 alrm_add(&alarms, argv[i + 1]);
1218 i++;
1219 } else if (!strcmp(argv[i], "--cfg-file") || !strcmp(argv[i], "-f")) {
1220 SET_STRING(config_file, argv[i + 1]);
1221 i++;
1222 } else if (!strcmp(argv[i], "--no-locale") || !strcmp(argv[i], "-nl")) {
1223 use_locale = False;
1224 } else if (!strcmp(argv[i], "--style") || !strcmp(argv[i], "-s")) {
1225 SET_STRING(style_name, argv[i + 1]);
1226 i++;
1227 } else if (!strcmp(argv[i], "--style-dir") || !strcmp(argv[i], "-sd")) {
1228 SET_STRING(style_dir, argv[i + 1]);
1229 i++;
1230 } else if (!strcmp(argv[i], "--label") || !strcmp(argv[i], "-l")) {
1231 SET_STRING(label, argv[i + 1]);
1232 i++;
1233 } else if (!strcmp(argv[i], "--show-cal") || !strcmp(argv[i], "-sc")) {
1234 showcal = True;
1235 } else if (!strcmp(argv[i], "--cal-alrm") || !strcmp(argv[i], "-ca")) {
1236 calalrms = True;
1237 load_calalrms();
1238 } else {
1239 fprintf(stderr, "%s: unrecognized option '%s'\n", argv[0], argv[i]);
1240 print_help(argv[0]), exit(1);
1246 static void print_help(char *prog)
1248 printf("Usage : %s [OPTIONS]\n"
1249 "%s - Window Maker digital clock dockapp\n"
1250 " -h, --help show this help text and exit\n"
1251 " -v, --version show program version and exit\n"
1252 " -d, --display <string> display to use\n"
1253 " -bl, --backlight turn on back-light\n"
1254 " -lc, --light-color <color> backlight/led colour\n"
1255 " -tm, --time-mode <mode> start with time mode (0: clock, 1: internet time, 2: binary clock)\n"
1256 " -w, --windowed run the application in windowed mode\n"
1257 " -bw, --broken-wm activate broken window manager fix\n"
1258 " -a, --alarm <HH:MM> set alarm time to HH:MM (24h clock mode)\n"
1259 " -c, --alarm-cmd <string> command to launch when alarm raises\n"
1260 " -mc, --message-cmd <string> command to launch when alarm raises and a message is associated\n"
1261 " -12, --h12 12 hours clock mode (default is 24)\n"
1262 " -s, --style <file> style to use for display\n"
1263 " -sd, --style-dir <dir> directory where styles are stored\n"
1264 " -nb, --no-blink disable blinking when alarm is raised\n"
1265 " -f, --cfgfile <filename> use 'filename' as configuration file\n"
1266 " -nl, --no-locale don't use current locale\n"
1267 " -l, --label <string> use a label instead of date\n"
1268 " -ca, --cal-alrm load calendar alarms for today\n"
1269 " -sc, --show-cal show calendar at startup/00:00\n",
1270 prog, prog);
1274 static void time_update() {
1275 time_t tnow;
1277 time(&tnow);
1278 timeinfos = localtime(&tnow);
1280 if (time_mode == INTERNET) {
1281 long localtmzone;
1282 swtime = timeinfos->tm_hour * 3600
1283 + timeinfos->tm_min * 60
1284 + timeinfos->tm_sec;
1285 #ifdef BSDTIMEZONE
1286 localtmzone = timeinfos->tm_gmtoff;
1287 #else
1288 localtmzone = timezone;
1289 #endif
1290 swtime += localtmzone+3600;
1291 if (timeinfos->tm_isdst) swtime -= 3600;
1292 swtime *= 1000;
1293 swtime /= 86400;
1294 if (swtime >= 1000)
1295 swtime -= 1000;
1296 else
1297 if (swtime < 0) swtime += 1000;
1302 static Bool raise_alarm() {
1303 if ((timeinfos->tm_hour == 0) &&
1304 (timeinfos->tm_min == 0) &&
1305 (timeinfos->tm_sec == 0)) {
1306 if (showcal) show_cal();
1307 if (calalrms) reload_alarms();
1309 if (alarms) {
1310 Alarm *alrm = alarms;
1311 char thistime[MAXSTRLEN + 1];
1312 char thisdate[MAXSTRLEN + 1];
1314 strftime(thistime, MAXSTRLEN, "%H:%M", timeinfos);
1315 strftime(thisdate, MAXSTRLEN, "%u", timeinfos);
1316 while (alrm) {
1317 if (alrm->on && (strcmp(thistime, alrm->alarm_time) == 0)) {
1318 message = alrm->message;
1319 if (alrm->alarm_date) {
1320 if (strcmp(thisdate, alrm->alarm_date) == 0)
1321 return True;
1322 else
1323 message = NULL;
1324 } else {
1325 return True;
1328 alrm = alrm->next;
1331 return False;
1335 static Bool fexist(const char *filename) {
1336 FILE *file;
1338 if ((file = fopen(filename, "r")) == NULL) return False;
1339 fclose(file);
1341 return True;
1345 static Bool filestat(const char *filename, time_t *time, int mode) {
1346 struct stat s;
1347 time_t t = *time;
1349 if (stat(filename, &s) == -1) {
1350 if (*time == 0) return False;
1351 return True;
1353 switch (mode) {
1354 case MTIME: t = s.st_mtime; break;
1355 case ATIME: t = s.st_atime; break;
1356 default: break;
1358 if (t == *time) {
1359 return False;
1360 } else {
1361 *time = t;
1362 return True;
1367 static int my_system(char *cmd, char *opt) {
1368 int pid;
1369 extern char **environ;
1371 if (cmd == NULL) return 1;
1372 pid = fork();
1373 if (pid == -1) return -1;
1374 if (pid == 0) {
1375 pid = fork();
1376 if (pid == 0) {
1377 char *argv[3];
1378 char *thiscommand = xmalloc(strlen(cmd)
1379 + (opt ? strlen(opt) + 4 : 1));
1380 sprintf(thiscommand, "%s %s%s%s", cmd,
1381 opt ? "\"" : "",
1382 opt ? opt : "",
1383 opt ? "\"" : "");
1384 argv[0] = "sh";
1385 argv[1] = "-c";
1386 argv[2] = thiscommand;
1387 argv[3] = 0;
1388 execve("/bin/sh", argv, environ);
1389 FREE(thiscommand);
1390 exit(0);
1392 exit(0);
1394 return 0;
1398 void *xmalloc(size_t size) {
1399 void *ret = malloc(size);
1400 if (ret == NULL) {
1401 perror("malloc() ");
1402 exit(-1);
1403 } else
1404 return ret;
1408 char *xstrdup(const char *string) {
1409 char *ret = string ? strdup(string) : NULL;
1410 if (string && (ret == NULL)) {
1411 perror("strdup() ");
1412 exit(-1);
1413 } else
1414 return ret;
1418 static void put_alrm(Alarm **list, const char *entry,
1419 char *time, char *date, char *ison, char *mesg, Bool cal) {
1420 Alarm *lst = *list;
1421 Bool ok = True;
1423 if (! lst) {
1424 lst = xmalloc(sizeof(Alarm));
1425 *list = lst;
1426 } else {
1427 if (strcmp(entry, lst->entry) == 0) ok = False;
1428 while ( (lst->next) && ok) {
1429 lst = lst->next;
1430 if (strcmp(entry, lst->entry) == 0) ok = False;
1432 if (! ok) return;
1433 lst->next = xmalloc(sizeof(Alarm));
1434 lst = lst->next;
1436 lst->entry = xstrdup(entry);
1437 lst->alarm_time = time ? xstrdup(time) : NULL;
1438 lst->alarm_date = date ? xstrdup(date) : NULL;
1439 lst->on = ison ? getbool(ison) : True;
1440 lst->message = mesg ? xstrdup(mesg) : NULL;
1441 lst->cal = cal;
1442 lst->next = NULL;
1446 static void alrm_add(Alarm **list, const char *value) {
1447 char *time = NULL, *date = NULL, *ison = NULL, *mesg = NULL, *at;
1448 char *tokstr = xstrdup(value);
1449 char *toksav = tokstr;
1451 if (! value) return;
1452 at = strchr(value, '@');
1453 if (at) ison = strtok(tokstr, "@");
1454 time = strtok(at ? NULL : tokstr, "-.");
1455 if (strchr(value, '-')) date = strtok(NULL, ".");
1456 mesg = strtok(NULL, "\n\0");
1458 put_alrm(list, value, time, date, ison, mesg, False);
1460 FREE(toksav);
1464 static void free_alrm(Alarm **list) {
1465 Alarm *lst = *list, *next;
1466 while (lst) {
1467 next = lst->next;
1468 FREE(lst->entry);
1469 FREE(lst->alarm_time);
1470 FREE(lst->alarm_date);
1471 FREE(lst->message);
1472 free(lst);
1473 lst = next;
1475 *list = NULL;
1479 static int nb_alrm(Alarm *list) {
1480 Alarm *alrm = list;
1481 int n = 0;
1482 while (alrm) {
1483 n++;
1484 alrm = alrm->next;
1486 return n;
1490 static Bool alarms_on(Alarm *list) {
1491 Alarm *alrm = list;
1493 while (alrm) {
1494 if (alrm->on) return True;
1495 alrm = alrm->next;
1497 return False;
1501 static void switch_alarms(Alarm *list) {
1502 Alarm *alrm = list;
1503 Bool set_to = True;
1505 if (alarms_on(list)) set_to = False;
1506 while (alrm) {
1507 char *tokstr = xstrdup(alrm->entry);
1508 char *toksav = tokstr;
1509 Bool is_on = alrm->cal || getbool(strtok(tokstr, "@"));
1511 if ((is_on && set_to) || (!set_to)) {
1512 alrm->on = set_to;
1514 alrm = alrm->next;
1515 FREE(toksav);
1520 static Bool getbool(char *value) {
1521 int i;
1522 for (i = 0 ; value[i] ; i++) value[i] = tolower(value[i]);
1523 if (strcmp(value, "0") == 0) return False;
1524 if (strcmp(value, "1") == 0) return True;
1525 if (strcmp(value, "true") == 0) return True;
1526 if (strcmp(value, "false") == 0) return False;
1527 if (strcmp(value, "yes") == 0) return True;
1528 if (strcmp(value, "no") == 0) return False;
1529 if (strcmp(value, "on") == 0) return True;
1530 if (strcmp(value, "off") == 0) return False;
1531 printf("Error in converting \"%s\" to boolean value.\n", value);
1532 return False;
1536 static Bool load_cfgfile() {
1537 FILE *file;
1538 int i = 0, ok = True;
1539 char line[MAXSTRLEN + 1];
1540 char *value;
1542 if ((file = fopen(config_file, "r")) == NULL) {
1543 if (strstr(config_file, "/"DEFAULT_CFGFILE) == NULL)
1544 printf("Unable to open configuration file \"%s\".\n", config_file);
1545 ok = False;
1547 if (ok && (! filestat(config_file, &config_mtime, MTIME))) {
1548 fclose(file);
1549 ok = False;
1551 if (ok) {
1552 if (alarms) free_alrm(&alarms);
1553 while (! feof(file)) {
1554 bzero(line, MAXSTRLEN + 1);
1555 fgets(line, MAXSTRLEN, file);
1556 i++;
1557 if (line[strlen(line) - 1] == '\n') line[strlen(line) - 1] = 0;
1558 if ((line[0] == '#') || (line[0] == 0)) continue;
1559 value = strchr(line, '=');
1560 if (! value) continue;
1561 value++;
1562 while ((value[0] != 0) && (value[0] == ' ')) value++;
1563 if (value[0] == 0) continue;
1565 if (strncmp(line, "Backlight", 9) == 0) {
1566 backlight = getbool(value) ? LIGHTON : LIGHTOFF;
1567 } else if (strncmp(line, "Color", 5) == 0) {
1568 SET_STRING(light_color, value);
1569 } else if (strncmp(line, "Alarm", 5) == 0) {
1570 alrm_add(&alarms, value);
1571 } else if (strncmp(line, "Command", 7) == 0) {
1572 SET_STRING(command, value);
1573 } else if (strncmp(line, "MessageCmd", 10) == 0) {
1574 SET_STRING(msgcmd, value);
1575 } else if (strncmp(line, "Blink", 5) == 0) {
1576 switch_authorized = getbool(value);
1577 } else if (strncmp(line, "H12", 3) == 0) {
1578 h12 = getbool(value);
1579 } else if (strncmp(line, "Locale", 6) == 0) {
1580 use_locale = getbool(value);
1581 } else if (strncmp(line, "StyleDir", 8) == 0) {
1582 SET_STRING(style_dir, value);
1583 } else if (strncmp(line, "Style", 5) == 0) {
1584 SET_STRING(style_name, value);
1585 } else if (strncmp(line, "TimeMode", 5) == 0) {
1586 time_mode = atoi(value);
1587 } else if (strncmp(line, "ShowCal", 7) == 0) {
1588 showcal = getbool(value);
1589 } else if (strncmp(line, "CalAlrms", 8) == 0) {
1590 calalrms = getbool(value);
1591 } else {
1592 printf("Error in %s at line %d :\n[%s].\n", config_file, i, line);
1595 if (calalrms) load_calalrms();
1596 fclose(file);
1597 } else if (calalrms && cal_alrms_chg()) {
1598 reload_alarms();
1600 return ok;
1604 static char *get_calend_file(int type) {
1605 char *Home = robust_home();
1606 char *filename = xmalloc(
1607 strlen(Home) +
1608 strlen(DEFAULT_CONFIGDIR) +
1609 18);
1611 switch (type) {
1612 case 1:
1613 sprintf(filename, "%s/%s/%04d-%02d-%02d",
1614 Home,
1615 DEFAULT_CONFIGDIR,
1616 timeinfos->tm_year + 1900,
1617 timeinfos->tm_mon + 1,
1618 timeinfos->tm_mday);
1619 break;
1620 case 2:
1621 sprintf(filename, "%s/%s/XXXX-%02d-%02d",
1622 Home,
1623 DEFAULT_CONFIGDIR,
1624 timeinfos->tm_mon + 1,
1625 timeinfos->tm_mday);
1626 break;
1627 case 3:
1628 sprintf(filename, "%s/%s/XXXX-XX-%02d",
1629 Home,
1630 DEFAULT_CONFIGDIR,
1631 timeinfos->tm_mday);
1632 break;
1633 default: exit(1);
1635 return filename;
1639 static int cal_alrms_chg() {
1640 static time_t cal_u_mtime = 0;
1641 static time_t cal_y_mtime = 0;
1642 static time_t cal_m_mtime = 0;
1643 char *cal_u_fname = NULL;
1644 char *cal_y_fname = NULL;
1645 char *cal_m_fname = NULL;
1646 int chg_u, chg_y, chg_m;
1648 if (! calalrms) return False;
1649 cal_u_fname = get_calend_file(1);
1650 cal_y_fname = get_calend_file(2);
1651 cal_m_fname = get_calend_file(3);
1652 chg_u = filestat(cal_u_fname, &cal_u_mtime, MTIME);
1653 chg_y = filestat(cal_y_fname, &cal_y_mtime, MTIME);
1654 chg_m = filestat(cal_m_fname, &cal_m_mtime, MTIME);
1655 free(cal_u_fname);
1656 free(cal_y_fname);
1657 free(cal_m_fname);
1658 if (chg_u || chg_y || chg_m) return True;
1659 return False;
1663 static void load_cal_file(int type) {
1664 FILE *file;
1665 char *calend_file = get_calend_file(type);
1668 if ((file = fopen(calend_file, "r")) != NULL) {
1669 while (! feof(file)) {
1670 char line[MAXSTRLEN + 1];
1671 bzero(line, MAXSTRLEN + 1);
1672 fgets(line, MAXSTRLEN, file);
1673 if ( (line[0] != 0) && (strncmp(line, "@ ", 2) == 0) ) {
1674 char time[MAXSTRLEN + 1];
1675 sscanf(line, "@ %s ", time);
1676 put_alrm(&alarms, line, time, NULL, NULL, line + 8, True);
1679 fclose(file);
1681 free(calend_file);
1685 static void load_calalrms() {
1686 int i;
1687 for (i = 1 ; i < 4 ; i++) load_cal_file(i);
1691 static void reload_alarms() {
1692 FILE *file;
1693 int i = 0;
1694 char line[MAXSTRLEN + 1];
1695 char *value;
1697 if (alarms) free_alrm(&alarms);
1698 if ((file = fopen(config_file, "r")) == NULL) {
1699 if (strstr(config_file, "/"DEFAULT_CFGFILE) == NULL)
1700 printf("Unable to open configuration file \"%s\".\n", config_file);
1701 } else {
1702 while (! feof(file)) {
1703 bzero(line, MAXSTRLEN + 1);
1704 fgets(line, MAXSTRLEN, file);
1705 i++;
1706 if (line[strlen(line) - 1] == '\n') line[strlen(line) - 1] = 0;
1707 if ((line[0] == '#') || (line[0] == 0)) continue;
1708 value = strchr (line, '=') + 1;
1709 while ((value[0] == ' ') && (value[0] != 0)) value++;
1710 if (value[0] == 0) continue;
1712 if (strncmp(line, "Alarm", 5) == 0) alrm_add(&alarms, value);
1715 if (calalrms) load_calalrms();
1719 static void show_cal_file(int type) {
1720 FILE *file;
1721 char *data = NULL;
1722 char *tmp = NULL;
1723 char *calend_file = get_calend_file(type);
1725 if ((file = fopen(calend_file, "r")) != NULL) {
1726 while (! feof(file)) {
1727 char line[MAXSTRLEN + 1];
1728 bzero(line, MAXSTRLEN + 1);
1729 fgets(line, MAXSTRLEN, file);
1730 if (line[0] != 0) {
1731 int len = data ? strlen(data) : 0;
1732 tmp = xmalloc(len + strlen(line) + 1);
1733 sprintf(tmp, "%s%s", data ? data : "", line);
1734 FREE(data);
1735 data = tmp;
1736 tmp = NULL;
1739 fclose(file);
1740 my_system(msgcmd, data);
1741 FREE(data);
1743 free(calend_file);
1747 static void show_cal() {
1748 int i;
1749 for (i = 1 ; i < 4 ; i++) show_cal_file(i);
1753 static char *robust_home() {
1754 if (getenv("HOME"))
1755 return getenv("HOME");
1756 else if (getenv("USER") && getpwnam(getenv("USER")))
1757 return getpwnam(getenv("USER"))->pw_dir;
1758 else if (getenv("LOGNAME") && getpwnam(getenv("LOGNAME")))
1759 return getpwnam (getenv ("LOGNAME") )->pw_dir;
1760 else if ((getuid() != -1) && (getpwuid(getuid())))
1761 return getpwuid(getuid())->pw_dir;
1762 else
1763 return "/";