summary refs log tree commit diff
path: root/bin/gfx
diff options
context:
space:
mode:
authorJune McEnroe <june@causal.agency>2018-02-09 15:14:33 -0500
committerJune McEnroe <june@causal.agency>2018-02-09 15:14:33 -0500
commit68dab38c2c8b000a0918f6427d6d53261a6ce5bb (patch)
tree0abcfc5fa42d3e79a90acf5e1ead79f1e06ed3fa /bin/gfx
parentAdd janky X11 graphics frontend (diff)
downloadsrc-68dab38c2c8b000a0918f6427d6d53261a6ce5bb.tar.gz
src-68dab38c2c8b000a0918f6427d6d53261a6ce5bb.zip
Move gfx frontends around to simplify build
I forgot that you can expand variables inside variables names in make.
Certainly makes some fun things possible.
Diffstat (limited to 'bin/gfx')
-rw-r--r--bin/gfx/cocoa.m159
-rw-r--r--bin/gfx/fb.c90
-rw-r--r--bin/gfx/none.c21
-rw-r--r--bin/gfx/x11.c124
4 files changed, 394 insertions, 0 deletions
diff --git a/bin/gfx/cocoa.m b/bin/gfx/cocoa.m
new file mode 100644
index 00000000..d3d2ef46
--- /dev/null
+++ b/bin/gfx/cocoa.m
@@ -0,0 +1,159 @@
+/* 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/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+#import <err.h>
+#import <stdbool.h>
+#import <stdint.h>
+#import <stdlib.h>
+#import <sysexits.h>
+
+#define UNUSED __attribute__((unused))
+
+extern int init(int argc, char *argv[]);
+extern const char *status(void);
+extern void draw(uint32_t *buf, size_t xres, size_t yres);
+extern bool input(char in);
+
+@interface BufferView : NSView {
+    size_t bufSize;
+    uint32_t *buf;
+    CGColorSpaceRef colorSpace;
+    CGDataProviderRef dataProvider;
+}
+@end
+
+@implementation BufferView
+- (instancetype) initWithFrame: (NSRect) frameRect {
+    colorSpace = CGColorSpaceCreateDeviceRGB();
+    return [super initWithFrame: frameRect];
+}
+
+- (void) setWindowTitle {
+    [[self window] setTitle: [NSString stringWithUTF8String: status()]];
+}
+
+- (void) draw {
+    draw(buf, [self frame].size.width, [self frame].size.height);
+    [self setNeedsDisplay: YES];
+}
+
+- (void) setFrameSize: (NSSize) newSize {
+    [super setFrameSize: newSize];
+    size_t newBufSize = 4 * newSize.width * newSize.height;
+    if (newBufSize > bufSize) {
+        bufSize = newBufSize;
+        buf = malloc(bufSize);
+        if (!buf) err(EX_OSERR, "malloc(%zu)", bufSize);
+        CGDataProviderRelease(dataProvider);
+        dataProvider = CGDataProviderCreateWithData(NULL, buf, bufSize, NULL);
+    }
+    [self draw];
+}
+
+- (void) drawRect: (NSRect) UNUSED dirtyRect {
+    NSSize size = [self frame].size;
+    CGContextRef ctx = [[NSGraphicsContext currentContext] CGContext];
+    CGImageRef image = CGImageCreate(
+        size.width, size.height,
+        8, 32, 4 * size.width,
+        colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst,
+        dataProvider,
+        NULL, false, kCGRenderingIntentDefault
+    );
+    CGContextDrawImage(ctx, [self frame], image);
+    CGImageRelease(image);
+}
+
+- (BOOL) acceptsFirstResponder {
+    return YES;
+}
+
+- (void) keyDown: (NSEvent *) event {
+    char in;
+    BOOL converted = [
+        [event characters]
+        getBytes: &in
+        maxLength: 1
+        usedLength: NULL
+        encoding: NSASCIIStringEncoding
+        options: 0
+        range: NSMakeRange(0, 1)
+        remainingRange: NULL
+    ];
+    if (converted) {
+        if (!input(in)) {
+            [NSApp terminate: self];
+        }
+        [self setWindowTitle];
+        [self draw];
+    }
+}
+@end
+
+@interface Delegate : NSObject <NSApplicationDelegate>
+@end
+
+@implementation Delegate
+- (BOOL) applicationShouldTerminateAfterLastWindowClosed:
+    (NSApplication *) UNUSED sender {
+    return YES;
+}
+@end
+
+int main(int argc, char *argv[]) {
+    int error = init(argc, argv);
+    if (error) return error;
+
+    [NSApplication sharedApplication];
+    [NSApp setActivationPolicy: NSApplicationActivationPolicyRegular];
+    [NSApp setDelegate: [Delegate new]];
+
+    NSString *name = [[NSProcessInfo processInfo] processName];
+    NSMenu *menu = [NSMenu new];
+    NSMenuItem *quit = [
+        [NSMenuItem alloc]
+        initWithTitle: [@"Quit " stringByAppendingString: name]
+        action: @selector(terminate:)
+        keyEquivalent: @"q"
+    ];
+    [menu addItem: quit];
+    NSMenuItem *menuItem = [NSMenuItem new];
+    [menuItem setSubmenu: menu];
+    [NSApp setMainMenu: [NSMenu new]];
+    [[NSApp mainMenu] addItem: menuItem];
+
+    NSUInteger style = NSTitledWindowMask
+        | NSClosableWindowMask
+        | NSMiniaturizableWindowMask
+        | NSResizableWindowMask;
+    NSWindow *window = [
+        [NSWindow alloc]
+        initWithContentRect: NSMakeRect(0, 0, 800, 600)
+        styleMask: style
+        backing: NSBackingStoreBuffered
+        defer: YES
+    ];
+    [window center];
+
+    BufferView *view = [[BufferView alloc] initWithFrame: [window frame]];
+    [window setContentView: view];
+    [view setWindowTitle];
+
+    [window makeKeyAndOrderFront: nil];
+    [NSApp activateIgnoringOtherApps: YES];
+    [NSApp run];
+}
diff --git a/bin/gfx/fb.c b/bin/gfx/fb.c
new file mode 100644
index 00000000..bc4d9d46
--- /dev/null
+++ b/bin/gfx/fb.c
@@ -0,0 +1,90 @@
+/* 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/>.
+ */
+
+#include <err.h>
+#include <fcntl.h>
+#include <linux/fb.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sysexits.h>
+#include <termios.h>
+#include <unistd.h>
+
+extern int init(int argc, char *argv[]);
+extern const char *status(void);
+extern void draw(uint32_t *buf, size_t xres, size_t yres);
+extern bool input(char in);
+
+static struct termios saveTerm;
+static void restoreTerm(void) {
+    tcsetattr(STDERR_FILENO, TCSADRAIN, &saveTerm);
+}
+
+int main(int argc, char *argv[]) {
+    int error;
+
+    error = init(argc, argv);
+    if (error) return error;
+
+    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;
+    error = ioctl(fb, FBIOGET_VSCREENINFO, &info);
+    if (error) err(EX_IOERR, "%s", path);
+
+    size_t size = 4 * info.xres * info.yres;
+    uint32_t *buf = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fb, 0);
+    if (buf == MAP_FAILED) err(EX_IOERR, "%s", path);
+
+    error = tcgetattr(STDERR_FILENO, &saveTerm);
+    if (error) err(EX_IOERR, "tcgetattr");
+    atexit(restoreTerm);
+
+    struct termios term = saveTerm;
+    term.c_lflag &= ~(ICANON | ECHO);
+    error = tcsetattr(STDERR_FILENO, TCSADRAIN, &term);
+    if (error) err(EX_IOERR, "tcsetattr");
+
+    uint32_t saveBg = buf[0];
+
+    uint32_t back[info.xres * info.yres];
+    for (;;) {
+        draw(back, info.xres, info.yres);
+        memcpy(buf, back, size);
+
+        char in;
+        ssize_t len = read(STDERR_FILENO, &in, 1);
+        if (len < 0) err(EX_IOERR, "read");
+        if (!len) return EX_DATAERR;
+
+        if (!input(in)) {
+            for (uint32_t i = 0; i < info.xres * info.yres; ++i) {
+                buf[i] = saveBg;
+            }
+            fprintf(stderr, "%s\n", status());
+            return EX_OK;
+        }
+    }
+}
diff --git a/bin/gfx/none.c b/bin/gfx/none.c
new file mode 100644
index 00000000..7f78ce8a
--- /dev/null
+++ b/bin/gfx/none.c
@@ -0,0 +1,21 @@
+/* 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/>.
+ */
+
+#include <sysexits.h>
+
+int main() {
+    return EX_CONFIG;
+}
diff --git a/bin/gfx/x11.c b/bin/gfx/x11.c
new file mode 100644
index 00000000..53d84895
--- /dev/null
+++ b/bin/gfx/x11.c
@@ -0,0 +1,124 @@
+/* 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/>.
+ */
+
+#include <X11/Xlib.h>
+#include <err.h>
+#include <sysexits.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+extern int init(int argc, char *argv[]);
+extern const char *status(void);
+extern void draw(uint32_t *buf, size_t width, size_t height);
+extern bool input(char in);
+
+static size_t width;
+static size_t height;
+
+static size_t bufSize;
+static uint32_t *buf;
+
+static Display *display;
+static Window window;
+static GC gc;
+static XImage *image;
+static Pixmap pixmap;
+
+static void resize(size_t newWidth, size_t newHeight) {
+    size_t newSize = 4 * newWidth * newHeight;
+    if (newSize > bufSize) {
+        free(buf);
+        buf = malloc(newSize);
+        if (!buf) err(EX_OSERR, "malloc(%zu)", newSize);
+        bufSize = newSize;
+    }
+
+    image->data = (char *)buf;
+    image->width = newWidth;
+    image->height = newHeight;
+    image->bytes_per_line = 4 * newWidth;
+
+    if (pixmap) XFreePixmap(display, pixmap);
+    pixmap = XCreatePixmap(display, window, newWidth, newHeight, 24);
+
+    width = newWidth;
+    height = newHeight;
+}
+
+static void drawWindow(void) {
+    draw(buf, width, height);
+    XPutImage(display, pixmap, gc, image, 0, 0, 0, 0, width, height);
+    XCopyArea(display, pixmap, window, gc, 0, 0, width, height, 0, 0);
+}
+
+int main(int argc, char *argv[]) {
+    int error = init(argc, argv);
+    if (error) return error;
+
+    display = XOpenDisplay(NULL);
+    if (!display) errx(EX_UNAVAILABLE, "XOpenDisplay: %s", XDisplayName(NULL));
+
+    Window root = DefaultRootWindow(display);
+    window = XCreateSimpleWindow(display, root, 0, 0, 800, 600, 0, 0, 0);
+    gc = XCreateGC(display, window, 0, NULL);
+    image = XCreateImage(display, NULL, 24, ZPixmap, 0, NULL, 0, 0, 32, 0);
+
+    Atom WM_DELETE_WINDOW = XInternAtom(display, "WM_DELETE_WINDOW", false);
+    XSetWMProtocols(display, window, &WM_DELETE_WINDOW, 1);
+
+    XStoreName(display, window, status());
+    XMapWindow(display, window);
+
+    XEvent event;
+    XSelectInput(display, window, ExposureMask | StructureNotifyMask | KeyPressMask);
+    for (;;) {
+        XNextEvent(display, &event);
+        switch (event.type) {
+            case KeyPress: {
+                XKeyEvent key = event.xkey;
+                KeySym sym = XLookupKeysym(&key, key.state);
+                if (sym > 128) break;
+                if (!input(sym)) return EX_OK;
+                drawWindow();
+            } break;
+
+            case ConfigureNotify: {
+                XConfigureEvent configure = event.xconfigure;
+                resize(configure.width, configure.height);
+                drawWindow();
+            } break;
+
+            case Expose: {
+                XExposeEvent expose = event.xexpose;
+                XCopyArea(
+                    display,
+                    pixmap, window, gc,
+                    expose.x, expose.y,
+                    expose.width, expose.height,
+                    expose.x, expose.y
+                );
+            } break;
+
+            case ClientMessage: {
+                XClientMessageEvent message = event.xclient;
+                if ((Atom)message.data.l[0] == WM_DELETE_WINDOW) {
+                    return EX_OK;
+                }
+            } break;
+        }
+    }
+}