wmtime: Update TODO.
[dockapps.git] / wmtime / wmtime.c
blobc925c6890c8ac3ff36bff75d99e9ac65c3b57644
1 /*
2 Code based on wmppp/wmifs
4 [Orig WMMON comments]
6 This code was mainly put together by looking at the
7 following programs:
9 asclock
10 A neat piece of equip, used to display the date
11 and time on the screen.
12 Comes with every AfterStep installation.
14 Source used:
15 How do I create a not so solid window?
16 How do I open a window?
17 How do I use pixmaps?
19 ------------------------------------------------------------
21 Author: Martijn Pieterse (pieterse@xs4all.nl)
23 This program is distributed under the GPL license.
24 (as were asclock and pppstats)
26 ----
27 Changes:
28 ----
29 15/07/2008 (Paul Harris, harris.pc@gmail.com)
30 * Minor changes to correct build warnings
31 09/10/2003 (Simon Law, sfllaw@debian.org)
32 * Add -geometry support
33 * Add -noseconds support
34 * Make the digital clock fill the space provided
35 * Eliminated exploitable static buffers
36 17/05/1998 (Antoine Nulle, warp@xs4all.nl)
37 * Updated version number and some other minor stuff
38 16/05/1998 (Antoine Nulle, warp@xs4all.nl)
39 * Added Locale support, based on original diff supplied
40 by Alen Salamun (snowman@hal9000.medinet.si)
41 04/05/1998 (Martijn Pieterse, pieterse@xs4all.nl)
42 * Moved the hands one pixel down.
43 * Removed the RedrawWindow out of the main loop
44 02/05/1998 (Martijn Pieterse, pieterse@xs4all.nl)
45 * Removed a lot of code that was in the wmgeneral dir.
46 02/05/1998 (Antoine Nulle, warp@xs4all.nl)
47 * Updated master-xpm, hour dots where a bit 'off'
48 30/04/1998 (Martijn Pieterse, pieterse@xs4all.nl)
49 * Added anti-aliased hands
50 23/04/1998 (Martijn Pieterse, pieterse@xs4all.nl)
51 * Changed the hand lengths.. again! ;)
52 * Zombies were created, so added wait code
53 21/04/1998 (Martijn Pieterse, pieterse@xs4all.nl)
54 * Added digital/analog switching support
55 18/04/1998 (Martijn Pieterse, pieterse@xs4all.nl)
56 * Started this project.
57 * Copied the source from wmmon.
60 #define _GNU_SOURCE
61 #include <X11/X.h> /* for ButtonPress, ButtonRelease, etc */
62 #include <X11/Xlib.h> /* for XEvent, XButtonEvent, etc */
63 #include <X11/xpm.h>
64 #include <ctype.h> /* for toupper */
65 #include <iconv.h> /* for iconv, iconv_close, etc */
66 #include <langinfo.h> /* for nl_langinfo, ABDAY_1, etc */
67 #include <locale.h> /* for NULL, setlocale, LC_ALL */
68 #include <math.h> /* for floor, cos, sin, M_PI */
69 #include <stddef.h> /* for size_t */
70 #include <stdio.h> /* for printf, asprintf, snprintf, etc */
71 #include <stdlib.h> /* for abs, free, exit, getenv */
72 #include <string.h> /* for strcmp, strdup, strncpy, etc */
73 #include <sys/wait.h> /* for waitpid, WNOHANG */
74 #include <time.h> /* for tm, time, localtime */
75 #include <unistd.h> /* for usleep */
76 #include "wmgeneral/misc.h" /* for execCommand */
77 #include "wmgeneral/wmgeneral.h" /* for copyXPMArea, RedrawWindow, etc */
78 #include "wmtime-mask.xbm" /* for wmtime_mask_bits */
79 #include "wmtime-master.xpm" /* for wmtime_master_xpm */
82 /***********/
83 /* Defines */
84 /***********/
86 const char* default_left_action = NULL;
87 const char* default_middle_action = NULL;
88 const char* default_right_action = NULL;
90 #define WMTIME_VERSION "1.2"
92 /********************/
93 /* Global Variables */
94 /********************/
96 int digital = 0;
97 int noseconds = 0;
98 char day_of_week[7][3] = { "SU", "MO", "TU", "WE", "TH", "FR", "SA" };
99 char mon_of_year[12][4] = { "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" };
100 XpmIcon wmgen;
101 char color[256];
103 /* functions */
104 void usage(char *);
105 void printversion(void);
107 void wmtime_routine(int, char **);
108 void get_lang();
111 int main(int argc, char *argv[]) {
113 int i;
114 char *name = argv[0];
115 char locale[256];
117 locale[0] = 0;
118 color[0] = 0;
120 for (i=1; i<argc; i++) {
121 char *arg = argv[i];
123 if (*arg=='-') {
124 switch (arg[1]) {
125 case 'c' :
126 if (argc > i+1) {
127 strcpy(color, argv[i+1]);
128 i++;
130 break;
131 case 'd' :
132 if (strcmp(arg+1, "display")
133 && strcmp(arg+1, "digital") && strcmp(arg+1, "d")) {
134 usage(name);
135 return 1;
137 if (!strcmp(arg+1, "digital") || !(strcmp(arg+1, "d")))
138 digital = 1;
139 break;
140 case 'g' :
141 if (strcmp(arg+1, "geometry")) {
142 usage(name);
143 return 1;
145 break;
146 case 'n' :
147 if (strcmp(arg+1, "noseconds") && strcmp(arg+1, "n")) {
148 usage(name);
149 return 1;
150 } else {
151 noseconds = 1;
153 break;
154 case 'v' :
155 printversion();
156 return 0;
157 case 'l':
158 if (argc > i+1) {
159 strcpy(locale, argv[i+1]);
160 i++;
162 break;
163 default:
164 usage(name);
165 return 1;
170 if (setlocale(LC_ALL, locale) == NULL)
171 fprintf(stderr,
172 "warning: locale '%s' not recognized; defaulting to '%s'.",
173 locale, setlocale(LC_ALL, NULL));
174 get_lang();
176 wmtime_routine(argc, argv);
177 return 0;
180 /************/
181 /* get_lang */
182 /************/
183 void get_lang(void)
185 char langbuf[10], outbuf[10];
186 char *inp, *outp;
187 iconv_t icd;
188 int i, ret;
189 size_t insize, outsize;
191 icd = iconv_open("ASCII//TRANSLIT", nl_langinfo(CODESET));
192 if (icd == (iconv_t) -1) {
193 perror("wmtime: Error allocating charset conversion descriptor");
194 return;
197 for (i = 0; i < 7; i++) {
198 strncpy(langbuf, nl_langinfo(ABDAY_1 + i), 10);
199 insize = outsize = 10;
200 inp = langbuf;
201 outp = outbuf;
202 do {
203 ret = iconv(icd, &inp, &insize, &outp, &outsize);
204 } while (outsize > 0 && ret > 0);
205 if (strstr(outbuf,"?") != NULL) return;
206 for (outp = outbuf, outsize = 0; *outp != 0 && outsize < 2;
207 outp++, outsize++)
208 day_of_week[i][outsize] = toupper(*outp);
210 for (i = 0; i < 12; i++) {
211 strncpy(langbuf, nl_langinfo(ABMON_1 + i), 10);
212 insize = outsize = 10;
213 inp = langbuf;
214 outp = outbuf;
215 do {
216 ret = iconv(icd, &inp, &insize, &outp, &outsize);
217 } while (outsize > 0 && ret > 0);
218 if (strstr(outbuf,"?") != NULL) return;
219 for (outp = outbuf, outsize = 0; *outp != 0 && outsize < 3;
220 outp++, outsize++)
221 mon_of_year[i][outsize] = toupper(*outp);
224 iconv_close(icd);
227 Pixel scale_pixel(Pixel pixel, float scale)
229 int red, green, blue;
231 red = pixel / ( 1 << 16 );
232 green = pixel % (1 << 16) / (1 << 8);
233 blue = pixel % (1 << 8);
235 red *= scale;
236 green *= scale;
237 blue *= scale;
239 return red * (1 << 16) + green * (1 << 8) + blue;
242 /*******************************************************************************\
243 |* wmtime_routine *|
244 \*******************************************************************************/
246 char *left_action = NULL;
247 char *right_action = NULL;
248 char *middle_action = NULL;
250 void DrawTime(int, int, int);
251 void DrawWijzer(int, int, int);
252 void DrawDate(int, int, int);
254 void wmtime_routine(int argc, char **argv) {
256 rckeys wmtime_keys[] = {
257 { "left", &left_action },
258 { "right", &right_action },
259 { "middle", &middle_action },
260 { NULL, NULL }
263 int i;
264 XEvent Event;
265 int but_stat = -1;
267 struct tm *time_struct;
269 long starttime;
270 long curtime;
272 char *conffile = NULL;
274 /* Scan through ~/.wmtimerc for the mouse button actions. */
275 if (default_left_action) left_action = strdup(default_left_action);
276 if (default_middle_action) middle_action = strdup(default_middle_action);
277 if (default_right_action) right_action = strdup(default_right_action);
279 /* Scan through the .rc files */
280 if (asprintf(&conffile, "/etc/wmtimerc") >= 0) {
281 parse_rcfile(conffile, wmtime_keys);
282 free(conffile);
285 if (asprintf(&conffile, "%s/.wmtimerc", getenv("HOME")) >= 0) {
286 parse_rcfile(conffile, wmtime_keys);
287 free(conffile);
290 if (asprintf(&conffile, "/etc/wmtimerc.fixed") >= 0) {
291 parse_rcfile(conffile, wmtime_keys);
292 free(conffile);
295 /* set user-defined colors */
296 if (color[0] != 0) {
297 Window Root;
298 XColor col;
299 XWindowAttributes attributes;
300 int screen;
301 Pixel pixel;
302 #define NUMSYMBOLS 10
303 XpmColorSymbol user_color[NUMSYMBOLS] = {
304 {NULL, "#2081B2CAAEBA", 0}, /* O */
305 {NULL, "#000049244103", 0}, /* + */
306 {NULL, "#00007DF771C6", 0}, /* @ */
307 {NULL, "#18618A288617", 0}, /* # */
308 {NULL, "#18619A699658", 0}, /* ; */
309 {NULL, "#0820861779E7", 0}, /* : */
310 {NULL, "#000071C66185", 0}, /* > */
311 {NULL, "#000061855144", 0}, /* , */
312 {NULL, "#00004D344103", 0}, /* < */
313 {NULL, "#10407DF779E7", 0} /* 1 */
317 /* code based on GetColor() from wmgeneral.c */
318 /* we need a temporary display to parse the color */
319 display = XOpenDisplay(NULL);
320 screen = DefaultScreen(display);
321 Root = RootWindow(display, screen);
322 XGetWindowAttributes(display, Root, &attributes);
324 col.pixel = 0;
325 if (!XParseColor(display, attributes.colormap, color, &col)) {
326 fprintf(stderr, "wmtime: can't parse %s.\n", color);
327 goto draw_window;
328 } else if (!XAllocColor(display, attributes.colormap, &col)) {
329 fprintf(stderr, "wmtime: can't allocate %s.\n", color);
330 goto draw_window;
333 pixel = col.pixel;
335 /* replace colors from wmtime-master.xpm */
336 user_color[0].pixel = pixel;
337 user_color[1].pixel = scale_pixel(pixel, .4);
338 user_color[2].pixel = scale_pixel(pixel, .7);
339 user_color[3].pixel = scale_pixel(pixel, .8);
340 user_color[4].pixel = scale_pixel(pixel, .9);
341 user_color[5].pixel = scale_pixel(pixel, .8);
342 user_color[6].pixel = scale_pixel(pixel, .6);
343 user_color[7].pixel = scale_pixel(pixel, .5);
344 user_color[8].pixel = scale_pixel(pixel, .4);
345 user_color[9].pixel = scale_pixel(pixel, .7);
347 wmgen.attributes.valuemask |= XpmColorSymbols;
348 wmgen.attributes.numsymbols = NUMSYMBOLS;
349 wmgen.attributes.colorsymbols = user_color;
351 XCloseDisplay(display);
354 draw_window:
355 openXwindow(argc, argv, wmtime_master_xpm, (char*)wmtime_mask_bits, 128, 64);
357 /* Mask out the right parts of the clock */
358 copyXPMArea(0, 0, 128, 64, 0, 98); /* Draw the borders */
359 copyXPMArea(0, 0, 64, 64, 64, 0); /* Draw the clock face */
360 copyXPMArea(64, 98, 64, 64, 0, 0); /* Draw the LCD background */
361 setMaskXY(0, 0);
363 /* add mouse region */
364 AddMouseRegion(0, 5, 48, 58, 60);
365 AddMouseRegion(1, 5, 5, 58, 46);
367 starttime = time(0);
369 curtime = time(0);
370 time_struct = localtime(&curtime);
372 while (1) {
373 curtime = time(0);
375 waitpid(0, NULL, WNOHANG);
377 time_struct = localtime(&curtime);
380 if (curtime >= starttime) {
381 if (!digital) {
382 /* Now to update the seconds */
384 DrawWijzer(time_struct->tm_hour, time_struct->tm_min, time_struct->tm_sec);
386 DrawDate(time_struct->tm_wday, time_struct->tm_mday, time_struct->tm_mon);
388 } else {
390 DrawTime(time_struct->tm_hour, time_struct->tm_min, time_struct->tm_sec);
392 DrawDate(time_struct->tm_wday, time_struct->tm_mday, time_struct->tm_mon);
394 RedrawWindow();
398 while (XPending(display)) {
399 XNextEvent(display, &Event);
400 switch (Event.type) {
401 case Expose:
402 RedrawWindow();
403 break;
404 case DestroyNotify:
405 XCloseDisplay(display);
406 exit(0);
407 break;
408 case ButtonPress:
409 but_stat = CheckMouseRegion(Event.xbutton.x, Event.xbutton.y);
410 break;
411 case ButtonRelease:
412 i = CheckMouseRegion(Event.xbutton.x, Event.xbutton.y);
413 if (but_stat == i && but_stat >= 0) {
414 switch (but_stat) {
415 case 0:
416 digital = 1-digital;
418 if (digital) {
419 copyXPMArea(64, 98, 64, 64, 0, 0);
420 DrawTime(time_struct->tm_hour, time_struct->tm_min, time_struct->tm_sec);
421 DrawDate(time_struct->tm_wday, time_struct->tm_mday, time_struct->tm_mon);
422 } else {
423 copyXPMArea(0, 98, 64, 64, 0, 0);
424 DrawWijzer(time_struct->tm_hour, time_struct->tm_min, time_struct->tm_sec);
425 DrawDate(time_struct->tm_wday, time_struct->tm_mday, time_struct->tm_mon);
427 RedrawWindow();
428 break;
429 case 1:
430 switch (Event.xbutton.button) {
431 case 1:
432 if (left_action)
433 execCommand(left_action);
434 break;
435 case 2:
436 if (middle_action)
437 execCommand(middle_action);
438 break;
439 case 3:
440 if (right_action)
441 execCommand(right_action);
442 break;
446 break;
450 /* Sleep 0.3 seconds */
451 usleep(300000L);
455 /*******************************************************************************\
456 |* DrawTime *|
457 \*******************************************************************************/
459 void DrawTime(int hr, int min, int sec) {
460 #define TIME_SIZE 16
461 char time[TIME_SIZE];
462 char *p = time;
463 int i,j,k=6;
464 int numfields;
466 /* 7x13 */
468 if (noseconds) {
469 snprintf(time, TIME_SIZE, "%02d:%02d ", hr, min);
470 numfields = 2;
472 else {
473 snprintf(time, TIME_SIZE, "%02d:%02d:%02d ", hr, min, sec);
474 numfields = 3;
477 for (i=0; i < numfields; i++) {
478 for (j=0; j<2; j++) {
479 copyXPMArea((*p-'0')*7 + 1, 84, 8, 13, k, 18);
480 k += 7;
481 p++;
483 if (*p == ':') {
484 copyXPMArea(71, 84, 5, 13, k, 18);
485 k += 4;
486 p++;
491 /*******************************************************************************\
492 |* DrawDate *|
493 \*******************************************************************************/
495 void DrawDate(int wkday, int dom, int month) {
496 #define DATE_SIZE 16
497 char date[DATE_SIZE];
498 char *p = date;
499 int i,k;
501 /* 7x13 */
503 snprintf(date, DATE_SIZE,
504 "%.2s%02d%.3s ", day_of_week[wkday], dom, mon_of_year[month]);
506 k = 5;
507 for (i=0; i<2; i++) {
508 if (*p < 'A')
509 copyXPMArea((*p-'0')*6, 64, 6, 9, k, 49);
510 else
511 copyXPMArea((*p-'A')*6, 74, 6, 9, k, 49);
512 k += 6;
513 p++;
515 k = 23;
516 for (i=0; i<2; i++) {
517 copyXPMArea((*p-'0')*6, 64, 6, 9, k, 49);
518 k += 6;
519 p++;
521 copyXPMArea(61, 64, 4, 9, k, 49);
522 k += 4;
523 for (i=0; i<3; i++) {
524 if (*p < 'A')
525 copyXPMArea((*p-'0')*6, 64, 6, 9, k, 49);
526 else
527 copyXPMArea((*p-'A')*6, 74, 6, 9, k, 49);
528 k += 6;
529 p++;
533 /*******************************************************************************\
534 |* DrawWijzer *|
535 \*******************************************************************************/
537 void DrawWijzer(int hr, int min, int sec) {
539 double psi;
540 int dx,dy;
541 int x,y;
542 int ddx,ddy;
543 int adder;
544 int k;
546 int i;
548 hr %= 12;
550 copyXPMArea(5+64, 5, 54, 40, 5, 5);
552 /**********************************************************************/
553 psi = hr * (M_PI / 6.0);
554 psi += min * (M_PI / 360);
556 dx = floor(sin(psi) * 22 * 0.7 + 0.5);
557 dy = floor(-cos(psi) * 16 * 0.7 + 0.5);
559 /* dx, dy is het punt waar we naar toe moeten.
560 * Zoek alle punten die ECHT op de lijn liggen: */
562 ddx = 1;
563 ddy = 1;
564 if (dx < 0) ddx = -1;
565 if (dy < 0) ddy = -1;
567 x = 0;
568 y = 0;
570 if (abs(dx) > abs(dy)) {
571 if (dy != 0)
572 adder = abs(dx) / 2;
573 else
574 adder = 0;
575 for (i=0; i<abs(dx); i++) {
576 /* laat de kleur afhangen van de adder.
577 * adder loopt van abs(dx) tot 0 */
579 k = 12 - adder / (abs(dx) / 12.0);
580 copyXPMArea(79+k, 67, 1, 1, x + 31, y + 24 - ddy);
582 copyXPMArea(79, 67, 1, 1, x + 31, y + 24);
584 k = 12-k;
585 copyXPMArea(79+k, 67, 1, 1, x + 31, y + 24 + ddy);
588 x += ddx;
590 adder -= abs(dy);
591 if (adder < 0) {
592 adder += abs(dx);
593 y += ddy;
596 } else {
597 if (dx != 0)
598 adder = abs(dy) / 2;
599 else
600 adder = 0;
601 for (i=0; i<abs(dy); i++) {
602 k = 12 - adder / (abs(dy) / 12.0);
603 copyXPMArea(79+k, 67, 1, 1, x + 31 - ddx, y + 24);
605 copyXPMArea(79, 67, 1, 1, x + 31, y + 24);
607 k = 12-k;
608 copyXPMArea(79+k, 67, 1, 1, x + 31 + ddx, y + 24);
610 y += ddy;
612 adder -= abs(dx);
613 if (adder < 0) {
614 adder += abs(dy);
615 x += ddx;
619 /**********************************************************************/
620 psi = min * (M_PI / 30.0);
621 psi += sec * (M_PI / 1800);
623 dx = floor(sin(psi) * 22 * 0.55 + 0.5);
624 dy = floor(-cos(psi) * 16 * 0.55 + 0.5);
626 /* dx, dy is het punt waar we naar toe moeten.
627 * Zoek alle punten die ECHT op de lijn liggen: */
629 dx += dx;
630 dy += dy;
632 ddx = 1;
633 ddy = 1;
634 if (dx < 0) ddx = -1;
635 if (dy < 0) ddy = -1;
637 x = 0;
638 y = 0;
640 if (abs(dx) > abs(dy)) {
641 if (dy != 0)
642 adder = abs(dx) / 2;
643 else
644 adder = 0;
645 for (i=0; i<abs(dx); i++) {
646 /* laat de kleur afhangen van de adder.
647 * adder loopt van abs(dx) tot 0 */
649 k = 12 - adder / (abs(dx) / 12.0);
650 copyXPMArea(79+k, 67, 1, 1, x + 31, y + 24 - ddy);
652 copyXPMArea(79, 67, 1, 1, x + 31, y + 24);
654 k = 12-k;
655 copyXPMArea(79+k, 67, 1, 1, x + 31, y + 24 + ddy);
658 x += ddx;
660 adder -= abs(dy);
661 if (adder < 0) {
662 adder += abs(dx);
663 y += ddy;
666 } else {
667 if (dx != 0)
668 adder = abs(dy) / 2;
669 else
670 adder = 0;
671 for (i=0; i<abs(dy); i++) {
672 k = 12 - adder / (abs(dy) / 12.0);
673 copyXPMArea(79+k, 67, 1, 1, x + 31 - ddx, y + 24);
675 copyXPMArea(79, 67, 1, 1, x + 31, y + 24);
677 k = 12-k;
678 copyXPMArea(79+k, 67, 1, 1, x + 31 + ddx, y + 24);
680 y += ddy;
682 adder -= abs(dx);
683 if (adder < 0) {
684 adder += abs(dy);
685 x += ddx;
689 /**********************************************************************/
690 if (noseconds)
691 return; /* Skip drawing the seconds. */
693 psi = sec * (M_PI / 30.0);
695 dx = floor(sin(psi) * 22 * 0.9 + 0.5);
696 dy = floor(-cos(psi) * 16 * 0.9 + 0.5);
698 /* dx, dy is het punt waar we naar toe moeten.
699 * Zoek alle punten die ECHT op de lijn liggen: */
701 ddx = 1;
702 ddy = 1;
703 if (dx < 0) ddx = -1;
704 if (dy < 0) ddy = -1;
706 if (dx == 0) ddx = 0;
707 if (dy == 0) ddy = 0;
709 x = 0;
710 y = 0;
713 if (abs(dx) > abs(dy)) {
714 if (dy != 0)
715 adder = abs(dx) / 2;
716 else
717 adder = 0;
718 for (i=0; i<abs(dx); i++) {
719 /* laat de kleur afhangen van de adder.
720 * adder loopt van abs(dx) tot 0 */
722 k = 12 - adder / (abs(dx) / 12.0);
723 copyXPMArea(79+k, 70, 1, 1, x + 31, y + 24 - ddy);
725 k = 12-k;
726 copyXPMArea(79+k, 70, 1, 1, x + 31, y + 24);
729 x += ddx;
731 adder -= abs(dy);
732 if (adder < 0) {
733 adder += abs(dx);
734 y += ddy;
737 } else {
738 if (dx != 0)
739 adder = abs(dy) / 2;
740 else
741 adder = 0;
742 for (i=0; i<abs(dy); i++) {
743 k = 12 - adder / (abs(dy) / 12.0);
744 copyXPMArea(79+k, 70, 1, 1, x + 31 - ddx, y + 24);
746 k = 12-k;
747 copyXPMArea(79+k, 70, 1, 1, x + 31, y + 24);
749 y += ddy;
751 adder -= abs(dx);
752 if (adder < 0) {
753 adder += abs(dy);
754 x += ddx;
760 /*******************************************************************************\
761 |* usage *|
762 \*******************************************************************************/
764 void usage(char *name) {
765 printf("Usage: %s [OPTION]...\n", name);
766 printf("WindowMaker dockapp that displays the time and date.\n");
767 printf("\n");
768 printf(" -d, -digital display the digital clock\n");
769 printf(" -display DISPLAY contact the DISPLAY X server\n");
770 printf(" -geometry GEOMETRY position the clock at GEOMETRY\n");
771 printf(" -n, -noseconds disables the second hand\n");
772 printf(" -l LOCALE set locale to LOCALE\n");
773 printf(" -h display this help and exit\n");
774 printf(" -v output version information and exit\n");
775 printf(" -c set color\n");
778 /*******************************************************************************\
779 |* printversion *|
780 \*******************************************************************************/
782 void printversion(void) {
783 printf("WMTime version %s\n", WMTIME_VERSION);
786 /* vim: sw=4 ts=4 columns=82