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 */
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
{
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 */
150 int timePos12
[NUM_TIME_POSITIONS
] = { 5, 14, 24, 28, 37 };
151 int timePos24
[NUM_TIME_POSITIONS
] = { 4, 8, 17, 22, 31 };
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
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
];
171 XSizeHints sizeHints
;
173 Pixel bgPixel
, fgPixel
;
178 char *className
= "WMClock";
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";
191 char *userWeekdayXpm
;
192 int useUserClockXpm
= 0;
193 int useUserMonthXpm
= 0;
194 int useUserWeekdayXpm
= 0;
196 XpmIcon clockBg
, led
, months
, dateNums
, weekdays
;
202 static struct tm
*localTime
;
204 char *usageText
[] = {
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",
221 char *version
= VERSION
;
223 /**********************************************************************/
224 /* Display usage information */
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");
238 /* Display the program version */
241 fprintf(stderr
, "%s version %s\n", progName
, version
);
245 /* Build the shell command to execute */
246 int buildCommand(char *command
, char **buf
, int *buf_len
, int *i
)
250 status
= append_string_to_buf(buf
, buf_len
, i
, command
);
251 if (APPEND_FAILURE
== status
)
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
)
268 status
= system(command
);
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
);
289 /* Konvertiere XPMIcons nach Pixmaps */
292 static char **clock_xpm
;
294 XWindowAttributes attributes
;
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
);
331 status
= XpmReadFileToPixmap(dpy
, rootWindow
, userClockXpm
,
332 &clockBg
.pixmap
, &clockBg
.mask
,
333 &clockBg
.attributes
);
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
,
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
);
378 status
= XpmReadFileToPixmap(dpy
, rootWindow
, userMonthXpm
,
379 &months
.pixmap
, &months
.mask
,
384 status
= XpmCreatePixmapFromData(dpy
, rootWindow
, month_xpm
,
385 &months
.pixmap
, &months
.mask
,
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
);
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
)
431 while (XCheckTypedWindowEvent(dpy
, w
, Expose
, &dummy
))
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
)
453 XWindowAttributes attributes
;
455 XGetWindowAttributes(dpy
, rootWindow
, &attributes
);
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
);
468 /* Fetch the system time and time zone */
474 gettimeofday(&tv
, &tz
);
479 /* Display the current year in the LED display */
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)
512 int localHour
= localTime
->tm_hour
% 12;
518 if (localTime
->tm_hour
< 12)
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
);
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
;
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)
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
]);
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
)
599 else if (enable12HourClock
)
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
]);
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
]);
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
]);
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
;
661 prog_name
= strrchr(argv0
, '/');
662 if (NULL
== prog_name
)
674 /* Process program arguments and set corresponding options */
675 int processArgs(int argc
, char **argv
)
679 for (i
= 1; i
< argc
; i
++)
681 if (0 == strcmp(argv
[i
], "--"))
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")))
705 commandToExec
= argv
[i
];
707 else if ((0 == strcmp(argv
[i
], "-led")) ||
708 (0 == strcmp(argv
[i
], "-l")) ||
709 (0 == strcmp(argv
[i
], "--led")))
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
726 userClockXpm
= argv
[i
];
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")))
738 userMonthXpm
= argv
[i
];
741 else if ((0 == strcmp(argv
[i
], "-weekdayxpm")) ||
742 (0 == strcmp(argv
[i
], "-w")) ||
743 (0 == strcmp(argv
[i
], "--weekdayxpm")))
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")))
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
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
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")))
796 else if ((0 == strcmp(argv
[i
], "-help")) ||
797 (0 == strcmp(argv
[i
], "-h")) ||
798 (0 == strcmp(argv
[i
], "--help")))
804 fprintf(stderr
, "%s: unrecognized option `%s'\n",
812 /**********************************************************************/
813 int main(int argc
, char **argv
)
816 unsigned int borderWidth
= 0;
817 char *displayName
= NULL
;
819 unsigned long gcMask
;
821 XTextProperty wmName
;
822 XClassHint classHint
;
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
];
858 xPos
[i
] += timePos12
[i
];
862 /* Open the display */
863 dpy
= XOpenDisplay(displayName
);
866 fprintf(stderr
, "%s: can't open display %s\n", progName
,
867 XDisplayName(displayName
));
870 screen
= DefaultScreen(dpy
);
871 rootWindow
= RootWindow(dpy
, screen
);
872 displayDepth
= DefaultDepth(dpy
, screen
);
873 xFd
= XConnectionNumber(dpy
);
875 /* Icon Daten nach XImage konvertieren */
878 /* Create a window to hold the banner */
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",
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
,
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
|
945 XSetWMHints(dpy
, win
, &wmHints
);
947 XSetCommand(dpy
, win
, argv
, argc
);
951 redrawWindow(&visible
);
954 if (actualTime
!= mytime())
956 actualTime
= mytime();
957 if (actualMinutes
!= (actualTime
/ 60))
962 redrawWindow(&visible
);
965 if (enableBlinking
&& (!enableYearDisplay
))
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
]);
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 */
989 fprintf(stderr
, "%s: cleaning up zombies (time %ld)\n",
990 progName
, actualTime
);
992 if (NULL
!= commandToExec
)
994 waitpid(0, NULL
, WNOHANG
);
1000 while (XPending(dpy
))
1002 XNextEvent(dpy
, &event
);
1006 if (0 == event
.xexpose
.count
)
1008 redrawWindow(&visible
);
1012 if (NULL
!= commandToExec
)
1016 if ((NULL
== commandBuf
) &&
1017 (!buildCommand(commandToExec
, &commandBuf
,
1018 &commandLength
, &commandIndex
)))
1026 /* We're the child process;
1027 * run the command and exit.
1029 executeCommand(commandBuf
);
1030 /* When the system() call finishes, we're done. */
1034 /* We're the parent process, but
1035 * fork() gave an error.
1040 /* We're the parent process;
1041 * keep on doing what we normally do.
1049 XFreeGC(dpy
, normalGC
);
1050 XDestroyWindow(dpy
, win
);
1051 XDestroyWindow(dpy
, iconWin
);
1053 #ifdef ONLY_SHAPED_WINDOW
1054 XFreePixmap(dpy
, visible
.pixmap
);
1055 #endif /* ONLY_SHAPED_WINDOW */
1064 if (enableYearDisplay
)
1066 poll((struct poll
*) 0, (size_t) 0, 200); /* 1/5 sec */
1070 poll((struct poll
*) 0, (size_t) 0, 50); /* 5/100 sec */
1073 /* We compute the date of next event, in order to avoid polling */
1076 gettimeofday(&nextEvent
,NULL
);
1077 nextEvent
.tv_sec
= 0;
1078 nextEvent
.tv_usec
= 1000000-nextEvent
.tv_usec
;
1082 if (enableYearDisplay
)
1084 nextEvent
.tv_sec
= 86400-actualTime
%86400;
1085 nextEvent
.tv_usec
= 0;
1089 nextEvent
.tv_sec
= 60-actualTime
%60;
1090 nextEvent
.tv_usec
= 0;
1094 FD_SET(xFd
,&xFdSet
);
1095 select(FD_SETSIZE
,&xFdSet
,NULL
,NULL
,&nextEvent
);