1 #include <math.h>
2 #include <time.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include <X11/X.h>
6 #include <X11/Xlib.h>
7 #include <X11/Xutil.h>
8 #include <X11/extensions/shape.h>
9 
10 #define DEFAULT_RADIUS 70
11 
12 struct stuff {
13      Display *disp;
14      Pixmap bound, clip;
15      Pixmap hbound, hclip;
16      GC gc, cgc, copygc;
17      XFontStruct *font;
18      int x, y, orbit, radius, width;
19      Window window;
20 };
21 
vector(int xo,int yo,double angle,double radius,int * x,int * y)22 void vector(int xo, int yo, double angle, double radius, int *x, int *y)
23 {
24     *x = (int) (xo + radius * cos(angle) + 0.5);
25     *y = (int) (yo - radius * sin(angle) + 0.5);
26 }
27 
28 
drawface(struct stuff * st)29 void drawface(struct stuff *st)
30 {
31     int h, radius, xo, yo, mg;
32     char buf[5];
33     XCharStruct cs;
34 
35     xo = st->x / 2;
36     yo = st->y / 2;
37     mg = (st->font->ascent + st->font->descent) / 2;
38     h = XTextWidth(st->font, "3", 1) / 2;
39     mg = h > mg ? h : mg;
40     h = XTextWidth(st->font, "9", 1) / 2;
41     mg = h > mg ? h : mg;
42     radius = (st->x < st->y ? st->x : st->y) / 2 - mg - 1;
43     st->radius = radius;
44 
45     XFillRectangle(st->disp, st->bound, st->cgc, 0, 0, st->x, st->y);
46     XFillRectangle(st->disp, st->clip, st->cgc, 0, 0, st->x, st->y);
47     for (h = 0; h < 12; ++h) {
48 	double angle;
49 	int x, y, i, j;
50 
51 	sprintf(buf, "%d", (14 - h) % 12 + 1);
52 	angle = h * M_PI * 2.0 / 12.0;
53 	vector(xo, yo, angle, (double) radius, &x, &y);
54         x -= XTextWidth(st->font, buf, strlen(buf)) / 2;
55 	y += (st->font->ascent - st->font->descent) / 2;
56 
57 	for (i = -(st->orbit); i <= st->orbit; ++i) {
58 	    for (j = -(st->orbit); j <= st->orbit; ++j) {
59 		XDrawString(st->disp, st->bound, st->gc, x + i, y + j,
60 		    buf, strlen(buf));
61 	    }
62 	}
63 	XDrawString(st->disp, st->clip, st->gc, x, y, buf, strlen(buf));
64     }
65 }
66 
67 
drawline(struct stuff * st,int x1,int y1,int x2,int y2,int width)68 void drawline(struct stuff *st, int x1, int y1, int x2, int y2, int width)
69 {
70     XSetLineAttributes(st->disp, st->gc, width + st->orbit * 2,
71 	LineSolid, CapRound, JoinMiter);
72     XDrawLine(st->disp, st->hbound, st->gc, x1, y1, x2, y2);
73 
74     XSetLineAttributes(st->disp, st->gc, width,
75 	LineSolid, CapRound, JoinMiter);
76     XDrawLine(st->disp, st->hclip, st->gc, x1, y1, x2, y2);
77 }
78 
79 
drawhands(struct stuff * st)80 void drawhands(struct stuff *st)
81 {
82     double hangle, mangle;
83     time_t t;
84     struct tm *lt;
85     int m, dm, qdm, xo, yo, x, y;
86 
87     dm = 60 * 12;
88     qdm = 60 * 3;
89 
90     /* get time, figure hand positions */
91     time(&t);
92     lt = localtime(&t);
93 
94     m = ((lt->tm_hour % 12) * 60 + lt->tm_min);
95     m = (dm - m + qdm - 1) % dm;
96     hangle = (double) m / dm * 2 * M_PI;
97 
98     m = lt->tm_min;
99     m = (60 - m + 15 - 1) % 60;
100     mangle = (double) m / 60 * 2 * M_PI;
101 
102     /* copy over face pixmaps */
103     XCopyArea(st->disp, st->bound, st->hbound, st->copygc,
104 	0, 0, st->x, st->y, 0, 0);
105     XCopyArea(st->disp, st->clip, st->hclip, st->copygc,
106 	0, 0, st->x, st->y, 0, 0);
107 
108     /* draw hands */
109     xo = st->x / 2;
110     yo = st->y / 2;
111     vector(xo, yo, mangle, st->radius * .75, &x, &y);
112     drawline(st, xo, yo, x, y, st->width);
113 
114     vector(xo, yo, hangle, st->radius * .5, &x, &y);
115     drawline(st, xo, yo, x, y, st->width * 2);
116 
117     /* re-shape window */
118     XShapeCombineMask(st->disp, st->window, ShapeBounding, 0, 0, st->hbound,
119 	ShapeSet);
120     XShapeCombineMask(st->disp, st->window, ShapeClip, 0, 0, st->hclip,
121 	ShapeSet);
122     XSync(st->disp, True);
123 }
124 
125 
resizepm(struct stuff * st,Pixmap * p)126 void resizepm(struct stuff *st, Pixmap *p)
127 {
128     Display *d = st->disp;
129 
130     if (*p != None)
131 	XFreePixmap(d, *p);
132 
133     *p = XCreatePixmap(d, RootWindow(d, DefaultScreen(d)), st->x, st->y, 1);
134 }
135 
136 
resize(struct stuff * st,int x,int y)137 void resize(struct stuff *st, int x, int y)
138 {
139     if (st->x == x && st->y == y)
140 	return;
141 
142     st->x = x;
143     st->y = y;
144 
145     resizepm(st, &(st->bound));
146     resizepm(st, &(st->clip));
147     resizepm(st, &(st->hbound));
148     resizepm(st, &(st->hclip));
149 
150     drawface(st);
151     drawhands(st);
152 }
153 
basename(char * s)154 static char *basename(char *s)
155 {
156     char *t;
157 
158     if (t = strrchr(s, '/'))
159 	return ++t;
160 
161     return s;
162 }
163 
main(int argc,char ** argv)164 main(int argc, char **argv)
165 {
166     Display *disp;
167     XFontStruct *font;
168     int screen, orbit;
169     Window rw, w;
170     Pixmap sp;
171     GC gc, cgc, copygc;
172     struct stuff st;
173     XClassHint xch = { "lmclock", "Lmclock" };
174     int i, x, y, err;
175     int radius;
176     char *geom, *dstr;
177     XSizeHints xsh = {
178 	PMinSize | PMaxSize | PResizeInc | PAspect | PBaseSize | PWinGravity,
179 	0, 0, 0, 0,
180 	0, 0, 0, 0, 0, 0,
181 	{1, 1}, {1, 1},
182 	0, 0,
183 	NorthWestGravity
184     };
185 
186     radius = DEFAULT_RADIUS;
187     xsh.min_width = xsh.min_height = xsh.max_width = xsh.max_height =
188 	xsh.base_width = xsh.base_height = 2 * radius;
189     geom = 0;
190     dstr = 0;
191     orbit = 2;
192     err = 0;
193     for (i = 1; i < argc && '-' == argv[i][0]; ++i) {
194 	if ('-' != argv[i][0])
195 	    break;
196 
197 	if (!strcmp(argv[i], "-") || !strcmp(argv[i], "--")) {
198 	    ++i;
199 	    break;
200 	}
201 
202 	if (!strncmp(argv[i], "-display", 2)) {
203 	    ++i;
204 	    if (i >= argc) {
205 		++err;
206 		break;
207 	    }
208 
209 	    dstr = argv[i];
210 	} else if (!strncmp(argv[i], "-geometry", 2)) {
211 	    ++i;
212 	    if (i >= argc) {
213 		++err;
214 		break;
215 	    }
216 
217 	    geom = argv[i];
218 	}
219     }
220 
221     if (err || i < argc) {
222 	fprintf(stderr, "usage: %s [-display <display>] [-geometry <geometry>]\n",
223 	    argv[0] ? basename(argv[0]) : "lmclock");
224 	exit(2);
225     }
226 
227     disp = XOpenDisplay(dstr);
228     if (!disp) {
229 	fprintf(stderr, "Cannot open display: %s\n", dstr ? dstr : "");
230 	exit(1);
231     }
232     screen = DefaultScreen(disp);
233     rw = RootWindow(disp, screen);
234 
235     x = y = 0;
236     if (geom) {
237 	char pgeom[256];
238         int w, h, g;
239 
240 	sprintf(pgeom, "%dx%d", 2 * radius, 2 * radius);
241 	XWMGeometry(disp, screen, geom, pgeom, 0, &xsh, &x, &y,
242 	    &w, &h, &g);
243 	xsh.flags = USPosition;
244     }
245 
246     /* open font */
247     font = XLoadQueryFont(disp, "-adobe-courier-*-r-*-*-18-*");
248 
249     w =  XCreateWindow(disp, rw, x, y, radius * 2, radius * 2, 0,
250 	CopyFromParent, InputOutput, CopyFromParent,
251 	0L, (XSetWindowAttributes *) 0);
252     XSetWindowBackground(disp, w, BlackPixel(disp, screen));
253     XSetWindowBorder(disp, w, WhitePixel(disp, screen));
254     XmbSetWMProperties(disp, w, "lmclock",
255        "lmclock", argv, argc, &xsh, NULL, &xch);
256     XMapWindow(disp, w);
257 
258     /* create GCs */
259     sp = XCreatePixmap(disp, w, 1, 1, 1);
260 
261     gc = XCreateGC(disp, sp, 0L, (XGCValues *) 0);
262     XSetFont(disp, gc, font->fid);
263     XSetLineAttributes(disp, gc, 5, LineSolid, CapRound, JoinMiter);
264     XSetFunction(disp, gc, GXset);
265 
266     cgc = XCreateGC(disp, sp, 0L, (XGCValues *) 0);
267     XSetFunction(disp, cgc, GXclear);
268 
269     copygc = XCreateGC(disp, sp, 0L, (XGCValues *) 0);
270     XSetFunction(disp, copygc, GXcopy);
271 
272     XFreePixmap(disp, sp);
273 
274     st.disp = disp;
275     st.bound = st.hbound = st.clip = st.hclip = None;
276     st.gc = gc;
277     st.cgc = cgc;
278     st.copygc = copygc;
279     st.font = font;
280     st.x = st.y = 0;
281     st.orbit = st.width = orbit;
282     st.window = w;
283 
284     resize(&st, radius * 2, radius * 2);
285     for (;;) {
286 	sleep(30);
287 	drawhands(&st);
288     }
289 }
290