wmclock: Show correct year when -year is given.
[dockapps.git] / wmclock / wmclock.c
blob3feb9371c6facfa676a4287f5dabd339eed74f12
1 /* wmclock.c: a dockable clock applet for Window Maker
2 * created 1999-Apr-09 jmk
3 *
4 * by Jim Knoble <jmknoble@pobox.com>
5 * Copyright (C) 1999 Jim Knoble
6 *
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 */
150 int timePos12[NUM_TIME_POSITIONS] = { 5, 14, 24, 28, 37 };
151 int timePos24[NUM_TIME_POSITIONS] = { 4, 8, 17, 22, 31 };
152 /* with shape */
153 int xPosShaped[NUM_X_POSITIONS] = { 0, 0, 0, 0, 0, 40, 17, 17, 22, 27, 15 };
154 int yPosShaped[NUM_Y_POSITIONS] = { 3, 21, 30, 45 };
156 #ifndef ONLY_SHAPED_WINDOW
157 /* no shape */
158 int xPosUnshaped[NUM_X_POSITIONS] = { 5, 5, 5, 5, 5, 45, 21, 21, 26, 31, 19 };
159 int yPosUnshaped[NUM_Y_POSITIONS] = { 7, 25, 34, 49 };
160 #endif /* !ONLY_SHAPED_WINDOW */
162 int xPos[NUM_X_POSITIONS];
163 int yPos[NUM_Y_POSITIONS];
165 Display *dpy;
166 Window rootWindow;
167 int screen;
168 int xFd;
169 fd_set xFdSet;
170 int displayDepth;
171 XSizeHints sizeHints;
172 XWMHints wmHints;
173 Pixel bgPixel, fgPixel;
174 GC normalGC;
175 Window iconWin, win;
177 char *progName;
178 char *className = "WMClock";
179 char *geometry = "";
180 char *ledColor = "LightSeaGreen";
182 char *commandToExec = NULL;
183 char *commandBuf = NULL;
184 int commandLength = 0;
185 int commandIndex = 0;
187 char *errColorCells = "not enough free color cells or xpm not found\n";
189 char *userClockXpm;
190 char *userMonthXpm;
191 char *userWeekdayXpm;
192 int useUserClockXpm = 0;
193 int useUserMonthXpm = 0;
194 int useUserWeekdayXpm = 0;
196 XpmIcon clockBg, led, months, dateNums, weekdays;
197 XpmIcon visible;
199 time_t actualTime;
200 long actualMinutes;
202 static struct tm *localTime;
204 char *usageText[] = {
205 "Options:",
206 " -12 show 12-hour time (am/pm)",
207 " -24 show 24-hour time",
208 " -year show year instead of time",
209 " -noblink don't blink",
210 " -exe <command> start <command> on mouse click",
211 " -led <color> use <color> as color of led",
212 #ifndef ONLY_SHAPED_WINDOW
213 " -clockxpm <filename> get clock background from pixmap in <filename>",
214 #endif /* !ONLY_SHAPED_WINDOW */
215 " -monthxpm <filename> get month names from pixmap in <filename>",
216 " -weekdayxpm <filename> get weekday names from pixmap in <filename>",
217 " -version display the version",
218 NULL
221 char *version = VERSION;
223 /**********************************************************************/
224 /* Display usage information */
225 void showUsage(void)
227 char **cpp;
229 fprintf(stderr, "Usage: %s [option [option ...]]\n\n", progName);
230 for (cpp = usageText; *cpp; cpp++)
232 fprintf(stderr, "%s\n", *cpp);
234 fprintf(stderr,"\n");
235 exit(1);
238 /* Display the program version */
239 void showVersion()
241 fprintf(stderr, "%s version %s\n", progName, version);
242 exit(1);
245 /* Build the shell command to execute */
246 int buildCommand(char *command, char **buf, int *buf_len, int *i)
248 int status;
250 status = append_string_to_buf(buf, buf_len, i, command);
251 if (APPEND_FAILURE == status)
253 return (0);
255 status = append_string_to_buf(buf, buf_len, i, " &");
256 return ((APPEND_FAILURE == status) ? 0 : 1);
259 /* Execute the given shell command */
260 void executeCommand(char *command)
262 int status;
264 if (NULL == command)
266 return;
268 status = system(command);
270 if (-1 == status)
272 perror("system");
276 /* Display an error message */
277 void showError(const char *message, const char *data)
279 fprintf(stderr,"%s: can't %s %s\n", progName, message, data);
282 /* Display an error message and exit */
283 void showFatalError(const char *message, const char *data)
285 showError(message, data);
286 exit(1);
289 /* Konvertiere XPMIcons nach Pixmaps */
290 void GetXpms(void)
292 static char **clock_xpm;
293 XColor color;
294 XWindowAttributes attributes;
295 char ledBright[64];
296 char ledDim[64];
297 int status;
299 #ifdef ONLY_SHAPED_WINDOW
300 clock_xpm = mask_xpm;
301 #else /* !ONLY_SHAPED_WINDOW */
302 clock_xpm = enableShapedWindow ? mask_xpm : clk_xpm;
303 #endif /* ONLY_SHAPED_WINDOW */
305 /* for the colormap */
306 XGetWindowAttributes(dpy, rootWindow, &attributes);
308 /* get user-defined color */
309 if (!XParseColor(dpy, attributes.colormap, ledColor, &color))
311 showError("parse color", ledColor);
314 sprintf(ledBright, "%c c #%04X%04X%04X", LED_XPM_BRIGHT_CHAR,
315 color.red, color.green, color.blue);
316 led_xpm[LED_XPM_BRIGHT_LINE_INDEX] = &ledBright[0];
318 color.red = makeDimColor(color.red);
319 color.green = makeDimColor(color.green);
320 color.blue = makeDimColor(color.blue);
321 sprintf(&ledDim[0], "%c c #%04X%04X%04X", LED_XPM_DIM_CHAR,
322 color.red, color.green, color.blue);
323 led_xpm[LED_XPM_DIM_LINE_INDEX] = &ledDim[0];
325 clockBg.attributes.closeness = DEFAULT_XPM_CLOSENESS;
326 clockBg.attributes.valuemask |=
327 (XpmReturnPixels | XpmReturnExtensions | XpmCloseness);
329 if (useUserClockXpm)
331 status = XpmReadFileToPixmap(dpy, rootWindow, userClockXpm,
332 &clockBg.pixmap, &clockBg.mask,
333 &clockBg.attributes);
335 else
337 status = XpmCreatePixmapFromData(dpy, rootWindow, clock_xpm,
338 &clockBg.pixmap, &clockBg.mask,
339 &clockBg.attributes);
341 if (XpmSuccess != status)
343 showFatalError("create clock pixmap:", errColorCells);
346 #ifdef ONLY_SHAPED_WINDOW
347 visible.attributes.depth = displayDepth;
348 visible.attributes.width = clockBg.attributes.width;
349 visible.attributes.height = clockBg.attributes.height;
350 visible.pixmap = XCreatePixmap(dpy, rootWindow, visible.attributes.width,
351 visible.attributes.height,
352 visible.attributes.depth);
353 #else /* !ONLY_SHAPED_WINDOW */
354 visible.attributes.closeness = DEFAULT_XPM_CLOSENESS;
355 visible.attributes.valuemask |=
356 (XpmReturnPixels | XpmReturnExtensions | XpmCloseness);
357 status = XpmCreatePixmapFromData(dpy, rootWindow, clk_xpm,
358 &visible.pixmap, &visible.mask,
359 &visible.attributes);
360 #endif /* ONLY_SHAPED_WINDOW */
362 led.attributes.closeness = DEFAULT_XPM_CLOSENESS;
363 led.attributes.valuemask |=
364 (XpmReturnPixels | XpmReturnExtensions | XpmCloseness);
365 status = XpmCreatePixmapFromData(dpy, rootWindow, led_xpm,
366 &led.pixmap, &led.mask,
367 &led.attributes);
368 if (XpmSuccess != status)
370 showFatalError("create led pixmap:", errColorCells);
373 months.attributes.closeness = DEFAULT_XPM_CLOSENESS;
374 months.attributes.valuemask |=
375 (XpmReturnPixels | XpmReturnExtensions | XpmCloseness);
376 if (useUserMonthXpm)
378 status = XpmReadFileToPixmap(dpy, rootWindow, userMonthXpm,
379 &months.pixmap, &months.mask,
380 &months.attributes);
382 else
384 status = XpmCreatePixmapFromData(dpy, rootWindow, month_xpm,
385 &months.pixmap, &months.mask,
386 &months.attributes);
388 if (XpmSuccess != status)
390 showFatalError("create month pixmap:", errColorCells);
393 dateNums.attributes.closeness = DEFAULT_XPM_CLOSENESS;
394 dateNums.attributes.valuemask |=
395 (XpmReturnPixels | XpmReturnExtensions | XpmCloseness);
396 status = XpmCreatePixmapFromData(dpy, rootWindow, date_xpm,
397 &dateNums.pixmap, &dateNums.mask,
398 &dateNums.attributes);
399 if (XpmSuccess != status)
401 showFatalError("create date pixmap:", errColorCells);
404 weekdays.attributes.closeness = DEFAULT_XPM_CLOSENESS;
405 weekdays.attributes.valuemask |=
406 (XpmReturnPixels | XpmReturnExtensions | XpmCloseness);
407 if (useUserWeekdayXpm)
409 status = XpmReadFileToPixmap(dpy, rootWindow, userWeekdayXpm,
410 &weekdays.pixmap, &weekdays.mask,
411 &weekdays.attributes);
413 else
415 status = XpmCreatePixmapFromData(dpy, rootWindow, weekday_xpm,
416 &weekdays.pixmap, &weekdays.mask,
417 &weekdays.attributes);
419 if (XpmSuccess != status)
421 showFatalError("create weekday pixmap:", errColorCells);
425 /* Remove expose events for a specific window from the queue */
426 int flushExposeEvents(Window w)
428 XEvent dummy;
429 int i = 0;
431 while (XCheckTypedWindowEvent(dpy, w, Expose, &dummy))
433 i++;
435 return(i);
438 /* (Re-)Draw the main window and the icon window */
439 void redrawWindow(XpmIcon *v)
441 flushExposeEvents(iconWin);
442 XCopyArea(dpy, v->pixmap, iconWin, normalGC,
443 0, 0, v->attributes.width, v->attributes.height, 0, 0);
444 flushExposeEvents(win);
445 XCopyArea(dpy, v->pixmap, win, normalGC,
446 0, 0, v->attributes.width, v->attributes.height, 0, 0);
449 /* Get a Pixel for the given color name */
450 Pixel GetColor(const char *colorName)
452 XColor color;
453 XWindowAttributes attributes;
455 XGetWindowAttributes(dpy, rootWindow, &attributes);
456 color.pixel = 0;
457 if (!XParseColor(dpy, attributes.colormap, colorName, &color))
459 showError("parse color", colorName);
461 else if (!XAllocColor(dpy, attributes.colormap, &color))
463 showError("allocate color", colorName);
465 return(color.pixel);
468 /* Fetch the system time and time zone */
469 int mytime(void)
471 struct timeval tv;
472 struct timezone tz;
474 gettimeofday(&tv, &tz);
476 return(tv.tv_sec);
479 /* Display the current year in the LED display */
480 void showYear(void)
482 int year;
483 int digitXOffset;
484 int digitYOffset;
486 year = localTime->tm_year + 1900;
488 digitYOffset = LED_NUM_Y_OFFSET;
489 digitXOffset = LED_NUM_WIDTH * (year / 1000);
490 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
491 digitXOffset , digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
492 xPos[DIGIT_1_X_POS], yPos[DIGIT_Y_POS]);
493 digitXOffset = LED_NUM_WIDTH * ((year / 100) % 10);
494 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
495 digitXOffset , digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
496 xPos[DIGIT_2_X_POS], yPos[DIGIT_Y_POS]);
497 digitXOffset = LED_NUM_WIDTH * ((year / 10) % 10);
498 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
499 digitXOffset , digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
500 xPos[DIGIT_3_X_POS], yPos[DIGIT_Y_POS]);
501 digitXOffset = LED_NUM_WIDTH * (year % 10);
502 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
503 digitXOffset , digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
504 xPos[DIGIT_4_X_POS], yPos[DIGIT_Y_POS]);
507 /* Display time in twelve-hour mode, with am/pm indicator */
508 void showTime12(void)
510 int digitXOffset;
511 int digitYOffset;
512 int localHour = localTime->tm_hour % 12;
514 if (0 == localHour)
516 localHour = 12;
518 if (localTime->tm_hour < 12)
520 /* AM */
521 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
522 AM_X_OFFSET, AM_Y_OFFSET, AM_WIDTH, AM_HEIGHT,
523 xPos[AMPM_X_POS], yPos[DIGIT_Y_POS] + AM_Y_OFFSET);
525 else
527 /* PM */
528 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
529 PM_X_OFFSET, PM_Y_OFFSET, PM_WIDTH, PM_HEIGHT,
530 xPos[AMPM_X_POS], yPos[DIGIT_Y_POS] + PM_Y_OFFSET);
533 digitYOffset = LED_NUM_Y_OFFSET;
534 if (localHour > 9)
536 digitXOffset = LED_THIN_1_X_OFFSET;
537 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
538 digitXOffset, digitYOffset, LED_THIN_1_WIDTH, LED_NUM_HEIGHT,
539 xPos[DIGIT_1_X_POS], yPos[DIGIT_Y_POS]);
541 digitXOffset = LED_NUM_WIDTH * (localHour % 10);
542 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
543 digitXOffset, digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
544 xPos[DIGIT_2_X_POS], yPos[DIGIT_Y_POS]);
545 digitXOffset = LED_NUM_WIDTH * (localTime->tm_min / 10);
546 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
547 digitXOffset, digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
548 xPos[DIGIT_3_X_POS], yPos[DIGIT_Y_POS]);
549 digitXOffset = LED_NUM_WIDTH * (localTime->tm_min % 10);
550 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
551 digitXOffset, digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
552 xPos[DIGIT_4_X_POS], yPos[DIGIT_Y_POS]);
555 /* Display time in 24-hour mode, without am/pm indicator */
556 void showTime24(void)
558 int digitXOffset;
559 int digitYOffset;
561 digitYOffset = LED_NUM_Y_OFFSET;
562 digitXOffset = LED_NUM_WIDTH * (localTime->tm_hour / 10);
563 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
564 digitXOffset, digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
565 xPos[DIGIT_1_X_POS], yPos[DIGIT_Y_POS]);
566 digitXOffset = LED_NUM_WIDTH * (localTime->tm_hour % 10);
567 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
568 digitXOffset, digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
569 xPos[DIGIT_2_X_POS], yPos[DIGIT_Y_POS]);
570 digitXOffset = LED_NUM_WIDTH * (localTime->tm_min / 10);
571 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
572 digitXOffset, digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
573 xPos[DIGIT_3_X_POS], yPos[DIGIT_Y_POS]);
574 digitXOffset = LED_NUM_WIDTH * (localTime->tm_min % 10);
575 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
576 digitXOffset, digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
577 xPos[DIGIT_4_X_POS], yPos[DIGIT_Y_POS]);
580 void showTime(void)
582 int xOffset;
583 int yOffset;
585 /* Zeit auslesen */
586 actualTime = mytime();
587 actualMinutes = actualTime / 60;
589 localTime = localtime(&actualTime);
591 /* leere clock holen */
592 XCopyArea(dpy, clockBg.pixmap, visible.pixmap, normalGC,
593 0, 0, sizeHints.width, sizeHints.height, 0, 0);
595 if (enableYearDisplay)
597 showYear();
599 else if (enable12HourClock)
601 showTime12();
603 else
605 showTime24();
608 /* Monat */
609 xOffset = MONTH_X_OFFSET;
610 yOffset = MONTH_HEIGHT * (localTime->tm_mon);
611 XCopyArea(dpy, months.pixmap, visible.pixmap, normalGC,
612 xOffset, yOffset, MONTH_WIDTH, MONTH_HEIGHT,
613 xPos[MONTH_X_POS], yPos[MONTH_Y_POS]);
615 /* Datum */
616 yOffset = DATE_Y_OFFSET;
617 if (localTime->tm_mday > 9)
619 xOffset = DATE_NUM_WIDTH * (((localTime->tm_mday / 10) + 9) % 10);
620 XCopyArea(dpy, dateNums.pixmap, visible.pixmap, normalGC,
621 xOffset, yOffset, DATE_NUM_WIDTH, DATE_NUM_HEIGHT,
622 xPos[DATE_LEFT_X_POS], yPos[DATE_Y_POS]);
623 xOffset = DATE_NUM_WIDTH * (((localTime->tm_mday % 10) + 9) % 10);
624 XCopyArea(dpy, dateNums.pixmap, visible.pixmap, normalGC,
625 xOffset, yOffset, DATE_NUM_WIDTH, DATE_NUM_HEIGHT,
626 xPos[DATE_RIGHT_X_POS], yPos[DATE_Y_POS]);
628 else
630 xOffset = DATE_NUM_WIDTH * (localTime->tm_mday - 1);
631 XCopyArea(dpy, dateNums.pixmap, visible.pixmap, normalGC,
632 xOffset, yOffset, DATE_NUM_WIDTH, DATE_NUM_HEIGHT,
633 xPos[DATE_CENTER_X_POS], yPos[DATE_Y_POS]);
636 /* Wochentag */
637 xOffset = WEEKDAY_X_OFFSET;
638 yOffset = WEEKDAY_HEIGHT * ((localTime->tm_wday + 6) % 7);
639 XCopyArea(dpy, weekdays.pixmap, visible.pixmap, normalGC,
640 xOffset, yOffset, WEEKDAY_WIDTH, WEEKDAY_HEIGHT,
641 xPos[WEEKDAY_X_POS], yPos[WEEKDAY_Y_POS]);
643 if ((!enableBlinking) && (!enableYearDisplay))
645 /* Sekunden Doppelpunkt ein */
646 xOffset = COLON_X_OFFSET;
647 yOffset = COLON_Y_OFFSET;
648 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
649 xOffset, yOffset, COLON_WIDTH, COLON_HEIGHT,
650 xPos[COLON_X_POS], yPos[COLON_Y_POS]);
654 /* Extract program name from the zeroth program argument */
655 char *extractProgName(char *argv0)
657 char *prog_name = NULL;
659 if (NULL != argv0)
661 prog_name = strrchr(argv0, '/');
662 if (NULL == prog_name)
664 prog_name = argv0;
666 else
668 prog_name++;
671 return (prog_name);
674 /* Process program arguments and set corresponding options */
675 int processArgs(int argc, char **argv)
677 int i;
679 for (i = 1; i < argc; i++)
681 if (0 == strcmp(argv[i], "--"))
683 break;
685 else if ((0 == strcmp(argv[i], "-12")) ||
686 (0 == strcmp(argv[i], "-1")) ||
687 (0 == strcmp(argv[i], "--12")))
689 enable12HourClock = 1;
691 else if ((0 == strcmp(argv[i], "-24")) ||
692 (0 == strcmp(argv[i], "-2")) ||
693 (0 == strcmp(argv[i], "--24")))
695 enable12HourClock = 0;
697 else if ((0 == strcmp(argv[i], "-exe")) ||
698 (0 == strcmp(argv[i], "-e")) ||
699 (0 == strcmp(argv[i], "--exe")))
701 if (++i >= argc)
703 showUsage();
705 commandToExec = argv[i];
707 else if ((0 == strcmp(argv[i], "-led")) ||
708 (0 == strcmp(argv[i], "-l")) ||
709 (0 == strcmp(argv[i], "--led")))
711 if (++i >= argc)
713 showUsage();
715 ledColor = argv[i];
717 else if ((0 == strcmp(argv[i], "-clockxpm")) ||
718 (0 == strcmp(argv[i], "-c")) ||
719 (0 == strcmp(argv[i], "--clockxpm")))
721 #ifndef ONLY_SHAPED_WINDOW
722 if (++i >= argc)
724 showUsage();
726 userClockXpm = argv[i];
727 useUserClockXpm = 1;
728 #endif /* !ONLY_SHAPED_WINDOW */
730 else if ((0 == strcmp(argv[i], "-monthxpm")) ||
731 (0 == strcmp(argv[i], "-m")) ||
732 (0 == strcmp(argv[i], "--monthxpm")))
734 if (++i >= argc)
736 showUsage();
738 userMonthXpm = argv[i];
739 useUserMonthXpm = 1;
741 else if ((0 == strcmp(argv[i], "-weekdayxpm")) ||
742 (0 == strcmp(argv[i], "-w")) ||
743 (0 == strcmp(argv[i], "--weekdayxpm")))
745 if (++i >= argc)
747 showUsage();
749 userWeekdayXpm = argv[i];
750 useUserWeekdayXpm = 1;
752 else if ((0 == strcmp(argv[i], "-noblink")) ||
753 (0 == strcmp(argv[i], "-n")) ||
754 (0 == strcmp(argv[i], "--noblink")))
756 enableBlinking = 0;
758 else if ((0 == strcmp(argv[i], "-year")) ||
759 (0 == strcmp(argv[i], "-y")) ||
760 (0 == strcmp(argv[i], "--year")))
762 enableYearDisplay = 1;
764 else if ((0 == strcmp(argv[i], "-position")) ||
765 (0 == strcmp(argv[i], "-p")) ||
766 (0 == strcmp(argv[i], "--position")))
768 #ifndef ONLY_SHAPED_WINDOW
769 if (++i >= argc)
771 showUsage();
773 geometry = argv[i];
774 #endif /* !ONLY_SHAPED_WINDOW */
776 else if ((0 == strcmp(argv[i], "-shape")) ||
777 (0 == strcmp(argv[i], "-s")) ||
778 (0 == strcmp(argv[i], "--shape")))
780 enableShapedWindow = 1;
782 else if ((0 == strcmp(argv[i], "-iconic")) ||
783 (0 == strcmp(argv[i], "-i")) ||
784 (0 == strcmp(argv[i], "--iconic")))
786 #ifndef ONLY_SHAPED_WINDOW
787 startIconified = 1;
788 #endif /* !ONLY_SHAPED_WINDOW */
790 else if ((0 == strcmp(argv[i], "-version")) ||
791 (0 == strcmp(argv[i], "-V")) ||
792 (0 == strcmp(argv[i], "--version")))
794 showVersion();
796 else if ((0 == strcmp(argv[i], "-help")) ||
797 (0 == strcmp(argv[i], "-h")) ||
798 (0 == strcmp(argv[i], "--help")))
800 showUsage();
802 else
804 fprintf(stderr, "%s: unrecognized option `%s'\n",
805 progName, argv[i]);
806 showUsage();
809 return (i);
812 /**********************************************************************/
813 int main(int argc, char **argv)
815 int i;
816 unsigned int borderWidth = 0;
817 char *displayName = NULL;
818 XGCValues gcValues;
819 unsigned long gcMask;
820 XEvent event;
821 XTextProperty wmName;
822 XClassHint classHint;
823 Pixmap shapeMask;
824 struct timeval nextEvent;
826 /* Parse command line options */
827 progName = extractProgName(argv[0]);
828 processArgs(argc, argv);
830 /* init led position */
831 #ifndef ONLY_SHAPED_WINDOW
832 for (i = 0; i < NUM_Y_POSITIONS; i++)
834 yPos[i] = enableShapedWindow ? yPosShaped[i] : yPosUnshaped[i];
836 for (i = 0; i < NUM_X_POSITIONS; i++)
838 xPos[i] = enableShapedWindow ? xPosShaped[i] : xPosUnshaped[i];
840 #else /* ONLY_SHAPED_WINDOW */
841 for (i = 0; i < NUM_Y_POSITIONS; i++)
843 yPos[i] = yPosShaped[i];
845 for (i = 0; i < NUM_X_POSITIONS; i++)
847 xPos[i] = xPosShaped[i];
849 #endif /* !ONLY_SHAPED_WINDOW */
850 for (i = 0; i < NUM_TIME_POSITIONS; i++)
852 if (enable12HourClock && (!enableYearDisplay))
854 xPos[i] += timePos24[i];
856 else
858 xPos[i] += timePos12[i];
862 /* Open the display */
863 dpy = XOpenDisplay(displayName);
864 if (NULL == dpy)
866 fprintf(stderr, "%s: can't open display %s\n", progName,
867 XDisplayName(displayName));
868 exit(1);
870 screen = DefaultScreen(dpy);
871 rootWindow = RootWindow(dpy, screen);
872 displayDepth = DefaultDepth(dpy, screen);
873 xFd = XConnectionNumber(dpy);
875 /* Icon Daten nach XImage konvertieren */
876 GetXpms();
878 /* Create a window to hold the banner */
879 sizeHints.x = 0;
880 sizeHints.y = 0;
881 sizeHints.min_width = clockBg.attributes.width;
882 sizeHints.min_height = clockBg.attributes.height;
883 sizeHints.max_width = clockBg.attributes.width;
884 sizeHints.max_height = clockBg.attributes.height;
885 sizeHints.base_width = clockBg.attributes.width;
886 sizeHints.base_height = clockBg.attributes.height;
887 sizeHints.flags = USSize | USPosition | PMinSize | PMaxSize | PBaseSize;
889 bgPixel = GetColor("white");
890 fgPixel = GetColor("black");
892 XWMGeometry(dpy, screen, geometry, NULL, borderWidth, &sizeHints,
893 &sizeHints.x, &sizeHints.y, &sizeHints.width, &sizeHints.height,
894 &sizeHints.win_gravity);
895 sizeHints.width = clockBg.attributes.width;
896 sizeHints.height = clockBg.attributes.height;
898 win = XCreateSimpleWindow(dpy, rootWindow, sizeHints.x, sizeHints.y,
899 sizeHints.width, sizeHints.height,
900 borderWidth, fgPixel, bgPixel);
901 iconWin = XCreateSimpleWindow(dpy, win, sizeHints.x, sizeHints.y,
902 sizeHints.width, sizeHints.height,
903 borderWidth, fgPixel, bgPixel);
905 /* Hints aktivieren */
906 XSetWMNormalHints(dpy, win, &sizeHints);
907 classHint.res_name = progName;
908 classHint.res_class = className;
909 XSetClassHint(dpy, win, &classHint);
911 XSelectInput(dpy, win, OUR_WINDOW_EVENTS);
912 XSelectInput(dpy, iconWin, OUR_WINDOW_EVENTS);
914 if (0 == XStringListToTextProperty(&progName, 1, &wmName))
916 fprintf(stderr, "%s: can't allocate window name text property\n",
917 progName);
918 exit(-1);
920 XSetWMName(dpy, win, &wmName);
922 /* Create a GC for drawing */
923 gcMask = GCForeground | GCBackground | GCGraphicsExposures;
924 gcValues.foreground = fgPixel;
925 gcValues.background = bgPixel;
926 gcValues.graphics_exposures = False;
927 normalGC = XCreateGC(dpy, rootWindow, gcMask, &gcValues);
929 if (enableShapedWindow)
931 shapeMask = XCreateBitmapFromData(dpy, win, (char *)mask_bits,
932 mask_width, mask_height);
933 XShapeCombineMask(dpy, win, ShapeBounding, 0, 0, shapeMask, ShapeSet);
934 XShapeCombineMask(dpy, iconWin, ShapeBounding, 0, 0, shapeMask,
935 ShapeSet);
938 wmHints.initial_state = WithdrawnState;
939 wmHints.icon_window = iconWin;
940 wmHints.icon_x = sizeHints.x;
941 wmHints.icon_y = sizeHints.y;
942 wmHints.window_group = win;
943 wmHints.flags = StateHint | IconWindowHint | IconPositionHint |
944 WindowGroupHint;
945 XSetWMHints(dpy, win, &wmHints);
947 XSetCommand(dpy, win, argv, argc);
948 XMapWindow(dpy,win);
950 showTime();
951 redrawWindow(&visible);
952 while (1)
954 if (actualTime != mytime())
956 actualTime = mytime();
957 if (actualMinutes != (actualTime / 60))
959 showTime();
960 if (!enableBlinking)
962 redrawWindow(&visible);
965 if (enableBlinking && (!enableYearDisplay))
967 if (actualTime % 2)
969 /* Sekunden Doppelpunkt ein */
970 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
971 COLON_X_OFFSET, COLON_Y_OFFSET,
972 COLON_WIDTH, COLON_HEIGHT,
973 xPos[COLON_X_POS], yPos[COLON_Y_POS]);
975 else
977 /* Sekunden Doppelpunkt aus */
978 XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
979 BLANK_X_OFFSET, BLANK_Y_OFFSET,
980 COLON_WIDTH, COLON_HEIGHT,
981 xPos[COLON_X_POS], yPos[COLON_Y_POS]);
983 redrawWindow(&visible);
985 if (0 == (actualTime % 2))
987 /* Clean up zombie processes */
988 #ifdef DEBUG
989 fprintf(stderr, "%s: cleaning up zombies (time %ld)\n",
990 progName, actualTime);
991 #endif /* DEBUG */
992 if (NULL != commandToExec)
994 waitpid(0, NULL, WNOHANG);
999 /* read a packet */
1000 while (XPending(dpy))
1002 XNextEvent(dpy, &event);
1003 switch(event.type)
1005 case Expose:
1006 if (0 == event.xexpose.count)
1008 redrawWindow(&visible);
1010 break;
1011 case ButtonPress:
1012 if (NULL != commandToExec)
1014 pid_t fork_pid;
1016 if ((NULL == commandBuf) &&
1017 (!buildCommand(commandToExec, &commandBuf,
1018 &commandLength, &commandIndex)))
1020 break;
1022 fork_pid = fork();
1023 switch (fork_pid)
1025 case 0:
1026 /* We're the child process;
1027 * run the command and exit.
1029 executeCommand(commandBuf);
1030 /* When the system() call finishes, we're done. */
1031 exit(0);
1032 break;
1033 case -1:
1034 /* We're the parent process, but
1035 * fork() gave an error.
1037 perror("fork");
1038 break;
1039 default:
1040 /* We're the parent process;
1041 * keep on doing what we normally do.
1043 break;
1046 break;
1047 case DestroyNotify:
1048 #if 0
1049 XFreeGC(dpy, normalGC);
1050 XDestroyWindow(dpy, win);
1051 XDestroyWindow(dpy, iconWin);
1052 #endif /* 0 */
1053 #ifdef ONLY_SHAPED_WINDOW
1054 XFreePixmap(dpy, visible.pixmap);
1055 #endif /* ONLY_SHAPED_WINDOW */
1056 XCloseDisplay(dpy);
1057 exit(0);
1058 default:
1059 break;
1062 XFlush(dpy);
1063 #ifdef SYSV
1064 if (enableYearDisplay)
1066 poll((struct poll *) 0, (size_t) 0, 200); /* 1/5 sec */
1068 else
1070 poll((struct poll *) 0, (size_t) 0, 50); /* 5/100 sec */
1072 #else
1073 /* We compute the date of next event, in order to avoid polling */
1074 if (enableBlinking)
1076 gettimeofday(&nextEvent,NULL);
1077 nextEvent.tv_sec = 0;
1078 nextEvent.tv_usec = 1000000-nextEvent.tv_usec;
1080 else
1082 if (enableYearDisplay)
1084 nextEvent.tv_sec = 86400-actualTime%86400;
1085 nextEvent.tv_usec = 0;
1087 else
1089 nextEvent.tv_sec = 60-actualTime%60;
1090 nextEvent.tv_usec = 0;
1093 FD_ZERO(&xFdSet);
1094 FD_SET(xFd,&xFdSet);
1095 select(FD_SETSIZE,&xFdSet,NULL,NULL,&nextEvent);
1096 #endif
1098 return (0);