wmclockmon: update change-log
[dockapps.git] / wmclockmon / src / main.c
blobe485cb4c0189861adb6ce694ab1986955888b5e4
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(void);
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(void);
237 static void control(unsigned int btn, int x, int y, unsigned int state);
238 static void draw_dockapp(void);
239 static void update(void);
240 static void switch_light(void);
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(void);
247 static void draw_datedigit(void);
248 static void draw_itimedigit(void);
249 static void draw_binarytime(void);
250 static void parse_arguments(int argc, char **argv);
251 static void print_help(char *prog);
252 static void time_update(void);
253 static Bool raise_alarm(void);
254 static Bool filestat(const char *filename, time_t *time, int mode);
255 static int my_system(char *cmd, char *opt);
256 void *xmalloc(size_t size);
257 char *xstrdup(const char *string);
258 static void alrm_add(Alarm **list, const char *value);
259 static void free_alrm(Alarm **list);
260 static Bool alarms_on(Alarm *list);
261 static void switch_alarms(Alarm *list);
262 static Bool getbool(char *value);
263 static Bool load_cfgfile(void);
264 static char *get_calend_file(int type);
265 static int cal_alrms_chg(void);
266 static void load_cal_file(int type);
267 static void load_calalrms(void);
268 static void reload_alarms(void);
269 static void show_cal_file(int type);
270 static void show_cal(void);
271 static char *robust_home(void);
275 int main(int argc, char **argv) {
276 XEvent event;
277 struct sigaction sa;
279 sa.sa_handler = SIG_IGN;
280 #ifdef SA_NOCLDWAIT
281 sa.sa_flags = SA_NOCLDWAIT;
282 #else
283 sa.sa_flags = 0;
284 #endif
285 sigemptyset(&sa.sa_mask);
286 sigaction(SIGCHLD, &sa, NULL);
288 /* Set default for style dir : */
289 SET_STRING(style_dir, DATADIR);
291 /* Init time */
292 time_update();
294 /* Load default configuration file */
295 if (! config_file) {
296 char *Home = robust_home();
297 config_file = xmalloc(strlen(Home) + strlen(DEFAULT_CFGFILE) + 2);
298 sprintf(config_file, "%s/%s", Home, DEFAULT_CFGFILE);
300 load_cfgfile();
301 FREE(config_file);
303 /* Parse CommandLine */
304 parse_arguments(argc, argv);
306 if (! config_file) {
307 char *Home = robust_home();
308 config_file = xmalloc(strlen(Home) + strlen(DEFAULT_CFGFILE) + 2);
309 sprintf(config_file, "%s/%s", Home, DEFAULT_CFGFILE);
310 } else {
311 load_cfgfile();
313 style = default_style;
314 load_style(style_name);
316 if (use_locale) {
317 setlocale(LC_TIME, "");
318 setlocale(LC_CTYPE, "");
319 setlocale(LC_COLLATE, "");
322 /* Initialize Application */
323 dockapp_open_window(display_name, argv[0], SIZE, SIZE, argc, argv);
324 dockapp_set_eventmask(ButtonPressMask);
326 graphics_init();
328 if (showcal) show_cal();
330 /* Main loop */
331 while (1) {
332 if (dockapp_nextevent_or_timeout(&event, update_interval * 1000 - 10)) {
333 /* Next Event */
334 switch (event.type) {
335 case ButtonPress:
336 control(event.xbutton.button,
337 event.xbutton.x,
338 event.xbutton.y,
339 event.xbutton.state);
340 break;
341 default: break;
343 } else {
344 /* Time Out */
345 update();
349 return 0;
353 static void rotate_style(void) {
354 DIR *dir;
355 struct dirent *dir_ent;
356 char *next = NULL;
357 Bool is_next = False;
359 if (! style_dir) return;
360 if ((dir = opendir(style_dir)) == NULL) return;
361 while ((dir_ent = readdir(dir)) != NULL) {
362 if (strstr(dir_ent->d_name, ".mwcs") != NULL) {
363 if (is_next || (! style_name)) {
364 next = xstrdup(dir_ent->d_name);
365 is_next = False;
366 break;
368 if (style_name && (strstr(style_name, dir_ent->d_name) != NULL))
369 is_next = True;
372 closedir(dir);
373 if (next) {
374 FREE(style_name);
375 style_name = xmalloc(strlen(next)+strlen(style_dir)+2);
376 sprintf(style_name, "%s/%s", style_dir, next);
377 } else {
378 FREE(style_name);
379 FREE(style.parts_s);
380 FREE(style.letters_s);
381 FREE(style.itime_s);
382 FREE(style.btime_s);
383 FREE(style.parts);
384 FREE(style.letters);
385 FREE(style.backl);
386 FREE(style.backu);
387 FREE(style.ibackl);
388 FREE(style.ibacku);
389 FREE(style.bbackl);
390 FREE(style.bbacku);
391 style = default_style;
393 FREE(next);
397 static Bool stringdiff(const char *s1, const char *s2) {
398 if (!s1 && !s2) return False;
399 if (!s1 && s2) return True;
400 if (s1 && !s2) return True;
401 if (strcmp(s1, s2) == 0) return False;
402 return True;
406 static char *set_filename(const char *file_name, const char *ext) {
407 int length = style_dir ? strlen(style_dir) + 1 : 0;
408 char *filename = NULL;
409 if (! file_name) return NULL;
410 if ((file_name[0] == '.') || (file_name[0] == '/') ||
411 (ext && (strstr(file_name, ext) != NULL))) { /* local file */
412 char *e;
413 filename = xstrdup(file_name);
414 e = strrchr(filename, '/');
415 if ((!style_dir) && (e != NULL)) {
416 char *p = filename;
417 int i = 0, style_len = strlen(filename) - strlen(e);
418 style_dir = xmalloc(style_len + 2);
419 while (p != e) {
420 style_dir[i++] = p[0];
421 p++;
423 style_dir[i] = 0;
425 } else {
426 length += strlen(file_name);
427 length += ext ? strlen(ext) + 1 : 1;
428 filename = xmalloc(length);
429 sprintf(filename, "%s%s%s%s",
430 style_dir ? style_dir : "",
431 style_dir ? "/" : "",
432 file_name,
433 ext ? ext : "");
435 return filename;
439 static Bool set_style_value(StyleDef opt, char *value) {
440 Bool res = True;
441 char *string1 = NULL;
442 char *string2 = NULL;
444 switch (opt.type) {
445 case T_INT:
446 *(int *)(opt.var) = atoi(value);
447 break;
448 case T_BOOL:
449 *(Bool *)(opt.var) = getbool(value);
450 break;
451 case T_STRING:
452 string1 = *(char **)(opt.var);
453 SET_STRING(string1, value);
454 *(char **)(opt.var) = string1;
455 break;
456 case T_FILE:
457 string1 = *(char **)(opt.var);
458 string2 = set_filename(value, NULL);
459 SET_STRING(string1, string2);
460 *(char **)(opt.var) = string1;
461 FREE(string2);
462 break;
463 default: res = False;
465 return res;
469 static void set_style_default(StyleDef opt) {
470 char *string1 = NULL;
471 char *string2 = NULL;
473 switch (opt.type) {
474 case T_INT:
475 *(int *)(opt.var) = *(int *)(opt.defval);
476 break;
477 case T_BOOL:
478 *(Bool *)(opt.var) = *(Bool *)(opt.defval);
479 break;
480 case T_STRING:
481 string1 = *(char **)(opt.var);
482 string2 = *(char **)(opt.defval);
483 SET_STRING(string1, string2);
484 *(char **)(opt.var) = string1;
485 break;
486 case T_FILE:
487 string1 = *(char **)(opt.var);
488 string2 = set_filename(*(char **)(opt.defval), NULL);
489 SET_STRING(string1, string2);
490 *(char **)(opt.var) = string1;
491 FREE(string2);
492 break;
497 static void load_stylepart(const char *filename, StyleDef *opts) {
498 FILE *file;
499 int i = 0;
501 if ((!filename) || (filename[0] == '-')) {
502 for (i = 0 ; opts[i].key ; i++)
503 set_style_default(opts[i]);
504 return;
506 if ((file = fopen(filename, "r")) == NULL) return;
507 while (!feof(file)) {
508 char line[MAXSTRLEN + 1], *value;
509 int j, set = False;
510 size_t n;
512 if (!fgets(line, MAXSTRLEN, file))
513 break;
515 i++;
516 n = strlen(line);
517 if (n == 0)
518 continue;
519 if (line[n - 1] == '\n') line[n - 1] = 0;
520 if ((line[0] == '#') || (line[0] == 0)) continue;
521 value = strchr(line, '=') + 1;
522 while ((value[0] == ' ') && (value[0] != 0)) value++;
523 if (value[0] == 0) continue;
525 for (j = 0 ; opts[j].key ; j++) {
526 size_t length = strlen(opts[j].key);
527 if (strlen(line) <= length) continue;
528 if (strncmp(line, opts[j].key, length) == 0) {
529 set_style_value(opts[j], value);
530 set = True;
531 break;
534 if (!set)
535 fprintf(stderr, "Error in %s at line %d :\n[%s].\n",
536 filename, i, line);
541 static void load_style(const char *stylename) {
542 StyleDef parts_style_opts[] = {
543 {"Parts", T_FILE, &style.parts, &default_style.parts},
544 {"BDigitHeight", T_INT, &style.bdigith, &default_style.bdigith},
545 {"BDigitWidth", T_INT, &style.bdigitw, &default_style.bdigitw},
546 {"SDigitHeight", T_INT, &style.sdigith, &default_style.sdigith},
547 {"SDigitWidth", T_INT, &style.sdigitw, &default_style.sdigitw},
548 {"BDigitSep", T_INT, &style.bsep, &default_style.bsep},
549 {"SDigitSep", T_INT, &style.ssep, &default_style.ssep},
550 {NULL, T_STRING, NULL, NULL}
552 StyleDef letters_style_opts[] = {
553 {"Letters", T_FILE, &style.letters, &default_style.letters},
554 {"LetterHeight", T_INT, &style.letterh, &default_style.letterh},
555 {"LetterWidth", T_INT, &style.letterw, &default_style.letterw},
556 {"LetterSep", T_INT, &style.lsep, &default_style.lsep},
557 {NULL, T_STRING, NULL, NULL}
559 StyleDef itime_style_opts[] = {
560 {"IBacklightOn", T_FILE, &style.ibackl, &default_style.ibackl},
561 {"IBacklightOff", T_FILE, &style.ibacku, &default_style.ibacku},
562 {"Beats_PosX", T_INT, &style.bposx, &default_style.bposx},
563 {"Beats_PosY", T_INT, &style.bposy, &default_style.bposy},
564 {"Beats_Big", T_BOOL, &style.bbig, &default_style.bbig},
565 {"10thOB_PosX", T_INT, &style.tposx, &default_style.tposx},
566 {"10thOB_PosY", T_INT, &style.tposy, &default_style.tposy},
567 {"10thOB_Big", T_BOOL, &style.tbig, &default_style.tbig},
568 {"10thOB_Display", T_BOOL, &style.tdisp, &default_style.tdisp},
569 {"Graph_PosX", T_INT, &style.gposx, &default_style.gposx},
570 {"Graph_PosY", T_INT, &style.gposy, &default_style.gposy},
571 {"Graph_Display", T_BOOL, &style.gdisp, &default_style.gdisp},
572 {NULL, T_STRING, NULL, NULL}
574 StyleDef btime_style_opts[] = {
575 {"BBacklightOn", T_FILE, &style.bbackl, &default_style.bbackl},
576 {"BBacklightOff", T_FILE, &style.bbacku, &default_style.bbacku},
577 {"Bin_HX", T_INT, &style.binhx, &default_style.binhx},
578 {"Bin_HY", T_INT, &style.binhy, &default_style.binhy},
579 {"Bin_MX", T_INT, &style.binmx, &default_style.binmx},
580 {"Bin_MY", T_INT, &style.binmy, &default_style.binmy},
581 {"Bin_SX", T_INT, &style.binsx, &default_style.binsx},
582 {"Bin_SY", T_INT, &style.binsy, &default_style.binsy},
583 {"Bin_ZX", T_INT, &style.binzx, &default_style.binzx},
584 {"Bin_ZY", T_INT, &style.binzy, &default_style.binzy},
585 {"Bin_WX", T_INT, &style.binwx, &default_style.binwx},
586 {"Bin_WY", T_INT, &style.binwy, &default_style.binwy},
587 {"Bin_DX", T_INT, &style.bindx, &default_style.bindx},
588 {"Bin_DY", T_INT, &style.bindy, &default_style.bindy},
589 {"Bin_OX", T_INT, &style.binox, &default_style.binox},
590 {"Bin_OY", T_INT, &style.binoy, &default_style.binoy},
591 {"Bin_IX", T_INT, &style.binix, &default_style.binix},
592 {"Bin_IY", T_INT, &style.biniy, &default_style.biniy},
593 {"Bin_d1X", T_INT, &style.bind1x, &default_style.bind1x},
594 {"Bin_d1Y", T_INT, &style.bind1y, &default_style.bind1y},
595 {"Bin_d2X", T_INT, &style.bind2x, &default_style.bind2x},
596 {"Bin_d2Y", T_INT, &style.bind2y, &default_style.bind2y},
597 {"Bin_d3X", T_INT, &style.bind3x, &default_style.bind3x},
598 {"Bin_d3Y", T_INT, &style.bind3y, &default_style.bind3y},
599 {"Bin_d4X", T_INT, &style.bind4x, &default_style.bind4x},
600 {"Bin_d4Y", T_INT, &style.bind4y, &default_style.bind4y},
601 {NULL, T_STRING, NULL, NULL}
603 StyleDef main_style_opts[] = {
604 {"PartsStyle", T_FILE, &style.parts_s, &default_style.parts_s},
605 {"LettersStyle", T_FILE, &style.letters_s, &default_style.letters_s},
606 {"ITimeStyle", T_FILE, &style.itime_s, &default_style.itime_s},
607 {"BTimeStyle", T_FILE, &style.btime_s, &default_style.btime_s},
608 {"BacklightOn", T_FILE, &style.backl, &default_style.backl},
609 {"BacklightOff", T_FILE, &style.backu, &default_style.backu},
610 {"NbColors", T_INT, &style.ncolors, &default_style.ncolors},
611 {"Hours_PosX", T_INT, &style.hposx, &default_style.hposx},
612 {"Hours_PosY", T_INT, &style.hposy, &default_style.hposy},
613 {"Hours_Big", T_BOOL, &style.hbig, &default_style.hbig},
614 {"Minutes_PosX", T_INT, &style.mposx, &default_style.mposx},
615 {"Minutes_PosY", T_INT, &style.mposy, &default_style.mposy},
616 {"Minutes_Big", T_BOOL, &style.mbig, &default_style.mbig},
617 {"Seconds_PosX", T_INT, &style.sposx, &default_style.sposx},
618 {"Seconds_PosY", T_INT, &style.sposy, &default_style.sposy},
619 {"Seconds_Big", T_BOOL, &style.sbig, &default_style.sbig},
620 {"Seconds_Colon", T_BOOL, &style.csec, &default_style.csec},
621 {"AM_PosX", T_INT, &style.aposx, &default_style.aposx},
622 {"AM_PosY", T_INT, &style.aposy, &default_style.aposy},
623 {"PM_PosX", T_INT, &style.pposx, &default_style.pposx},
624 {"PM_PosY", T_INT, &style.pposy, &default_style.pposy},
625 {"ALRM_PosX", T_INT, &style.lposx, &default_style.lposx},
626 {"ALRM_PosY", T_INT, &style.lposy, &default_style.lposy},
627 {"Weekday_PosX", T_INT, &style.wposx, &default_style.wposx},
628 {"Weekday_PosY", T_INT, &style.wposy, &default_style.wposy},
629 {"Day_PosX", T_INT, &style.dposx, &default_style.dposx},
630 {"Day_PosY", T_INT, &style.dposy, &default_style.dposy},
631 {"Month_PosX", T_INT, &style.oposx, &default_style.oposx},
632 {"Month_PosY", T_INT, &style.oposy, &default_style.oposy},
633 {NULL, T_STRING, NULL, NULL}
635 char *partsfile;
636 char *lettersfile;
637 char *itimefile;
638 char *btimefile;
639 char *filename;
641 if (! stylename) return;
643 partsfile = xstrdup(style.parts_s);
644 lettersfile = xstrdup(style.letters_s);
645 itimefile = xstrdup(style.itime_s);
646 btimefile = xstrdup(style.btime_s);
647 filename = set_filename(stylename, ".mwcs");
649 /* main style */
650 load_stylepart(filename, main_style_opts);
652 /* parts */
653 if (stringdiff(partsfile, style.parts_s) == True)
654 load_stylepart(style.parts_s, parts_style_opts);
656 /* letters */
657 if (stringdiff(lettersfile, style.letters_s) == True)
658 load_stylepart(style.letters_s, letters_style_opts);
660 /* internet time */
661 if (stringdiff(itimefile, style.itime_s) == True)
662 load_stylepart(style.itime_s, itime_style_opts);
664 /* binary clock */
665 if (stringdiff(btimefile, style.btime_s) == True)
666 load_stylepart(style.btime_s, btime_style_opts);
668 FREE(partsfile);
669 FREE(lettersfile);
670 FREE(itimefile);
671 FREE(btimefile);
672 FREE(filename);
676 static void init_pixmap(char **src_pix, Pixmap *dst_pix, const char *text,
677 XpmColorSymbol *c, int n, int keep_mask) {
678 if (*dst_pix) XFreePixmap(display, *dst_pix);
679 if (!dockapp_xpm2pixmap(src_pix, dst_pix, &mask, c, n)) {
680 fprintf(stderr, "Error initializing %s image.\n", text);
681 exit(1);
683 if (!keep_mask && mask) XFreePixmap(display, mask);
687 static void init_pixfile(char *src_name, Pixmap *dst_pix, const char *text,
688 XpmColorSymbol *c, int n, int keep_mask) {
689 if (*dst_pix) XFreePixmap(display, *dst_pix);
690 if (!dockapp_file2pixmap(src_name, dst_pix, &mask, c, n)) {
691 fprintf(stderr, "Error initializing %s image.\n", text);
692 exit(1);
694 if (!keep_mask && mask) XFreePixmap(display, mask);
698 static void graphics_init(void) {
699 XpmColorSymbol colors[3] = { {"Back0", NULL, 0}, {"Back1", NULL, 0}, {"Back2", NULL, 0} };
700 int ncolor = 0;
702 if (light_color) {
703 ncolor = style.ncolors;
704 if (ncolor == 2) {
705 colors[0].pixel = dockapp_getcolor(light_color);
706 colors[1].pixel = dockapp_blendedcolor(light_color, -24, -24, -24, 1.0);
707 } else {
708 colors[0].pixel = dockapp_getcolor(light_color);
709 colors[1].pixel = dockapp_dividecolor(light_color, 3);
710 colors[2].pixel = dockapp_blendedcolor(light_color, -50, -50, -50, 1.0);
714 /* change raw xpm data to pixmap */
716 if (dockapp_iswindowed)
717 backlit_pix[1] = backgrd_pix[1] = WINDOWED_BG;
720 if (style.parts)
721 init_pixfile(style.parts, &parts, "parts",
722 colors, ncolor, False);
723 else
724 init_pixmap(parts_xpm, &parts, "parts",
725 colors, ncolor, False);
727 if (style.letters)
728 init_pixfile(style.letters, &letters, "letters",
729 colors, ncolor, False);
730 else
731 init_pixmap(letters_xpm, &letters, "letters",
732 colors, ncolor, False);
734 if (style.ibackl)
735 init_pixfile(style.ibackl, &backdropI_on, "backlit background 2",
736 colors, ncolor, False);
737 else
738 init_pixmap(backlightI_on_xpm, &backdropI_on, "backlit background 2",
739 colors, ncolor, False);
741 if (style.ibacku)
742 init_pixfile(style.ibacku, &backdropI_off, "background 2",
743 colors, ncolor, False);
744 else
745 init_pixmap(backlightI_off_xpm, &backdropI_off, "background 2",
746 colors, ncolor, False);
748 if (style.bbackl)
749 init_pixfile(style.bbackl, &backdropB_on, "backlit background 3",
750 colors, ncolor, False);
751 else
752 init_pixmap(backlightB_on_xpm, &backdropB_on, "backlit background 3",
753 colors, ncolor, False);
755 if (style.bbacku)
756 init_pixfile(style.bbacku, &backdropB_off, "background 3",
757 colors, ncolor, False);
758 else
759 init_pixmap(backlightB_off_xpm, &backdropB_off, "background 3",
760 colors, ncolor, False);
762 if (style.backl)
763 init_pixfile(style.backl, &backdrop_on, "backlit background",
764 colors, ncolor, False);
765 else
766 init_pixmap(backlight0_on_xpm, &backdrop_on, "backlit background",
767 colors, ncolor, False);
769 if (style.backu)
770 init_pixfile(style.backu, &backdrop_off, "background",
771 colors, ncolor, True);
772 else
773 init_pixmap(backlight0_off_xpm, &backdrop_off, "background",
774 colors, ncolor, True);
776 /* shape window */
777 if (!dockapp_iswindowed) dockapp_setshape(mask, 0, 0);
778 if (mask) XFreePixmap(display, mask);
780 /* pixmap : draw area */
781 if (pixmap) XFreePixmap(display, pixmap);
782 pixmap = dockapp_XCreatePixmap(SIZE, SIZE);
784 /* Initialize pixmap */
785 draw_background(backlight);
787 dockapp_set_background(pixmap);
788 update();
789 dockapp_show();
793 static void control(unsigned int btn, int x, int y, unsigned int state) {
794 switch (btn) {
795 case 1:
796 if (time_mode == CLOCK) {
797 if ( (x >= style.aposx) && (x <= style.aposx + 12) &&
798 (y >= style.aposy) && (y <= style.aposy + 7) ) {
799 h12 = !h12;
800 break;
801 } else if ( (x >= style.pposx) && (x <= style.pposx + 12) &&
802 (y >= style.pposy) && (y <= style.pposy + 7) ) {
803 h12 = !h12;
804 break;
805 } else if ( (x >= style.lposx) && (x <= style.lposx + 22) &&
806 (y >= style.lposy) && (y <= style.lposy + 7) ) {
807 if (alarms) switch_alarms(alarms);
808 break;
811 if (state & ControlMask) {
812 time_mode += 1;
813 if (time_mode == 3) time_mode = CLOCK;
814 time_update();
815 draw_dockapp();
816 } else {
817 switch_light();
819 break;
820 case 2:
821 if (state & ControlMask) {
822 my_system("wmclockmon-config -f", config_file);
823 } else {
824 rotate_style();
825 load_style(style_name);
826 graphics_init();
827 draw_dockapp();
829 break;
830 case 3:
831 if (state & ControlMask) {
832 my_system("wmclockmon-cal", NULL);
833 } else {
834 switch_authorized = !switch_authorized;
836 break;
837 default: break;
842 static void draw_dockapp(void) {
843 /* all clear */
844 draw_background(backlight);
846 /* draw digit */
847 if (time_mode == INTERNET) {
848 draw_itimedigit();
849 } else if (time_mode == BINARY) {
850 draw_binarytime();
851 } else {
852 draw_timedigit();
853 draw_datedigit();
856 /* show */
857 dockapp_copy2window(pixmap);
861 /* called by timer */
862 static void update(void) {
863 static Light pre_backlight;
864 static Bool in_alarm_mode = False;
866 /* check config_file modifications */
867 if (load_cfgfile()) graphics_init();
869 /* get current time */
870 time_update();
872 /* alarm mode */
873 if (raise_alarm()) {
874 if (!in_alarm_mode) {
875 in_alarm_mode = True;
876 my_system(command, NULL);
877 if (message) my_system(msgcmd, message);
878 pre_backlight = backlight;
880 if ( (switch_authorized) ||
881 ( (switch_authorized) && (backlight != pre_backlight) ) ) {
882 switch_light();
883 return;
885 } else {
886 if (in_alarm_mode) {
887 in_alarm_mode = False;
888 if (backlight != pre_backlight) {
889 switch_light();
890 return;
894 draw_dockapp();
898 /* called when mouse button pressed */
899 static void switch_light(void) {
900 switch (backlight) {
901 case LIGHTOFF: backlight = LIGHTON; break;
902 case LIGHTON: backlight = LIGHTOFF; break;
904 /* redraw digit */
905 time_update();
906 draw_dockapp();
910 static void draw_background(Light back) {
911 if (back == LIGHTON) {
912 if (time_mode == INTERNET)
913 dockapp_copyarea(backdropI_on, pixmap, 0, 0, 58, 58, 0, 0);
914 else if (time_mode == BINARY)
915 dockapp_copyarea(backdropB_on, pixmap, 0, 0, 58, 58, 0, 0);
916 else
917 dockapp_copyarea(backdrop_on, pixmap, 0, 0, 58, 58, 0, 0);
918 } else {
919 if (time_mode == INTERNET)
920 dockapp_copyarea(backdropI_off, pixmap, 0, 0, 58, 58, 0, 0);
921 else if (time_mode == BINARY)
922 dockapp_copyarea(backdropB_off, pixmap, 0, 0, 58, 58, 0, 0);
923 else
924 dockapp_copyarea(backdrop_off, pixmap, 0, 0, 58, 58, 0, 0);
929 static void draw_bigdigit(int num, int x, int y) {
930 int dy = 0, w = style.bdigitw, h = style.bdigith;
931 int incr = style.bdigitw + style.bsep;
933 if (num < 0) num = 0;
934 if (backlight == LIGHTON) dy = style.bdigith;
936 /* draw digit */
937 dockapp_copyarea(parts, pixmap, (num % 10) * w, dy, w, h, x + incr, y);
938 dockapp_copyarea(parts, pixmap, (num / 10) * w, dy, w, h, x, y);
942 static void draw_smalldigit(int num, int x, int y) {
943 int dx = 0, w = style.sdigitw, h = style.sdigith;
944 int incr = style.sdigitw + style.bsep;
945 int dy = 2 * style.bdigith;
947 if (num < 0) num = 0;
948 if (backlight == LIGHTON) dx = style.sdigitw * 10;
950 /* draw digit */
951 dockapp_copyarea(parts, pixmap, (num % 10) * w + dx, dy, w, h, x + incr, y);
952 dockapp_copyarea(parts, pixmap, (num / 10) * w + dx, dy, w, h, x, y);
956 static char equiv(char letter) {
957 int i, sign, oldsign = 0;
958 char letr[2], upcase, ret = 0;
960 upcase = toupper(letter);
961 letr[0] = upcase; letr[1] = 0;
962 if (! isupper(upcase)) return letter;
963 for (i = 0 ; upcases[i] ; i++) {
964 sign = strcoll(letr, upcases[i]);
965 if ((oldsign > 0) && (sign < 0)) ret = upcases[i-1][0];
966 oldsign = sign;
968 if (ret == 0) ret = upcase;
969 return ret;
973 static void draw_textdigit(const char *text, int x, int y) {
974 int i, dy = style.letterh, incr = style.letterw + style.lsep;
975 int w = style.letterw, h = style.letterh;
977 if (backlight == LIGHTON) dy = style.letterh * 2;
978 for (i = 0 ; text[i] ; i ++) {
979 int pos = equiv(text[i]) - 'A';
980 dockapp_copyarea(letters, pixmap, pos * w, dy, w, h, x + i * incr, y);
985 static void draw_timedigit(void) {
986 int hour = timeinfos->tm_hour, dx = 0, dy = 0;
988 if (backlight == LIGHTON) {
989 dx = style.sdigitw * 10; /* AM/PM/ALRM parts positions */
990 dy = style.bdigith; /* colon seconds */
993 if (h12) {
994 if (hour == 0)
995 hour = 12;
996 else
997 hour = (hour > 12) ? hour - 12 : hour;
1000 if (style.hbig)
1001 draw_bigdigit(hour, style.hposx, style.hposy);
1002 else
1003 draw_smalldigit(hour, style.hposx, style.hposy);
1005 if (style.mbig)
1006 draw_bigdigit(timeinfos->tm_min, style.mposx, style.mposy);
1007 else
1008 draw_smalldigit(timeinfos->tm_min, style.mposx, style.mposy);
1010 if (style.csec) {
1011 if (timeinfos->tm_sec % 2 == 1) {
1012 if (style.sbig) {
1013 dockapp_copyarea(parts, pixmap, 4, dy, 2, 2, style.sposx, style.sposy);
1014 dockapp_copyarea(parts, pixmap, 4, dy, 2, 2, style.sposx, style.sposy + 9);
1015 } else {
1016 dockapp_copyarea(parts, pixmap, 4, dy, 1, 1, style.sposx, style.sposy);
1017 dockapp_copyarea(parts, pixmap, 4, dy, 1, 1, style.sposx, style.sposy + 4);
1020 } else {
1021 if (style.sbig)
1022 draw_bigdigit(timeinfos->tm_sec, style.sposx, style.sposy);
1023 else
1024 draw_smalldigit(timeinfos->tm_sec, style.sposx, style.sposy);
1027 if (h12) {
1028 /* Some say pm is when h>12 or h==0 but others (and my watch) told me
1029 * that the good way to handle am/pm is what is below
1031 if (timeinfos->tm_hour >= 12) /* PM */
1032 dockapp_copyarea(parts, pixmap, 36 + dx, 49, 12, 7, style.pposx, style.pposy);
1033 else
1034 dockapp_copyarea(parts, pixmap, 23 + dx, 49, 12, 7, style.aposx, style.aposy);
1036 if (alarms_on(alarms))
1037 dockapp_copyarea(parts, pixmap, dx, 49, 22, 7, style.lposx, style.lposy);
1041 static void draw_datedigit(void) {
1042 char text[5];
1044 if (label) {
1045 draw_textdigit(label, style.wposx, style.wposy);
1046 } else {
1047 strftime(text, 4, "%a", timeinfos);
1048 draw_textdigit(text, style.wposx, style.wposy);
1049 draw_smalldigit(timeinfos->tm_mday, style.dposx, style.dposy);
1050 strftime(text, 4, "%b", timeinfos);
1051 draw_textdigit(text, style.oposx, style.oposy);
1056 static void draw_itimedigit(void) {
1057 int dx = 0, dy = 0, v1, v10, v100, v0, v00, nb, pc;
1058 int bw = style.bdigitw, bh = style.bdigith;
1059 int binc = style.bdigitw + 2;
1060 int tw = style.sdigitw, th = style.bdigith;
1061 int xd = 100;
1063 if (backlight == LIGHTON) {
1064 dx = 50;
1065 dy = 20;
1066 xd = 102;
1069 /* use floor(3) */
1070 v100 = swtime / 100.0;
1071 v10 = (swtime - v100 * 100) / 10.0;
1072 v1 = (swtime - v100 * 100.0 - v10 * 10.0);
1073 v0 = (swtime - v100 * 100.0 - v10 * 10.0 - v1) * 10;
1074 v00 = (swtime - v100 * 100.0 - v10 * 10.0 - v1) * 1000;
1075 pc = v00 - v0 * 100;
1077 /* draw main beats digit */
1078 if (!style.bbig) {
1079 bw = tw;
1080 bh = th;
1082 dockapp_copyarea(parts, pixmap, v1 * 10, dy, bw, bh, style.bposx + 2 * binc, style.bposy);
1083 dockapp_copyarea(parts, pixmap, v10 * 10, dy, bw, bh, style.bposx + binc, style.bposy);
1084 dockapp_copyarea(parts, pixmap, v100 * 10, dy, bw, bh, style.bposx, style.bposy);
1086 /* draw 10th of beats */
1087 dockapp_copyarea(parts, pixmap, v0 * 5 + dx, 40, 5, 9, 49, 28);
1089 /* graph */
1090 for (nb = 0 ; nb < pc / 6.25 ; nb++)
1091 dockapp_copyarea(parts, pixmap, xd, 0, 2, 9, 6 + nb * 3, 45);
1095 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) {
1096 int v = num;
1097 int dx = u * (sx + d2x) + d1x;
1098 int dy = sy + d1y;
1099 int b[4], i = 0;
1100 int bx = 0;
1101 int by = 2 * style.bdigith + style.sdigith + 7 + d;
1103 if (num == 0) return;
1104 if (backlight == LIGHTON) bx = 50;
1106 b[3] = 0; b[2] = 0; b[1] = 0; b[0] = 0;
1107 while (v != 0) {
1108 if (i == 4) {
1109 fprintf(stderr, "Error : num is too big (%d)\n", num);
1110 exit(1);
1112 b[i] = v % 2;
1113 v = v / 2;
1114 i++;
1116 for (i = 0 ; i < 4 ; i++) {
1117 if (b[i]) {
1118 int px = x + dx;
1119 int py = y + (3 - i) * dy + d2y;
1120 dockapp_copyarea(parts, pixmap, bx, by, sx, sy, px, py);
1126 static void draw_binarytime(void) {
1127 int sx = style.binzx;
1128 int sy = style.binzy;
1129 int d1x = style.bind1x;
1130 int d1y = style.bind1y;
1131 int d2x = style.bind2x;
1132 int d2y = style.bind2y;
1134 draw_bits(timeinfos->tm_hour / 10, style.binhx, style.binhy, 0, 0,
1135 sx, sy, d1x, d1y, d2x, d2y);
1136 draw_bits(timeinfos->tm_hour % 10, style.binhx, style.binhy, 1, 0,
1137 sx, sy, d1x, d1y, d2x, d2y);
1139 draw_bits(timeinfos->tm_min / 10, style.binmx, style.binmy, 0, 0,
1140 sx, sy, d1x, d1y, d2x, d2y);
1141 draw_bits(timeinfos->tm_min % 10, style.binmx, style.binmy, 1, 0,
1142 sx, sy, d1x, d1y, d2x, d2y);
1144 draw_bits(timeinfos->tm_sec / 10, style.binsx, style.binsy, 0, 0,
1145 sx, sy, d1x, d1y, d2x, d2y);
1146 draw_bits(timeinfos->tm_sec % 10, style.binsx, style.binsy, 1, 0,
1147 sx, sy, d1x, d1y, d2x, d2y);
1149 if (style.binix != -1) {
1150 int wd = timeinfos->tm_wday == 0 ? 7 : timeinfos->tm_wday;
1152 sx = style.binix;
1153 sy = style.biniy;
1154 d1x = style.bind3x;
1155 d1y = style.bind3y;
1156 d2x = style.bind4x;
1157 d2y = style.bind4y;
1159 draw_bits(wd, style.binwx, style.binwy, 0, 1,
1160 sx, sy, d1x, d1y, d2x, d2y);
1162 draw_bits(timeinfos->tm_mday / 10, style.bindx, style.bindy, 0, 1,
1163 sx, sy, d1x, d1y, d2x, d2y);
1164 draw_bits(timeinfos->tm_mday % 10, style.bindx, style.bindy, 1, 1,
1165 sx, sy, d1x, d1y, d2x, d2y);
1167 draw_bits((timeinfos->tm_mon + 1) / 10, style.binox, style.binoy, 0, 1,
1168 sx, sy, d1x, d1y, d2x, d2y);
1169 draw_bits((timeinfos->tm_mon + 1) % 10, style.binox, style.binoy, 1, 1,
1170 sx, sy, d1x, d1y, d2x, d2y);
1175 static void parse_arguments(int argc, char **argv) {
1176 int i, integer;
1177 for (i = 1; i < argc; i++) {
1178 if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "-h")) {
1179 print_help(argv[0]), exit(0);
1180 } else if (!strcmp(argv[i], "--version") || !strcmp(argv[i], "-v")) {
1181 printf("%s version %s\n", PACKAGE, VERSION), exit(0);
1182 } else if (!strcmp(argv[i], "--display") || !strcmp(argv[i], "-d")) {
1183 display_name = argv[i + 1];
1184 i++;
1185 } else if (!strcmp(argv[i], "--backlight") || !strcmp(argv[i], "-bl")) {
1186 backlight = LIGHTON;
1187 } else if (!strcmp(argv[i], "--light-color") || !strcmp(argv[i], "-lc")) {
1188 if (argc == i + 1)
1189 fprintf(stderr, "%s: argument \"%s\" needs an option.\n", argv[0], argv[i]), exit(1);
1190 SET_STRING(light_color, argv[i + 1]);
1191 i++;
1192 } else if (!strcmp(argv[i], "--windowed") || !strcmp(argv[i], "-w")) {
1193 dockapp_iswindowed = True;
1194 } else if (!strcmp(argv[i], "--broken-wm") || !strcmp(argv[i], "-bw")) {
1195 dockapp_isbrokenwm = True;
1196 } else if (!strcmp(argv[i], "--no-blink") || !strcmp(argv[i], "-nb")) {
1197 switch_authorized = False;
1198 } else if (!strcmp(argv[i], "--h12") || !strcmp(argv[i], "-12")) {
1199 h12 = True;
1200 } else if (!strcmp(argv[i], "--time-mode") || !strcmp(argv[i], "-tm")) {
1201 if (argc == i + 1)
1202 fprintf(stderr, "%s: error parsing argument for option %s\n",
1203 argv[0], argv[i]), exit(1);
1204 if (sscanf(argv[i + 1], "%i", &integer) != 1)
1205 fprintf(stderr, "%s: error parsing argument for option %s\n",
1206 argv[0], argv[i]), exit(1);
1207 if ((integer < 0) || (integer > 2))
1208 fprintf(stderr, "%s: argument %s must be in [0,2]\n",
1209 argv[0], argv[i]), exit(1);
1210 time_mode = integer;
1211 i++;
1212 } else if (!strcmp(argv[i], "--alarm-cmd") || !strcmp(argv[i], "-c")) {
1213 SET_STRING(command, argv[i + 1]);
1214 i++;
1215 } else if (!strcmp(argv[i], "--message-cmd") || !strcmp(argv[i], "-mc")) {
1216 SET_STRING(msgcmd, argv[i + 1]);
1217 i++;
1218 } else if (!strcmp(argv[i], "--alarm") || !strcmp(argv[i], "-a")) {
1219 alrm_add(&alarms, argv[i + 1]);
1220 i++;
1221 } else if (!strcmp(argv[i], "--cfg-file") || !strcmp(argv[i], "-f")) {
1222 SET_STRING(config_file, argv[i + 1]);
1223 i++;
1224 } else if (!strcmp(argv[i], "--no-locale") || !strcmp(argv[i], "-nl")) {
1225 use_locale = False;
1226 } else if (!strcmp(argv[i], "--style") || !strcmp(argv[i], "-s")) {
1227 SET_STRING(style_name, argv[i + 1]);
1228 i++;
1229 } else if (!strcmp(argv[i], "--style-dir") || !strcmp(argv[i], "-sd")) {
1230 SET_STRING(style_dir, argv[i + 1]);
1231 i++;
1232 } else if (!strcmp(argv[i], "--label") || !strcmp(argv[i], "-l")) {
1233 SET_STRING(label, argv[i + 1]);
1234 i++;
1235 } else if (!strcmp(argv[i], "--show-cal") || !strcmp(argv[i], "-sc")) {
1236 showcal = True;
1237 } else if (!strcmp(argv[i], "--cal-alrm") || !strcmp(argv[i], "-ca")) {
1238 calalrms = True;
1239 load_calalrms();
1240 } else {
1241 fprintf(stderr, "%s: unrecognized option '%s'\n", argv[0], argv[i]);
1242 print_help(argv[0]), exit(1);
1248 static void print_help(char *prog)
1250 printf("Usage : %s [OPTIONS]\n"
1251 "%s - Window Maker digital clock dockapp\n"
1252 " -h, --help show this help text and exit\n"
1253 " -v, --version show program version and exit\n"
1254 " -d, --display <string> display to use\n"
1255 " -bl, --backlight turn on back-light\n"
1256 " -lc, --light-color <color> backlight/led colour\n"
1257 " -tm, --time-mode <mode> start with time mode (0: clock, 1: internet time, 2: binary clock)\n"
1258 " -w, --windowed run the application in windowed mode\n"
1259 " -bw, --broken-wm activate broken window manager fix\n"
1260 " -a, --alarm <HH:MM> set alarm time to HH:MM (24h clock mode)\n"
1261 " -c, --alarm-cmd <string> command to launch when alarm raises\n"
1262 " -mc, --message-cmd <string> command to launch when alarm raises and a message is associated\n"
1263 " -12, --h12 12 hours clock mode (default is 24)\n"
1264 " -s, --style <file> style to use for display\n"
1265 " -sd, --style-dir <dir> directory where styles are stored\n"
1266 " -nb, --no-blink disable blinking when alarm is raised\n"
1267 " -f, --cfgfile <filename> use 'filename' as configuration file\n"
1268 " -nl, --no-locale don't use current locale\n"
1269 " -l, --label <string> use a label instead of date\n"
1270 " -ca, --cal-alrm load calendar alarms for today\n"
1271 " -sc, --show-cal show calendar at startup/00:00\n",
1272 prog, prog);
1276 static void time_update(void) {
1277 time_t tnow;
1279 time(&tnow);
1280 timeinfos = localtime(&tnow);
1282 if (time_mode == INTERNET) {
1283 long localtmzone;
1284 swtime = timeinfos->tm_hour * 3600
1285 + timeinfos->tm_min * 60
1286 + timeinfos->tm_sec;
1287 #ifdef BSDTIMEZONE
1288 localtmzone = timeinfos->tm_gmtoff;
1289 #else
1290 localtmzone = timezone;
1291 #endif
1292 swtime += localtmzone+3600;
1293 if (timeinfos->tm_isdst) swtime -= 3600;
1294 swtime *= 1000;
1295 swtime /= 86400;
1296 if (swtime >= 1000)
1297 swtime -= 1000;
1298 else
1299 if (swtime < 0) swtime += 1000;
1304 static Bool raise_alarm(void) {
1305 if ((timeinfos->tm_hour == 0) &&
1306 (timeinfos->tm_min == 0) &&
1307 (timeinfos->tm_sec == 0)) {
1308 if (showcal) show_cal();
1309 if (calalrms) reload_alarms();
1311 if (alarms) {
1312 Alarm *alrm = alarms;
1313 char thistime[MAXSTRLEN + 1];
1314 char thisdate[MAXSTRLEN + 1];
1316 strftime(thistime, MAXSTRLEN, "%H:%M", timeinfos);
1317 strftime(thisdate, MAXSTRLEN, "%u", timeinfos);
1318 while (alrm) {
1319 if (alrm->on && (strcmp(thistime, alrm->alarm_time) == 0)) {
1320 message = alrm->message;
1321 if (alrm->alarm_date) {
1322 if (strcmp(thisdate, alrm->alarm_date) == 0)
1323 return True;
1324 else
1325 message = NULL;
1326 } else {
1327 return True;
1330 alrm = alrm->next;
1333 return False;
1337 static Bool filestat(const char *filename, time_t *time, int mode) {
1338 struct stat s;
1339 time_t t = *time;
1341 if (stat(filename, &s) == -1) {
1342 if (*time == 0) return False;
1343 return True;
1345 switch (mode) {
1346 case MTIME: t = s.st_mtime; break;
1347 case ATIME: t = s.st_atime; break;
1348 default: break;
1350 if (t == *time) {
1351 return False;
1352 } else {
1353 *time = t;
1354 return True;
1359 static int my_system(char *cmd, char *opt) {
1360 int pid;
1361 extern char **environ;
1363 if (cmd == NULL) return 1;
1364 pid = fork();
1365 if (pid == -1) return -1;
1366 if (pid == 0) {
1367 pid = fork();
1368 if (pid == 0) {
1369 char *argv[4];
1370 char *thiscommand = xmalloc(strlen(cmd)
1371 + (opt ? strlen(opt) + 4 : 1));
1372 sprintf(thiscommand, "%s %s%s%s", cmd,
1373 opt ? "\"" : "",
1374 opt ? opt : "",
1375 opt ? "\"" : "");
1376 argv[0] = "sh";
1377 argv[1] = "-c";
1378 argv[2] = thiscommand;
1379 argv[3] = 0;
1380 execve("/bin/sh", argv, environ);
1381 FREE(thiscommand);
1382 exit(0);
1384 exit(0);
1386 return 0;
1390 void *xmalloc(size_t size) {
1391 void *ret = malloc(size);
1392 if (ret == NULL) {
1393 perror("malloc() ");
1394 exit(-1);
1395 } else
1396 return ret;
1400 char *xstrdup(const char *string) {
1401 char *ret = string ? strdup(string) : NULL;
1402 if (string && (ret == NULL)) {
1403 perror("strdup() ");
1404 exit(-1);
1405 } else
1406 return ret;
1410 static void put_alrm(Alarm **list, const char *entry,
1411 char *time, char *date, char *ison, char *mesg, Bool cal) {
1412 Alarm *lst = *list;
1413 Bool ok = True;
1415 if (! lst) {
1416 lst = xmalloc(sizeof(Alarm));
1417 *list = lst;
1418 } else {
1419 if (strcmp(entry, lst->entry) == 0) ok = False;
1420 while ( (lst->next) && ok) {
1421 lst = lst->next;
1422 if (strcmp(entry, lst->entry) == 0) ok = False;
1424 if (! ok) return;
1425 lst->next = xmalloc(sizeof(Alarm));
1426 lst = lst->next;
1428 lst->entry = xstrdup(entry);
1429 lst->alarm_time = time ? xstrdup(time) : NULL;
1430 lst->alarm_date = date ? xstrdup(date) : NULL;
1431 lst->on = ison ? getbool(ison) : True;
1432 lst->message = mesg ? xstrdup(mesg) : NULL;
1433 lst->cal = cal;
1434 lst->next = NULL;
1438 static void alrm_add(Alarm **list, const char *value) {
1439 char *time = NULL, *date = NULL, *ison = NULL, *mesg = NULL, *at;
1440 char *tokstr = xstrdup(value);
1441 char *toksav = tokstr;
1443 if (! value) return;
1444 at = strchr(value, '@');
1445 if (at) ison = strtok(tokstr, "@");
1446 time = strtok(at ? NULL : tokstr, "-.");
1447 if (strchr(value, '-')) date = strtok(NULL, ".");
1448 mesg = strtok(NULL, "\n\0");
1450 put_alrm(list, value, time, date, ison, mesg, False);
1452 FREE(toksav);
1456 static void free_alrm(Alarm **list) {
1457 Alarm *lst = *list, *next;
1458 while (lst) {
1459 next = lst->next;
1460 FREE(lst->entry);
1461 FREE(lst->alarm_time);
1462 FREE(lst->alarm_date);
1463 FREE(lst->message);
1464 free(lst);
1465 lst = next;
1467 *list = NULL;
1471 static Bool alarms_on(Alarm *list) {
1472 Alarm *alrm = list;
1474 while (alrm) {
1475 if (alrm->on) return True;
1476 alrm = alrm->next;
1478 return False;
1482 static void switch_alarms(Alarm *list) {
1483 Alarm *alrm = list;
1484 Bool set_to = True;
1486 if (alarms_on(list)) set_to = False;
1487 while (alrm) {
1488 char *tokstr = xstrdup(alrm->entry);
1489 char *toksav = tokstr;
1490 Bool is_on = alrm->cal || getbool(strtok(tokstr, "@"));
1492 if ((is_on && set_to) || (!set_to)) {
1493 alrm->on = set_to;
1495 alrm = alrm->next;
1496 FREE(toksav);
1501 static Bool getbool(char *value) {
1502 int i;
1503 for (i = 0 ; value[i] ; i++) value[i] = tolower(value[i]);
1504 if (strcmp(value, "0") == 0) return False;
1505 if (strcmp(value, "1") == 0) return True;
1506 if (strcmp(value, "true") == 0) return True;
1507 if (strcmp(value, "false") == 0) return False;
1508 if (strcmp(value, "yes") == 0) return True;
1509 if (strcmp(value, "no") == 0) return False;
1510 if (strcmp(value, "on") == 0) return True;
1511 if (strcmp(value, "off") == 0) return False;
1512 printf("Error in converting \"%s\" to boolean value.\n", value);
1513 return False;
1517 static Bool load_cfgfile(void) {
1518 FILE *file;
1519 int i = 0, ok = True;
1520 char line[MAXSTRLEN + 1];
1521 char *value;
1523 if ((file = fopen(config_file, "r")) == NULL) {
1524 if (strstr(config_file, "/"DEFAULT_CFGFILE) == NULL)
1525 printf("Unable to open configuration file \"%s\".\n", config_file);
1526 ok = False;
1528 if (ok && (! filestat(config_file, &config_mtime, MTIME))) {
1529 fclose(file);
1530 ok = False;
1532 if (ok) {
1533 if (alarms) free_alrm(&alarms);
1534 while (! feof(file)) {
1535 size_t n;
1537 if (!fgets(line, MAXSTRLEN, file))
1538 break;
1540 i++;
1541 n = strlen(line);
1542 if (n == 0)
1543 continue;
1544 if (line[n - 1] == '\n') line[n - 1] = 0;
1545 if ((line[0] == '#') || (line[0] == 0)) continue;
1546 value = strchr(line, '=');
1547 if (! value) continue;
1548 value++;
1549 while ((value[0] != 0) && (value[0] == ' ')) value++;
1550 if (value[0] == 0) continue;
1552 if (strncmp(line, "Backlight", 9) == 0) {
1553 backlight = getbool(value) ? LIGHTON : LIGHTOFF;
1554 } else if (strncmp(line, "Color", 5) == 0) {
1555 SET_STRING(light_color, value);
1556 } else if (strncmp(line, "Alarm", 5) == 0) {
1557 alrm_add(&alarms, value);
1558 } else if (strncmp(line, "Command", 7) == 0) {
1559 SET_STRING(command, value);
1560 } else if (strncmp(line, "MessageCmd", 10) == 0) {
1561 SET_STRING(msgcmd, value);
1562 } else if (strncmp(line, "Blink", 5) == 0) {
1563 switch_authorized = getbool(value);
1564 } else if (strncmp(line, "H12", 3) == 0) {
1565 h12 = getbool(value);
1566 } else if (strncmp(line, "Locale", 6) == 0) {
1567 use_locale = getbool(value);
1568 } else if (strncmp(line, "StyleDir", 8) == 0) {
1569 SET_STRING(style_dir, value);
1570 } else if (strncmp(line, "Style", 5) == 0) {
1571 SET_STRING(style_name, value);
1572 } else if (strncmp(line, "TimeMode", 5) == 0) {
1573 time_mode = atoi(value);
1574 } else if (strncmp(line, "ShowCal", 7) == 0) {
1575 showcal = getbool(value);
1576 } else if (strncmp(line, "CalAlrms", 8) == 0) {
1577 calalrms = getbool(value);
1578 } else {
1579 printf("Error in %s at line %d :\n[%s].\n", config_file, i, line);
1582 if (calalrms) load_calalrms();
1583 fclose(file);
1584 } else if (calalrms && cal_alrms_chg()) {
1585 reload_alarms();
1587 return ok;
1591 static char *get_calend_file(int type) {
1592 char *Home = robust_home();
1593 char *filename = xmalloc(
1594 strlen(Home) +
1595 strlen(DEFAULT_CONFIGDIR) +
1596 18);
1598 switch (type) {
1599 case 1:
1600 sprintf(filename, "%s/%s/%04d-%02d-%02d",
1601 Home,
1602 DEFAULT_CONFIGDIR,
1603 timeinfos->tm_year + 1900,
1604 timeinfos->tm_mon + 1,
1605 timeinfos->tm_mday);
1606 break;
1607 case 2:
1608 sprintf(filename, "%s/%s/XXXX-%02d-%02d",
1609 Home,
1610 DEFAULT_CONFIGDIR,
1611 timeinfos->tm_mon + 1,
1612 timeinfos->tm_mday);
1613 break;
1614 case 3:
1615 sprintf(filename, "%s/%s/XXXX-XX-%02d",
1616 Home,
1617 DEFAULT_CONFIGDIR,
1618 timeinfos->tm_mday);
1619 break;
1620 default: exit(1);
1622 return filename;
1626 static int cal_alrms_chg(void) {
1627 static time_t cal_u_mtime = 0;
1628 static time_t cal_y_mtime = 0;
1629 static time_t cal_m_mtime = 0;
1630 char *cal_u_fname = NULL;
1631 char *cal_y_fname = NULL;
1632 char *cal_m_fname = NULL;
1633 int chg_u, chg_y, chg_m;
1635 if (! calalrms) return False;
1636 cal_u_fname = get_calend_file(1);
1637 cal_y_fname = get_calend_file(2);
1638 cal_m_fname = get_calend_file(3);
1639 chg_u = filestat(cal_u_fname, &cal_u_mtime, MTIME);
1640 chg_y = filestat(cal_y_fname, &cal_y_mtime, MTIME);
1641 chg_m = filestat(cal_m_fname, &cal_m_mtime, MTIME);
1642 free(cal_u_fname);
1643 free(cal_y_fname);
1644 free(cal_m_fname);
1645 if (chg_u || chg_y || chg_m) return True;
1646 return False;
1650 static void load_cal_file(int type) {
1651 FILE *file;
1652 char *calend_file = get_calend_file(type);
1655 if ((file = fopen(calend_file, "r")) != NULL) {
1656 while (! feof(file)) {
1657 char line[MAXSTRLEN + 1];
1659 if (!fgets(line, MAXSTRLEN, file))
1660 break;
1662 if ( (line[0] != 0) && (strncmp(line, "@ ", 2) == 0) ) {
1663 char time[MAXSTRLEN + 1];
1664 sscanf(line, "@ %s ", time);
1665 put_alrm(&alarms, line, time, NULL, NULL, line + 8, True);
1668 fclose(file);
1670 free(calend_file);
1674 static void load_calalrms(void) {
1675 int i;
1676 for (i = 1 ; i < 4 ; i++) load_cal_file(i);
1680 static void reload_alarms(void) {
1681 FILE *file;
1682 char line[MAXSTRLEN + 1];
1683 char *value;
1685 if (alarms) free_alrm(&alarms);
1686 if ((file = fopen(config_file, "r")) == NULL) {
1687 if (strstr(config_file, "/"DEFAULT_CFGFILE) == NULL)
1688 printf("Unable to open configuration file \"%s\".\n", config_file);
1689 } else {
1690 while (! feof(file)) {
1691 size_t n;
1693 if (!fgets(line, MAXSTRLEN, file))
1694 break;
1696 n = strlen(line);
1697 if (n == 0)
1698 continue;
1699 if (line[n - 1] == '\n') line[n - 1] = 0;
1700 if ((line[0] == '#') || (line[0] == 0)) continue;
1701 value = strchr (line, '=') + 1;
1702 while ((value[0] == ' ') && (value[0] != 0)) value++;
1703 if (value[0] == 0) continue;
1705 if (strncmp(line, "Alarm", 5) == 0) alrm_add(&alarms, value);
1708 if (calalrms) load_calalrms();
1712 static void show_cal_file(int type) {
1713 FILE *file;
1714 char *data = NULL;
1715 char *tmp = NULL;
1716 char *calend_file = get_calend_file(type);
1718 if ((file = fopen(calend_file, "r")) != NULL) {
1719 while (! feof(file)) {
1720 char line[MAXSTRLEN + 1];
1722 if (!fgets(line, MAXSTRLEN, file))
1723 break;
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(void) {
1743 int i;
1744 for (i = 1 ; i < 4 ; i++) show_cal_file(i);
1748 static char *robust_home(void) {
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 (getpwuid(getuid()))
1756 return getpwuid(getuid())->pw_dir;
1757 else
1758 return "/";