#define _POSIX_C_SOURCE 199309L #include #include #include #include #include #include #include #include #include #include "sqlite/sqlite3.h" static const char *DATABASE = "booking.db"; // Unfortunately, 0 = Sunday. int weekday(int year, int month, int day) { struct tm tm = { .tm_year = year - 1900, .tm_mon = month - 1, .tm_mday = day, }; mktime(&tm); return tm.tm_wday; } 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 { int year; int month; int day; int wday; int hour; int minute; int end_hour; int end_minute; char title[SIZEOF_TITLE]; }; struct event the_events[MAX_EVENTS]; int num_events; char * read_stream(char *s, size_t size, void *d) { return fgets(s, (int)size, (FILE *)d); } void load_the_events_old(void) { icalparser *p = icalparser_new(); assert(p); FILE *f = fopen("events.ical", "r"); assert(f); icalparser_set_gen_data(p, f); for (;;) { char *line = icalparser_get_line(p, read_stream); if (!line) { break; } icalcomponent *comp = icalparser_add_line(p, line); if (!comp) { continue; } for (icalcompiter i = icalcomponent_begin_component( comp, ICAL_VEVENT_COMPONENT); icalcompiter_deref(&i); icalcompiter_next(&i)) { if (MAX_EVENTS <= num_events) { warnx("MAX_EVENTS exceeded at %u", num_events); break; } icalcomponent *c = icalcompiter_deref(&i); struct icaltimetype dts = icalcomponent_get_dtstart(c); const char *sum = icalcomponent_get_summary(c); the_events[num_events].year = dts.year; the_events[num_events].month = dts.month; the_events[num_events].day = dts.day; the_events[num_events].hour = dts.hour; the_events[num_events].minute = dts.minute; strncpy(the_events[num_events].title, sum, SIZEOF_TITLE); the_events[num_events].wday = weekday(dts.year, dts.month, dts.day); ++num_events; } icalcomponent_free(comp); } icalparser_free(p); } void load_the_events(void) { sqlite3 *db; if (sqlite3_open(DATABASE, &db)) errx(1, "sqlite3_open: %s", sqlite3_errmsg(db)); const char *sql = "SELECT " "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)); 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 <= num_events) { warnx("MAX_EVENTS exceeded at %u", num_events); break; } int year = sqlite3_column_int(stmt, 0); int month = sqlite3_column_int(stmt, 1); int day = sqlite3_column_int(stmt, 2); int wday = sqlite3_column_int(stmt, 3); int hour = sqlite3_column_int(stmt, 4); int minute = sqlite3_column_int(stmt, 5); int end_hour = sqlite3_column_int(stmt, 6); int end_minute = sqlite3_column_int(stmt, 7); const char *summary = (const void *)sqlite3_column_text(stmt, 8); assert(summary); the_events[num_events].year = year; the_events[num_events].month = month; the_events[num_events].day = day; the_events[num_events].hour = hour; the_events[num_events].minute = minute; the_events[num_events].end_hour = end_hour; the_events[num_events].end_minute = end_minute; strncpy(the_events[num_events].title, summary, SIZEOF_TITLE); the_events[num_events].wday = wday; ++num_events; } sqlite3_close(db); } const char *the_non_ascii = "ÄÅÉËÞÜÚÍÓÖÁÐFGHÏŒØÆŒ©®BÑΜ" "äåéëþüúíóöáðfghïœøæœ©®bñµß"; Shader the_shader; RenderTexture2D the_target; int the_shader_u_time; void blur(int x, int y, Font f, Color c, char *s) { const int sh = 1080; Vector2 m = MeasureTextEx(f, s, f.baseSize, 0); BeginTextureMode(the_target); ClearBackground((Color){0}); DrawTextEx(f, s, (Vector2){0, 0}, (float)f.baseSize, 0, c); EndTextureMode(); BeginShaderMode(the_shader); float t = GetTime(); SetShaderValue(the_shader, the_shader_u_time, &t, SHADER_UNIFORM_FLOAT); DrawTextureRec(the_target.texture, (Rectangle){0, sh - m.y, m.x, -m.y}, (Vector2){x, y}, WHITE); EndShaderMode(); } void line(int x, int y, Font f, Color c, char *s) { DrawTextEx(f, s, (Vector2){x, y}, (float)f.baseSize, 0, c); } int main(void) { load_the_events(); const int sw = 1920; const int sh = 1080; SetTraceLogLevel(LOG_WARNING); InitWindow(sw, sh, "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); Font font_h = LoadFontEx("font/adventpro-bold.ttf", 60, cp, ncp); // Font font_p = LoadFontEx("font/adventpro-semibold.ttf", 40, cp, ncp); Font font_p = LoadFontEx("font/NHaasGroteskTXPro-55Rg.ttf", 40, cp, ncp); UnloadCodepoints(cp); free(codes); Color bg = RAYWHITE; Color fg = BLACK; Color hd = {0xf0, 0x4a, 0x00, 0xff}; the_shader = LoadShader(0, "s.glsl"); the_shader_u_time = GetShaderLocation(the_shader, "u_time"); the_target = LoadRenderTexture(sw, sh); int fx = 0; while (!WindowShouldClose()) { struct timespec now = {0}; clock_gettime(CLOCK_REALTIME, &now); struct tm *ti = localtime(&now.tv_sec); char ts[64] = {0}; snprintf(ts, sizeof(ts), "%02d:%02d", ti->tm_hour, ti->tm_min); if (IsKeyPressed(KEY_F)) fx = !fx; BeginDrawing(); ClearBackground(bg); Vector2 v2_ts = MeasureTextEx(font_h, ts, font_h.baseSize, 0); line(sw / 2 - v2_ts.x / 2, 0, font_h, fg, ts); int y = 0; int year = 0, month = 0, day = 0; for (int i = 0; i < num_events; ++i) { struct event *e = the_events + i; if (year != e->year || month != e->month || day != e->day) { year = e->year; month = e->month; day = e->day; char s[64] = {0}; snprintf(s, sizeof(s), "%s %u. %s", ukedag[e->wday], e->day, maaned[e->month]); if (fx) blur(25, y += 50, font_h, hd, s); else line(25, y += 50, font_h, hd, s); y += 20; } { y += 40; char s[64] = {0}; snprintf(s, sizeof(s), "%02u:%02u - %02u:%02u", e->hour, e->minute, e->end_hour, e->end_minute); Vector2 m = MeasureTextEx(font_p, s, font_p.baseSize, 0); line(5.8 * font_p.baseSize - m.x, y, font_p, fg, s); line(6.5 * font_p.baseSize, y, font_p, fg, e->title); } } EndDrawing(); } CloseWindow(); }