summary refs log tree commit diff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--bin/.gitignore3
-rw-r--r--bin/Makefile25
-rw-r--r--bin/fbclock.c117
3 files changed, 134 insertions, 11 deletions
diff --git a/bin/.gitignore b/bin/.gitignore
index a9bc170e..1197fb01 100644
--- a/bin/.gitignore
+++ b/bin/.gitignore
@@ -1,5 +1,4 @@
 atch
-bri
 dtch
 hnel
 pbcopy
@@ -11,3 +10,5 @@ jrp
 klon
 typo
 watch
+bri
+fbclock
diff --git a/bin/Makefile b/bin/Makefile
index a51ff062..f1b99edd 100644
--- a/bin/Makefile
+++ b/bin/Makefile
@@ -1,11 +1,16 @@
-PINS = atch bri dtch hnel pbcopy pbd pbpaste wake xx
-BINS = $(PINS) jrp klon typo watch
+ANY_BINS = atch dtch hnel pbcopy pbd pbpaste wake xx
+BSD_BINS = jrp klon typo watch
+LIN_BINS = bri fbclock
+ALL_BINS = $(ANY_BINS) $(BSD_BINS) $(LIN_BINS)
+
 CFLAGS += -Wall -Wextra -Wpedantic
-LDLIBS = -lcurses -ledit -lutil
+LDLIBS = -lcurses -ledit -lutil -lz
+
+bsd: $(ANY_BINS) $(BSD_BINS) .gitignore
 
-all: $(BINS) .gitignore
+linux: $(ANY_BINS) $(LIN_BINS) .gitignore
 
-portable: $(PINS) .gitignore
+any: $(ANY_BINS) .gitignore
 
 atch: dtch
 	ln -f dtch atch
@@ -18,15 +23,15 @@ setuid: bri
 	chmod u+s bri
 
 clean:
-	rm -f $(BINS)
+	rm -f $(ALL_BINS)
 
 link:
-	ln -s -f $(BINS:%=$(PWD)/%) ~/.bin
+	ln -s -f $(ALL_BINS:%=$(PWD)/%) ~/.bin
 
 unlink:
-	rm -f $(BINS:%=~/.bin/%)
+	rm -f $(ALL_BINS:%=~/.bin/%)
 
 .gitignore: Makefile
-	echo $(BINS) | tr ' ' '\n' > .gitignore
+	echo $(ALL_BINS) | tr ' ' '\n' > .gitignore
 
-.PHONY: all portable setuid clean link unlink
+.PHONY: bsd linux any setuid clean link unlink
diff --git a/bin/fbclock.c b/bin/fbclock.c
new file mode 100644
index 00000000..6a8a096c
--- /dev/null
+++ b/bin/fbclock.c
@@ -0,0 +1,117 @@
+/* Copyright (c) 2018, June McEnroe <programble@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+// Linux framebuffer clock.
+
+#include <assert.h>
+#include <err.h>
+#include <fcntl.h>
+#include <linux/fb.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sysexits.h>
+#include <time.h>
+#include <unistd.h>
+#include <zlib.h>
+
+static const char *FONT = "/usr/share/kbd/consolefonts/Lat2-Terminus16.psfu.gz";
+
+static const uint32_t PSF2_MAGIC = 0x864AB572;
+struct Psf2Header {
+    uint32_t magic;
+    uint32_t version;
+    uint32_t headerSize;
+    uint32_t flags;
+    uint32_t glyphCount;
+    uint32_t glyphSize;
+    uint32_t glyphHeight;
+    uint32_t glyphWidth;
+};
+
+static const uint32_t BG = 0x1D2021;
+static const uint32_t FG = 0xA99A84;
+
+int main() {
+    size_t count;
+
+    gzFile font = gzopen(FONT, "r");
+    if (!font) err(EX_NOINPUT, "%s", FONT);
+
+    struct Psf2Header header;
+    count = gzfread(&header, sizeof(header), 1, font);
+    if (!count) errx(EX_IOERR, "%s: %s", FONT, gzerror(font, NULL));
+
+    assert(header.magic == PSF2_MAGIC);
+    assert(header.headerSize == sizeof(struct Psf2Header));
+
+    uint8_t glyphs[header.glyphCount][header.glyphSize];
+    count = gzfread(glyphs, header.glyphSize, header.glyphCount, font);
+    if (!count) errx(EX_IOERR, "%s: %s", FONT, gzerror(font, NULL));
+
+    assert(Z_OK == gzclose(font));
+
+    const char *path = getenv("FRAMEBUFFER");
+    if (!path) path = "/dev/fb0";
+
+    int fb = open(path, O_RDWR);
+    if (fb < 0) err(EX_OSFILE, "%s", path);
+
+    struct fb_var_screeninfo info;
+    int error = ioctl(fb, FBIOGET_VSCREENINFO, &info);
+    if (error) err(EX_IOERR, "%s", path);
+
+    size_t len = 4 * info.xres * info.yres;
+    uint32_t *buf = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fb, 0);
+    if (buf == MAP_FAILED) err(EX_IOERR, "%s", path);
+
+    for (;;) {
+        time_t t = time(NULL);
+        if (t < 0) err(EX_OSERR, "time");
+        const struct tm *local = localtime(&t);
+        if (!local) err(EX_OSERR, "localtime");
+
+        char str[64];
+        size_t len = strftime(str, sizeof(str), "%H:%M", local);
+
+        for (int i = 0; i < (60 - local->tm_sec); ++i) {
+            uint32_t left = info.xres - header.glyphWidth * len;
+            uint32_t bottom = header.glyphHeight;
+
+            for (uint32_t y = 0; y < bottom; ++y) {
+                buf[y * info.xres + left - 1] = FG;
+            }
+            for (uint32_t x = left - 1; x < info.xres; ++x) {
+                buf[bottom * info.xres + x] = FG;
+            }
+
+            for (char *s = str; *s; ++s) {
+                uint8_t *glyph = glyphs[(uint8_t)*s];
+                for (uint32_t y = 0; y < header.glyphHeight; ++y) {
+                    for (uint32_t x = 0; x < header.glyphWidth; ++x) {
+                        uint8_t b = glyph[y] >> (header.glyphWidth - x) & 1;
+                        buf[y * info.xres + left + x] = b ? FG : BG;
+                    }
+                }
+                left += header.glyphWidth;
+            }
+
+            sleep(1);
+        }
+    }
+}