#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;
}