a small clock for X11 that displays time as pixels
at master 355 lines 8.3 kB view raw
1/* vim:ts=8 2 * $Id: pixelclock.c,v 1.8 2009/03/09 06:35:26 jcs Exp $ 3 * 4 * pixelclock 5 * a different way of looking at time 6 * 7 * Copyright (c) 2005,2008-2009 joshua stein <jcs@jcs.org> 8 * Copyright (c) 2005 Federico G. Schwindt 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. The name of the author may not be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34#include <err.h> 35#include <getopt.h> 36#include <signal.h> 37#include <stdio.h> 38#include <stdlib.h> 39#include <string.h> 40#include <time.h> 41#include <unistd.h> 42#include <sys/types.h> 43 44#include <X11/Xlib.h> 45#include <X11/Xutil.h> 46 47/* default clock size */ 48#define DEFSIZE 3 49 50/* default position is along the right side */ 51#define DEFPOS 'r' 52 53/* so our window manager knows us */ 54char* win_name = "pixelclock"; 55 56/* default hours to highlight (9am, noon, 5pm) */ 57const float defhours[3] = { 9.0, 12.0, 17.0 }; 58 59struct xinfo { 60 Display* dpy; 61 int dpy_width, dpy_height; 62 int screen; 63 Window win; 64 int size; 65 char position; 66 GC gc; 67 Colormap win_colormap; 68} x; 69 70const struct option longopts[] = { 71 { "display", required_argument, NULL, 'd' }, 72 { "size", required_argument, NULL, 's' }, 73 { "left", no_argument, NULL, 'l' }, 74 { "right", no_argument, NULL, 'r' }, 75 { "top", no_argument, NULL, 't' }, 76 { "bottom", no_argument, NULL, 'b' }, 77 78 { NULL, 0, NULL, 0 } 79}; 80 81extern char *__progname; 82 83long getcolor(const char *); 84void handler(int sig); 85void init_x(const char *); 86void usage(void); 87 88int 89main(int argc, char* argv[]) 90{ 91 char *display = NULL, *p; 92 int c, i, y; 93 int hourtick, lastpos = -1, newpos = 0; 94 struct timeval tv[2]; 95 time_t now; 96 struct tm *t; 97 98 float *hihours; 99 int nhihours; 100 101 XEvent event; 102 103 bzero(&x, sizeof(struct xinfo)); 104 x.size = DEFSIZE; 105 x.position = NULL; 106 107 while ((c = getopt_long_only(argc, argv, "", longopts, NULL)) != -1) { 108 switch (c) { 109 case 'd': 110 display = optarg; 111 break; 112 113 case 'b': 114 case 't': 115 case 'l': 116 case 'r': 117 if (x.position) 118 errx(1, "only one of -top, -bottom, -left, " 119 "-right allowed"); 120 /* NOTREACHED */ 121 122 x.position = c; 123 break; 124 125 case 's': 126 x.size = strtol(optarg, &p, 10); 127 if (*p || x.size < 1) 128 errx(1, "illegal value -- %s", optarg); 129 /* NOTREACHED */ 130 break; 131 132 default: 133 usage(); 134 /* NOTREACHED */ 135 } 136 } 137 138 if (!x.position) 139 x.position = DEFPOS; 140 141 argc -= optind; 142 argv += optind; 143 144 if (argc == 0) { 145 /* use default times */ 146 nhihours = sizeof(defhours) / sizeof(defhours[0]); 147 if ((hihours = alloca(sizeof(defhours))) == NULL) 148 err(1, NULL); 149 150 for (i = 0; i < nhihours; i++) 151 hihours[i] = defhours[i]; 152 } else { 153 /* get times from args */ 154 nhihours = argc; 155 if ((hihours = alloca(nhihours * sizeof(float))) == NULL) 156 err(1, NULL); 157 158 for (i = 0; i < argc; ++i) { 159 int h, m; 160 char *p = argv[i]; 161 162 /* parse times like 14:12 */ 163 h = atoi(p); 164 if ((p = strchr(p, ':')) == NULL) 165 errx(1, "invalid time %s", argv[i]); 166 m = atoi(p + 1); 167 168 if (h > 23 || h < 0 || m > 59 || m < 0) 169 errx(1, "Invalid time %s", argv[i]); 170 171 hihours[i] = h + (m / 60.0); 172 } 173 } 174 175 init_x(display); 176 177 signal(SIGINT, handler); 178 signal(SIGTERM, handler); 179 180 /* each hour will be this many pixels away */ 181 hourtick = ((x.position == 'b' || x.position == 't') ? x.dpy_width : 182 x.dpy_height) / 24; 183 184 for (;;) { 185 if (gettimeofday(&tv[0], NULL)) 186 errx(1, "gettimeofday"); 187 /* NOTREACHED */ 188 189 now = tv[0].tv_sec; 190 if ((t = localtime(&now)) == NULL) 191 errx(1, "localtime"); 192 /* NOTREACHED */ 193 194 newpos = (hourtick * t->tm_hour) + 195 (float)(((float)t->tm_min / 60.0) * hourtick) - 3; 196 197 /* check if we just got exposed */ 198 bzero(&event, sizeof(XEvent)); 199 XCheckWindowEvent(x.dpy, x.win, ExposureMask, &event); 200 201 /* only redraw if our time changed enough to move the box or if 202 * we were just exposed */ 203 if ((newpos != lastpos) || (event.type == Expose)) { 204 XClearWindow(x.dpy, x.win); 205 206 /* draw the current time */ 207 XSetForeground(x.dpy, x.gc, getcolor("yellow")); 208 if (x.position == 'b' || x.position == 't') 209 XFillRectangle(x.dpy, x.win, x.gc, 210 newpos, 0, 6, x.size); 211 else 212 XFillRectangle(x.dpy, x.win, x.gc, 213 0, newpos, x.size, 6); 214 215 /* draw the hour ticks */ 216 XSetForeground(x.dpy, x.gc, getcolor("blue")); 217 for (y = 1; y <= 23; y++) 218 if (x.position == 'b' || x.position == 't') 219 XFillRectangle(x.dpy, x.win, x.gc, 220 (y * hourtick), 0, 2, x.size); 221 else 222 XFillRectangle(x.dpy, x.win, x.gc, 223 0, (y * hourtick), x.size, 2); 224 225 /* highlight requested times */ 226 XSetForeground(x.dpy, x.gc, getcolor("green")); 227 for (i = 0; i < nhihours; i++) 228 if (x.position == 'b' || x.position == 't') 229 XFillRectangle(x.dpy, x.win, x.gc, 230 (hihours[i] * hourtick), 0, 231 2, x.size); 232 else 233 XFillRectangle(x.dpy, x.win, x.gc, 234 0, (hihours[i] * hourtick), 235 x.size, 2); 236 237 lastpos = newpos; 238 239 XFlush(x.dpy); 240 } 241 242 sleep(1); 243 } 244 245 exit(1); 246} 247 248void 249init_x(const char *display) 250{ 251 int rc; 252 int left = 0, top = 0, width = 0, height = 0; 253 XGCValues values; 254 XSetWindowAttributes attributes; 255 XTextProperty win_name_prop; 256 257 if (!(x.dpy = XOpenDisplay(display))) 258 errx(1, "unable to open display %s", XDisplayName(display)); 259 /* NOTREACHED */ 260 261 x.screen = DefaultScreen(x.dpy); 262 263 x.dpy_width = DisplayWidth(x.dpy, x.screen); 264 x.dpy_height = DisplayHeight(x.dpy, x.screen); 265 266 x.win_colormap = DefaultColormap(x.dpy, DefaultScreen(x.dpy)); 267 268 switch (x.position) { 269 case 'b': 270 left = 0; 271 height = x.size; 272 top = x.dpy_height - height; 273 width = x.dpy_width; 274 break; 275 case 't': 276 left = 0; 277 top = 0; 278 height = x.size; 279 width = x.dpy_width; 280 break; 281 case 'l': 282 left = 0; 283 top = 0; 284 height = x.dpy_height; 285 width = x.size; 286 break; 287 case 'r': 288 width = x.size; 289 left = x.dpy_width - width; 290 top = 0; 291 height = x.dpy_height; 292 break; 293 } 294 295 x.win = XCreateSimpleWindow(x.dpy, RootWindow(x.dpy, x.screen), 296 left, top, width, height, 297 0, 298 BlackPixel(x.dpy, x.screen), 299 BlackPixel(x.dpy, x.screen)); 300 301 if (!(rc = XStringListToTextProperty(&win_name, 1, &win_name_prop))) 302 errx(1, "XStringListToTextProperty"); 303 /* NOTREACHED */ 304 305 XSetWMName(x.dpy, x.win, &win_name_prop); 306 307 /* remove all window manager decorations and force our position/size */ 308 /* XXX: apparently this is not very nice */ 309 attributes.override_redirect = True; 310 XChangeWindowAttributes(x.dpy, x.win, CWOverrideRedirect, &attributes); 311 312 if (!(x.gc = XCreateGC(x.dpy, x.win, 0, &values))) 313 errx(1, "XCreateGC"); 314 /* NOTREACHED */ 315 316 XMapWindow(x.dpy, x.win); 317 318 /* we want to know when we're exposed */ 319 XSelectInput(x.dpy, x.win, ExposureMask); 320 321 XFlush(x.dpy); 322 XSync(x.dpy, False); 323} 324 325long 326getcolor(const char *color) 327{ 328 int rc; 329 330 XColor tcolor; 331 332 if (!(rc = XAllocNamedColor(x.dpy, x.win_colormap, color, &tcolor, 333 &tcolor))) 334 errx(1, "can't allocate %s", color); 335 336 return tcolor.pixel; 337} 338 339void 340handler(int sig) 341{ 342 XCloseDisplay(x.dpy); 343 344 exit(0); 345 /* NOTREACHED */ 346} 347 348void 349usage(void) 350{ 351 fprintf(stderr, "usage: %s %s\n", __progname, 352 "[-display host:dpy] [-left|-right|-top|-bottom] [-size <pixels>] " 353 "[time time2 ...]"); 354 exit(1); 355}