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.
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>
31 #include <X11/Xatom.h>
34 #include <X11/extensions/shape.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
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
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
63 #define COLON_HEIGHT 11
66 #define AM_X_OFFSET 94
68 #define PM_X_OFFSET 107
75 #define MONTH_X_POS 10
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
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
111 #endif /* !ONLY_SHAPED_WINDOW */
114 #include "xpm/date.xpm"
115 #include "xpm/led.xpm"
116 #include "xpm/mask.xbm"
117 #include "xpm/mask.xpm"
119 typedef struct _XpmIcon
{
122 XpmAttributes attributes
;
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*);
132 int flushExposeEvents(Window
);
133 void redrawWindow(XpmIcon
*);
134 Pixel
GetColor(const char *);
137 void showTime12(void);
138 void showTime24(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 unsigned 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 };
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
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
];
172 XSizeHints sizeHints
;
174 Pixel bgPixel
, fgPixel
;
179 char *className
= "WMClock";
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";
192 char *userWeekdayXpm
;
193 int useUserClockXpm
= 0;
194 int useUserMonthXpm
= 0;
195 int useUserWeekdayXpm
= 0;
197 XpmIcon clockBg
, led
, months
, dateNums
, weekdays
;
203 static struct tm
*localTime
;
205 char *usageText
[] = {
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",
223 char *version
= VERSION
;
225 /**********************************************************************/
226 /* Display usage information */
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");
240 /* Display the program version */
243 fprintf(stderr
, "%s version %s\n", progName
, version
);
247 /* Build the shell command to execute */
248 int buildCommand(char *command
, char **buf
, int *buf_len
, int *i
)
252 status
= append_string_to_buf(buf
, buf_len
, i
, command
);
253 if (APPEND_FAILURE
== status
)
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
)
270 status
= system(command
);
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
);
291 /* Konvertiere XPMIcons nach Pixmaps */
294 static char **clock_xpm
;
296 XWindowAttributes attributes
;
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
);
333 status
= XpmReadFileToPixmap(dpy
, rootWindow
, userClockXpm
,
334 &clockBg
.pixmap
, &clockBg
.mask
,
335 &clockBg
.attributes
);
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
,
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
);
380 status
= XpmReadFileToPixmap(dpy
, rootWindow
, userMonthXpm
,
381 &months
.pixmap
, &months
.mask
,
386 status
= XpmCreatePixmapFromData(dpy
, rootWindow
, month_xpm
,
387 &months
.pixmap
, &months
.mask
,
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
);
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
)
433 while (XCheckTypedWindowEvent(dpy
, w
, Expose
, &dummy
))
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
)
455 XWindowAttributes attributes
;
457 XGetWindowAttributes(dpy
, rootWindow
, &attributes
);
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
);
470 /* Fetch the system time and time zone */
476 gettimeofday(&tv
, &tz
);
481 /* Display the current year in the LED display */
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)
514 int localHour
= localTime
->tm_hour
% 12;
520 if (localTime
->tm_hour
< 12)
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
);
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
;
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)
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
]);
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
)
601 else if (enable12HourClock
)
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
]);
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
]);
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
]);
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
;
663 prog_name
= strrchr(argv0
, '/');
664 if (NULL
== prog_name
)
676 /* Process program arguments and set corresponding options */
677 int processArgs(int argc
, char **argv
)
681 for (i
= 1; i
< argc
; i
++)
683 if (0 == strcmp(argv
[i
], "--"))
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")))
707 commandToExec
= argv
[i
];
709 else if ((0 == strcmp(argv
[i
], "-led")) ||
710 (0 == strcmp(argv
[i
], "-l")) ||
711 (0 == strcmp(argv
[i
], "--led")))
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
728 userClockXpm
= argv
[i
];
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")))
740 userMonthXpm
= argv
[i
];
743 else if ((0 == strcmp(argv
[i
], "-weekdayxpm")) ||
744 (0 == strcmp(argv
[i
], "-w")) ||
745 (0 == strcmp(argv
[i
], "--weekdayxpm")))
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")))
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
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
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")))
798 else if ((0 == strcmp(argv
[i
], "-help")) ||
799 (0 == strcmp(argv
[i
], "-h")) ||
800 (0 == strcmp(argv
[i
], "--help")))
804 else if ((0 == strcmp(argv
[i
], "-interval")) ||
805 (0 == strcmp(argv
[i
], "--interval")))
811 blinkInterval
= atoi(argv
[i
]);
816 fprintf(stderr
, "%s: unrecognized option `%s'\n",
824 /**********************************************************************/
825 int main(int argc
, char **argv
)
828 unsigned int borderWidth
= 0;
829 char *displayName
= NULL
;
831 unsigned long gcMask
;
833 XTextProperty wmName
;
834 XClassHint classHint
;
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
];
871 xPos
[i
] += timePos12
[i
];
875 /* Open the display */
876 dpy
= XOpenDisplay(displayName
);
879 fprintf(stderr
, "%s: can't open display %s\n", progName
,
880 XDisplayName(displayName
));
883 screen
= DefaultScreen(dpy
);
884 rootWindow
= RootWindow(dpy
, screen
);
885 displayDepth
= DefaultDepth(dpy
, screen
);
886 xFd
= XConnectionNumber(dpy
);
888 /* Icon Daten nach XImage konvertieren */
891 /* Create a window to hold the banner */
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",
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
,
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
|
958 XSetWMHints(dpy
, win
, &wmHints
);
960 XSetCommand(dpy
, win
, argv
, argc
);
964 redrawWindow(&visible
);
967 if (actualTime
!= mytime())
969 actualTime
= mytime();
970 if (actualMinutes
!= (actualTime
/ 60))
975 redrawWindow(&visible
);
978 if (0 == (actualTime
% 2))
980 /* Clean up zombie processes */
982 fprintf(stderr
, "%s: cleaning up zombies (time %ld)\n",
983 progName
, actualTime
);
985 if (NULL
!= commandToExec
)
987 waitpid(0, NULL
, WNOHANG
);
991 if (enableBlinking
&& (!enableYearDisplay
))
995 if (blinkCounter
>= 20*blinkInterval
)
997 if (blinkCounter
>= 2*blinkInterval
)
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
]);
1009 if (blinkCounter
== 10*blinkInterval
)
1011 if (blinkCounter
== blinkInterval
)
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
);
1024 while (XPending(dpy
))
1026 XNextEvent(dpy
, &event
);
1030 if (0 == event
.xexpose
.count
)
1032 redrawWindow(&visible
);
1036 if (NULL
!= commandToExec
)
1040 if ((NULL
== commandBuf
) &&
1041 (!buildCommand(commandToExec
, &commandBuf
,
1042 &commandLength
, &commandIndex
)))
1050 /* We're the child process;
1051 * run the command and exit.
1053 executeCommand(commandBuf
);
1054 /* When the system() call finishes, we're done. */
1058 /* We're the parent process, but
1059 * fork() gave an error.
1064 /* We're the parent process;
1065 * keep on doing what we normally do.
1073 XFreeGC(dpy
, normalGC
);
1074 XDestroyWindow(dpy
, win
);
1075 XDestroyWindow(dpy
, iconWin
);
1077 #ifdef ONLY_SHAPED_WINDOW
1078 XFreePixmap(dpy
, visible
.pixmap
);
1079 #endif /* ONLY_SHAPED_WINDOW */
1088 if (enableYearDisplay
)
1090 poll((struct poll
*) 0, (size_t) 0, 200); /* 1/5 sec */
1094 poll((struct poll
*) 0, (size_t) 0, 50); /* 5/100 sec */
1097 /* We compute the date of next event, in order to avoid polling */
1100 gettimeofday(&nextEvent
,NULL
);
1101 nextEvent
.tv_sec
= 0;
1102 if (nextEvent
.tv_usec
< 500000)
1103 nextEvent
.tv_usec
= 500000-nextEvent
.tv_usec
;
1105 nextEvent
.tv_usec
= 1000000-nextEvent
.tv_usec
;
1109 if (enableYearDisplay
)
1111 nextEvent
.tv_sec
= 86400-actualTime
%86400;
1112 nextEvent
.tv_usec
= 0;
1116 nextEvent
.tv_sec
= 60-actualTime
%60;
1117 nextEvent
.tv_usec
= 0;
1121 FD_SET(xFd
,&xFdSet
);
1122 select(FD_SETSIZE
,&xFdSet
,NULL
,NULL
,&nextEvent
);