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/.gitignore | 6 +-- bin/Makefile | 31 ++++------- bin/gfb.c | 90 -------------------------------- bin/gfcocoa.m | 159 -------------------------------------------------------- bin/gfx/cocoa.m | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ bin/gfx/fb.c | 90 ++++++++++++++++++++++++++++++++ bin/gfx/none.c | 21 ++++++++ bin/gfx/x11.c | 124 +++++++++++++++++++++++++++++++++++++++++++ bin/gfx11.c | 124 ------------------------------------------- 9 files changed, 405 insertions(+), 399 deletions(-) delete mode 100644 bin/gfb.c delete mode 100644 bin/gfcocoa.m 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 delete mode 100644 bin/gfx11.c diff --git a/bin/.gitignore b/bin/.gitignore index c7a9c768..bdc4ce80 100644 --- a/bin/.gitignore +++ b/bin/.gitignore @@ -2,6 +2,7 @@ tags *.o atch dtch +gfxx hnel pbcopy pbd @@ -12,11 +13,6 @@ jrp klon typo watch -gfxx-cocoa -gfxx bri fbatt fbclock -gfxx-fb -gfxx -gfxx-x11 diff --git a/bin/Makefile b/bin/Makefile index 5801ec74..2a4658f1 100644 --- a/bin/Makefile +++ b/bin/Makefile @@ -1,36 +1,25 @@ -ANY_BINS = atch dtch hnel pbcopy pbd pbpaste wake xx +ANY_BINS = atch dtch gfxx hnel pbcopy pbd pbpaste wake xx BSD_BINS = jrp klon typo watch -MAC_BINS = gfxx-cocoa gfxx -LIN_BINS = bri fbatt fbclock gfxx-fb gfxx -ALL_BINS = $(ANY_BINS) $(BSD_BINS) $(MAC_BINS) $(LIN_BINS) gfxx-x11 +LIN_BINS = bri fbatt fbclock +ALL_BINS = $(ANY_BINS) $(BSD_BINS) $(LIN_BINS) +GFX = none CFLAGS += -Wall -Wextra -Wpedantic LDLIBS = -lcurses -ledit -lutil -lz +LDLIBS_cocoa = $(LDLIBS) -framework Cocoa +LDLIBS_x11 = $(LDLIBS) -lX11 any: .gitignore tags $(ANY_BINS) bsd: any $(BSD_BINS) -mac: bsd $(MAC_BINS) - linux: any $(LIN_BINS) atch: dtch ln -f dtch atch -gfxx-cocoa: gfxx.o gfcocoa.o - $(CC) $(LDFLAGS) gfxx.o gfcocoa.o $(LDLIBS) -framework Cocoa -o $@ - -gfxx-fb: gfxx.o gfb.o - $(CC) $(LDFLAGS) gfxx.o gfb.o $(LDLIBS) -o $@ - -gfxx-x11: gfxx.o gfx11.o - $(CC) $(LDFLAGS) gfxx.o gfx11.o $(LDLIBS) -lX11 -o $@ - -gfxx: - [ -f gfxx-fb ] && ln -s -f gfxx-fb gfxx || true - [ -f gfxx-x11 ] && ln -s -f gfxx-x11 gfxx || true - [ -f gfxx-cocoa ] && ln -s -f gfxx-cocoa gfxx || true +gfxx: gfxx.o gfx/$(GFX).o + $(CC) $(LDFLAGS) gfxx.o gfx/$(GFX).o $(LDLIBS_$(GFX)) -o $@ pbcopy pbpaste: pbd ln -f pbd $@ @@ -40,7 +29,7 @@ setuid: bri chmod u+s bri clean: - rm -f *.o $(ALL_BINS) + rm -f *.o gfx/*.o $(ALL_BINS) link: ln -s -f $(ALL_BINS:%=$(PWD)/%) ~/.bin @@ -54,4 +43,4 @@ unlink: tags: *.c ctags *.c -.PHONY: any bsd mac linux setuid clean link unlink +.PHONY: any bsd linux setuid clean link unlink diff --git a/bin/gfb.c b/bin/gfb.c deleted file mode 100644 index 503a8781..00000000 --- a/bin/gfb.c +++ /dev/null @@ -1,90 +0,0 @@ -/* 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/gfcocoa.m b/bin/gfcocoa.m deleted file mode 100644 index ece79fb5..00000000 --- a/bin/gfcocoa.m +++ /dev/null @@ -1,159 +0,0 @@ -/* 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/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; + } + } +} diff --git a/bin/gfx11.c b/bin/gfx11.c deleted file mode 100644 index b1b8caca..00000000 --- a/bin/gfx11.c +++ /dev/null @@ -1,124 +0,0 @@ -/* 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