c.c

screenshot

Makefile

o = c

all: $o

c: -lcairo -lX11 -lm

clean:
	$(RM) $o

c.c

#include <cairo/cairo.h>
#include <cairo/cairo-xlib.h>
#include <X11/Xlib.h>
#include <math.h>
#include <time.h>
#include <stdio.h>
#define WIDTH  200
#define HEIGHT 200
#define APP_NAME "c.c"

Display *dpy;
int      screen;
int      depth;
Window   w, root;
Pixmap   p;
GC       gc;
Visual  *visual;
time_t   up_t;
struct timespec stop;
int      conn;

void
set_grey(cairo_t *cr, int n)
{
   double c = n / 256.0;
   cairo_set_source_rgb(cr, c, c, c);
}

void
hand(cairo_t *cr, double val, int cx, int cy, int l, int m, int w)
{
   double rad = (val - 1/4.0) * (2 * M_PI);
   double x = cos(rad);
   double y = sin(rad);
   cairo_move_to(cr, cx - m * x, cy - m * y);
   cairo_line_to(cr, cx + l * x, cy + l * y);
   cairo_set_line_width(cr, w);
   cairo_stroke(cr);
}

void
idx(cairo_t *cr, double val, int cx, int cy, int l, int m, int w)
{
   hand(cr, val, cx, cy, l, -m, w);
}

void
disk_grey(cairo_t *cr, int cx, int cy, int r, int n)
{
   set_grey(cr, n);
   cairo_arc(cr, cx, cy, r, 0, 2 * M_PI);
   cairo_fill(cr);
}

void
arc_grey(cairo_t *cr, int cx, int cy, int r, double from, double to, int n)
{
   double f = (from - 1/4.0) * (2 * M_PI);
   double t = (to   - 1/4.0) * (2 * M_PI);
   set_grey(cr, n);
   cairo_arc(cr, cx, cy, r, f, t);
   cairo_fill(cr);
}

void
sub(void)
{
   cairo_surface_t *s;

   double h, m;
   int i;
   time_t tt = time(NULL);
   int up_s = tt - up_t;
   int up_m = up_s / 60; up_s %= 60;
   int up_h = up_m / 60; up_m %= 60;

   struct timespec ttt;
   clock_gettime(CLOCK_REALTIME, &ttt);

   struct tm t;
   localtime_r(&tt, &t);
   m = t.tm_min + t.tm_sec / 60.0;
   h = t.tm_hour + m / 60.0;

   gmtime_r(&tt, &t);
   int g_m = t.tm_min + t.tm_sec / 60.0;
   int g_h = t.tm_hour + g_m / 60.0;

   s = cairo_xlib_surface_create(dpy, p, visual, WIDTH, HEIGHT);
   cairo_xlib_surface_set_size(s, WIDTH, HEIGHT);

   cairo_t *cr = cairo_create(s);
   cairo_set_line_width(cr, 3.0);
   cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);

   /* clear */
   disk_grey(cr, 100, 100, 200, 0x28);
   disk_grey(cr, 100, 100,  90, 0xa0);
   disk_grey(cr, 100, 100,  80, 0x28);

   /* dial */

   /* 12 */
   set_grey(cr, 0x50);
   idx(cr, 0, 100, 100, 30, 25, 40);

   /* dial - hour */
   set_grey(cr, 0x50);
   for (i = 0; i < 12; i++) {
      idx(cr, i / 12.0, 100, 100, i % 3 ? 65 : 75, 55, 6); }

   /* dial - minute */
   for (i = 0; i < 60; i++) {
      if (!(i % 15)) continue;
      idx(cr, i / 60.0, 100, 100, 75, 70, 2); }

