1128 lines
30 KiB
C
1128 lines
30 KiB
C
/* wmclock.c: a dockable clock applet for Window Maker
|
|
* created 1999-Apr-09 jmk
|
|
*
|
|
* by Jim Knoble <jmknoble@pobox.com>
|
|
* Copyright (C) 1999 Jim Knoble
|
|
*
|
|
* Significant portions of this software are derived from asclock by
|
|
* Beat Christen <spiff@longstreet.ch>. Such portions are copyright
|
|
* by Beat Christen and the other authors of asclock.
|
|
*
|
|
* Disclaimer:
|
|
*
|
|
* The software is provided "as is", without warranty of any kind,
|
|
* express or implied, including but not limited to the warranties of
|
|
* merchantability, fitness for a particular purpose and
|
|
* noninfringement. In no event shall the author(s) be liable for any
|
|
* claim, damages or other liability, whether in an action of
|
|
* contract, tort or otherwise, arising from, out of or in connection
|
|
* with the software or the use or other dealings in the software.
|
|
*/
|
|
|
|
#include <sys/select.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/time.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <X11/Xatom.h>
|
|
#include <X11/Xlib.h>
|
|
#include <X11/xpm.h>
|
|
#include <X11/extensions/shape.h>
|
|
|
|
#include "dynlist.h"
|
|
|
|
/**********************************************************************/
|
|
#define ONLY_SHAPED_WINDOW 1
|
|
|
|
#define NUM_TIME_POSITIONS 5
|
|
#define NUM_X_POSITIONS 11
|
|
#define NUM_Y_POSITIONS 4
|
|
|
|
#define DIGIT_1_X_POS 0
|
|
#define DIGIT_2_X_POS 1
|
|
#define DIGIT_3_X_POS 3
|
|
#define DIGIT_4_X_POS 4
|
|
#define DIGIT_Y_POS 0
|
|
#define LED_NUM_Y_OFFSET 0
|
|
#define LED_THIN_1_X_OFFSET 13
|
|
#define LED_NUM_WIDTH 9
|
|
#define LED_NUM_HEIGHT 11
|
|
#define LED_THIN_1_WIDTH 5
|
|
|
|
#define COLON_X_POS 2
|
|
#define COLON_Y_POS DIGIT_Y_POS
|
|
#define COLON_X_OFFSET 90
|
|
#define COLON_Y_OFFSET 0
|
|
#define BLANK_X_OFFSET 119
|
|
#define BLANK_Y_OFFSET COLON_Y_OFFSET
|
|
#define COLON_WIDTH 3
|
|
#define COLON_HEIGHT 11
|
|
|
|
#define AMPM_X_POS 5
|
|
#define AM_X_OFFSET 94
|
|
#define AM_Y_OFFSET 5
|
|
#define PM_X_OFFSET 107
|
|
#define PM_Y_OFFSET 5
|
|
#define AM_WIDTH 12
|
|
#define AM_HEIGHT 6
|
|
#define PM_WIDTH 11
|
|
#define PM_HEIGHT 6
|
|
|
|
#define MONTH_X_POS 10
|
|
#define MONTH_Y_POS 3
|
|
#define MONTH_X_OFFSET 0
|
|
#define MONTH_WIDTH 22
|
|
#define MONTH_HEIGHT 6
|
|
|
|
#define DATE_LEFT_X_POS 7
|
|
#define DATE_CENTER_X_POS 8
|
|
#define DATE_RIGHT_X_POS 9
|
|
#define DATE_Y_POS 2
|
|
#define DATE_Y_OFFSET 0
|
|
#define DATE_NUM_WIDTH 9
|
|
#define DATE_NUM_HEIGHT 13
|
|
|
|
#define WEEKDAY_X_POS 6
|
|
#define WEEKDAY_Y_POS 1
|
|
#define WEEKDAY_X_OFFSET 0
|
|
#define WEEKDAY_WIDTH 21
|
|
#define WEEKDAY_HEIGHT 6
|
|
|
|
#define OUR_WINDOW_EVENTS (ExposureMask | ButtonPressMask | StructureNotifyMask)
|
|
|
|
#define LED_XPM_BRIGHT_LINE_INDEX 3
|
|
#define LED_XPM_BRIGHT_CHAR '+'
|
|
#define LED_XPM_DIM_LINE_INDEX 4
|
|
#define LED_XPM_DIM_CHAR '@'
|
|
|
|
#define DEFAULT_XPM_CLOSENESS 40000
|
|
|
|
#define DIM_NUMERATOR 5
|
|
#define DIM_DENOMINATOR 10
|
|
#define makeDimColor(c) (((c) * DIM_NUMERATOR) / DIM_DENOMINATOR)
|
|
|
|
/**********************************************************************/
|
|
#ifndef ONLY_SHAPED_WINDOW
|
|
# include "clk.xpm"
|
|
#endif /* !ONLY_SHAPED_WINDOW */
|
|
#include MONTH_XPM
|
|
#include WEEKDAY_XPM
|
|
#include "xpm/date.xpm"
|
|
#include "xpm/led.xpm"
|
|
#include "xpm/mask.xbm"
|
|
#include "xpm/mask.xpm"
|
|
|
|
typedef struct _XpmIcon {
|
|
Pixmap pixmap;
|
|
Pixmap mask;
|
|
XpmAttributes attributes;
|
|
} XpmIcon;
|
|
|
|
void showUsage(void);
|
|
void showVersion(void);
|
|
int buildCommand(char *, char **, int *, int *);
|
|
void executeCommand(char *);
|
|
void showError(const char *, const char*);
|
|
void showFatalError(const char *, const char*);
|
|
void GetXpms(void);
|
|
int flushExposeEvents(Window);
|
|
void redrawWindow(XpmIcon *);
|
|
Pixel GetColor(const char *);
|
|
int mytime(void);
|
|
void showYear(void);
|
|
void showTime12(void);
|
|
void showTime24(void);
|
|
void showTime(void);
|
|
char* extractProgName(char *);
|
|
int processArgs(int, char **);
|
|
|
|
/**********************************************************************/
|
|
int enable12HourClock = 0; /* default value is 24h format */
|
|
int enableShapedWindow = 1; /* default value is noshape */
|
|
int enableBlinking = 1; /* default is blinking */
|
|
int startIconified = 0; /* default is not iconified */
|
|
int enableYearDisplay = 0; /* default is to show time, not year */
|
|
unsigned int blinkInterval = 2; /* default is a 2-second blink cycle */
|
|
|
|
int timePos12[NUM_TIME_POSITIONS] = { 5, 14, 24, 28, 37 };
|
|
int timePos24[NUM_TIME_POSITIONS] = { 4, 8, 17, 22, 31 };
|
|
/* with shape */
|
|
int xPosShaped[NUM_X_POSITIONS] = { 0, 0, 0, 0, 0, 40, 17, 17, 22, 27, 15 };
|
|
int yPosShaped[NUM_Y_POSITIONS] = { 3, 21, 30, 45 };
|
|
|
|
#ifndef ONLY_SHAPED_WINDOW
|
|
/* no shape */
|
|
int xPosUnshaped[NUM_X_POSITIONS] = { 5, 5, 5, 5, 5, 45, 21, 21, 26, 31, 19 };
|
|
int yPosUnshaped[NUM_Y_POSITIONS] = { 7, 25, 34, 49 };
|
|
#endif /* !ONLY_SHAPED_WINDOW */
|
|
|
|
int xPos[NUM_X_POSITIONS];
|
|
int yPos[NUM_Y_POSITIONS];
|
|
|
|
Display *dpy;
|
|
Window rootWindow;
|
|
int screen;
|
|
int xFd;
|
|
fd_set xFdSet;
|
|
int displayDepth;
|
|
XSizeHints sizeHints;
|
|
XWMHints wmHints;
|
|
Pixel bgPixel, fgPixel;
|
|
GC normalGC;
|
|
Window iconWin, win;
|
|
|
|
char *progName;
|
|
char *className = "WMClock";
|
|
char *geometry = "";
|
|
char *ledColor = "LightSeaGreen";
|
|
|
|
char *commandToExec = NULL;
|
|
char *commandBuf = NULL;
|
|
int commandLength = 0;
|
|
int commandIndex = 0;
|
|
|
|
char *errColorCells = "not enough free color cells or xpm not found\n";
|
|
|
|
char *userClockXpm;
|
|
char *userMonthXpm;
|
|
char *userWeekdayXpm;
|
|
int useUserClockXpm = 0;
|
|
int useUserMonthXpm = 0;
|
|
int useUserWeekdayXpm = 0;
|
|
|
|
XpmIcon clockBg, led, months, dateNums, weekdays;
|
|
XpmIcon visible;
|
|
|
|
time_t actualTime;
|
|
long actualMinutes;
|
|
|
|
static struct tm *localTime;
|
|
|
|
char *usageText[] = {
|
|
"Options:",
|
|
" -12 show 12-hour time (am/pm)",
|
|
" -24 show 24-hour time",
|
|
" -year show year instead of time",
|
|
" -noblink don't blink",
|
|
" -interval <seconds> set blink interval",
|
|
" -exe <command> start <command> on mouse click",
|
|
" -led <color> use <color> as color of led",
|
|
#ifndef ONLY_SHAPED_WINDOW
|
|
" -clockxpm <filename> get clock background from pixmap in <filename>",
|
|
#endif /* !ONLY_SHAPED_WINDOW */
|
|
" -monthxpm <filename> get month names from pixmap in <filename>",
|
|
" -weekdayxpm <filename> get weekday names from pixmap in <filename>",
|
|
" -version display the version",
|
|
NULL
|
|
};
|
|
|
|
char *version = VERSION;
|
|
|
|
/**********************************************************************/
|
|
/* Display usage information */
|
|
void showUsage(void)
|
|
{
|
|
char **cpp;
|
|
|
|
fprintf(stderr, "Usage: %s [option [option ...]]\n\n", progName);
|
|
for (cpp = usageText; *cpp; cpp++)
|
|
{
|
|
fprintf(stderr, "%s\n", *cpp);
|
|
}
|
|
fprintf(stderr,"\n");
|
|
exit(1);
|
|
}
|
|
|
|
/* Display the program version */
|
|
void showVersion()
|
|
{
|
|
fprintf(stderr, "%s version %s\n", progName, version);
|
|
exit(1);
|
|
}
|
|
|
|
/* Build the shell command to execute */
|
|
int buildCommand(char *command, char **buf, int *buf_len, int *i)
|
|
{
|
|
int status;
|
|
|
|
status = append_string_to_buf(buf, buf_len, i, command);
|
|
if (APPEND_FAILURE == status)
|
|
{
|
|
return (0);
|
|
}
|
|
status = append_string_to_buf(buf, buf_len, i, " &");
|
|
return ((APPEND_FAILURE == status) ? 0 : 1);
|
|
}
|
|
|
|
/* Execute the given shell command */
|
|
void executeCommand(char *command)
|
|
{
|
|
int status;
|
|
|
|
if (NULL == command)
|
|
{
|
|
return;
|
|
}
|
|
status = system(command);
|
|
|
|
if (-1 == status)
|
|
{
|
|
perror("system");
|
|
}
|
|
}
|
|
|
|
/* Display an error message */
|
|
void showError(const char *message, const char *data)
|
|
{
|
|
fprintf(stderr,"%s: can't %s %s\n", progName, message, data);
|
|
}
|
|
|
|
/* Display an error message and exit */
|
|
void showFatalError(const char *message, const char *data)
|
|
{
|
|
showError(message, data);
|
|
exit(1);
|
|
}
|
|
|
|
/* Konvertiere XPMIcons nach Pixmaps */
|
|
void GetXpms(void)
|
|
{
|
|
static char **clock_xpm;
|
|
XColor color;
|
|
XWindowAttributes attributes;
|
|
char ledBright[64];
|
|
char ledDim[64];
|
|
int status;
|
|
|
|
#ifdef ONLY_SHAPED_WINDOW
|
|
clock_xpm = mask_xpm;
|
|
#else /* !ONLY_SHAPED_WINDOW */
|
|
clock_xpm = enableShapedWindow ? mask_xpm : clk_xpm;
|
|
#endif /* ONLY_SHAPED_WINDOW */
|
|
|
|
/* for the colormap */
|
|
XGetWindowAttributes(dpy, rootWindow, &attributes);
|
|
|
|
/* get user-defined color */
|
|
if (!XParseColor(dpy, attributes.colormap, ledColor, &color))
|
|
{
|
|
showError("parse color", ledColor);
|
|
}
|
|
|
|
sprintf(ledBright, "%c c #%04X%04X%04X", LED_XPM_BRIGHT_CHAR,
|
|
color.red, color.green, color.blue);
|
|
led_xpm[LED_XPM_BRIGHT_LINE_INDEX] = &ledBright[0];
|
|
|
|
color.red = makeDimColor(color.red);
|
|
color.green = makeDimColor(color.green);
|
|
color.blue = makeDimColor(color.blue);
|
|
sprintf(&ledDim[0], "%c c #%04X%04X%04X", LED_XPM_DIM_CHAR,
|
|
color.red, color.green, color.blue);
|
|
led_xpm[LED_XPM_DIM_LINE_INDEX] = &ledDim[0];
|
|
|
|
clockBg.attributes.closeness = DEFAULT_XPM_CLOSENESS;
|
|
clockBg.attributes.valuemask |=
|
|
(XpmReturnPixels | XpmReturnExtensions | XpmCloseness);
|
|
|
|
if (useUserClockXpm)
|
|
{
|
|
status = XpmReadFileToPixmap(dpy, rootWindow, userClockXpm,
|
|
&clockBg.pixmap, &clockBg.mask,
|
|
&clockBg.attributes);
|
|
}
|
|
else
|
|
{
|
|
status = XpmCreatePixmapFromData(dpy, rootWindow, clock_xpm,
|
|
&clockBg.pixmap, &clockBg.mask,
|
|
&clockBg.attributes);
|
|
}
|
|
if (XpmSuccess != status)
|
|
{
|
|
showFatalError("create clock pixmap:", errColorCells);
|
|
}
|
|
|
|
#ifdef ONLY_SHAPED_WINDOW
|
|
visible.attributes.depth = displayDepth;
|
|
visible.attributes.width = clockBg.attributes.width;
|
|
visible.attributes.height = clockBg.attributes.height;
|
|
visible.pixmap = XCreatePixmap(dpy, rootWindow, visible.attributes.width,
|
|
visible.attributes.height,
|
|
visible.attributes.depth);
|
|
#else /* !ONLY_SHAPED_WINDOW */
|
|
visible.attributes.closeness = DEFAULT_XPM_CLOSENESS;
|
|
visible.attributes.valuemask |=
|
|
(XpmReturnPixels | XpmReturnExtensions | XpmCloseness);
|
|
status = XpmCreatePixmapFromData(dpy, rootWindow, clk_xpm,
|
|
&visible.pixmap, &visible.mask,
|
|
&visible.attributes);
|
|
#endif /* ONLY_SHAPED_WINDOW */
|
|
|
|
led.attributes.closeness = DEFAULT_XPM_CLOSENESS;
|
|
led.attributes.valuemask |=
|
|
(XpmReturnPixels | XpmReturnExtensions | XpmCloseness);
|
|
status = XpmCreatePixmapFromData(dpy, rootWindow, led_xpm,
|
|
&led.pixmap, &led.mask,
|
|
&led.attributes);
|
|
if (XpmSuccess != status)
|
|
{
|
|
showFatalError("create led pixmap:", errColorCells);
|
|
}
|
|
|
|
months.attributes.closeness = DEFAULT_XPM_CLOSENESS;
|
|
months.attributes.valuemask |=
|
|
(XpmReturnPixels | XpmReturnExtensions | XpmCloseness);
|
|
if (useUserMonthXpm)
|
|
{
|
|
status = XpmReadFileToPixmap(dpy, rootWindow, userMonthXpm,
|
|
&months.pixmap, &months.mask,
|
|
&months.attributes);
|
|
}
|
|
else
|
|
{
|
|
status = XpmCreatePixmapFromData(dpy, rootWindow, month_xpm,
|
|
&months.pixmap, &months.mask,
|
|
&months.attributes);
|
|
}
|
|
if (XpmSuccess != status)
|
|
{
|
|
showFatalError("create month pixmap:", errColorCells);
|
|
}
|
|
|
|
dateNums.attributes.closeness = DEFAULT_XPM_CLOSENESS;
|
|
dateNums.attributes.valuemask |=
|
|
(XpmReturnPixels | XpmReturnExtensions | XpmCloseness);
|
|
status = XpmCreatePixmapFromData(dpy, rootWindow, date_xpm,
|
|
&dateNums.pixmap, &dateNums.mask,
|
|
&dateNums.attributes);
|
|
if (XpmSuccess != status)
|
|
{
|
|
showFatalError("create date pixmap:", errColorCells);
|
|
}
|
|
|
|
weekdays.attributes.closeness = DEFAULT_XPM_CLOSENESS;
|
|
weekdays.attributes.valuemask |=
|
|
(XpmReturnPixels | XpmReturnExtensions | XpmCloseness);
|
|
if (useUserWeekdayXpm)
|
|
{
|
|
status = XpmReadFileToPixmap(dpy, rootWindow, userWeekdayXpm,
|
|
&weekdays.pixmap, &weekdays.mask,
|
|
&weekdays.attributes);
|
|
}
|
|
else
|
|
{
|
|
status = XpmCreatePixmapFromData(dpy, rootWindow, weekday_xpm,
|
|
&weekdays.pixmap, &weekdays.mask,
|
|
&weekdays.attributes);
|
|
}
|
|
if (XpmSuccess != status)
|
|
{
|
|
showFatalError("create weekday pixmap:", errColorCells);
|
|
}
|
|
}
|
|
|
|
/* Remove expose events for a specific window from the queue */
|
|
int flushExposeEvents(Window w)
|
|
{
|
|
XEvent dummy;
|
|
int i = 0;
|
|
|
|
while (XCheckTypedWindowEvent(dpy, w, Expose, &dummy))
|
|
{
|
|
i++;
|
|
}
|
|
return(i);
|
|
}
|
|
|
|
/* (Re-)Draw the main window and the icon window */
|
|
void redrawWindow(XpmIcon *v)
|
|
{
|
|
flushExposeEvents(iconWin);
|
|
XCopyArea(dpy, v->pixmap, iconWin, normalGC,
|
|
0, 0, v->attributes.width, v->attributes.height, 0, 0);
|
|
flushExposeEvents(win);
|
|
XCopyArea(dpy, v->pixmap, win, normalGC,
|
|
0, 0, v->attributes.width, v->attributes.height, 0, 0);
|
|
}
|
|
|
|
/* Get a Pixel for the given color name */
|
|
Pixel GetColor(const char *colorName)
|
|
{
|
|
XColor color;
|
|
XWindowAttributes attributes;
|
|
|
|
XGetWindowAttributes(dpy, rootWindow, &attributes);
|
|
color.pixel = 0;
|
|
if (!XParseColor(dpy, attributes.colormap, colorName, &color))
|
|
{
|
|
showError("parse color", colorName);
|
|
}
|
|
else if (!XAllocColor(dpy, attributes.colormap, &color))
|
|
{
|
|
showError("allocate color", colorName);
|
|
}
|
|
return(color.pixel);
|
|
}
|
|
|
|
/* Fetch the system time and time zone */
|
|
int mytime(void)
|
|
{
|
|
struct timeval tv;
|
|
struct timezone tz;
|
|
|
|
gettimeofday(&tv, &tz);
|
|
|
|
return(tv.tv_sec);
|
|
}
|
|
|
|
/* Display the current year in the LED display */
|
|
void showYear(void)
|
|
{
|
|
int year;
|
|
int digitXOffset;
|
|
int digitYOffset;
|
|
|
|
year = localTime->tm_year + 1900;
|
|
|
|
digitYOffset = LED_NUM_Y_OFFSET;
|
|
digitXOffset = LED_NUM_WIDTH * (year / 1000);
|
|
XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
|
|
digitXOffset , digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
|
|
xPos[DIGIT_1_X_POS], yPos[DIGIT_Y_POS]);
|
|
digitXOffset = LED_NUM_WIDTH * ((year / 100) % 10);
|
|
XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
|
|
digitXOffset , digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
|
|
xPos[DIGIT_2_X_POS], yPos[DIGIT_Y_POS]);
|
|
digitXOffset = LED_NUM_WIDTH * ((year / 10) % 10);
|
|
XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
|
|
digitXOffset , digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
|
|
xPos[DIGIT_3_X_POS], yPos[DIGIT_Y_POS]);
|
|
digitXOffset = LED_NUM_WIDTH * (year % 10);
|
|
XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
|
|
digitXOffset , digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
|
|
xPos[DIGIT_4_X_POS], yPos[DIGIT_Y_POS]);
|
|
}
|
|
|
|
/* Display time in twelve-hour mode, with am/pm indicator */
|
|
void showTime12(void)
|
|
{
|
|
int digitXOffset;
|
|
int digitYOffset;
|
|
int localHour = localTime->tm_hour % 12;
|
|
|
|
if (0 == localHour)
|
|
{
|
|
localHour = 12;
|
|
}
|
|
if (localTime->tm_hour < 12)
|
|
{
|
|
/* AM */
|
|
XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
|
|
AM_X_OFFSET, AM_Y_OFFSET, AM_WIDTH, AM_HEIGHT,
|
|
xPos[AMPM_X_POS], yPos[DIGIT_Y_POS] + AM_Y_OFFSET);
|
|
}
|
|
else
|
|
{
|
|
/* PM */
|
|
XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
|
|
PM_X_OFFSET, PM_Y_OFFSET, PM_WIDTH, PM_HEIGHT,
|
|
xPos[AMPM_X_POS], yPos[DIGIT_Y_POS] + PM_Y_OFFSET);
|
|
}
|
|
|
|
digitYOffset = LED_NUM_Y_OFFSET;
|
|
if (localHour > 9)
|
|
{
|
|
digitXOffset = LED_THIN_1_X_OFFSET;
|
|
XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
|
|
digitXOffset, digitYOffset, LED_THIN_1_WIDTH, LED_NUM_HEIGHT,
|
|
xPos[DIGIT_1_X_POS], yPos[DIGIT_Y_POS]);
|
|
}
|
|
digitXOffset = LED_NUM_WIDTH * (localHour % 10);
|
|
XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
|
|
digitXOffset, digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
|
|
xPos[DIGIT_2_X_POS], yPos[DIGIT_Y_POS]);
|
|
digitXOffset = LED_NUM_WIDTH * (localTime->tm_min / 10);
|
|
XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
|
|
digitXOffset, digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
|
|
xPos[DIGIT_3_X_POS], yPos[DIGIT_Y_POS]);
|
|
digitXOffset = LED_NUM_WIDTH * (localTime->tm_min % 10);
|
|
XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
|
|
digitXOffset, digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
|
|
xPos[DIGIT_4_X_POS], yPos[DIGIT_Y_POS]);
|
|
}
|
|
|
|
/* Display time in 24-hour mode, without am/pm indicator */
|
|
void showTime24(void)
|
|
{
|
|
int digitXOffset;
|
|
int digitYOffset;
|
|
|
|
digitYOffset = LED_NUM_Y_OFFSET;
|
|
digitXOffset = LED_NUM_WIDTH * (localTime->tm_hour / 10);
|
|
XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
|
|
digitXOffset, digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
|
|
xPos[DIGIT_1_X_POS], yPos[DIGIT_Y_POS]);
|
|
digitXOffset = LED_NUM_WIDTH * (localTime->tm_hour % 10);
|
|
XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
|
|
digitXOffset, digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
|
|
xPos[DIGIT_2_X_POS], yPos[DIGIT_Y_POS]);
|
|
digitXOffset = LED_NUM_WIDTH * (localTime->tm_min / 10);
|
|
XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
|
|
digitXOffset, digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
|
|
xPos[DIGIT_3_X_POS], yPos[DIGIT_Y_POS]);
|
|
digitXOffset = LED_NUM_WIDTH * (localTime->tm_min % 10);
|
|
XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
|
|
digitXOffset, digitYOffset, LED_NUM_WIDTH, LED_NUM_HEIGHT,
|
|
xPos[DIGIT_4_X_POS], yPos[DIGIT_Y_POS]);
|
|
}
|
|
|
|
void showTime(void)
|
|
{
|
|
int xOffset;
|
|
int yOffset;
|
|
|
|
/* Zeit auslesen */
|
|
actualTime = mytime();
|
|
actualMinutes = actualTime / 60;
|
|
|
|
localTime = localtime(&actualTime);
|
|
|
|
/* leere clock holen */
|
|
XCopyArea(dpy, clockBg.pixmap, visible.pixmap, normalGC,
|
|
0, 0, sizeHints.width, sizeHints.height, 0, 0);
|
|
|
|
if (enableYearDisplay)
|
|
{
|
|
showYear();
|
|
}
|
|
else if (enable12HourClock)
|
|
{
|
|
showTime12();
|
|
}
|
|
else
|
|
{
|
|
showTime24();
|
|
}
|
|
|
|
/* Monat */
|
|
xOffset = MONTH_X_OFFSET;
|
|
yOffset = MONTH_HEIGHT * (localTime->tm_mon);
|
|
XCopyArea(dpy, months.pixmap, visible.pixmap, normalGC,
|
|
xOffset, yOffset, MONTH_WIDTH, MONTH_HEIGHT,
|
|
xPos[MONTH_X_POS], yPos[MONTH_Y_POS]);
|
|
|
|
/* Datum */
|
|
yOffset = DATE_Y_OFFSET;
|
|
if (localTime->tm_mday > 9)
|
|
{
|
|
xOffset = DATE_NUM_WIDTH * (((localTime->tm_mday / 10) + 9) % 10);
|
|
XCopyArea(dpy, dateNums.pixmap, visible.pixmap, normalGC,
|
|
xOffset, yOffset, DATE_NUM_WIDTH, DATE_NUM_HEIGHT,
|
|
xPos[DATE_LEFT_X_POS], yPos[DATE_Y_POS]);
|
|
xOffset = DATE_NUM_WIDTH * (((localTime->tm_mday % 10) + 9) % 10);
|
|
XCopyArea(dpy, dateNums.pixmap, visible.pixmap, normalGC,
|
|
xOffset, yOffset, DATE_NUM_WIDTH, DATE_NUM_HEIGHT,
|
|
xPos[DATE_RIGHT_X_POS], yPos[DATE_Y_POS]);
|
|
}
|
|
else
|
|
{
|
|
xOffset = DATE_NUM_WIDTH * (localTime->tm_mday - 1);
|
|
XCopyArea(dpy, dateNums.pixmap, visible.pixmap, normalGC,
|
|
xOffset, yOffset, DATE_NUM_WIDTH, DATE_NUM_HEIGHT,
|
|
xPos[DATE_CENTER_X_POS], yPos[DATE_Y_POS]);
|
|
}
|
|
|
|
/* Wochentag */
|
|
xOffset = WEEKDAY_X_OFFSET;
|
|
yOffset = WEEKDAY_HEIGHT * ((localTime->tm_wday + 6) % 7);
|
|
XCopyArea(dpy, weekdays.pixmap, visible.pixmap, normalGC,
|
|
xOffset, yOffset, WEEKDAY_WIDTH, WEEKDAY_HEIGHT,
|
|
xPos[WEEKDAY_X_POS], yPos[WEEKDAY_Y_POS]);
|
|
|
|
if ((!enableBlinking) && (!enableYearDisplay))
|
|
{
|
|
/* Sekunden Doppelpunkt ein */
|
|
xOffset = COLON_X_OFFSET;
|
|
yOffset = COLON_Y_OFFSET;
|
|
XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
|
|
xOffset, yOffset, COLON_WIDTH, COLON_HEIGHT,
|
|
xPos[COLON_X_POS], yPos[COLON_Y_POS]);
|
|
}
|
|
}
|
|
|
|
/* Extract program name from the zeroth program argument */
|
|
char *extractProgName(char *argv0)
|
|
{
|
|
char *prog_name = NULL;
|
|
|
|
if (NULL != argv0)
|
|
{
|
|
prog_name = strrchr(argv0, '/');
|
|
if (NULL == prog_name)
|
|
{
|
|
prog_name = argv0;
|
|
}
|
|
else
|
|
{
|
|
prog_name++;
|
|
}
|
|
}
|
|
return (prog_name);
|
|
}
|
|
|
|
/* Process program arguments and set corresponding options */
|
|
int processArgs(int argc, char **argv)
|
|
{
|
|
int i;
|
|
|
|
for (i = 1; i < argc; i++)
|
|
{
|
|
if (0 == strcmp(argv[i], "--"))
|
|
{
|
|
break;
|
|
}
|
|
else if ((0 == strcmp(argv[i], "-12")) ||
|
|
(0 == strcmp(argv[i], "-1")) ||
|
|
(0 == strcmp(argv[i], "--12")))
|
|
{
|
|
enable12HourClock = 1;
|
|
}
|
|
else if ((0 == strcmp(argv[i], "-24")) ||
|
|
(0 == strcmp(argv[i], "-2")) ||
|
|
(0 == strcmp(argv[i], "--24")))
|
|
{
|
|
enable12HourClock = 0;
|
|
}
|
|
else if ((0 == strcmp(argv[i], "-exe")) ||
|
|
(0 == strcmp(argv[i], "-e")) ||
|
|
(0 == strcmp(argv[i], "--exe")))
|
|
{
|
|
if (++i >= argc)
|
|
{
|
|
showUsage();
|
|
}
|
|
commandToExec = argv[i];
|
|
}
|
|
else if ((0 == strcmp(argv[i], "-led")) ||
|
|
(0 == strcmp(argv[i], "-l")) ||
|
|
(0 == strcmp(argv[i], "--led")))
|
|
{
|
|
if (++i >= argc)
|
|
{
|
|
showUsage();
|
|
}
|
|
ledColor = argv[i];
|
|
}
|
|
else if ((0 == strcmp(argv[i], "-clockxpm")) ||
|
|
(0 == strcmp(argv[i], "-c")) ||
|
|
(0 == strcmp(argv[i], "--clockxpm")))
|
|
{
|
|
#ifndef ONLY_SHAPED_WINDOW
|
|
if (++i >= argc)
|
|
{
|
|
showUsage();
|
|
}
|
|
userClockXpm = argv[i];
|
|
useUserClockXpm = 1;
|
|
#endif /* !ONLY_SHAPED_WINDOW */
|
|
}
|
|
else if ((0 == strcmp(argv[i], "-monthxpm")) ||
|
|
(0 == strcmp(argv[i], "-m")) ||
|
|
(0 == strcmp(argv[i], "--monthxpm")))
|
|
{
|
|
if (++i >= argc)
|
|
{
|
|
showUsage();
|
|
}
|
|
userMonthXpm = argv[i];
|
|
useUserMonthXpm = 1;
|
|
}
|
|
else if ((0 == strcmp(argv[i], "-weekdayxpm")) ||
|
|
(0 == strcmp(argv[i], "-w")) ||
|
|
(0 == strcmp(argv[i], "--weekdayxpm")))
|
|
{
|
|
if (++i >= argc)
|
|
{
|
|
showUsage();
|
|
}
|
|
userWeekdayXpm = argv[i];
|
|
useUserWeekdayXpm = 1;
|
|
}
|
|
else if ((0 == strcmp(argv[i], "-noblink")) ||
|
|
(0 == strcmp(argv[i], "-n")) ||
|
|
(0 == strcmp(argv[i], "--noblink")))
|
|
{
|
|
enableBlinking = 0;
|
|
}
|
|
else if ((0 == strcmp(argv[i], "-year")) ||
|
|
(0 == strcmp(argv[i], "-y")) ||
|
|
(0 == strcmp(argv[i], "--year")))
|
|
{
|
|
enableYearDisplay = 1;
|
|
}
|
|
else if ((0 == strcmp(argv[i], "-position")) ||
|
|
(0 == strcmp(argv[i], "-p")) ||
|
|
(0 == strcmp(argv[i], "--position")))
|
|
{
|
|
#ifndef ONLY_SHAPED_WINDOW
|
|
if (++i >= argc)
|
|
{
|
|
showUsage();
|
|
}
|
|
geometry = argv[i];
|
|
#endif /* !ONLY_SHAPED_WINDOW */
|
|
}
|
|
else if ((0 == strcmp(argv[i], "-shape")) ||
|
|
(0 == strcmp(argv[i], "-s")) ||
|
|
(0 == strcmp(argv[i], "--shape")))
|
|
{
|
|
enableShapedWindow = 1;
|
|
}
|
|
else if ((0 == strcmp(argv[i], "-iconic")) ||
|
|
(0 == strcmp(argv[i], "-i")) ||
|
|
(0 == strcmp(argv[i], "--iconic")))
|
|
{
|
|
#ifndef ONLY_SHAPED_WINDOW
|
|
startIconified = 1;
|
|
#endif /* !ONLY_SHAPED_WINDOW */
|
|
}
|
|
else if ((0 == strcmp(argv[i], "-version")) ||
|
|
(0 == strcmp(argv[i], "-V")) ||
|
|
(0 == strcmp(argv[i], "--version")))
|
|
{
|
|
showVersion();
|
|
}
|
|
else if ((0 == strcmp(argv[i], "-help")) ||
|
|
(0 == strcmp(argv[i], "-h")) ||
|
|
(0 == strcmp(argv[i], "--help")))
|
|
{
|
|
showUsage();
|
|
}
|
|
else if ((0 == strcmp(argv[i], "-interval")) ||
|
|
(0 == strcmp(argv[i], "--interval")))
|
|
{
|
|
if (++i >= argc)
|
|
{
|
|
showUsage();
|
|
}
|
|
blinkInterval = atoi(argv[i]);
|
|
}
|
|
|
|
else
|
|
{
|
|
fprintf(stderr, "%s: unrecognized option `%s'\n",
|
|
progName, argv[i]);
|
|
showUsage();
|
|
}
|
|
}
|
|
return (i);
|
|
}
|
|
|
|
/**********************************************************************/
|
|
int main(int argc, char **argv)
|
|
{
|
|
int i;
|
|
unsigned int borderWidth = 0;
|
|
char *displayName = NULL;
|
|
XGCValues gcValues;
|
|
unsigned long gcMask;
|
|
XEvent event;
|
|
XTextProperty wmName;
|
|
XClassHint classHint;
|
|
Pixmap shapeMask;
|
|
struct timeval nextEvent;
|
|
unsigned int blinkCounter = 0;
|
|
|
|
/* Parse command line options */
|
|
progName = extractProgName(argv[0]);
|
|
processArgs(argc, argv);
|
|
|
|
/* init led position */
|
|
#ifndef ONLY_SHAPED_WINDOW
|
|
for (i = 0; i < NUM_Y_POSITIONS; i++)
|
|
{
|
|
yPos[i] = enableShapedWindow ? yPosShaped[i] : yPosUnshaped[i];
|
|
}
|
|
for (i = 0; i < NUM_X_POSITIONS; i++)
|
|
{
|
|
xPos[i] = enableShapedWindow ? xPosShaped[i] : xPosUnshaped[i];
|
|
}
|
|
#else /* ONLY_SHAPED_WINDOW */
|
|
for (i = 0; i < NUM_Y_POSITIONS; i++)
|
|
{
|
|
yPos[i] = yPosShaped[i];
|
|
}
|
|
for (i = 0; i < NUM_X_POSITIONS; i++)
|
|
{
|
|
xPos[i] = xPosShaped[i];
|
|
}
|
|
#endif /* !ONLY_SHAPED_WINDOW */
|
|
for (i = 0; i < NUM_TIME_POSITIONS; i++)
|
|
{
|
|
if (enable12HourClock && (!enableYearDisplay))
|
|
{
|
|
xPos[i] += timePos24[i];
|
|
}
|
|
else
|
|
{
|
|
xPos[i] += timePos12[i];
|
|
}
|
|
}
|
|
|
|
/* Open the display */
|
|
dpy = XOpenDisplay(displayName);
|
|
if (NULL == dpy)
|
|
{
|
|
fprintf(stderr, "%s: can't open display %s\n", progName,
|
|
XDisplayName(displayName));
|
|
exit(1);
|
|
}
|
|
screen = DefaultScreen(dpy);
|
|
rootWindow = RootWindow(dpy, screen);
|
|
displayDepth = DefaultDepth(dpy, screen);
|
|
xFd = XConnectionNumber(dpy);
|
|
|
|
/* Icon Daten nach XImage konvertieren */
|
|
GetXpms();
|
|
|
|
/* Create a window to hold the banner */
|
|
sizeHints.x = 0;
|
|
sizeHints.y = 0;
|
|
sizeHints.min_width = clockBg.attributes.width;
|
|
sizeHints.min_height = clockBg.attributes.height;
|
|
sizeHints.max_width = clockBg.attributes.width;
|
|
sizeHints.max_height = clockBg.attributes.height;
|
|
sizeHints.base_width = clockBg.attributes.width;
|
|
sizeHints.base_height = clockBg.attributes.height;
|
|
sizeHints.flags = USSize | USPosition | PMinSize | PMaxSize | PBaseSize;
|
|
|
|
bgPixel = GetColor("white");
|
|
fgPixel = GetColor("black");
|
|
|
|
XWMGeometry(dpy, screen, geometry, NULL, borderWidth, &sizeHints,
|
|
&sizeHints.x, &sizeHints.y, &sizeHints.width, &sizeHints.height,
|
|
&sizeHints.win_gravity);
|
|
sizeHints.width = clockBg.attributes.width;
|
|
sizeHints.height = clockBg.attributes.height;
|
|
|
|
win = XCreateSimpleWindow(dpy, rootWindow, sizeHints.x, sizeHints.y,
|
|
sizeHints.width, sizeHints.height,
|
|
borderWidth, fgPixel, bgPixel);
|
|
iconWin = XCreateSimpleWindow(dpy, win, sizeHints.x, sizeHints.y,
|
|
sizeHints.width, sizeHints.height,
|
|
borderWidth, fgPixel, bgPixel);
|
|
|
|
/* Hints aktivieren */
|
|
XSetWMNormalHints(dpy, win, &sizeHints);
|
|
classHint.res_name = progName;
|
|
classHint.res_class = className;
|
|
XSetClassHint(dpy, win, &classHint);
|
|
|
|
XSelectInput(dpy, win, OUR_WINDOW_EVENTS);
|
|
XSelectInput(dpy, iconWin, OUR_WINDOW_EVENTS);
|
|
|
|
if (0 == XStringListToTextProperty(&progName, 1, &wmName))
|
|
{
|
|
fprintf(stderr, "%s: can't allocate window name text property\n",
|
|
progName);
|
|
exit(-1);
|
|
}
|
|
XSetWMName(dpy, win, &wmName);
|
|
|
|
/* Create a GC for drawing */
|
|
gcMask = GCForeground | GCBackground | GCGraphicsExposures;
|
|
gcValues.foreground = fgPixel;
|
|
gcValues.background = bgPixel;
|
|
gcValues.graphics_exposures = False;
|
|
normalGC = XCreateGC(dpy, rootWindow, gcMask, &gcValues);
|
|
|
|
if (enableShapedWindow)
|
|
{
|
|
shapeMask = XCreateBitmapFromData(dpy, win, (char *)mask_bits,
|
|
mask_width, mask_height);
|
|
XShapeCombineMask(dpy, win, ShapeBounding, 0, 0, shapeMask, ShapeSet);
|
|
XShapeCombineMask(dpy, iconWin, ShapeBounding, 0, 0, shapeMask,
|
|
ShapeSet);
|
|
}
|
|
|
|
wmHints.initial_state = WithdrawnState;
|
|
wmHints.icon_window = iconWin;
|
|
wmHints.icon_x = sizeHints.x;
|
|
wmHints.icon_y = sizeHints.y;
|
|
wmHints.window_group = win;
|
|
wmHints.flags = StateHint | IconWindowHint | IconPositionHint |
|
|
WindowGroupHint;
|
|
XSetWMHints(dpy, win, &wmHints);
|
|
|
|
XSetCommand(dpy, win, argv, argc);
|
|
XMapWindow(dpy,win);
|
|
|
|
showTime();
|
|
redrawWindow(&visible);
|
|
while (1)
|
|
{
|
|
if (actualTime != mytime())
|
|
{
|
|
actualTime = mytime();
|
|
if (actualMinutes != (actualTime / 60))
|
|
{
|
|
showTime();
|
|
if (!enableBlinking)
|
|
{
|
|
redrawWindow(&visible);
|
|
}
|
|
}
|
|
if (0 == (actualTime % 2))
|
|
{
|
|
/* Clean up zombie processes */
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "%s: cleaning up zombies (time %ld)\n",
|
|
progName, actualTime);
|
|
#endif /* DEBUG */
|
|
if (NULL != commandToExec)
|
|
{
|
|
waitpid(0, NULL, WNOHANG);
|
|
}
|
|
}
|
|
}
|
|
if (enableBlinking && (!enableYearDisplay))
|
|
{
|
|
blinkCounter++;
|
|
#ifdef SYSV
|
|
if (blinkCounter >= 20*blinkInterval)
|
|
#else
|
|
if (blinkCounter >= 2*blinkInterval)
|
|
#endif
|
|
blinkCounter = 0;
|
|
if (blinkCounter == 0)
|
|
{
|
|
/* Sekunden Doppelpunkt ein */
|
|
XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
|
|
COLON_X_OFFSET, COLON_Y_OFFSET,
|
|
COLON_WIDTH, COLON_HEIGHT,
|
|
xPos[COLON_X_POS], yPos[COLON_Y_POS]);
|
|
}
|
|
#ifdef SYSV
|
|
if (blinkCounter == 10*blinkInterval)
|
|
#else
|
|
if (blinkCounter == blinkInterval)
|
|
#endif
|
|
{
|
|
/* Sekunden Doppelpunkt aus */
|
|
XCopyArea(dpy, led.pixmap, visible.pixmap, normalGC,
|
|
BLANK_X_OFFSET, BLANK_Y_OFFSET,
|
|
COLON_WIDTH, COLON_HEIGHT,
|
|
xPos[COLON_X_POS], yPos[COLON_Y_POS]);
|
|
}
|
|
redrawWindow(&visible);
|
|
}
|
|
|
|
/* read a packet */
|
|
while (XPending(dpy))
|
|
{
|
|
XNextEvent(dpy, &event);
|
|
switch(event.type)
|
|
{
|
|
case Expose:
|
|
if (0 == event.xexpose.count)
|
|
{
|
|
redrawWindow(&visible);
|
|
}
|
|
break;
|
|
case ButtonPress:
|
|
if (NULL != commandToExec)
|
|
{
|
|
pid_t fork_pid;
|
|
|
|
if ((NULL == commandBuf) &&
|
|
(!buildCommand(commandToExec, &commandBuf,
|
|
&commandLength, &commandIndex)))
|
|
{
|
|
break;
|
|
}
|
|
fork_pid = fork();
|
|
switch (fork_pid)
|
|
{
|
|
case 0:
|
|
/* We're the child process;
|
|
* run the command and exit.
|
|
*/
|
|
executeCommand(commandBuf);
|
|
/* When the system() call finishes, we're done. */
|
|
exit(0);
|
|
break;
|
|
case -1:
|
|
/* We're the parent process, but
|
|
* fork() gave an error.
|
|
*/
|
|
perror("fork");
|
|
break;
|
|
default:
|
|
/* We're the parent process;
|
|
* keep on doing what we normally do.
|
|
*/
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case DestroyNotify:
|
|
#if 0
|
|
XFreeGC(dpy, normalGC);
|
|
XDestroyWindow(dpy, win);
|
|
XDestroyWindow(dpy, iconWin);
|
|
#endif /* 0 */
|
|
#ifdef ONLY_SHAPED_WINDOW
|
|
XFreePixmap(dpy, visible.pixmap);
|
|
#endif /* ONLY_SHAPED_WINDOW */
|
|
XCloseDisplay(dpy);
|
|
exit(0);
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
XFlush(dpy);
|
|
#ifdef SYSV
|
|
if (enableYearDisplay)
|
|
{
|
|
poll((struct poll *) 0, (size_t) 0, 200); /* 1/5 sec */
|
|
}
|
|
else
|
|
{
|
|
poll((struct poll *) 0, (size_t) 0, 50); /* 5/100 sec */
|
|
}
|
|
#else
|
|
/* We compute the date of next event, in order to avoid polling */
|
|
if (enableBlinking)
|
|
{
|
|
gettimeofday(&nextEvent,NULL);
|
|
nextEvent.tv_sec = 0;
|
|
if (nextEvent.tv_usec < 500000)
|
|
nextEvent.tv_usec = 500000-nextEvent.tv_usec;
|
|
else
|
|
nextEvent.tv_usec = 1000000-nextEvent.tv_usec;
|
|
}
|
|
else
|
|
{
|
|
if (enableYearDisplay)
|
|
{
|
|
nextEvent.tv_sec = 86400-actualTime%86400;
|
|
nextEvent.tv_usec = 0;
|
|
}
|
|
else
|
|
{
|
|
nextEvent.tv_sec = 60-actualTime%60;
|
|
nextEvent.tv_usec = 0;
|
|
}
|
|
}
|
|
FD_ZERO(&xFdSet);
|
|
FD_SET(xFd,&xFdSet);
|
|
select(FD_SETSIZE,&xFdSet,NULL,NULL,&nextEvent);
|
|
#endif
|
|
}
|
|
return (0);
|
|
}
|
|
|