/* * glitchyclock.c * * Copyright © 2019 Thomas White * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* Get LED display fonts from here: https://www.keshikan.net/fonts-e.html */ #include #include struct glitchyclock { GtkWidget *da; PangoFontDescription *fontdesc; int nglitch; double glitch_base; }; const int glitch_times[] = { 4, 15, 23, 58, -1, -1 }; static double get_monotonic_seconds() { struct timespec tp; clock_gettime(CLOCK_MONOTONIC, &tp); return tp.tv_sec + (double)tp.tv_nsec/1e9; } static void do_glitch(struct glitchyclock *gc) { //if ( glitch_times[2*(gc->nglitch+1)] == -1 ) return; //gc->nglitch++; gc->glitch_base = get_monotonic_seconds(); } static gboolean draw_sig(GtkWidget *widget, cairo_t *cr, struct glitchyclock *gc) { int w, h; int lw, lh; double sf; PangoLayout *layout; const double screen_w_frac = 0.6; int glitch_hours, glitch_minutes; char timestr[64]; double seconds_f; char col; double seconds_if; int hours, minutes, seconds; w = gtk_widget_get_allocated_width(widget); h = gtk_widget_get_allocated_height(widget); /* Overall background */ cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); cairo_paint(cr); layout = pango_layout_new(gtk_widget_get_pango_context(widget)); pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); pango_layout_set_font_description(layout, gc->fontdesc); seconds_f = get_monotonic_seconds() - gc->glitch_base; col = (modf(seconds_f, &seconds_if) < 0.5) ? ':' : ' '; seconds = seconds_if; if ( seconds > 16 ) seconds = 16; hours = (seconds/3600 + 4) % 24; minutes = (seconds/60 + 48) % 60; seconds += 15; snprintf(timestr, 63, "%02i%c%02i%c%02i", hours, col, minutes, col, seconds); double duty = 0.5; if ( seconds_f > 17.0 ) { duty = 0.5 + (seconds_f - 17.0)/10.0; if ( duty > 1.0 ) duty = 1.0; } if ( ((seconds_f > 7.1) && (seconds_f < 7.9)) || ((seconds_f > 12.2) && (seconds_f < 13.1)) || ((seconds_f > 17.0)) ) { if ( fmod(seconds_f, 0.05) > duty*0.05 ) { strcpy(timestr, "00:rK:c1"); } else { strcpy(timestr, "88:v8:68"); } } pango_layout_set_text(layout, "88:88:88", -1); pango_cairo_update_layout(cr, layout); pango_layout_get_size(layout, &lw, &lh); sf = (double)PANGO_SCALE*screen_w_frac*w/lw; cairo_scale(cr, sf, sf); pango_cairo_update_layout(cr, layout); pango_layout_get_size(layout, &lw, &lh); lw /= PANGO_SCALE; lh /= PANGO_SCALE; w /= sf; h /= sf; cairo_translate(cr, (w-lw)/2.0, h-lh-20.0); pango_cairo_update_layout(cr, layout); cairo_set_source_rgb(cr, 0.10, 0.0, 0.0); pango_cairo_show_layout(cr, layout); pango_layout_set_text(layout, timestr, -1); cairo_set_source_rgb(cr, 0.95, 0.0, 0.05); pango_cairo_show_layout(cr, layout); return FALSE; } static gboolean redraw_cb(gpointer data) { gint w, h; struct glitchyclock *gc = data; w = gtk_widget_get_allocated_width(GTK_WIDGET(gc->da)); h = gtk_widget_get_allocated_height(GTK_WIDGET(gc->da)); gtk_widget_queue_draw_area(GTK_WIDGET(gc->da), 0, 0, w, h); return G_SOURCE_CONTINUE; } static gboolean key_press_sig(GtkWidget *da, GdkEventKey *event, struct glitchyclock *gc) { if ( event->keyval == GDK_KEY_KP_Enter ) { do_glitch(gc); redraw_cb(gc); return TRUE; } return FALSE; } static gint realise_sig(GtkWidget *da, struct glitchyclock *gc) { GdkWindow *win = gtk_widget_get_window(da); /* Keyboard and input method stuff */ gdk_window_set_accept_focus(win, TRUE); g_signal_connect(G_OBJECT(da), "key-press-event", G_CALLBACK(key_press_sig), gc); gc->fontdesc = pango_font_description_from_string("DSEG7 Modern Bold Italic 16"); return FALSE; } int main(int argc, char *argv[]) { struct glitchyclock gc; GtkWidget *mainwindow; gc.nglitch = 0; gc.glitch_base = get_monotonic_seconds(); gtk_init(&argc, &argv); mainwindow = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_fullscreen(GTK_WINDOW(mainwindow)); g_signal_connect_swapped(G_OBJECT(mainwindow), "destroy", gtk_main_quit, NULL); gc.da = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER(mainwindow), GTK_WIDGET(gc.da)); gtk_widget_set_can_focus(GTK_WIDGET(gc.da), TRUE); gtk_widget_add_events(gc.da, GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK); g_signal_connect(G_OBJECT(gc.da), "draw", G_CALLBACK(draw_sig), &gc); g_signal_connect(G_OBJECT(gc.da), "realize", G_CALLBACK(realise_sig), &gc); gtk_widget_grab_focus(GTK_WIDGET(gc.da)); gtk_widget_show_all(mainwindow); g_timeout_add(20, redraw_cb, &gc); gtk_main(); return 0; }