From 5fead1096ee4ffdfed9957b575e2b09e6d9637cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Stensg=C3=A5rd?= Date: Tue, 22 Jul 2025 11:41:10 +0200 Subject: install, bin/opplysning-* --- .gitignore | 2 - Makefile | 37 ++++-- bin/.gitignore | 3 + opplysning-endre.c | 95 +++++++++++++++ opplysning-forfrisk.sh | 10 ++ opplysning-vis.c | 306 +++++++++++++++++++++++++++++++++++++++++++++++++ opplysning.c | 306 ------------------------------------------------- refresh.sh | 10 -- update.c | 95 --------------- 9 files changed, 441 insertions(+), 423 deletions(-) create mode 100644 bin/.gitignore create mode 100644 opplysning-endre.c create mode 100755 opplysning-forfrisk.sh create mode 100644 opplysning-vis.c delete mode 100644 opplysning.c delete mode 100755 refresh.sh delete mode 100644 update.c diff --git a/.gitignore b/.gitignore index c25e3e7..ae94ce9 100644 --- a/.gitignore +++ b/.gitignore @@ -3,8 +3,6 @@ /check_ical /check_ical_out.txt /events.ical -/opplysning /select /sqlite3 -/update core diff --git a/Makefile b/Makefile index 4ab78c2..ac4e349 100644 --- a/Makefile +++ b/Makefile @@ -6,28 +6,48 @@ LIBS_RAYLIB = -lraylib -lGLESv2 -lEGL \ $$([ `uname -m` = aarch64 ] && echo '-lvcos -lvchiq_arm -lgbm -ldrm') LIBS_SQLITE = -ldl -lm -lpthread -all: check_ical opplysning select update +all: \ + check_ical \ + bin/opplysning-endre \ + bin/opplysning-forfrisk \ + bin/opplysning-vis \ + select check: all ./check_ical < check_ical_in.ical > check_ical_out.txt diff -u check_ical_ans.txt check_ical_out.txt && echo PASS || echo FAIL clean: + rm -f bin/* rm -f check_ical check_ical_out.txt - rm -f opplysning rm -f select rm -f sqlite.o rm -f sqlite3 - rm -f update -.PHONY: all check clean +install: all + mkdir -p /opt/opplysning/bin + cp -f bin/opplysning-endre /opt/opplysning/bin/ + cp -f bin/opplysning-forfrisk /opt/opplysning/bin/ + cp -f bin/opplysning-vis /opt/opplysning/bin/ + mkdir -p /opt/opplysning/font + cp -f font/BellCentennialStd-Address.ttf /opt/opplysning/font/ + cp -f font/adventpro-bold.ttf /opt/opplysning/font/ + mkdir -p /opt/opplysning/var + +.PHONY: all check clean install + +bin/opplysning-endre: opplysning-endre.c sqlite.o + $(CC) $(CFLAGS) -o $@ opplysning-endre.c sqlite.o $(LIBS_LIBICAL) $(LIBS_SQLITE) + +bin/opplysning-forfrisk: opplysning-forfrisk.sh + cp -p opplysning-forfrisk.sh $@ + +bin/opplysning-vis: opplysning-vis.c sqlite.o + $(CC) $(CFLAGS) -o $@ opplysning-vis.c sqlite.o $(LIBS_RAYLIB) $(LIBS_SQLITE) check_ical: check_ical.c $(CC) $(CFLAGS) -o $@ check_ical.c $(LIBS_LIBICAL) -opplysning: opplysning.c sqlite.o - $(CC) $(CFLAGS) -o $@ opplysning.c sqlite.o $(LIBS_RAYLIB) $(LIBS_SQLITE) - select: select.c sqlite.o $(CC) $(CFLAGS) -o $@ select.c sqlite.o $(LIBS_SQLITE) @@ -36,6 +56,3 @@ sqlite.o: sqlite/sqlite3.c sqlite3: sqlite/shell.c sqlite/sqlite3.c $(CC) $(CFLAGS) -o sqlite3 sqlite/shell.c sqlite/sqlite3.c $(LIBS_SQLITE) - -update: update.c sqlite.o - $(CC) $(CFLAGS) -o $@ update.c sqlite.o $(LIBS_LIBICAL) $(LIBS_SQLITE) diff --git a/bin/.gitignore b/bin/.gitignore new file mode 100644 index 0000000..e39c6c6 --- /dev/null +++ b/bin/.gitignore @@ -0,0 +1,3 @@ +* + +!/.gitignore diff --git a/opplysning-endre.c b/opplysning-endre.c new file mode 100644 index 0000000..b55c679 --- /dev/null +++ b/opplysning-endre.c @@ -0,0 +1,95 @@ +#include +#include +#include + +#include + +#include "sqlite/sqlite3.h" + +static char *SCHEMA = "CREATE TABLE event (" + " start TIMESTAMP," + " end TIMESTAMP," + " summary TEXT" + ")"; + +static char * +read_stream(char *s, size_t size, void *d) +{ + return fgets(s, (int)size, (FILE *)d); +} + +static void +component(sqlite3 *db, icalcomponent *c) +{ + struct icaltimetype dts = icalcomponent_get_dtstart(c); + struct icaltimetype dte = icalcomponent_get_dtend(c); + + icaltimezone *utc = icaltimezone_get_utc_timezone(); + dts = icaltime_convert_to_zone(dts, utc); + dte = icaltime_convert_to_zone(dte, utc); + + time_t start = icaltime_as_timet(dts); + time_t end = icaltime_as_timet(dte); + const char *summary = icalcomponent_get_summary(c); + + sqlite3_stmt *stmt = NULL; + const char *sql = "INSERT INTO event (start, end, summary) VALUES (" + "datetime(?, 'unixepoch')," + "datetime(?, 'unixepoch')" + ", ?)"; + if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL)) + errx(1, "sqlite3_prepare_v2"); + + if (sqlite3_bind_int64(stmt, 1, start)) + errx(1, "sqlite3_bind_int64"); + if (sqlite3_bind_int64(stmt, 2, end)) + errx(1, "sqlite3_bind_int64"); + if (sqlite3_bind_text(stmt, 3, summary, -1, SQLITE_STATIC)) + errx(1, "sqlite3_bind_text"); + if (SQLITE_DONE != sqlite3_step(stmt)) + errx(1, "sqlite3_step"); +} + +int +main(int argc, const char *argv[static argc]) +{ + if (2 != argc) + errx(1, "usage: %s dbfile < icalfile", argv[0]); + + icalparser *parser = icalparser_new(); + assert(parser); + FILE *stream = stdin; + assert(stream); + icalparser_set_gen_data(parser, stream); + + sqlite3 *db; + char *errmsg = NULL; + if (sqlite3_open(argv[1], &db)) + errx(1, "sqlite3_open: %s", errmsg); + if (sqlite3_exec(db, "BEGIN", NULL, NULL, &errmsg)) + errx(1, "sqlite3_exec: %s", errmsg); + if (sqlite3_exec(db, "DROP TABLE IF EXISTS event", NULL, NULL, &errmsg)) + errx(1, "sqlite3_exec: %s", errmsg); + if (sqlite3_exec(db, SCHEMA, NULL, NULL, &errmsg)) + errx(1, "sqlite3_exec: %s", errmsg); + for (;;) { + char *line = icalparser_get_line(parser, read_stream); + if (!line) + break; + icalcomponent *c = icalparser_add_line(parser, line); + if (!c) + continue; + for (icalcompiter i = icalcomponent_begin_component( + c, ICAL_VEVENT_COMPONENT); + icalcompiter_deref(&i); icalcompiter_next(&i)) { + icalcomponent *ic = icalcompiter_deref(&i); + component(db, ic); + } + icalcomponent_free(c); + } + if (sqlite3_exec(db, "COMMIT", NULL, NULL, &errmsg)) + errx(1, "sqlite3_exec: %s", errmsg); + sqlite3_close(db); + + icalparser_free(parser); +} diff --git a/opplysning-forfrisk.sh b/opplysning-forfrisk.sh new file mode 100755 index 0000000..264b695 --- /dev/null +++ b/opplysning-forfrisk.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +set -eu +set -x + +mkdir -p var +curl -o var/google.ical https://calendar.google.com/calendar/ical/u3054u2f4kpkl7edub90faijvo@group.calendar.google.com/public/basic.ics +curl -o var/meetup.ical https://www.meetup.com/bitraf/events/ical/ +./bin/opplysning-endre var/google.db < var/google.ical +./bin/opplysning-endre var/meetup.db < var/meetup.ical diff --git a/opplysning-vis.c b/opplysning-vis.c new file mode 100644 index 0000000..ba4d9c9 --- /dev/null +++ b/opplysning-vis.c @@ -0,0 +1,306 @@ +#define _POSIX_C_SOURCE 199309L + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "sqlite/sqlite3.h" + +enum { + SCREEN_W = 1920, + SCREEN_H = 1080, +}; + +const char *maaned[] = { + "", "JANUAR", "FEBRUAR", "MARS", "APRIL", "MAI", "JUNI", + "JULI", "AUGUST", "SEPTEMBER", "OKTOBER", "NOVEMBER", "DESEMBER", +}; +const char *ukedag[] = { + "SØNDAG", "MANDAG", "TIRSDAG", "ONSDAG", "TORSDAG", "FREDAG", "LØRDAG", +}; + +enum { + MAX_EVENTS = 64, + SIZEOF_TITLE = 60, +}; + +struct event { + time_t start; + time_t end; + int year; + int month; + int day; + int wday; + int hour; + int minute; + int end_hour; + int end_minute; + char title[SIZEOF_TITLE]; +}; + +struct calendar { + char *database; + char *title; + char *subtitle; + size_t num_events; + struct event events[MAX_EVENTS]; +}; + +void +load_events(struct calendar *cal) +{ + sqlite3 *db; + if (sqlite3_open(cal->database, &db)) + errx(1, "sqlite3_open: %s", sqlite3_errmsg(db)); + + const char *sql = "SELECT " + "unixepoch(start), " + "unixepoch(end), " + "CAST(strftime('%Y', start, 'localtime') as INT), " + "CAST(strftime('%m', start, 'localtime') as INT), " + "CAST(strftime('%d', start, 'localtime') as INT), " + "CAST(strftime('%w', start, 'localtime') as INT), " + "CAST(strftime('%H', start, 'localtime') as INT), " + "CAST(strftime('%M', start, 'localtime') as INT), " + "CAST(strftime('%H', end , 'localtime') as INT), " + "CAST(strftime('%M', end , 'localtime') as INT), " + "summary " + "FROM event " + "WHERE datetime('now', 'start of day') < start " + "AND summary IS NOT NULL " + "ORDER BY start"; + sqlite3_stmt *stmt = NULL; + if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL)) + errx(1, "sqlite3_prepare_v2: %s", sqlite3_errmsg(db)); + + size_t n = 0; + for (;;) { + int step = sqlite3_step(stmt); + if (SQLITE_DONE == step) + break; + if (SQLITE_ROW != step) + errx(1, "sqlite3_step: %s", sqlite3_errmsg(db)); + + if (MAX_EVENTS <= n) { + warnx("MAX_EVENTS exceeded at %zu", n); + break; + } + + int start = sqlite3_column_int(stmt, 0); + int end = sqlite3_column_int(stmt, 1); + int year = sqlite3_column_int(stmt, 2); + int month = sqlite3_column_int(stmt, 3); + int day = sqlite3_column_int(stmt, 4); + int wday = sqlite3_column_int(stmt, 5); + int hour = sqlite3_column_int(stmt, 6); + int minute = sqlite3_column_int(stmt, 7); + int end_hour = sqlite3_column_int(stmt, 8); + int end_minute = sqlite3_column_int(stmt, 9); + const char *summary = + (const void *)sqlite3_column_text(stmt, 10); + assert(summary); + + cal->events[n].start = start; + cal->events[n].end = end; + cal->events[n].year = year; + cal->events[n].month = month; + cal->events[n].day = day; + cal->events[n].hour = hour; + cal->events[n].minute = minute; + cal->events[n].end_hour = end_hour; + cal->events[n].end_minute = end_minute; + strncpy(cal->events[n].title, summary, SIZEOF_TITLE); + cal->events[n].wday = wday; + ++n; + } + + if (sqlite3_finalize(stmt)) + errx(1, "sqlite3_finalize: %s", sqlite3_errmsg(db)); + + if (sqlite3_close(db)) + errx(1, "sqlite3_close: %s", sqlite3_errmsg(db)); + + cal->num_events = n; +} + +struct ray { + Shader shader; + RenderTexture2D target; + int shader_u_time; + + Color bg; + Color fg; + Color hd; + Color rd; + + Font font_h; + Font font_p; +}; +struct ray R; + +const char *the_non_ascii = "ÄÅÉËÞÜÚÍÓÖÁÐFGHÏŒØÆŒ©®BÑΜ" + "äåéëþüúíóöáðfghïœøæœ©®bñµß" + "«»"; + +void +ray_init(void) +{ + SetTraceLogLevel(LOG_WARNING); + InitWindow(SCREEN_W, SCREEN_H, "opplysning"); + SetTargetFPS(60); + + char *codes = calloc(128 + strlen(the_non_ascii), 1); + if (!codes) + err(1, "calloc"); + for (int i = 1; i < 128; ++i) + codes[i] = i; + memcpy(codes + 128, the_non_ascii, strlen(the_non_ascii)); + int ncp = 0; + int *cp = LoadCodepoints(codes + 1, &ncp); + R.font_h = LoadFontEx("font/adventpro-bold.ttf", 48, cp, ncp); + R.font_p = + LoadFontEx("font/BellCentennialStd-Address.ttf", 28, cp, ncp); + UnloadCodepoints(cp); + free(codes); + + R.bg = RAYWHITE; + R.fg = BLACK; + R.hd = (Color){0xf0, 0x4a, 0x00, 0xff}; + R.rd = (Color){0xd7, 0x21, 0x17, 0xff}; + + R.shader = LoadShader(0, "s.glsl"); + R.shader_u_time = GetShaderLocation(R.shader, "u_time"); + R.target = LoadRenderTexture(SCREEN_W, SCREEN_H); +} + +void +blur(int x, int y, Font f, Color c, char *s) +{ + Vector2 m = MeasureTextEx(f, s, f.baseSize, 0); + + BeginTextureMode(R.target); + ClearBackground((Color){0}); + DrawTextEx(f, s, (Vector2){0, 0}, (float)f.baseSize, 0, c); + EndTextureMode(); + + BeginShaderMode(R.shader); + float t = GetTime(); + SetShaderValue(R.shader, R.shader_u_time, &t, SHADER_UNIFORM_FLOAT); + DrawTextureRec(R.target.texture, + (Rectangle){0, SCREEN_H - m.y, m.x, -m.y}, + (Vector2){x, y}, WHITE); + EndShaderMode(); +} +void +line(int x, int y, Font f, Color c, const char *s) +{ + DrawTextEx(f, s, (Vector2){x, y}, (float)f.baseSize, 0, c); +} + +void +draw_time(int x, int y, struct timespec now) +{ + struct tm *ti = localtime(&now.tv_sec); + char ts[64] = {0}; + snprintf(ts, sizeof(ts), "%02d:%02d", ti->tm_hour, ti->tm_min); + Vector2 v2_ts = MeasureTextEx(R.font_h, ts, R.font_h.baseSize, 0); + line(x - v2_ts.x / 2, y, R.font_h, R.fg, ts); +} + +void +draw_event(int x, int y, struct timespec now, const struct event *e) +{ + char s[128] = {0}; + snprintf(s, sizeof(s), "%02u:%02u - %02u:%02u %s", e->hour, e->minute, + e->end_hour, e->end_minute, e->title); + Color c = R.fg; + if (e->start <= now.tv_sec && now.tv_sec < e->end) + c = R.rd; + line(x, y, R.font_p, c, s); +} + +void +draw_date(int x, int y, struct timespec now, int wday, int day, int month, + int year) +{ + struct tm *ti = localtime(&now.tv_sec); + Color c = R.fg; + if ((year == 1900 + ti->tm_year) && (month == 1 + ti->tm_mon) && + (day == ti->tm_mday)) + c = R.rd; + char s[64] = {0}; + snprintf(s, sizeof(s), "%s %u. %s", ukedag[wday], day, maaned[month]); + line(x, y, R.font_h, c, s); +} + +void +draw_calendar(int x, int y, struct timespec now, struct calendar *cal) + +{ + x += 20; + line(x, y, R.font_h, R.fg, cal->title); + y += 50; + line(x, y, R.font_p, R.fg, cal->subtitle); + + int year = 0, month = 0, day = 0; + for (size_t i = 0; i < cal->num_events; ++i) { + const struct event *e = &cal->events[i]; + if (year != e->year || month != e->month || day != e->day) { + if (SCREEN_H - 2 * R.font_h.baseSize < y) + break; + year = e->year; + month = e->month; + day = e->day; + y += 40; + draw_date(x, y, now, e->wday, e->day, e->month, + e->year); + y += 20; + } + y += 40; + draw_event(x, y, now, e); + } +} + +int +main(void) +{ + struct calendar google = { + .database = "var/google.db", + .title = "Reservasjoner", + .subtitle = "Hentet fra Google-kalenderen «Bitraf booking»", + }; + struct calendar meetup = { + .database = "var/meetup.db", + .title = "Begivenheter", + .subtitle = "Hentet fra https://www.meetup.com/bitraf/", + }; + + ray_init(); + while (!WindowShouldClose()) { + if (IsKeyPressed(KEY_R)) { + double t0 = GetTime(); + load_events(&google); + load_events(&meetup); + double t1 = GetTime(); + fprintf(stderr, "%6.3f ms\n", (t1 - t0) * 1000.0); + } + + struct timespec now = {0}; + clock_gettime(CLOCK_REALTIME, &now); + + BeginDrawing(); + ClearBackground(R.bg); + draw_time(SCREEN_W / 2 - 100, 0, now); + draw_calendar(0, 0, now, &meetup); + draw_calendar(SCREEN_W / 2, 0, now, &google); + EndDrawing(); + } + CloseWindow(); +} diff --git a/opplysning.c b/opplysning.c deleted file mode 100644 index ba4d9c9..0000000 --- a/opplysning.c +++ /dev/null @@ -1,306 +0,0 @@ -#define _POSIX_C_SOURCE 199309L - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "sqlite/sqlite3.h" - -enum { - SCREEN_W = 1920, - SCREEN_H = 1080, -}; - -const char *maaned[] = { - "", "JANUAR", "FEBRUAR", "MARS", "APRIL", "MAI", "JUNI", - "JULI", "AUGUST", "SEPTEMBER", "OKTOBER", "NOVEMBER", "DESEMBER", -}; -const char *ukedag[] = { - "SØNDAG", "MANDAG", "TIRSDAG", "ONSDAG", "TORSDAG", "FREDAG", "LØRDAG", -}; - -enum { - MAX_EVENTS = 64, - SIZEOF_TITLE = 60, -}; - -struct event { - time_t start; - time_t end; - int year; - int month; - int day; - int wday; - int hour; - int minute; - int end_hour; - int end_minute; - char title[SIZEOF_TITLE]; -}; - -struct calendar { - char *database; - char *title; - char *subtitle; - size_t num_events; - struct event events[MAX_EVENTS]; -}; - -void -load_events(struct calendar *cal) -{ - sqlite3 *db; - if (sqlite3_open(cal->database, &db)) - errx(1, "sqlite3_open: %s", sqlite3_errmsg(db)); - - const char *sql = "SELECT " - "unixepoch(start), " - "unixepoch(end), " - "CAST(strftime('%Y', start, 'localtime') as INT), " - "CAST(strftime('%m', start, 'localtime') as INT), " - "CAST(strftime('%d', start, 'localtime') as INT), " - "CAST(strftime('%w', start, 'localtime') as INT), " - "CAST(strftime('%H', start, 'localtime') as INT), " - "CAST(strftime('%M', start, 'localtime') as INT), " - "CAST(strftime('%H', end , 'localtime') as INT), " - "CAST(strftime('%M', end , 'localtime') as INT), " - "summary " - "FROM event " - "WHERE datetime('now', 'start of day') < start " - "AND summary IS NOT NULL " - "ORDER BY start"; - sqlite3_stmt *stmt = NULL; - if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL)) - errx(1, "sqlite3_prepare_v2: %s", sqlite3_errmsg(db)); - - size_t n = 0; - for (;;) { - int step = sqlite3_step(stmt); - if (SQLITE_DONE == step) - break; - if (SQLITE_ROW != step) - errx(1, "sqlite3_step: %s", sqlite3_errmsg(db)); - - if (MAX_EVENTS <= n) { - warnx("MAX_EVENTS exceeded at %zu", n); - break; - } - - int start = sqlite3_column_int(stmt, 0); - int end = sqlite3_column_int(stmt, 1); - int year = sqlite3_column_int(stmt, 2); - int month = sqlite3_column_int(stmt, 3); - int day = sqlite3_column_int(stmt, 4); - int wday = sqlite3_column_int(stmt, 5); - int hour = sqlite3_column_int(stmt, 6); - int minute = sqlite3_column_int(stmt, 7); - int end_hour = sqlite3_column_int(stmt, 8); - int end_minute = sqlite3_column_int(stmt, 9); - const char *summary = - (const void *)sqlite3_column_text(stmt, 10); - assert(summary); - - cal->events[n].start = start; - cal->events[n].end = end; - cal->events[n].year = year; - cal->events[n].month = month; - cal->events[n].day = day; - cal->events[n].hour = hour; - cal->events[n].minute = minute; - cal->events[n].end_hour = end_hour; - cal->events[n].end_minute = end_minute; - strncpy(cal->events[n].title, summary, SIZEOF_TITLE); - cal->events[n].wday = wday; - ++n; - } - - if (sqlite3_finalize(stmt)) - errx(1, "sqlite3_finalize: %s", sqlite3_errmsg(db)); - - if (sqlite3_close(db)) - errx(1, "sqlite3_close: %s", sqlite3_errmsg(db)); - - cal->num_events = n; -} - -struct ray { - Shader shader; - RenderTexture2D target; - int shader_u_time; - - Color bg; - Color fg; - Color hd; - Color rd; - - Font font_h; - Font font_p; -}; -struct ray R; - -const char *the_non_ascii = "ÄÅÉËÞÜÚÍÓÖÁÐFGHÏŒØÆŒ©®BÑΜ" - "äåéëþüúíóöáðfghïœøæœ©®bñµß" - "«»"; - -void -ray_init(void) -{ - SetTraceLogLevel(LOG_WARNING); - InitWindow(SCREEN_W, SCREEN_H, "opplysning"); - SetTargetFPS(60); - - char *codes = calloc(128 + strlen(the_non_ascii), 1); - if (!codes) - err(1, "calloc"); - for (int i = 1; i < 128; ++i) - codes[i] = i; - memcpy(codes + 128, the_non_ascii, strlen(the_non_ascii)); - int ncp = 0; - int *cp = LoadCodepoints(codes + 1, &ncp); - R.font_h = LoadFontEx("font/adventpro-bold.ttf", 48, cp, ncp); - R.font_p = - LoadFontEx("font/BellCentennialStd-Address.ttf", 28, cp, ncp); - UnloadCodepoints(cp); - free(codes); - - R.bg = RAYWHITE; - R.fg = BLACK; - R.hd = (Color){0xf0, 0x4a, 0x00, 0xff}; - R.rd = (Color){0xd7, 0x21, 0x17, 0xff}; - - R.shader = LoadShader(0, "s.glsl"); - R.shader_u_time = GetShaderLocation(R.shader, "u_time"); - R.target = LoadRenderTexture(SCREEN_W, SCREEN_H); -} - -void -blur(int x, int y, Font f, Color c, char *s) -{ - Vector2 m = MeasureTextEx(f, s, f.baseSize, 0); - - BeginTextureMode(R.target); - ClearBackground((Color){0}); - DrawTextEx(f, s, (Vector2){0, 0}, (float)f.baseSize, 0, c); - EndTextureMode(); - - BeginShaderMode(R.shader); - float t = GetTime(); - SetShaderValue(R.shader, R.shader_u_time, &t, SHADER_UNIFORM_FLOAT); - DrawTextureRec(R.target.texture, - (Rectangle){0, SCREEN_H - m.y, m.x, -m.y}, - (Vector2){x, y}, WHITE); - EndShaderMode(); -} -void -line(int x, int y, Font f, Color c, const char *s) -{ - DrawTextEx(f, s, (Vector2){x, y}, (float)f.baseSize, 0, c); -} - -void -draw_time(int x, int y, struct timespec now) -{ - struct tm *ti = localtime(&now.tv_sec); - char ts[64] = {0}; - snprintf(ts, sizeof(ts), "%02d:%02d", ti->tm_hour, ti->tm_min); - Vector2 v2_ts = MeasureTextEx(R.font_h, ts, R.font_h.baseSize, 0); - line(x - v2_ts.x / 2, y, R.font_h, R.fg, ts); -} - -void -draw_event(int x, int y, struct timespec now, const struct event *e) -{ - char s[128] = {0}; - snprintf(s, sizeof(s), "%02u:%02u - %02u:%02u %s", e->hour, e->minute, - e->end_hour, e->end_minute, e->title); - Color c = R.fg; - if (e->start <= now.tv_sec && now.tv_sec < e->end) - c = R.rd; - line(x, y, R.font_p, c, s); -} - -void -draw_date(int x, int y, struct timespec now, int wday, int day, int month, - int year) -{ - struct tm *ti = localtime(&now.tv_sec); - Color c = R.fg; - if ((year == 1900 + ti->tm_year) && (month == 1 + ti->tm_mon) && - (day == ti->tm_mday)) - c = R.rd; - char s[64] = {0}; - snprintf(s, sizeof(s), "%s %u. %s", ukedag[wday], day, maaned[month]); - line(x, y, R.font_h, c, s); -} - -void -draw_calendar(int x, int y, struct timespec now, struct calendar *cal) - -{ - x += 20; - line(x, y, R.font_h, R.fg, cal->title); - y += 50; - line(x, y, R.font_p, R.fg, cal->subtitle); - - int year = 0, month = 0, day = 0; - for (size_t i = 0; i < cal->num_events; ++i) { - const struct event *e = &cal->events[i]; - if (year != e->year || month != e->month || day != e->day) { - if (SCREEN_H - 2 * R.font_h.baseSize < y) - break; - year = e->year; - month = e->month; - day = e->day; - y += 40; - draw_date(x, y, now, e->wday, e->day, e->month, - e->year); - y += 20; - } - y += 40; - draw_event(x, y, now, e); - } -} - -int -main(void) -{ - struct calendar google = { - .database = "var/google.db", - .title = "Reservasjoner", - .subtitle = "Hentet fra Google-kalenderen «Bitraf booking»", - }; - struct calendar meetup = { - .database = "var/meetup.db", - .title = "Begivenheter", - .subtitle = "Hentet fra https://www.meetup.com/bitraf/", - }; - - ray_init(); - while (!WindowShouldClose()) { - if (IsKeyPressed(KEY_R)) { - double t0 = GetTime(); - load_events(&google); - load_events(&meetup); - double t1 = GetTime(); - fprintf(stderr, "%6.3f ms\n", (t1 - t0) * 1000.0); - } - - struct timespec now = {0}; - clock_gettime(CLOCK_REALTIME, &now); - - BeginDrawing(); - ClearBackground(R.bg); - draw_time(SCREEN_W / 2 - 100, 0, now); - draw_calendar(0, 0, now, &meetup); - draw_calendar(SCREEN_W / 2, 0, now, &google); - EndDrawing(); - } - CloseWindow(); -} diff --git a/refresh.sh b/refresh.sh deleted file mode 100755 index 4c13c01..0000000 --- a/refresh.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -set -eu -set -x - -mkdir -p var -curl -o var/google.ical https://calendar.google.com/calendar/ical/u3054u2f4kpkl7edub90faijvo@group.calendar.google.com/public/basic.ics -curl -o var/meetup.ical https://www.meetup.com/bitraf/events/ical/ -./update var/google.db < var/google.ical -./update var/meetup.db < var/meetup.ical diff --git a/update.c b/update.c deleted file mode 100644 index b55c679..0000000 --- a/update.c +++ /dev/null @@ -1,95 +0,0 @@ -#include -#include -#include - -#include - -#include "sqlite/sqlite3.h" - -static char *SCHEMA = "CREATE TABLE event (" - " start TIMESTAMP," - " end TIMESTAMP," - " summary TEXT" - ")"; - -static char * -read_stream(char *s, size_t size, void *d) -{ - return fgets(s, (int)size, (FILE *)d); -} - -static void -component(sqlite3 *db, icalcomponent *c) -{ - struct icaltimetype dts = icalcomponent_get_dtstart(c); - struct icaltimetype dte = icalcomponent_get_dtend(c); - - icaltimezone *utc = icaltimezone_get_utc_timezone(); - dts = icaltime_convert_to_zone(dts, utc); - dte = icaltime_convert_to_zone(dte, utc); - - time_t start = icaltime_as_timet(dts); - time_t end = icaltime_as_timet(dte); - const char *summary = icalcomponent_get_summary(c); - - sqlite3_stmt *stmt = NULL; - const char *sql = "INSERT INTO event (start, end, summary) VALUES (" - "datetime(?, 'unixepoch')," - "datetime(?, 'unixepoch')" - ", ?)"; - if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL)) - errx(1, "sqlite3_prepare_v2"); - - if (sqlite3_bind_int64(stmt, 1, start)) - errx(1, "sqlite3_bind_int64"); - if (sqlite3_bind_int64(stmt, 2, end)) - errx(1, "sqlite3_bind_int64"); - if (sqlite3_bind_text(stmt, 3, summary, -1, SQLITE_STATIC)) - errx(1, "sqlite3_bind_text"); - if (SQLITE_DONE != sqlite3_step(stmt)) - errx(1, "sqlite3_step"); -} - -int -main(int argc, const char *argv[static argc]) -{ - if (2 != argc) - errx(1, "usage: %s dbfile < icalfile", argv[0]); - - icalparser *parser = icalparser_new(); - assert(parser); - FILE *stream = stdin; - assert(stream); - icalparser_set_gen_data(parser, stream); - - sqlite3 *db; - char *errmsg = NULL; - if (sqlite3_open(argv[1], &db)) - errx(1, "sqlite3_open: %s", errmsg); - if (sqlite3_exec(db, "BEGIN", NULL, NULL, &errmsg)) - errx(1, "sqlite3_exec: %s", errmsg); - if (sqlite3_exec(db, "DROP TABLE IF EXISTS event", NULL, NULL, &errmsg)) - errx(1, "sqlite3_exec: %s", errmsg); - if (sqlite3_exec(db, SCHEMA, NULL, NULL, &errmsg)) - errx(1, "sqlite3_exec: %s", errmsg); - for (;;) { - char *line = icalparser_get_line(parser, read_stream); - if (!line) - break; - icalcomponent *c = icalparser_add_line(parser, line); - if (!c) - continue; - for (icalcompiter i = icalcomponent_begin_component( - c, ICAL_VEVENT_COMPONENT); - icalcompiter_deref(&i); icalcompiter_next(&i)) { - icalcomponent *ic = icalcompiter_deref(&i); - component(db, ic); - } - icalcomponent_free(c); - } - if (sqlite3_exec(db, "COMMIT", NULL, NULL, &errmsg)) - errx(1, "sqlite3_exec: %s", errmsg); - sqlite3_close(db); - - icalparser_free(parser); -} -- cgit v1.2.3