From 3d6cfca2326fe21c90668e02794a81d2ed7615de Mon Sep 17 00:00:00 2001 From: Curtis McEnroe Date: Fri, 9 Feb 2018 15:14:33 -0500 Subject: 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. --- bin/gfx/cocoa.m | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ bin/gfx/fb.c | 90 ++++++++++++++++++++++++++++++++ bin/gfx/none.c | 21 ++++++++ bin/gfx/x11.c | 124 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 394 insertions(+) create mode 100644 bin/gfx/cocoa.m create mode 100644 bin/gfx/fb.c create mode 100644 bin/gfx/none.c create mode 100644 bin/gfx/x11.c (limited to 'bin/gfx') diff --git a/bin/gfx/cocoa.m b/bin/gfx/cocoa.m new file mode 100644 index 00000000..ece79fb5 --- /dev/null +++ b/bin/gfx/cocoa.m @@ -0,0 +1,159 @@ +/* Copyright (c) 2018, Curtis McEnroe + * + * 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 . + */ + +#import +#import +#import +#import +#import +#import + +#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 +@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..503a8781 --- /dev/null +++ b/bin/gfx/fb.c @@ -0,0 +1,90 @@ +/* Copyright (c) 2018, Curtis McEnroe + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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..b1558da5 --- /dev/null +++ b/bin/gfx/none.c @@ -0,0 +1,21 @@ +/* Copyright (c) 2018, Curtis McEnroe + * + * 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 . + */ + +#include + +int main() { + return EX_CONFIG; +} diff --git a/bin/gfx/x11.c b/bin/gfx/x11.c new file mode 100644 index 00000000..b1b8caca --- /dev/null +++ b/bin/gfx/x11.c @@ -0,0 +1,124 @@ +/* Copyright (c) 2018, Curtis McEnroe + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include + +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; + } + } +} -- cgit 1.4.1