From 56a17bd878a0f33217439209b460e451a4f07ce8 Mon Sep 17 00:00:00 2001 From: d_m Date: Mon, 13 Nov 2023 23:18:58 -0500 Subject: [PATCH] Toggle scaling from 1-3x using the F1 key. We reallocate a larger pixel buffer as well as a larger ximage, and then redraw. If the dimensions have not changed we will preserve the existing fg/bg data (since for pure rescaling those do not change and don't need to be reinitialized). --- src/devices/screen.c | 56 +++++++++++++++++++++++------------ src/devices/screen.h | 4 +-- src/uxn11.c | 70 +++++++++++++++++++++++++++++--------------- 3 files changed, 86 insertions(+), 44 deletions(-) diff --git a/src/devices/screen.c b/src/devices/screen.c index 1b73fb7..54a67ec 100644 --- a/src/devices/screen.c +++ b/src/devices/screen.c @@ -158,26 +158,37 @@ screen_palette(Uint8 *addr) } void -screen_resize(Uint16 width, Uint16 height) +screen_resize(Uint16 width, Uint16 height, int scale) { Uint8 *bg, *fg; Uint32 *pixels = NULL; - if(width < 0x8 || height < 0x8 || width >= 0x800 || height >= 0x800) + int dim_change = uxn_screen.width != width || uxn_screen.height != height; + if(width < 0x8 || height < 0x8 || width >= 0x800 || height >= 0x800 || scale < 1 || scale >= 4) return; - if(uxn_screen.width == width && uxn_screen.height == height) - return; - bg = malloc(width * height), fg = malloc(width * height); - if(bg && fg) - pixels = realloc(uxn_screen.pixels, width * height * sizeof(Uint32)); - if(!bg || !fg || !pixels) { - free(bg), free(fg); + if(uxn_screen.width == width && uxn_screen.height == height && uxn_screen.scale == scale) return; + + if(dim_change) { + bg = malloc(width * height), fg = malloc(width * height); + if(bg && fg) + pixels = realloc(uxn_screen.pixels, width * height * sizeof(Uint32) * scale * scale); + if(!bg || !fg || !pixels) { + free(bg), free(fg); + return; + } + free(uxn_screen.bg), free(uxn_screen.fg); + } else { + bg = uxn_screen.bg, fg = uxn_screen.fg; + pixels = realloc(uxn_screen.pixels, width * height * sizeof(Uint32) * scale * scale); + if(!pixels) + return; } - free(uxn_screen.bg), free(uxn_screen.fg); + uxn_screen.bg = bg, uxn_screen.fg = fg; uxn_screen.pixels = pixels; - uxn_screen.width = width, uxn_screen.height = height; - screen_fill(uxn_screen.bg, 0), screen_fill(uxn_screen.fg, 0); + uxn_screen.width = width, uxn_screen.height = height, uxn_screen.scale = scale; + if(dim_change) + screen_fill(uxn_screen.bg, 0), screen_fill(uxn_screen.fg, 0); emu_resize(width, height); screen_change(0, 0, width, height); } @@ -185,7 +196,7 @@ screen_resize(Uint16 width, Uint16 height) void screen_redraw(Uxn *u) { - int i, j, o, y; + int i, x, y, k, l, s = uxn_screen.scale; Uint8 *fg = uxn_screen.fg, *bg = uxn_screen.bg; Uint16 w = uxn_screen.width, h = uxn_screen.height; Uint16 x1 = uxn_screen.x1, y1 = uxn_screen.y1; @@ -197,9 +208,18 @@ screen_redraw(Uxn *u) screen_debugger(u); for(i = 0; i < 16; i++) palette[i] = uxn_screen.palette[(i >> 2) ? (i >> 2) : (i & 3)]; - for(y = y1; y < y2; y++) - for(o = y * w, i = x1 + o, j = x2 + o; i < j; i++) - pixels[i] = palette[fg[i] << 2 | bg[i]]; + for(y = y1; y < y2; y++) { + int ys = y * s; + int o = y * w; + for(x = x1, i = x1 + o; x < x2; x++, i++) { + int c = palette[fg[i] << 2 | bg[i]]; + for(k = 0; k < s; k++) { + int oo = ((ys + k) * w + x) * s; + for(l = 0; l < s; l++) + pixels[oo + l] = c; + } + } + } } Uint8 @@ -222,11 +242,11 @@ screen_deo(Uint8 *ram, Uint8 *d, Uint8 port) switch(port) { case 0x3: { Uint8 *port_width = d + 0x2; - screen_resize(PEEK2(port_width), uxn_screen.height); + screen_resize(PEEK2(port_width), uxn_screen.height, uxn_screen.scale); } break; case 0x5: { Uint8 *port_height = d + 0x4; - screen_resize(uxn_screen.width, PEEK2(port_height)); + screen_resize(uxn_screen.width, PEEK2(port_height), uxn_screen.scale); } break; case 0xe: { Uint8 ctrl = d[0xe]; diff --git a/src/devices/screen.h b/src/devices/screen.h index 0262804..ef88b8f 100644 --- a/src/devices/screen.h +++ b/src/devices/screen.h @@ -14,7 +14,7 @@ WITH REGARD TO THIS SOFTWARE. #define SCREEN_DEOMASK 0xc028 typedef struct UxnScreen { - int width, height, x1, y1, x2, y2; + int width, height, x1, y1, x2, y2, scale; Uint32 palette[4], *pixels; Uint8 *fg, *bg; } UxnScreen; @@ -25,7 +25,7 @@ extern int emu_resize(int width, int height); void screen_fill(Uint8 *layer, int color); void screen_rect(Uint8 *layer, Uint16 x1, Uint16 y1, Uint16 x2, Uint16 y2, int color); void screen_palette(Uint8 *addr); -void screen_resize(Uint16 width, Uint16 height); +void screen_resize(Uint16 width, Uint16 height, int scale); void screen_change(Uint16 x1, Uint16 y1, Uint16 x2, Uint16 y2); void screen_redraw(Uxn *u); diff --git a/src/uxn11.c b/src/uxn11.c index f4dd3b3..a672b40 100644 --- a/src/uxn11.c +++ b/src/uxn11.c @@ -31,6 +31,7 @@ WITH REGARD TO THIS SOFTWARE. static XImage *ximage; static Display *display; static Window window; +static char *loaded_rom; #define WIDTH (64 * 8) #define HEIGHT (40 * 8) @@ -43,6 +44,18 @@ clamp(int val, int min, int max) return (val >= min) ? (val <= max) ? val : max : min; } +static void +hide_cursor(void) +{ + XColor black = {0}; + static char empty[] = {0}; + Pixmap bitmap = XCreateBitmapFromData(display, window, empty, 1, 1); + Cursor blank = XCreatePixmapCursor(display, bitmap, bitmap, &black, &black, 0, 0); + XDefineCursor(display, window, blank); + XFreeCursor(display, blank); + XFreePixmap(display, bitmap); +} + Uint8 emu_dei(Uxn *u, Uint8 addr) { @@ -77,15 +90,21 @@ emu_deo(Uxn *u, Uint8 addr, Uint8 value) int emu_resize(int width, int height) { - (void)width; - (void)height; + int w = uxn_screen.width, h = uxn_screen.height, s = uxn_screen.scale; + static Visual *visual; + if(window) { + visual = DefaultVisual(display, 0); + ximage = XCreateImage(display, visual, DefaultDepth(display, DefaultScreen(display)), ZPixmap, 0, (char *)uxn_screen.pixels, uxn_screen.width * s, uxn_screen.height * s, 32, 0); + XResizeWindow(display, window, w * s, h * s); + XMapWindow(display, window); + } return 1; } static void emu_restart(Uxn *u, char *rom, int soft) { - screen_resize(WIDTH, HEIGHT); + screen_resize(WIDTH, HEIGHT, uxn_screen.scale); screen_rect(uxn_screen.bg, 0, 0, uxn_screen.width, uxn_screen.height, 0); screen_rect(uxn_screen.fg, 0, 0, uxn_screen.width, uxn_screen.height, 0); system_reboot(u, rom, soft); @@ -103,18 +122,6 @@ emu_end(Uxn *u) return u->dev[0x0f] & 0x7f; } -static void -hide_cursor(void) -{ - XColor black = {0}; - static char empty[] = {0}; - Pixmap bitmap = XCreateBitmapFromData(display, window, empty, 1, 1); - Cursor blank = XCreatePixmapCursor(display, bitmap, bitmap, &black, &black, 0, 0); - XDefineCursor(display, window, blank); - XFreeCursor(display, blank); - XFreePixmap(display, bitmap); -} - static Uint8 get_button(KeySym sym) { @@ -131,14 +138,23 @@ get_button(KeySym sym) return 0x00; } +static void +toggle_scale(Uxn *u) +{ + int s = uxn_screen.scale + 1; + if (s > 3) s = 1; + screen_resize(uxn_screen.width, uxn_screen.height, s); +} + static void emu_event(Uxn *u) { XEvent ev; + int s = uxn_screen.scale; XNextEvent(display, &ev); switch(ev.type) { case Expose: - XPutImage(display, window, DefaultGC(display, 0), ximage, 0, 0, PAD, PAD, uxn_screen.width, uxn_screen.height); + XPutImage(display, window, DefaultGC(display, 0), ximage, 0, 0, PAD, PAD, uxn_screen.width * s, uxn_screen.height * s); break; case ClientMessage: { emu_end(u); @@ -147,7 +163,9 @@ emu_event(Uxn *u) KeySym sym; char buf[7]; XLookupString((XKeyPressedEvent *)&ev, buf, 7, &sym, 0); - if(sym == XK_F2) + if(sym == XK_F1) + toggle_scale(u); + else if(sym == XK_F2) u->dev[0x0e] = !u->dev[0x0e]; else if(sym == XK_F4) emu_restart(u, boot_rom, 0); @@ -177,8 +195,10 @@ emu_event(Uxn *u) } break; case MotionNotify: { XMotionEvent *e = (XMotionEvent *)&ev; - int x = clamp((e->x - PAD), 0, uxn_screen.width - 1); - int y = clamp((e->y - PAD), 0, uxn_screen.height - 1); + int ex = (e->x - PAD) / s; + int ey = (e->y - PAD) / s; + int x = clamp(ex, 0, uxn_screen.width - 1); + int y = clamp(ey, 0, uxn_screen.height - 1); mouse_pos(u, &u->dev[0x90], x, y); } break; } @@ -190,24 +210,25 @@ emu_init(void) display = XOpenDisplay(NULL); if(!display) return system_error("X11", "Could not open display"); - screen_resize(WIDTH, HEIGHT); + screen_resize(WIDTH, HEIGHT, 1); return 1; } static int emu_run(Uxn *u, char *rom) { - int i = 1, n; + int i = 1, n, s = uxn_screen.scale; char expirations[8]; char coninp[CONINBUFSIZE]; struct pollfd fds[3]; static const struct itimerspec screen_tspec = {{0, 16666666}, {0, 16666666}}; + loaded_rom = rom; /* display */ Atom wmDelete; static Visual *visual; visual = DefaultVisual(display, 0); - window = XCreateSimpleWindow(display, RootWindow(display, 0), 0, 0, uxn_screen.width + PAD * 2, uxn_screen.height + PAD * 2, 1, 0, 0); + window = XCreateSimpleWindow(display, RootWindow(display, 0), 0, 0, uxn_screen.width * s + PAD * 2, uxn_screen.height * s + PAD * 2, 1, 0, 0); if(visual->class != TrueColor) return system_error("init", "True-color visual failed"); XSelectInput(display, window, ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ExposureMask | KeyPressMask | KeyReleaseMask); @@ -215,7 +236,7 @@ emu_run(Uxn *u, char *rom) XSetWMProtocols(display, window, &wmDelete, 1); XStoreName(display, window, rom); XMapWindow(display, window); - ximage = XCreateImage(display, visual, DefaultDepth(display, DefaultScreen(display)), ZPixmap, 0, (char *)uxn_screen.pixels, uxn_screen.width, uxn_screen.height, 32, 0); + ximage = XCreateImage(display, visual, DefaultDepth(display, DefaultScreen(display)), ZPixmap, 0, (char *)uxn_screen.pixels, uxn_screen.width * s, uxn_screen.height * s, 32, 0); hide_cursor(); /* timer */ @@ -234,7 +255,8 @@ emu_run(Uxn *u, char *rom) read(fds[1].fd, expirations, 8); /* Indicate we handled the timer */ uxn_eval(u, PEEK2(u->dev + 0x20)); /* Call the vector once, even if the timer fired multiple times */ if(uxn_screen.x2) { - int x1 = uxn_screen.x1, y1 = uxn_screen.y1, x2 = uxn_screen.x2, y2 = uxn_screen.y2; + s = uxn_screen.scale; + int x1 = uxn_screen.x1 * s, y1 = uxn_screen.y1 * s, x2 = uxn_screen.x2 * s, y2 = uxn_screen.y2 * s; screen_redraw(u); XPutImage(display, window, DefaultGC(display, 0), ximage, x1, y1, x1 + PAD, y1 + PAD, x2 - x1, y2 - y1); }