goto hms;

   /* mini dial 3 */
   disk_grey(cr, 140, 100, 25, 0x30);
   set_grey(cr, 0xa0);
   for (i = 0; i < 24; i++) {
      idx(cr, i / 24.0, 140, 100, i % 6 ? 23 : 25, 20, 1); }

   hand(cr, (up_h + up_m / 60.0) / 24.0, 140, 100, 10, 2, 2);
   hand(cr, (up_m + up_s / 60.0) / 60.0, 140, 100, 14, -10, 2);
   hand(cr, up_s / 60.0, 140, 100, 18, -14, 2);
   hand(cr, ttt.tv_nsec / 1000000000.0, 140, 100, 20, -18, 2);

   /* mini dial 6 */
   disk_grey(cr, 100, 140, 25, 0x50);
   arc_grey(cr, 100, 140, 20, 0.25, 0.75, 0x30);
   set_grey(cr, 0xa0);
   for (i = 0; i < 24; i++) {
      idx(cr, i / 24.0, 100, 140, i % 6 ? 23 : 25, 20, 1); }

   hand(cr, h / 24.0 + 0.5, 100, 140, 18, 2, 2); /* 24 hour */

   /* mini dial 9 */
   disk_grey(cr, 60, 100, 25, 0x30);
   set_grey(cr, 0xa0);
   for (i = 0; i < 12; i++) {
      idx(cr, i / 12.0, 60, 100, i % 3 ? 23 : 25, 20, 1); }

   hand(cr, (g_h + g_m / 60.0) / 12.0, 60, 100, 10, 2, 2); /* UTC */
   hand(cr, g_m / 60.0, 60, 100, 14, 2, 2);

hms:
   set_grey(cr, 0xa0);

   /* hour */
   hand(cr, h / 12.0, 100, 100, 50, 10, 6);

   /* minute */
   hand(cr, m / 60.0, 100, 100, 70, 10, 4);

   /* second */
   double val = (ttt.tv_sec * 100 + ttt.tv_nsec / 10000000) / 60.0 / 100.0;
   hand(cr, val, 100, 100, 79, -76, 5);

   disk_grey(cr, 100, 100, 6, 0xa0);
   disk_grey(cr, 100, 100, 2, 0x50);

out:
   cairo_destroy(cr);
   cairo_surface_flush(s);
   cairo_surface_destroy(s);

   XCopyArea(dpy, p, w, gc, 0, 0, WIDTH, HEIGHT, 0, 0);
}

#include <sys/select.h>
int
conn_ck(int conn)
{
   fd_set f;
   FD_ZERO(&f);
   FD_SET(conn, &f);
   struct timeval t = { .tv_sec = 0, .tv_usec = 125000/*us = 125ms */ };
   const int r = select(conn + 1, &f, NULL, NULL, &t);

   return FD_ISSET(conn, &f);
}

void
mainloop(void)
{
   XEvent e;

   for (;;) {
      if (!XPending(dpy) && !conn_ck(conn)) {
         sub();
         continue; }
      XNextEvent(dpy, &e);
      switch (e.type) {
      case Expose:
         sub();
         break;
      case ButtonPress:
         up_t = time(NULL);
         break; } }
}

void
init(void)
{
   conn   = ConnectionNumber(dpy);
   screen = DefaultScreen(dpy);
   gc     = DefaultGC(dpy, screen);
   depth  = DefaultDepth(dpy, screen);
   visual = DefaultVisual(dpy, screen);
   root   = DefaultRootWindow(dpy);

   w = XCreateSimpleWindow(dpy, root, 0, 0, WIDTH, HEIGHT, 0, 0, 0);
   p = XCreatePixmap(dpy, w, WIDTH, HEIGHT, depth);
   XSelectInput(dpy, w, ExposureMask | ButtonPressMask);
   XStoreName(dpy, w, APP_NAME);
   XMapWindow(dpy, w);

   up_t = time(NULL);
   clock_gettime(CLOCK_REALTIME, &stop);
}

void
fini(void)
{
   XFreePixmap(dpy, p);
   XDestroyWindow(dpy, w);
   XCloseDisplay(dpy);
}

int
main(void)
{
   dpy = XOpenDisplay(NULL);
   if (!dpy) return 1;

   init();
   mainloop();
   fini();
   return 0;
}