wmifs: Fix spelling error and escape hyphen in manpage.
[dockapps.git] / wmclock / wmclock.c
blob895b285f33580995d2b2072cae95f65005b41348
1 /* wmclock.c: a dockable clock applet for Window Maker
2 * created 1999-Apr-09 jmk
4 * by Jim Knoble <jmknoble@pobox.com>
5 * Copyright (C) 1999 Jim Knoble
7 * Significant portions of this software are derived from asclock by
8 * Beat Christen <spiff@longstreet.ch>. Such portions are copyright
9 * by Beat Christen and the other authors of asclock.
11 * Disclaimer:
13 * The software is provided "as is", without warranty of any kind,
14 * express or implied, including but not limited to the warranties of
15 * merchantability, fitness for a particular purpose and
16 * noninfringement. In no event shall the author(s) be liable for any
17 * claim, damages or other liability, whether in an action of
18 * contract, tort or otherwise, arising from, out of or in connection
19 * with the software or the use or other dealings in the software.
22 #include <sys/select.h>
23 #include <sys/types.h>
24 #include <sys/wait.h>
25 #include <sys/time.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <time.h>
30 #include <unistd.h>
31 #include <X11/Xatom.h>
32 #include <X11/Xlib.h>
33 #include <X11/xpm.h>
34 #include <X11/extensions/shape.h>
36 #include "dynlist.h"
38 /**********************************************************************/
39 #define ONLY_SHAPED_WINDOW 1
41 #define NUM_TIME_POSITIONS 5
42 #define NUM_X_POSITIONS 11
43 #define NUM_Y_POSITIONS 4
45 #define DIGIT_1_X_POS 0
46 #define DIGIT_2_X_POS 1
47 #define DIGIT_3_X_POS 3
48 #define DIGIT_4_X_POS 4
49 #define DIGIT_Y_POS 0
50 #define LED_NUM_Y_OFFSET 0
51 #define LED_THIN_1_X_OFFSET 13
52 #define LED_NUM_WIDTH 9
53 #define LED_NUM_HEIGHT 11
54 #define LED_THIN_1_WIDTH 5
56 #define COLON_X_POS 2
57 #define COLON_Y_POS DIGIT_Y_POS
58 #define COLON_X_OFFSET 90
59 #define COLON_Y_OFFSET 0
60 #define BLANK_X_OFFSET 119
61 #define BLANK_Y_OFFSET COLON_Y_OFFSET
62 #define COLON_WIDTH 3
63 #define COLON_HEIGHT 11
65 #define AMPM_X_POS 5
66 #define AM_X_OFFSET 94
67 #define AM_Y_OFFSET 5
68 #define PM_X_OFFSET 107
69 #define PM_Y_OFFSET 5
70 #define AM_WIDTH 12
71 #define AM_HEIGHT 6
72 #define PM_WIDTH 11
73 #define PM_HEIGHT 6
75 #define MONTH_X_POS 10
76 #define MONTH_Y_POS 3
77 #define MONTH_X_OFFSET 0
78 #define MONTH_WIDTH 22
79 #define MONTH_HEIGHT 6
81 #define DATE_LEFT_X_POS 7
82 #define DATE_CENTER_X_POS 8
83 #define DATE_RIGHT_X_POS 9
84 #define DATE_Y_POS 2
85 #define DATE_Y_OFFSET 0
86 #define DATE_NUM_WIDTH 9
87 #define DATE_NUM_HEIGHT 13
89 #define WEEKDAY_X_POS 6
90 #define WEEKDAY_Y_POS 1
91 #define WEEKDAY_X_OFFSET 0
92 #define WEEKDAY_WIDTH 21
93 #define WEEKDAY_HEIGHT 6
95 #define OUR_WINDOW_EVENTS (ExposureMask | ButtonPressMask | StructureNotifyMask)
97 #define LED_XPM_BRIGHT_LINE_INDEX 3
98 #define LED_XPM_BRIGHT_CHAR '+'
99 #define LED_XPM_DIM_LINE_INDEX 4
100 #define LED_XPM_DIM_CHAR '@'
102 #define DEFAULT_XPM_CLOSENESS 40000
104 #define DIM_NUMERATOR 5
105 #define DIM_DENOMINATOR 10
106 #define makeDimColor(c) (((c) * DIM_NUMERATOR) / DIM_DENOMINATOR)
108 /**********************************************************************/
109 #ifndef ONLY_SHAPED_WINDOW
110 # include "clk.xpm"
111 #endif /* !ONLY_SHAPED_WINDOW */
112 #include "month.xpm"
113 #include "weekday.xpm"
114 #include "xpm/date.xpm"
115 #include "xpm/led.xpm"
116 #include "xpm/mask.xbm"
117 #include "xpm/mask.xpm"
119 typedef struct _XpmIcon {
120 Pixmap pixmap;
121 Pixmap mask;
122 XpmAttributes attributes;
123 } XpmIcon;
125 void showUsage(void);
126 void showVersion(void);
127 int buildCommand(char *, char **, int *, int *);
128 void executeCommand(char *);
129 void showError(const char *, const char*);
130 void showFatalError(const char *, const char*);
131 void GetXpms(void);
132 int flushExposeEvents(Window);
133 void redrawWindow(XpmIcon *);
134 Pixel GetColor(const char *);
135 int mytime(void);
136 void showYear(void);
137 void showTime12(void);
138 void showTime24(void);
139 void showTime(void);
140 char* extractProgName(char *);
141 int processArgs(int, char **);
143 /**********************************************************************/
144 int enable12HourClock = 0; /* default value is 24h format */
145 int enableShapedWindow = 1; /* default value is noshape */
146 int enableBlinking = 1; /* default is blinking */
147 int startIconified = 0; /* default is not iconified */
148 int enableYearDisplay = 0; /* default is to show time, not year */
149 int blinkInterval = 2; /* default is a 2-second blink cycle */
151 int timePos12[NUM_TIME_POSITIONS] = { 5, 14, 24, 28, 37 };
152 int timePos24[NUM_TIME_POSITIONS] = { 4, 8, 17, 22, 31 };
153 /* with shape */
154 int xPosShaped[NUM_X_POSITIONS] = { 0, 0, 0, 0, 0, 40, 17, 17, 22, 27, 15 };
155 int yPosShaped[NUM_Y_POSITIONS] = { 3, 21, 30, 45 };
157 #ifndef ONLY_SHAPED_WINDOW
158 /* no shape */
159 int xPosUnshaped[NUM_X_POSITIONS] = { 5, 5, 5, 5, 5, 45, 21, 21, 26, 31, 19 };
160 int yPosUnshaped[NUM_Y_POSITIONS] = { 7, 25, 34, 49 };
161 #endif /* !ONLY_SHAPED_WINDOW */
163 int xPos[NUM_X_POSITIONS];
164 int yPos[NUM_Y_POSITIONS];
166 Display *dpy;
167 Window rootWindow;
168 int screen;
169 int xFd;
170 fd_set xFdSet;
171 int displayDepth;
172 XSizeHints sizeHints;
173 XWMHints wmHints;
174 Pixel bgPixel, fgPixel;
175 GC normalGC;
176 Window iconWin, win;
178 char *progName;
179 char *className = "WMClock";
180 char *geometry = "";
181 char *ledColor = "LightSeaGreen";
183 char *commandToExec = NULL;
184 char *commandBuf = NULL;
185 int commandLength = 0;
186 int commandIndex = 0;
188 char *errColorCells = "not enough free color cells or xpm not found\n";
190 char *userClockXpm;
191 char *userMonthXpm;
192 char *userWeekdayXpm;
193 int useUserClockXpm = 0;
194 int useUserMonthXpm = 0;
195 int useUserWeekdayXpm = 0;
197 XpmIcon clockBg, led, months, dateNums, weekdays;
198 XpmIcon visible;
200 time_t actualTime;
201 long actualMinutes;
203 static struct tm *localTime;
205 char *usageText[] = {
206 "Options:",
207 " -12 show 12-hour time (am/pm)",
208 " -24 show 24-hour time",
209 " -year show year instead of time",
210 " -noblink don't blink",
211 " -interval <seconds> set blink interval",
212 " -exe <command> start <command> on mouse click",
213 " -led <color> use <color> as color of led",
214 #ifndef ONLY_SHAPED_WINDOW
215 " -clockxpm <filename> get clock background from pixmap in <filename>",
216 #endif /* !ONLY_SHAPED_WINDOW */
217 " -monthxpm <filename> get month names from pixmap in <filename>",
218 " -weekdayxpm <filename> get weekday names from pixmap in <filename>",
219 " -version display the version",
220 NULL
223 char *version = VERSION;
225 /**********************************************************************/
226 /* Display usage information */
227 void showUsage(void)
229 char **cpp;
231 fprintf(stderr, "Usage: %s [option [option ...]]\n\n", progName);
232 for (cpp = usageText; *cpp; cpp++)
234 fprintf(stderr, "%s\n", *cpp);
236 fprintf(stderr,"\n");
237 exit(1);
240 /* Display the program version */
241 void showVersion()
243 fprintf(stderr, "%s version %s\n", progName, version);
244 exit(1);
247 /* Build the shell command to execute */
248 int buildCommand(char *command, char **buf, int *buf_len, int *i)
250 int status;
252 status = append_string_to_buf(buf, buf_len, i, command);
253 if (APPEND_FAILURE == status)
255 return (0);
257 status = append_string_to_buf(buf, buf_len, i, " &");
258 return ((APPEND_FAILURE == status) ? 0 : 1);
261 /* Execute the given shell command */
262 void executeCommand(char *command)
264 int status;
266 if (NULL == command)
268 return;
270 status = system(command);
272 if (-1 == status)
274 perror("system");
278 /* Display an error message */
279 void showError(const char *message, const char *data)
281 fprintf(stderr,"%s: can't %s %s\n", progName, message, data);
284 /* Display an error message and exit */
285 void showFatalError(const char *message, const char *data)
287 showError(message, data);
288 exit(1);
291 /* Konvertiere XPMIcons nach Pixmaps */
292 void GetXpms(void)
294 static char **clock_xpm;
295 XColor color;
296 XWindowAttributes attributes;
297 char ledBright[64];
298 char ledDim[64];
299 int status;
301 #ifdef ONLY_SHAPED_WINDOW
302 clock_xpm = mask_xpm;
303 #else /* !ONLY_SHAPED_WINDOW */
304 clock_xpm = enableShapedWindow ? mask_xpm : clk_xpm;
305 #endif /* ONLY_SHAPED_WINDOW */
307 /* for the colormap */
308 XGetWindowAttributes(dpy, rootWindow, &attributes);
310 /* get user-defined color */
311 if (!XParseColor(dpy, attributes.colormap, ledColor, &color))
313 showError("parse color", ledColor);
316 sprintf(ledBright, "%c c #%04X%04X%04X", LED_XPM_BRIGHT_CHAR,
317 color.red, color.green, color.blue);
318 led_xpm[LED_XPM_BRIGHT_LINE_INDEX] = &ledBright[0];
320 color.red = makeDimColor(color.red);
321 color.green = makeDimColor(color.green);
322 color.blue = makeDimColor(color.blue);
323 sprintf(&ledDim[0], "%c c #%04X%04X%04X", LED_XPM_DIM_CHAR,
324 color.red, color.green, color.blue);
325 led_xpm[LED_XPM_DIM_LINE_INDEX] = &ledDim[0];
327 clockBg.attributes.closeness = DEFAULT_XPM_CLOSENESS;
328 clockBg.attributes.valuemask |=
329 (XpmReturnPixels | XpmReturnExtensions | XpmCloseness);
331 if (useUserClockXpm)
333 status = XpmReadFileToPixmap(dpy, rootWindow, userClockXpm,
334 &clockBg.pixmap, &clockBg.mask,
335 &clockBg.attributes);
337 else
339 status = XpmCreatePixmapFromData(dpy, rootWindow, clock_xpm,
340 &clockBg.pixmap, &clockBg.mask,
341 &clockBg.attributes);
343 if (XpmSuccess != status)
345 showFatalError("create clock pixmap:", errColorCells);
348 #ifdef ONLY_SHAPED_WINDOW
349 visible.attributes.depth = displayDepth;
350 visible.attributes.width = clockBg.attributes.width;
351 visible.attributes.height = clockBg.attributes.height;
352 visible.pixmap = XCreatePixmap(dpy, rootWindow, visible.attributes.width,
353 visible.attributes.height,
354 visible.attributes.depth);
355 #else /* !ONLY_SHAPED_WINDOW */
356 visible.attributes.closeness = DEFAULT_XPM_CLOSENESS;
357 visible.attributes.valuemask |=
358 (XpmReturnPixels | XpmReturnExtensions | XpmCloseness);
359 status = XpmCreatePixmapFromData(dpy, rootWindow, clk_xpm,
360 &visible.pixmap, &visible.mask,
361 &visible.attributes);
362 #endif /* ONLY_SHAPED_WINDOW */
364 led.attributes.closeness = DEFAULT_XPM_CLOSENESS;
365 led.attributes.valuemask |=
366 (XpmReturnPixels | XpmReturnExtensions | XpmCloseness);
367 status = XpmCreatePixmapFromData(dpy, rootWindow, led_xpm,
368 &led.pixmap, &led.mask,
369 &led.attributes);
370 if (XpmSuccess != status)
372 showFatalError("create led pixmap:", errColorCells);
375 months.attributes.closeness = DEFAULT_XPM_CLOSENESS;
376 months.attributes.valuemask |=
377 (XpmReturnPixels | XpmReturnExtensions | XpmCloseness);
378 if (useUserMonthXpm)
380 status = XpmReadFileToPixmap(dpy, rootWindow, userMonthXpm,
381 &months.pixmap, &months.mask,
382 &months.attributes);
384 else
386 status = XpmCreatePixmapFromData(dpy, rootWindow, month_xpm,
387 &months.pixmap, &months.mask,
388 &months.attributes);
390 if (XpmSuccess != status)
392 showFatalError("create month pixmap:", errColorCells);
395 dateNums.attributes.closeness = DEFAULT_XPM_CLOSENESS;
396 dateNums.attributes.valuemask |=
397 (XpmReturnPixels | XpmReturnExtensions | XpmCloseness);
398 status = XpmCreatePixmapFromData(dpy, rootWindow, date_xpm,
399 &dateNums.pixmap, &dateNums.mask,
400 &dateNums.attributes);
401 if (XpmSuccess != status)
403 showFatalError("create date pixmap:", errColorCells);
406 weekdays.attributes.closeness = DEFAULT_XPM_CLOSENESS;
407 weekdays.attributes.valuemask |=
408 (XpmReturnPixels | XpmReturnExtensions | XpmCloseness);
409 if (useUserWeekdayXpm)
411 status = XpmReadFileToPixmap(dpy, rootWindow, userWeekdayXpm,
412 &weekdays.pixmap, &weekdays.mask,
413 &weekdays.attributes);
415 else
417 status = XpmCreatePixmapFromData(dpy, rootWindow, weekday_xpm,
418 &weekdays.pixmap, &weekdays.mask,
419 &weekdays.attributes);
421 if (XpmSuccess != status)
423 showFatalError("create weekday pixmap:", errColorCells);
427 /* Remove expose events for a specific window from the queue */
428 int flushExposeEvents(Window w)
430 XEvent dummy;
431 int i = 0;
433 while (XCheckTypedWindowEvent(dpy, w, Expose, &dummy))
435 i++;
437 return(i);
440 /* (Re-)Draw the main window and the icon window */
441 void redrawWindow(XpmIcon *v)
443 flushExposeEvents(iconWin);
444 XCopyArea(dpy, v->pixmap, iconWin, normalGC,
445 0, 0, v->attributes.width, v->attributes.height, 0, 0);
446 flushExposeEvents(win);
447 XCopyArea(dpy, v->pixmap, win, normalGC,
448 0, 0, v->attributes.width, v->attributes.height, 0, 0);
451 /* Get a Pixel for the given color name */
452 Pixel GetColor(const char *colorName)
454 XColor color;
455 XWindowAttributes attributes;
457 XGetWindowAttributes(dpy, rootWindow, &attributes);
458 color.pixel = 0;
459 if (!XParseColor(dpy, attributes.colormap, colorName, &color))
461 showError("parse color", colorName);
463 else if (!XAllocColor(dpy, attributes.colormap, &color))
465 showError("allocate color", colorName);
467 return(color.pixel);
470 /* Fetch the system time and time zone */
471 int mytime(void)
473 struct timeval tv;
474 struct timezone tz;
476 gettimeofday(&tv, &tz);
478 return(tv.tv_sec);
481 /* Display the current year in the LED display */
482 void showYear(void)
484 int year;
485 int digitXOffset;
486 int digitYOffset;
488 year = localTime->tm_year + 1900;
490 digitYOffset = LED_NUM_Y_OFFSET;
491 digitXOffset = LED_NUM_WIDTH * (year / 1000);
492 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
493 digitXOffset , digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
494 xPos[DIGIT_1_X_POS], yPos[DIGIT_Y_POS]);
495 digitXOffset = LED_NUM_WIDTH * ((year / 100) % 10);
496 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
497 digitXOffset , digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
498 xPos[DIGIT_2_X_POS], yPos[DIGIT_Y_POS]);
499 digitXOffset = LED_NUM_WIDTH * ((year / 10) % 10);
500 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
501 digitXOffset , digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
502 xPos[DIGIT_3_X_POS], yPos[DIGIT_Y_POS]);
503 digitXOffset = LED_NUM_WIDTH * (year % 10);
504 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
505 digitXOffset , digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
506 xPos[DIGIT_4_X_POS], yPos[DIGIT_Y_POS]);
509 /* Display time in twelve-hour mode, with am/pm indicator */
510 void showTime12(void)
512 int digitXOffset;
513 int digitYOffset;
514 int localHour = localTime->tm_hour % 12;
516 if (0 == localHour)
518 localHour = 12;
520 if (localTime->tm_hour < 12)
522 /* AM */
523 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
524 AM_X_OFFSET, AM_Y_OFFSET, AM_WIDTH, AM_HEIGHT,
525 xPos[AMPM_X_POS], yPos[DIGIT_Y_POS] + AM_Y_OFFSET);
527 else
529 /* PM */
530 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
531 PM_X_OFFSET, PM_Y_OFFSET, PM_WIDTH, PM_HEIGHT,
532 xPos[AMPM_X_POS], yPos[DIGIT_Y_POS] + PM_Y_OFFSET);
535 digitYOffset = LED_NUM_Y_OFFSET;
536 if (localHour > 9)
538 digitXOffset = LED_THIN_1_X_OFFSET;
539 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
540 digitXOffset, digitYOffset, LED_THIN_1_WIDTH, LED_NUM_HEIGHT,
541 xPos[DIGIT_1_X_POS], yPos[DIGIT_Y_POS]);
543 digitXOffset = LED_NUM_WIDTH * (localHour % 10);
544 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
545 digitXOffset, digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
546 xPos[DIGIT_2_X_POS], yPos[DIGIT_Y_POS]);
547 digitXOffset = LED_NUM_WIDTH * (localTime->tm_min / 10);
548 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
549 digitXOffset, digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
550 xPos[DIGIT_3_X_POS], yPos[DIGIT_Y_POS]);
551 digitXOffset = LED_NUM_WIDTH * (localTime->tm_min % 10);
552 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
553 digitXOffset, digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
554 xPos[DIGIT_4_X_POS], yPos[DIGIT_Y_POS]);
557 /* Display time in 24-hour mode, without am/pm indicator */
558 void showTime24(void)
560 int digitXOffset;
561 int digitYOffset;
563 digitYOffset = LED_NUM_Y_OFFSET;
564 digitXOffset = LED_NUM_WIDTH * (localTime->tm_hour / 10);
565 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
566 digitXOffset, digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
567 xPos[DIGIT_1_X_POS], yPos[DIGIT_Y_POS]);
568 digitXOffset = LED_NUM_WIDTH * (localTime->tm_hour % 10);
569 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
570 digitXOffset, digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
571 xPos[DIGIT_2_X_POS], yPos[DIGIT_Y_POS]);
572 digitXOffset = LED_NUM_WIDTH * (localTime->tm_min / 10);
573 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
574 digitXOffset, digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
575 xPos[DIGIT_3_X_POS], yPos[DIGIT_Y_POS]);
576 digitXOffset = LED_NUM_WIDTH * (localTime->tm_min % 10);
577 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
578 digitXOffset, digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
579 xPos[DIGIT_4_X_POS], yPos[DIGIT_Y_POS]);
582 void showTime(void)
584 int xOffset;
585 int yOffset;
587 /* Zeit auslesen */
588 actualTime = mytime();
589 actualMinutes = actualTime / 60;
591 localTime = localtime(&actualTime);
593 /* leere clock holen */
594 XCopyArea(dpy, clockBg.pixmap, visible.pixmap, normalGC,
595 0, 0, sizeHints.width, sizeHints.height, 0, 0);
597 if (enableYearDisplay)
599 showYear();
601 else if (enable12HourClock)
603 showTime12();
605 else
607 showTime24();
610 /* Monat */
611 xOffset = MONTH_X_OFFSET;
612 yOffset = MONTH_HEIGHT * (localTime->tm_mon);
613 XCopyArea(dpy, months.pixmap, visible.pixmap, normalGC,
614 xOffset, yOffset, MONTH_WIDTH, MONTH_HEIGHT,
615 xPos[MONTH_X_POS], yPos[MONTH_Y_POS]);
617 /* Datum */
618 yOffset = DATE_Y_OFFSET;
619 if (localTime->tm_mday > 9)
621 xOffset = DATE_NUM_WIDTH * (((localTime->tm_mday / 10) + 9) % 10);
622 XCopyArea(dpy, dateNums.pixmap, visible.pixmap, normalGC,
623 xOffset, yOffset, DATE_NUM_WIDTH, DATE_NUM_HEIGHT,
624 xPos[DATE_LEFT_X_POS], yPos[DATE_Y_POS]);
625 xOffset = DATE_NUM_WIDTH * (((localTime->tm_mday % 10) + 9) % 10);
626 XCopyArea(dpy, dateNums.pixmap, visible.pixmap, normalGC,
627 xOffset, yOffset, DATE_NUM_WIDTH, DATE_NUM_HEIGHT,
628 xPos[DATE_RIGHT_X_POS], yPos[DATE_Y_POS]);
630 else
632 xOffset = DATE_NUM_WIDTH * (localTime->tm_mday - 1);
633 XCopyArea(dpy, dateNums.pixmap, visible.pixmap, normalGC,
634 xOffset, yOffset, DATE_NUM_WIDTH, DATE_NUM_HEIGHT,
635 xPos[DATE_CENTER_X_POS], yPos[DATE_Y_POS]);
638 /* Wochentag */
639 xOffset = WEEKDAY_X_OFFSET;
640 yOffset = WEEKDAY_HEIGHT * ((localTime->tm_wday + 6) % 7);
641 XCopyArea(dpy, weekdays.pixmap, visible.pixmap, normalGC,
642 xOffset, yOffset, WEEKDAY_WIDTH, WEEKDAY_HEIGHT,
643 xPos[WEEKDAY_X_POS], yPos[WEEKDAY_Y_POS]);
645 if ((!enableBlinking) && (!enableYearDisplay))
647 /* Sekunden Doppelpunkt ein */
648 xOffset = COLON_X_OFFSET;
649 yOffset = COLON_Y_OFFSET;
650 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
651 xOffset, yOffset, COLON_WIDTH, COLON_HEIGHT,
652 xPos[COLON_X_POS], yPos[COLON_Y_POS]);
656 /* Extract program name from the zeroth program argument */
657 char *extractProgName(char *argv0)
659 char *prog_name = NULL;
661 if (NULL != argv0)
663 prog_name = strrchr(argv0, '/');
664 if (NULL == prog_name)
666 prog_name = argv0;
668 else
670 prog_name++;
673 return (prog_name);
676 /* Process program arguments and set corresponding options */
677 int processArgs(int argc, char **argv)
679 int i;
681 for (i = 1; i < argc; i++)
683 if (0 == strcmp(argv[i], "--"))
685 break;
687 else if ((0 == strcmp(argv[i], "-12")) ||
688 (0 == strcmp(argv[i], "-1")) ||
689 (0 == strcmp(argv[i], "--12")))
691 enable12HourClock = 1;
693 else if ((0 == strcmp(argv[i], "-24")) ||
694 (0 == strcmp(argv[i], "-2")) ||
695 (0 == strcmp(argv[i], "--24")))
697 enable12HourClock = 0;
699 else if ((0 == strcmp(argv[i], "-exe")) ||
700 (0 == strcmp(argv[i], "-e")) ||
701 (0 == strcmp(argv[i], "--exe")))
703 if (++i >= argc)
705 showUsage();
707 commandToExec = argv[i];
709 else if ((0 == strcmp(argv[i], "-led")) ||
710 (0 == strcmp(argv[i], "-l")) ||
711 (0 == strcmp(argv[i], "--led")))
713 if (++i >= argc)
715 showUsage();
717 ledColor = argv[i];
719 else if ((0 == strcmp(argv[i], "-clockxpm")) ||
720 (0 == strcmp(argv[i], "-c")) ||
721 (0 == strcmp(argv[i], "--clockxpm")))
723 #ifndef ONLY_SHAPED_WINDOW
724 if (++i >= argc)
726 showUsage();
728 userClockXpm = argv[i];
729 useUserClockXpm = 1;
730 #endif /* !ONLY_SHAPED_WINDOW */
732 else if ((0 == strcmp(argv[i], "-monthxpm")) ||
733 (0 == strcmp(argv[i], "-m")) ||
734 (0 == strcmp(argv[i], "--monthxpm")))
736 if (++i >= argc)
738 showUsage();
740 userMonthXpm = argv[i];
741 useUserMonthXpm = 1;
743 else if ((0 == strcmp(argv[i], "-weekdayxpm")) ||
744 (0 == strcmp(argv[i], "-w")) ||
745 (0 == strcmp(argv[i], "--weekdayxpm")))
747 if (++i >= argc)
749 showUsage();
751 userWeekdayXpm = argv[i];
752 useUserWeekdayXpm = 1;
754 else if ((0 == strcmp(argv[i], "-noblink")) ||
755 (0 == strcmp(argv[i], "-n")) ||
756 (0 == strcmp(argv[i], "--noblink")))
758 enableBlinking = 0;
760 else if ((0 == strcmp(argv[i], "-year")) ||
761 (0 == strcmp(argv[i], "-y")) ||
762 (0 == strcmp(argv[i], "--year")))
764 enableYearDisplay = 1;
766 else if ((0 == strcmp(argv[i], "-position")) ||
767 (0 == strcmp(argv[i], "-p")) ||
768 (0 == strcmp(argv[i], "--position")))
770 #ifndef ONLY_SHAPED_WINDOW
771 if (++i >= argc)
773 showUsage();
775 geometry = argv[i];
776 #endif /* !ONLY_SHAPED_WINDOW */
778 else if ((0 == strcmp(argv[i], "-shape")) ||
779 (0 == strcmp(argv[i], "-s")) ||
780 (0 == strcmp(argv[i], "--shape")))
782 enableShapedWindow = 1;
784 else if ((0 == strcmp(argv[i], "-iconic")) ||
785 (0 == strcmp(argv[i], "-i")) ||
786 (0 == strcmp(argv[i], "--iconic")))
788 #ifndef ONLY_SHAPED_WINDOW
789 startIconified = 1;
790 #endif /* !ONLY_SHAPED_WINDOW */
792 else if ((0 == strcmp(argv[i], "-version")) ||
793 (0 == strcmp(argv[i], "-V")) ||
794 (0 == strcmp(argv[i], "--version")))
796 showVersion();
798 else if ((0 == strcmp(argv[i], "-help")) ||
799 (0 == strcmp(argv[i], "-h")) ||
800 (0 == strcmp(argv[i], "--help")))
802 showUsage();
804 else if ((0 == strcmp(argv[i], "-interval")) ||
805 (0 == strcmp(argv[i], "--interval")))
807 if (++i >= argc)
809 showUsage();
811 blinkInterval = atoi(argv[i]);
814 else
816 fprintf(stderr, "%s: unrecognized option `%s'\n",
817 progName, argv[i]);
818 showUsage();
821 return (i);
824 /**********************************************************************/
825 int main(int argc, char **argv)
827 int i;
828 unsigned int borderWidth = 0;
829 char *displayName = NULL;
830 XGCValues gcValues;
831 unsigned long gcMask;
832 XEvent event;
833 XTextProperty wmName;
834 XClassHint classHint;
835 Pixmap shapeMask;
836 struct timeval nextEvent;
837 unsigned int blinkCounter = 0;
839 /* Parse command line options */
840 progName = extractProgName(argv[0]);
841 processArgs(argc, argv);
843 /* init led position */
844 #ifndef ONLY_SHAPED_WINDOW
845 for (i = 0; i < NUM_Y_POSITIONS; i++)
847 yPos[i] = enableShapedWindow ? yPosShaped[i] : yPosUnshaped[i];
849 for (i = 0; i < NUM_X_POSITIONS; i++)
851 xPos[i] = enableShapedWindow ? xPosShaped[i] : xPosUnshaped[i];
853 #else /* ONLY_SHAPED_WINDOW */
854 for (i = 0; i < NUM_Y_POSITIONS; i++)
856 yPos[i] = yPosShaped[i];
858 for (i = 0; i < NUM_X_POSITIONS; i++)
860 xPos[i] = xPosShaped[i];
862 #endif /* !ONLY_SHAPED_WINDOW */
863 for (i = 0; i < NUM_TIME_POSITIONS; i++)
865 if (enable12HourClock && (!enableYearDisplay))
867 xPos[i] += timePos24[i];
869 else
871 xPos[i] += timePos12[i];
875 /* Open the display */
876 dpy = XOpenDisplay(displayName);
877 if (NULL == dpy)
879 fprintf(stderr, "%s: can't open display %s\n", progName,
880 XDisplayName(displayName));
881 exit(1);
883 screen = DefaultScreen(dpy);
884 rootWindow = RootWindow(dpy, screen);
885 displayDepth = DefaultDepth(dpy, screen);
886 xFd = XConnectionNumber(dpy);
888 /* Icon Daten nach XImage konvertieren */
889 GetXpms();
891 /* Create a window to hold the banner */
892 sizeHints.x = 0;
893 sizeHints.y = 0;
894 sizeHints.min_width = clockBg.attributes.width;
895 sizeHints.min_height = clockBg.attributes.height;
896 sizeHints.max_width = clockBg.attributes.width;
897 sizeHints.max_height = clockBg.attributes.height;
898 sizeHints.base_width = clockBg.attributes.width;
899 sizeHints.base_height = clockBg.attributes.height;
900 sizeHints.flags = USSize | USPosition | PMinSize | PMaxSize | PBaseSize;
902 bgPixel = GetColor("white");
903 fgPixel = GetColor("black");
905 XWMGeometry(dpy, screen, geometry, NULL, borderWidth, &sizeHints,
906 &sizeHints.x, &sizeHints.y, &sizeHints.width, &sizeHints.height,
907 &sizeHints.win_gravity);
908 sizeHints.width = clockBg.attributes.width;
909 sizeHints.height = clockBg.attributes.height;
911 win = XCreateSimpleWindow(dpy, rootWindow, sizeHints.x, sizeHints.y,
912 sizeHints.width, sizeHints.height,
913 borderWidth, fgPixel, bgPixel);
914 iconWin = XCreateSimpleWindow(dpy, win, sizeHints.x, sizeHints.y,
915 sizeHints.width, sizeHints.height,
916 borderWidth, fgPixel, bgPixel);
918 /* Hints aktivieren */
919 XSetWMNormalHints(dpy, win, &sizeHints);
920 classHint.res_name = progName;
921 classHint.res_class = className;
922 XSetClassHint(dpy, win, &classHint);
924 XSelectInput(dpy, win, OUR_WINDOW_EVENTS);
925 XSelectInput(dpy, iconWin, OUR_WINDOW_EVENTS);
927 if (0 == XStringListToTextProperty(&progName, 1, &wmName))
929 fprintf(stderr, "%s: can't allocate window name text property\n",
930 progName);
931 exit(-1);
933 XSetWMName(dpy, win, &wmName);
935 /* Create a GC for drawing */
936 gcMask = GCForeground | GCBackground | GCGraphicsExposures;
937 gcValues.foreground = fgPixel;
938 gcValues.background = bgPixel;
939 gcValues.graphics_exposures = False;
940 normalGC = XCreateGC(dpy, rootWindow, gcMask, &gcValues);
942 if (enableShapedWindow)
944 shapeMask = XCreateBitmapFromData(dpy, win, (char *)mask_bits,
945 mask_width, mask_height);
946 XShapeCombineMask(dpy, win, ShapeBounding, 0, 0, shapeMask, ShapeSet);
947 XShapeCombineMask(dpy, iconWin, ShapeBounding, 0, 0, shapeMask,
948 ShapeSet);
951 wmHints.initial_state = WithdrawnState;
952 wmHints.icon_window = iconWin;
953 wmHints.icon_x = sizeHints.x;
954 wmHints.icon_y = sizeHints.y;
955 wmHints.window_group = win;
956 wmHints.flags = StateHint | IconWindowHint | IconPositionHint |
957 WindowGroupHint;
958 XSetWMHints(dpy, win, &wmHints);
960 XSetCommand(dpy, win, argv, argc);
961 XMapWindow(dpy,win);
963 showTime();
964 redrawWindow(&visible);
965 while (1)
967 if (actualTime != mytime())
969 actualTime = mytime();
970 if (actualMinutes != (actualTime / 60))
972 showTime();
973 if (!enableBlinking)
975 redrawWindow(&visible);
978 if (0 == (actualTime % 2))
980 /* Clean up zombie processes */
981 #ifdef DEBUG
982 fprintf(stderr, "%s: cleaning up zombies (time %ld)\n",
983 progName, actualTime);
984 #endif /* DEBUG */
985 if (NULL != commandToExec)
987 waitpid(0, NULL, WNOHANG);
991 if (enableBlinking && (!enableYearDisplay))
993 blinkCounter++;
994 #ifdef SYSV
995 if (blinkCounter >= 20*blinkInterval)
996 #else
997 if (blinkCounter >= 2*blinkInterval)
998 #endif
999 blinkCounter = 0;
1000 if (blinkCounter == 0)
1002 /* Sekunden Doppelpunkt ein */
1003 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
1004 COLON_X_OFFSET, COLON_Y_OFFSET,
1005 COLON_WIDTH, COLON_HEIGHT,
1006 xPos[COLON_X_POS], yPos[COLON_Y_POS]);
1008 #ifdef SYSV
1009 if (blinkCounter == 10*blinkInterval)
1010 #else
1011 if (blinkCounter == blinkInterval)
1012 #endif
1014 /* Sekunden Doppelpunkt aus */
1015 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
1016 BLANK_X_OFFSET, BLANK_Y_OFFSET,
1017 COLON_WIDTH, COLON_HEIGHT,
1018 xPos[COLON_X_POS], yPos[COLON_Y_POS]);
1020 redrawWindow(&visible);
1023 /* read a packet */
1024 while (XPending(dpy))
1026 XNextEvent(dpy, &event);
1027 switch(event.type)
1029 case Expose:
1030 if (0 == event.xexpose.count)
1032 redrawWindow(&visible);
1034 break;
1035 case ButtonPress:
1036 if (NULL != commandToExec)
1038 pid_t fork_pid;
1040 if ((NULL == commandBuf) &&
1041 (!buildCommand(commandToExec, &commandBuf,
1042 &commandLength, &commandIndex)))
1044 break;
1046 fork_pid = fork();
1047 switch (fork_pid)
1049 case 0:
1050 /* We're the child process;
1051 * run the command and exit.
1053 executeCommand(commandBuf);
1054 /* When the system() call finishes, we're done. */
1055 exit(0);
1056 break;
1057 case -1:
1058 /* We're the parent process, but
1059 * fork() gave an error.
1061 perror("fork");
1062 break;
1063 default:
1064 /* We're the parent process;
1065 * keep on doing what we normally do.
1067 break;
1070 break;
1071 case DestroyNotify:
1072 #if 0
1073 XFreeGC(dpy, normalGC);
1074 XDestroyWindow(dpy, win);
1075 XDestroyWindow(dpy, iconWin);
1076 #endif /* 0 */
1077 #ifdef ONLY_SHAPED_WINDOW
1078 XFreePixmap(dpy, visible.pixmap);
1079 #endif /* ONLY_SHAPED_WINDOW */
1080 XCloseDisplay(dpy);
1081 exit(0);
1082 default:
1083 break;
1086 XFlush(dpy);
1087 #ifdef SYSV
1088 if (enableYearDisplay)
1090 poll((struct poll *) 0, (size_t) 0, 200); /* 1/5 sec */
1092 else
1094 poll((struct poll *) 0, (size_t) 0, 50); /* 5/100 sec */
1096 #else
1097 /* We compute the date of next event, in order to avoid polling */
1098 if (enableBlinking)
1100 gettimeofday(&nextEvent,NULL);
1101 nextEvent.tv_sec = 0;
1102 if (nextEvent.tv_usec < 500000)
1103 nextEvent.tv_usec = 500000-nextEvent.tv_usec;
1104 else
1105 nextEvent.tv_usec = 1000000-nextEvent.tv_usec;
1107 else
1109 if (enableYearDisplay)
1111 nextEvent.tv_sec = 86400-actualTime%86400;
1112 nextEvent.tv_usec = 0;
1114 else
1116 nextEvent.tv_sec = 60-actualTime%60;
1117 nextEvent.tv_usec = 0;
1120 FD_ZERO(&xFdSet);
1121 FD_SET(xFd,&xFdSet);
1122 select(FD_SETSIZE,&xFdSet,NULL,NULL,&nextEvent);
1123 #endif
1125 return (0);