diff options
author | dweller <dweller@cabin.digital> | 2025-03-28 19:52:47 +0200 |
---|---|---|
committer | dweller <dweller@cabin.digital> | 2025-03-28 19:52:47 +0200 |
commit | b41ec911812e66931f01939378979845716b6119 (patch) | |
tree | 08be727857d9881182a723af382680c48fb89434 | |
parent | 2bcb97cade32e4781135ff4c1500b95fcf351889 (diff) |
experimenting with UI
-rw-r--r-- | Makefile | 24 | ||||
-rwxr-xr-x | build/x11 | 23 | ||||
-rwxr-xr-x | clean | 2 | ||||
-rw-r--r-- | resources/icon.tga | bin | 0 -> 1068 bytes | |||
-rw-r--r-- | resources/icon.xcf | bin | 0 -> 2778 bytes | |||
-rw-r--r-- | sources/bits.c | 72 | ||||
-rw-r--r-- | sources/main.c | 343 | ||||
-rw-r--r-- | sources/tga.c | 138 | ||||
-rw-r--r-- | sources/x11.c | 294 | ||||
-rwxr-xr-x | utils/bin2.c | 79 |
10 files changed, 937 insertions, 38 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4f32fb4 --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +.POSIX: +.SUFFIXES: + +CC = gcc +CFLAGS = -std=c89 -Wall -Wextra -fwhole-program +X11FLAGS = -lm -lX11 -lxcb -lXau -lXdmcp + +x11: artifacts/xip-8 + +artifacts/xip-8: sources/*.c resources/icon.tga.h + $(CC) $(CFLAGS) $(X11FLAGS) sources/main.c -o artifacts/xip-8 + +artifacts/bin2c: utils/bin2.c + $(CC) $(CFLAGS) utils/bin2.c -o artifacts/bin2c + +resources/icon.tga.h: resources/icon.tga artifacts/bin2c + ./artifacts/bin2c resources/icon.tga + +run: x11 + ./artifacts/xip-8 + +clean: + rm -f resources/*.h + rm -rf artifacts/* diff --git a/build/x11 b/build/x11 deleted file mode 100755 index cae1518..0000000 --- a/build/x11 +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/sh -e - -CC=${CC:-musl-gcc} -STATIC=${STATIC:-"-static"} -CFLAGS=" \ - -std=c89 \ - -Wall -Wextra -Wno-comment \ - -ggdb -Og \ -" - -bench() -{ - if [ x = "x$(which time 2>/dev/null)" ]; then - $@ - echo "time not installed" - else - time $@ - fi -} - -mkdir -p artifacts ->&2 bench $CC $CFLAGS sources/main.c $STATIC -o artifacts/xip-8 ->&2 echo "------------------------------------------------\n" @@ -1,2 +0,0 @@ -#!/bin/sh -e -rm -rf artifacts/* diff --git a/resources/icon.tga b/resources/icon.tga Binary files differnew file mode 100644 index 0000000..73d0cd3 --- /dev/null +++ b/resources/icon.tga diff --git a/resources/icon.xcf b/resources/icon.xcf Binary files differnew file mode 100644 index 0000000..25cb8c1 --- /dev/null +++ b/resources/icon.xcf diff --git a/sources/bits.c b/sources/bits.c index 0197436..de0aa0d 100644 --- a/sources/bits.c +++ b/sources/bits.c @@ -5,22 +5,70 @@ * See LICENSE for details */ +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; /* XXX: GCC extension */ +typedef signed char s8; +typedef signed short s16; +typedef signed int s32; +typedef signed long long s64; /* XXX: GCC extension */ + +typedef s8 bool; + +typedef u64 usize; +typedef s64 ssize; + +typedef float f32; +typedef double f64; + + +#define min_s8 ((s8) (0x80)) +#define min_s16 ((s16)(0x8000)) +#define min_s32 ((s32)(0x80000000L)) +#define min_s64 ((s64)(0x8000000000000000LL)) +#define min_ssize min_s64 + +#define max_s8 ((s8) (0x7F)) +#define max_s16 ((s16)(0x7FFF)) +#define max_s32 ((s32)(0x7FFFFFFFL)) +#define max_s64 ((s64)(0x7FFFFFFFFFFFFFFFLL)) +#define max_ssize max_s64 + +#define max_u8 ((u8) (0xFF)) +#define max_u16 ((u16)(0xFFFF)) +#define max_u32 ((u32)(0xFFFFFFFFUL)) +#define max_u64 ((u64)(0xFFFFFFFFFFFFFFFFULL)) +#define max_usize max_u64 + +#ifndef true + #define true 1 +#endif + +#ifndef false + #define false 0 +#endif #define iota __COUNTER__ +#define nil {0} -#define lengthof(x) (sizeof(x) / sizeof((x)[0])) +#define KB bit(10LL) +#define MB bit(20LL) +#define GB bit(30LL) +#define TB bit(40LL) -#define bit(x) (1 << (x)) +#define packed __attribute__((packed)) -#define KB bit(10) -#define MB bit(11) -#define GB bit(12) +#define bit(n) (1LL << (n)) +#define align(what, to) (((what) + ((to) - 1)) & ~((to) - 1)) /* NOTE: `to` *must* be pow. of 2 */ -typedef unsigned char u8; -typedef unsigned short u16; -typedef unsigned long long u64; -typedef u64 usize; +#define lengthof(arr) (sizeof(arr) / sizeof(arr[0])) +#ifndef offsetof + #define offsetof(strct, memb) ((ssize)(&((strct*)(0)->memb))) +#endif + +#define min(a, b) (((a) <= (b)) ? (a) : (b)) +#define max(a, b) (((a) >= (b)) ? (a) : (b)) +#define clamp(x, mn, mx) (min((mn), max((x), (mx)))) -typedef u8 bool; -#define true 1 -#define false 0 +#define unused(expr) ((void)(expr)) diff --git a/sources/main.c b/sources/main.c index 5bc1c42..791cf43 100644 --- a/sources/main.c +++ b/sources/main.c @@ -5,7 +5,6 @@ * See LICENSE for details */ - #define _DEFAULT_SOURCE #include <stdlib.h> #include <stdio.h> @@ -13,19 +12,360 @@ #include <assert.h> #include <time.h> #include <stdarg.h> +#include <ctype.h> #include <endian.h> +#include <unistd.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/Xutil.h> +#include <X11/Xresource.h> + #include "bits.c" #include "log.c" +#include "tga.c" +#include "x11.c" #include "chip8.c" #include "meta/disasm.c" #include "meta/exec.c" +typedef struct +{ + void* usr; + + usize hot; + usize active; + usize kbdfocus; + + /* mouse */ + u16 x; + u16 y; + + u8 pbtns; + u8 btns; + +} iui; + +void iui_start(iui* ui, void* usr) +{ + assert(ui); + + ui->usr = usr; + ui->hot = 0; +} + +void iui_end(iui* ui) +{ + assert(ui); + + if(!ui->btns) ui->active = 0; + else + { + if(!ui->active) ui->kbdfocus = 0; + } + + ui->pbtns = ui->btns; +} + +bool iui_mouse_inside(iui* ui, u16 x, u16 y, u16 w, u16 h) +{ + return !(ui->x < x || ui->y < y || ui->x >= x + w || ui->y >= y + h); +} + +int iui_button(iui* ui, u16 x, u16 y, int w, int h, char* label) +{ + xctx* x11 = (xctx*)ui->usr; + usize id = (usize)label; + bool imhot, imactive; + + if(iui_mouse_inside(ui, x, y, w, h)) + { + bool click = ui->btns & bit(1) && !(ui->pbtns & bit(1)); + if(!ui->active && click) ui->active = id; + ui->hot = id; + } + + imhot = ui->hot == id; + imactive = ui->active == id; + + { + XGCValues gcvals = {0}; + + if(imhot) gcvals.line_width = 2; + else gcvals.line_width = 1; + + if(imactive) gcvals.foreground = x11->cols.as.fg; + else gcvals.foreground = x11->cols.as.bg; + + XChangeGC(x11->disp, x11->gc, GCForeground | GCLineWidth, &gcvals); + XFillRectangle(x11->disp, x11->wnd, x11->gc, x, y, w, h); + + XSetForeground(x11->disp, x11->gc, x11->cols.as.fg); + XDrawLine(x11->disp, x11->wnd, x11->gc, x, y, x, y + h); + XDrawLine(x11->disp, x11->wnd, x11->gc, x + w, y, x, y); + + XSetForeground(x11->disp, x11->gc, x11->cols.as.fg); + XDrawLine(x11->disp, x11->wnd, x11->gc, x, y + h, x + w, y + h); + XDrawLine(x11->disp, x11->wnd, x11->gc, x + w, y + h, x + w, y); + } + + { + usize len; + int xx, yy; + int di, fa, fd; + int width, height; + XCharStruct chs = {0}; + + len = strlen(label); + XTextExtents(x11->xfs, label, len, &di, &fa, &fd, &chs); + width = chs.rbearing - chs.lbearing; + height = chs.ascent + chs.descent; + + xx = x + w / 2 - width / 2; + yy = y + h / 2 + height / 2; + + if(imactive) XSetForeground(x11->disp, x11->gc, x11->cols.as.bg); + else XSetForeground(x11->disp, x11->gc, x11->cols.as.fg); + XDrawString(x11->disp, x11->wnd, x11->gc, xx, yy, label, len); + } + + return imhot && imactive && !(ui->btns & bit(1)); +} + +int iui_textbox(iui* ui, u16 x, u16 y, int w, int h, char* buf, usize buflen) +{ + xctx* x11 = (xctx*)ui->usr; + usize id = (usize)buf; + bool imhot, imactive; + usize len; + + len = strlen(buf); + + if(iui_mouse_inside(ui, x, y, w, h)) + { + bool click = ui->btns & bit(1) && !(ui->pbtns & bit(1)); + if(!ui->active && click) ui->active = id; + ui->hot = id; + } + + imhot = ui->hot == id; + imactive = ui->active == id; + + { + XGCValues gcvals = {0}; + + if(imhot) gcvals.line_width = 2; + else gcvals.line_width = 1; + + gcvals.foreground = x11->cols.as.bg; + + XChangeGC(x11->disp, x11->gc, GCForeground | GCLineWidth, &gcvals); + XFillRectangle(x11->disp, x11->wnd, x11->gc, x, y, w, h); + + XSetForeground(x11->disp, x11->gc, x11->cols.as.fg); + XDrawLine(x11->disp, x11->wnd, x11->gc, x, y, x, y + h); + XDrawLine(x11->disp, x11->wnd, x11->gc, x + w, y, x, y); + + XSetForeground(x11->disp, x11->gc, x11->cols.as.fg); + XDrawLine(x11->disp, x11->wnd, x11->gc, x, y + h, x + w, y + h); + XDrawLine(x11->disp, x11->wnd, x11->gc, x + w, y + h, x + w, y); + } + + if(imactive) ui->kbdfocus = id; + if(ui->kbdfocus == id) + { + + XGCValues gcvals = {0}; + gcvals.foreground = x11->cols.as.fg; + + XChangeGC(x11->disp, x11->gc, GCForeground, &gcvals); + XFillRectangle(x11->disp, x11->wnd, x11->gc, x + 6 + (6 * len), y + 6, 10, h - 12); + } + else + { + XGCValues gcvals = {0}; + gcvals.foreground = x11->cols.as.fg; + gcvals.line_width = 1; + + XChangeGC(x11->disp, x11->gc, GCForeground | GCLineWidth, &gcvals); + XDrawRectangle(x11->disp, x11->wnd, x11->gc, x + 6 + (6 * len), y + 6, 9, h - 13); + } + + { + int xx, yy; + int di, fa, fd; + int width, height; + XCharStruct chs = {0}; + + XTextExtents(x11->xfs, buf, buflen, &di, &fa, &fd, &chs); + height = chs.ascent + chs.descent; + + xx = x + 6; + yy = y + h / 2 + height / 2; + + + if(imactive) XSetForeground(x11->disp, x11->gc, x11->cols.as.bg); + else XSetForeground(x11->disp, x11->gc, x11->cols.as.fg); + XDrawString(x11->disp, x11->wnd, x11->gc, xx, yy, buf, len); + } + + return imhot && imactive && !(ui->btns & bit(1)); +} + + int main(int argc, char** argv) { + xctx x11 = {0}; + iui ui = {0}; + bool run = true; + char txtbuf[256] = {0}; + + x11_init(800, 600, "xip-8", argc, argv, &x11); + + while(run) + { + XEvent ev; + + while(XCheckTypedWindowEvent(x11.disp, x11.wnd, ClientMessage, &ev)) + if((Atom)ev.xclient.data.l[0] == x11.xa_delwnd) run = false; + + while(XCheckWindowEvent(x11.disp, x11.wnd, max_u32, &ev)) + { + bool keyup = false; + switch(ev.type) + { + case KeymapNotify: + XRefreshKeyboardMapping(&ev.xmapping); + break; + + case KeyRelease: + keyup = true; + /* fall through */ + case KeyPress: + { + KeySym ks; + char name[32] = {0}; + int len = 0; + + len = XLookupString(&ev.xkey, name, sizeof(name) - 1, &ks, NULL); + if(keyup && ks == XK_Escape) run = false; + if(!keyup && len == 1) + { + if(ui.kbdfocus && (isgraph(name[0]) || name[0] == ' ')) + strcat((char*)ui.kbdfocus, name); + } + if(!keyup && ks == XK_BackSpace) + { + if(ui.kbdfocus) + { + ssize i = strlen((char*)ui.kbdfocus) - 1; + if(i >= 0) ((char*)ui.kbdfocus)[i] = '\0'; + } + } + } + break; + + case ButtonPress: + if(ev.xbutton.button == Button1) ui.btns |= bit(1); + if(ev.xbutton.button == Button2) ui.btns |= bit(2); + if(ev.xbutton.button == Button3) ui.btns |= bit(3); + break; + + case ButtonRelease: + if(ev.xbutton.button == Button1) ui.btns &= ~bit(1); + if(ev.xbutton.button == Button2) ui.btns &= ~bit(2); + if(ev.xbutton.button == Button3) ui.btns &= ~bit(3); + break; + + case MotionNotify: + ui.x = ev.xmotion.x; + ui.y = ev.xmotion.y; + break; + + case Expose: + { + XWindowAttributes wa = {0}; + XGCValues gcvals = {0}; + + gcvals.line_width = 1; + gcvals.foreground = x11.cols.as.bg; + + XGetWindowAttributes(x11.disp, x11.wnd, &wa); + + XChangeGC(x11.disp, x11.gc, GCForeground | GCLineWidth, &gcvals); + XFillRectangle(x11.disp, x11.wnd, x11.gc, 0, 0, wa.width, wa.height); + + /* TODO: draw ui + * for ui draw cmds + * draw cmd + */ + } + break; + + default: + break; + } + } + + { + int len = 0; + char buf[512] = {0}; + XGCValues gcvals = {0}; + + gcvals.foreground = x11.cols.as.bg; + XChangeGC(x11.disp, x11.gc, GCForeground | GCLineWidth, &gcvals); + XFillRectangle(x11.disp, x11.wnd, x11.gc, 0, 0, 800, 600); + + XSetForeground(x11.disp, x11.gc, x11.cols.as.fg); + len = snprintf(buf, lengthof(buf), "hot: %p", (void*)ui.hot); + XDrawString(x11.disp, x11.wnd, x11.gc, 650, 25, buf, len); + + len = snprintf(buf, lengthof(buf), "act: %p", (void*)ui.active); + XDrawString(x11.disp, x11.wnd, x11.gc, 650, 45, buf, len); + + len = snprintf(buf, lengthof(buf), "cur: %dx%d", ui.x, ui.y); + XDrawString(x11.disp, x11.wnd, x11.gc, 650, 65, buf, len); + + len = snprintf(buf, lengthof(buf), "btn: %d%d%d", + ui.btns & bit(1) ? 1 : 0, + ui.btns & bit(2) ? 2 : 0, + ui.btns & bit(3) ? 3 : 0); + XDrawString(x11.disp, x11.wnd, x11.gc, 650, 85, buf, len); + + len = snprintf(buf, lengthof(buf), "old: %d%d%d", + ui.pbtns & bit(1) ? 1 : 0, + ui.pbtns & bit(2) ? 2 : 0, + ui.pbtns & bit(3) ? 3 : 0); + XDrawString(x11.disp, x11.wnd, x11.gc, 650, 105, buf, len); + + len = snprintf(buf, lengthof(buf), "kbd: %p", (void*)ui.kbdfocus); + XDrawString(x11.disp, x11.wnd, x11.gc, 650, 125, buf, len); + } + + iui_start(&ui, &x11); + if(iui_button(&ui, 25, 25, 100, 35, "Click ME!")) report(DBG, "CLICK!\n"); + if(iui_button(&ui, 150, 25, 100, 35, "NOT ME!!!")) report(DBG, "WHYYYYYY!\n"); + + iui_textbox(&ui, 25, 75, 225, 35, txtbuf, lengthof(txtbuf)); + if(iui_button(&ui, 275, 75, 100, 35, "OK")) report(DBG, "textbox: '%s'!\n", txtbuf); + iui_end(&ui); + + usleep(10000); + } + + x11_cleanup(&x11); + return 0; +} + +int main2(int argc, char** argv) +{ u64 sz = 0; FILE* f = NULL; chip8 c8 = {0}; @@ -93,6 +433,7 @@ int main(int argc, char** argv) { char buf[512] = {0}; + c8_disasm(c8.RAM, c8.pc, buf); printf("%04X: %s\n", c8.pc, buf); diff --git a/sources/tga.c b/sources/tga.c new file mode 100644 index 0000000..ccdd64d --- /dev/null +++ b/sources/tga.c @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2024 dwlr <dweller@cabin.digital> + * + * BSD 3-Clause License (BSD-3-Clause) + * See LICENSE for details + */ + +typedef struct packed tga_head +{ + u8 id_len; + u8 cmap_type; + u8 img_type; + + struct packed + { + u16 first_entry_idx; + u16 length; + u8 entry_size; + + } cmap_spec; + + struct packed + { + u16 xorig; + u16 yorig; + u16 width; + u16 height; + u8 depth; + u8 desc; + + } img_spec; + +} tga_head; + +typedef enum tga_img_type +{ + TGA_IMG_NONE, + TGA_IMG_RAW_CMAP, + TGA_IMG_RAW_TRUE, + TGA_IMG_RAW_BW, + TGA_IMG_RLE_CMAP, + TGA_IMG_RLE_TRUE, + TGA_IMG_RLE_BW, + + TGA_IMG_TYPE_SIZE + +} tga_img_type; + +typedef union rgba +{ + u32 rgba; + struct + { + u8 b; + u8 g; + u8 r; + u8 a; + } c; + +} rgba; + +#define A c.a +#define R c.r +#define G c.g +#define B c.b + + +typedef struct texture +{ + ssize width; + ssize height; + + rgba* texels; + +} texture; + + +/* FIXME: return 1x1 magenta tex on fail? */ +bool tga2tex_from_mem(texture* tex, const u8* data) +{ + tga_head* hdr = NULL; + rgba* raw = NULL; + usize sz = 0; + + assert(tex); + assert(data); + + hdr = (tga_head*)data; + if(hdr->img_type != TGA_IMG_RAW_TRUE) return false; + if(hdr->cmap_type) return false; + + /* FIXME: check pixel depth */ + /* FIXME: check bit 5 and 4 of img_spec.desc (ordering) */ + + tex->width = hdr->img_spec.width; + tex->height = hdr->img_spec.height; + + sz = sizeof(tex->texels[0]) * tex->width * tex->height; + tex->texels = malloc(sz); + if(!tex->texels) return false; + + raw = (rgba*)(((u8*)hdr) + sizeof(*hdr) + hdr->id_len); + + /* FIXME: convert if needed */ + /* XXX: possibly misaligned */ + memcpy(tex->texels, raw, sz); + + return true; +} + + +bool tga2tex_from_file(texture* tex, const char* path) +{ + struct stat stat = {0}; + + bool ret = false; + s32 fd = -1; + u8* file = NULL; + + assert(tex); + assert(path); + + fd = open(path, O_RDONLY); + if(fd < 0) goto clean_close; + + if(fstat(fd, &stat) != 0) goto clean_close; + + file = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if(file == MAP_FAILED) goto clean_close; + + ret = tga2tex_from_mem(tex, file); + + munmap(file, stat.st_size); +clean_close: + close(fd); + + return ret; +} diff --git a/sources/x11.c b/sources/x11.c new file mode 100644 index 0000000..b9f27b3 --- /dev/null +++ b/sources/x11.c @@ -0,0 +1,294 @@ +#if true != True + #error "true != Xlib's True" +#endif + +#if false != False + #error "false != Xlib's False" +#endif + +#define RGB(r, g, b) (((r) << 16) | ((g) << 8) | (b)) + +static const char icon_tga[] = +{ + #include "../resources/icon.tga.h" +}; + + +typedef struct xctx +{ + Display* disp; + int scrn; /* X screen */ + Window root; + Window wnd; + GC gc; /* Not sure if I wanna keep just one GC, but works ok on modern linux */ + XFontStruct* xfs; + XrmDatabase xrdb; + Atom xa_delwnd; + + union + { + struct + { + u32 fg; + u32 bg; + u32 col[16]; + + } as; + + u32 arr[18]; + + } cols; + +} xctx; + + +static int x11_err_handler(Display* disp, XErrorEvent* eev) +{ + char buf[512] = {0}; + + assert(disp); + assert(eev); + + XGetErrorText(disp, eev->error_code, buf, sizeof(buf) - 1); + + report(ERR, "X11: '%s' resource - %X; opcode %d(%d)\n", + buf, eev->resourceid, eev->request_code, eev->minor_code); + + return 0; +} + + +static char ascii_lower(char c) +{ + if(c >= 'A' && c <= 'Z') return c + ('a' - 'A'); + else return c; +} + +/* TODO: write tests maybe? */ +/* TODO: parse #rgb */ +/* TODO: parse X11 color names */ +static long parse_color(const char* s, long len) +{ + long col = 0; + + if(!s || len == 0) + { + report(WARN, "parse_color: invalid input"); + goto fail; + } + + if(s[0] == '#') + { + long i; + + if(len < 7) + { + goto fail; + report(WARN, "parse_color: bad length"); + } + + for(i = 1; i < len; i++) + { + char c = ascii_lower(s[i]); + char r = 0; + + if(c >= '0' && c <= '9') r = c - '0'; + else if(c >= 'a' && c <= 'f') r = c - 'a' + 10; + else + { + goto fail; + report(WARN, "parse_color: bad hexcode"); + } + + col |= r << (20 - (i - 1)*4); + } + } + else goto fail; + + return col; +fail: + return RGB(255, 0, 255); +} + +static void x11_init(int width, int height, char* title, int argc, char** argv, xctx* ctx) +{ + if(!ctx) report(FATAL, "x11_init: ctx == NULL"); + + ctx->disp = XOpenDisplay(NULL); + if(!ctx->disp) report(FATAL, "XOpenDisplay"); + + XSetErrorHandler(x11_err_handler); + + ctx->scrn = DefaultScreen(ctx->disp); + ctx->root = RootWindow(ctx->disp, ctx->scrn); + + { + int xver = 0; + int xrev = 0; + int xrel = 0; + char* xvendor = NULL; + + xver = XProtocolVersion(ctx->disp); + xrev = XProtocolRevision(ctx->disp); + + xvendor = XServerVendor(ctx->disp); + xrel = XVendorRelease(ctx->disp); + + report(INFO, "X11: Connected to X server '%s' %d\n", xvendor, xrel); + report(INFO, "X11: Protocol ver %d.%d\n", xver, xrev); + report(INFO, "X11: Screen %d %dx%d\n", ctx->scrn, + DisplayWidth(ctx->disp, ctx->scrn), + DisplayHeight(ctx->disp, ctx->scrn)); + } + + /* TODO: make cfg_load() or something that loads the colorscheme */ + { + int i = 0; + char* rcmgr = NULL; + const long defcols[16] = + { + RGB(64,64,64), + RGB(128,0,0), + RGB(0,128,0), + RGB(128,128,0), + RGB(0,0,128), + RGB(128,0,128), + RGB(0,128,128), + RGB(220,220,220), + RGB(210,210,210), + RGB(255,0,0), + RGB(0,255,0), + RGB(255,255,0), + RGB(0,0,255), + RGB(255,0,255), + RGB(0,255,255), + RGB(255,255,255) + }; + + + ctx->cols.as.fg = RGB(0, 0, 0); + ctx->cols.as.bg = RGB(255, 255, 255); + for(i = 0; i < 16; i++) ctx->cols.arr[i+2] = defcols[i]; + + XrmInitialize(); + rcmgr = XResourceManagerString(ctx->disp); + if(rcmgr && (ctx->xrdb = XrmGetStringDatabase(rcmgr))) + { + XrmValue val = {0}; + char* type = NULL; + + if(XrmGetResource(ctx->xrdb, "foreground", "Foreground", &type, &val)) + ctx->cols.as.fg = parse_color(val.addr, val.size - 1); + + if(XrmGetResource(ctx->xrdb, "background", "Background", &type, &val)) + ctx->cols.as.bg = parse_color(val.addr, val.size - 1); + + for(i = 0; i < 16; i++) + { + char name[8] = {0}; + char class[8] = {0}; + snprintf(name, 8, "color%d", i); + strcpy(class, name); + class[0] = 'C'; + + if(XrmGetResource(ctx->xrdb, name, class, &type, &val)) + ctx->cols.arr[i+2] = parse_color(val.addr, val.size - 1); + } + } + else report(WARN, "X11: failed to load X Resources database\n"); + } + + ctx->wnd = XCreateSimpleWindow(ctx->disp, ctx->root, + 0, 0, width, height, 0, + BlackPixel(ctx->disp, ctx->scrn), WhitePixel(ctx->disp, ctx->scrn)); + + XSelectInput(ctx->disp, ctx->wnd, + KeyPressMask | KeyReleaseMask | KeymapStateMask + | ButtonPressMask | ButtonReleaseMask | PointerMotionMask + | FocusChangeMask | ExposureMask | StructureNotifyMask + ); + + { + Atom xa_wnd_type = {0}; + Atom xa_wnd_types[3] = {0}; + Atom xa_icon = {0}; + + XClassHint class = {0}; + XSizeHints size = {0}; + + texture icon_tex = {0}; + ssize icon_sz = 0; + long* icon = NULL; + + char buf[512] = {0}; + + + ctx->xa_delwnd = XInternAtom(ctx->disp, "WM_DELETE_WINDOW", false); + xa_wnd_type = XInternAtom(ctx->disp, "_NET_WM_WINDOW_TYPE", false); + xa_wnd_types[0] = XInternAtom(ctx->disp, "_NET_WM_WINDOW_TYPE_UTILITY", false); + xa_wnd_types[1] = XInternAtom(ctx->disp, "_NET_WM_WINDOW_TYPE_DIALOG", false); + xa_wnd_types[2] = XInternAtom(ctx->disp, "_NET_WM_WINDOW_TYPE_NORMAL", false); + xa_icon = XInternAtom(ctx->disp, "_NET_WM_ICON", false); + + + class.res_name = title; + + /* FIXME: add class arg */ + snprintf(buf, sizeof(buf) - 1, "digital.cabin.%s", title); + class.res_class = buf; + + size.flags = PSize | PMinSize | PMaxSize; + size.width = size.min_width = size.max_width = width; + size.height = size.min_height = size.max_height = height; + + Xutf8SetWMProperties(ctx->disp, ctx->wnd, title, class.res_name, argv, argc, &size, NULL, &class); + XSetWMProtocols(ctx->disp, ctx->wnd, &ctx->xa_delwnd, 1); + XChangeProperty(ctx->disp, ctx->wnd, xa_wnd_type, XA_ATOM, 32, PropModeReplace, + (u8*)xa_wnd_types, lengthof(xa_wnd_types)); + + /* FIXME: add u8[] icon arg */ + if(tga2tex_from_mem(&icon_tex, (u8*)icon_tga)) + { + icon_sz = 2 + icon_tex.width * icon_tex.height; + icon = malloc(sizeof(long) * icon_sz); + if(icon) + { + u32 x, y, y2; + icon[0] = icon_tex.width; + icon[1] = icon_tex.height; + + for(y = 0, y2 = icon_tex.height - 1; y < icon_tex.height; y++, y2--) + for(x = 0; x < icon_tex.width; x++) + (icon + 2)[x + icon_tex.height * y] = + icon_tex.texels[x + icon_tex.height * y2].rgba; + + XChangeProperty(ctx->disp, ctx->wnd, xa_icon, XA_CARDINAL, 32, PropModeReplace, + (u8*)icon, icon_sz); + + free(icon); + } + else report(ERR, "Could not allocate X ICON temporary buffer"); + + free(icon_tex.texels); + } + else report(ERR, "Could not read embedded icon.tga"); + } + + ctx->gc = XCreateGC(ctx->disp, ctx->wnd, 0, NULL); + ctx->xfs = XQueryFont(ctx->disp, XGContextFromGC(ctx->gc)); /* TODO: better font support */ + + XClearWindow(ctx->disp, ctx->wnd); + XMapWindow(ctx->disp, ctx->wnd); +} + +static void x11_cleanup(xctx* ctx) +{ + if(!ctx) return; + + XSetCloseDownMode(ctx->disp, DestroyAll); + + XFreeFontInfo(NULL, ctx->xfs, 1); + XFreeGC(ctx->disp, ctx->gc); + XDestroyWindow(ctx->disp, ctx->wnd); + XCloseDisplay(ctx->disp); +} diff --git a/utils/bin2.c b/utils/bin2.c new file mode 100755 index 0000000..81a1ada --- /dev/null +++ b/utils/bin2.c @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2025 dwlr <dweller@cabin.digital> + * + * BSD 3-Clause License (BSD-3-Clause) + * See LICENSE for details + */ + + +#define _XOPEN_SOURCE 500 + +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> + + +int main(int argc, char** argv) +{ + int rc = 0; + int ret = EXIT_SUCCESS; + FILE* in = stdin; + FILE* out = stdout; + const char* name = "stdin"; + char buffer[4096] = {0}; + size_t got = 0; + + if(argc == 2) + { + name = argv[1]; + in = fopen(name, "r"); + if(!in) + { + perror("fopen"); + exit(EXIT_FAILURE); + } + + rc = snprintf(buffer, sizeof(buffer) - 1, "%s.h", name); + if(rc < 0) + { + perror("snprintf"); + exit(EXIT_FAILURE); + } + + out = fopen(buffer, "w"); + if(!out) + { + perror("fopen"); + fclose(in); + exit(EXIT_FAILURE); + } + + } + else if(argc > 2) fprintf(stderr, "warning: ignoring excess paramters\n"); + + for(;;) + { + size_t i; + + got = fread(buffer, 1, sizeof(buffer), in); + rc = ferror(in); + if(rc) + { + perror("fread"); + ret = EXIT_FAILURE; + goto end; + } + + for(i = 0; i < got; i++) fprintf(out, "'\\x%02x',", (unsigned char)buffer[i]); + + rc = feof(in); + if(rc) break; + } + + fprintf(out, "\n"); + +end: + fclose(in); + fclose(out); + return ret; +} |