Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: SDL2 → SDL3 migration + GPU-accelerated effects + display/GPU info reporting

Switch from SDL2 (via sdl2-compat shim) to SDL3 directly for the optional
GPU display backend. All SDL2 API calls migrated to SDL3 equivalents.

GPU-accelerated paths added for blur (downscale+bilinear upscale), zoom
(scaled texture src rect), and spin (SDL_RenderTextureRotated) with
automatic CPU fallback when SDL3 is unavailable.

Display driver name ("sdl3:opengl", "drm", "fbdev", "wayland") and GPU
hardware info now reported in boot animation, JS bindings (system.hw),
machine.mjs local dashboard, machines.mjs remote dashboard, and session
server heartbeat.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+339 -62
+1 -1
fedac/native/Dockerfile.builder
··· 15 15 elfutils-libelf-devel openssl-devel \ 16 16 curl jq git tar xz findutils pkgconf-pkg-config zstd xorriso \ 17 17 ca-certificates \ 18 - alsa-lib-devel libdrm-devel flite-devel \ 18 + alsa-lib-devel libdrm-devel flite-devel SDL3-devel \ 19 19 ffmpeg-free-devel \ 20 20 wpa_supplicant dhcp-client iw \ 21 21 iwlwifi-mvm-firmware wireless-regdb \
+3 -3
fedac/native/Makefile
··· 47 47 LDFLAGS += $(AV_LIBS) 48 48 endif 49 49 50 - # SDL2 GPU-accelerated display (optional: make USE_SDL=1) 50 + # SDL3 GPU-accelerated display (optional: make USE_SDL=1) 51 51 ifdef USE_SDL 52 - SDL_CFLAGS := $(shell pkg-config --cflags sdl2 2>/dev/null) 53 - SDL_LIBS := $(shell pkg-config --libs sdl2 2>/dev/null || echo "-lSDL2") 52 + SDL_CFLAGS := $(shell pkg-config --cflags sdl3 2>/dev/null) 53 + SDL_LIBS := $(shell pkg-config --libs sdl3 2>/dev/null || echo "-lSDL3") 54 54 CFLAGS += -DUSE_SDL $(SDL_CFLAGS) 55 55 LDFLAGS += $(SDL_LIBS) 56 56 endif
+15 -1
fedac/native/pieces/machine.mjs
··· 227 227 write("software", { x: sx, y: ry, size: 1, font }); 228 228 ry += lineH + 2; 229 229 230 - const ver = system?.version || "unknown"; 230 + const ver = system?.version || 231 + [hw?.buildName, hw?.gitHash, hw?.buildTs].filter(Boolean).join(" ") || "unknown"; 231 232 ink(...c("fg")); 232 233 const verMaxC = Math.floor(colW / charW); 233 234 write(ver.slice(0, verMaxC), { x: sx, y: ry, size: 1, font }); 234 235 ry += lineH; 236 + 237 + // Display driver + GPU 238 + if (hw?.displayDriver) { 239 + ink(...c("dim")); 240 + write("display: " + hw.displayDriver, { x: sx, y: ry, size: 1, font }); 241 + ry += lineH; 242 + } 243 + if (hw?.gpu && hw.gpu !== "unknown") { 244 + ink(...c("dim")); 245 + const gpuStr = hw.gpu.length > verMaxC ? hw.gpu.slice(0, verMaxC - 1) + "…" : hw.gpu; 246 + write("gpu: " + gpuStr, { x: sx, y: ry, size: 1, font }); 247 + ry += lineH; 248 + } 235 249 236 250 const handle = system?.config?.handle; 237 251 if (handle) {
+7 -6
fedac/native/scripts/build-and-flash-initramfs.sh
··· 83 83 command -v iw &>/dev/null || PKGS_NEEDED="${PKGS_NEEDED} iw" 84 84 [ -d /lib/firmware ] && ls /lib/firmware/iwlwifi-cc-a0-* &>/dev/null || PKGS_NEEDED="${PKGS_NEEDED} iwlwifi-mvm-firmware" 85 85 [ -f /lib/firmware/regulatory.db ] || PKGS_NEEDED="${PKGS_NEEDED} wireless-regdb" 86 + # SDL3 for GPU-accelerated display 87 + pkg-config --exists sdl3 2>/dev/null || PKGS_NEEDED="${PKGS_NEEDED} SDL3-devel" 86 88 # ffmpeg libs for video recording 87 89 pkg-config --exists libavcodec 2>/dev/null || PKGS_NEEDED="${PKGS_NEEDED} ffmpeg-free-devel" 88 90 # Flash tools ··· 315 317 ln -sf lib64 "${INITRAMFS_DIR}/lib" 316 318 fi 317 319 318 - # Copy SDL2 + Mesa GPU libraries (if --sdl) 320 + # Copy SDL3 + Mesa GPU libraries (if --sdl) 319 321 if [ "${USE_SDL}" -eq 1 ]; then 320 - log "Bundling SDL2 + Mesa GPU stack..." 322 + log "Bundling SDL3 + Mesa GPU stack..." 321 323 mkdir -p "${INITRAMFS_DIR}/lib64/dri" 322 324 323 - # SDL2-compat + SDL3 (Fedora uses sdl2-compat over SDL3) 324 - for lib in libSDL2-2.0.so.0 libSDL3.so.0; do 325 + # SDL3 (direct, no sdl2-compat shim) 326 + for lib in libSDL3.so.0; do 325 327 src=$(readlink -f "/usr/lib64/${lib}" 2>/dev/null) 326 328 [ -f "$src" ] && cp "$src" "${INITRAMFS_DIR}/lib64/" && ln -sf "$(basename "$src")" "${INITRAMFS_DIR}/lib64/${lib}" 327 329 done ··· 340 342 for drv in iris_dri.so i915_dri.so kms_swrast_dri.so swrast_dri.so libdril_dri.so; do 341 343 src="/usr/lib64/dri/${drv}" 342 344 if [ -L "$src" ]; then 343 - # Copy as symlink 344 345 tgt=$(readlink "$src") 345 346 ln -sf "$tgt" "${INITRAMFS_DIR}/lib64/dri/${drv}" 346 347 elif [ -f "$src" ]; then ··· 355 356 done 356 357 357 358 GPU_SIZE=$(du -sh "${INITRAMFS_DIR}/lib64/libgallium"* "${INITRAMFS_DIR}/lib64/libSDL"* "${INITRAMFS_DIR}/lib64/libEGL"* "${INITRAMFS_DIR}/lib64/dri/" 2>/dev/null | tail -1 | cut -f1) 358 - log "SDL2 + Mesa GPU stack bundled (gallium: $(du -sh "${GALLIUM}" 2>/dev/null | cut -f1))" 359 + log "SDL3 + Mesa GPU stack bundled (gallium: $(du -sh "${GALLIUM}" 2>/dev/null | cut -f1))" 359 360 fi 360 361 361 362 # Copy ALSA config files (required for snd_pcm_open to resolve device names)
+7 -6
fedac/native/scripts/build-and-flash.sh
··· 83 83 command -v iw &>/dev/null || PKGS_NEEDED="${PKGS_NEEDED} iw" 84 84 [ -d /lib/firmware ] && ls /lib/firmware/iwlwifi-cc-a0-* &>/dev/null || PKGS_NEEDED="${PKGS_NEEDED} iwlwifi-mvm-firmware" 85 85 [ -f /lib/firmware/regulatory.db ] || PKGS_NEEDED="${PKGS_NEEDED} wireless-regdb" 86 + # SDL3 for GPU-accelerated display 87 + pkg-config --exists sdl3 2>/dev/null || PKGS_NEEDED="${PKGS_NEEDED} SDL3-devel" 86 88 # ffmpeg libs for video recording 87 89 pkg-config --exists libavcodec 2>/dev/null || PKGS_NEEDED="${PKGS_NEEDED} ffmpeg-free-devel" 88 90 # Flash tools ··· 257 259 ln -sf lib64 "${INITRAMFS_DIR}/lib" 258 260 fi 259 261 260 - # Copy SDL2 + Mesa GPU libraries (if --sdl) 262 + # Copy SDL3 + Mesa GPU libraries (if --sdl) 261 263 if [ "${USE_SDL}" -eq 1 ]; then 262 - log "Bundling SDL2 + Mesa GPU stack..." 264 + log "Bundling SDL3 + Mesa GPU stack..." 263 265 mkdir -p "${INITRAMFS_DIR}/lib64/dri" 264 266 265 - # SDL2-compat + SDL3 (Fedora uses sdl2-compat over SDL3) 266 - for lib in libSDL2-2.0.so.0 libSDL3.so.0; do 267 + # SDL3 (direct, no sdl2-compat shim) 268 + for lib in libSDL3.so.0; do 267 269 src=$(readlink -f "/usr/lib64/${lib}" 2>/dev/null) 268 270 [ -f "$src" ] && cp -L "$src" "${INITRAMFS_DIR}/lib64/${lib}" 269 271 done ··· 282 284 for drv in iris_dri.so i915_dri.so kms_swrast_dri.so swrast_dri.so libdril_dri.so; do 283 285 src="/usr/lib64/dri/${drv}" 284 286 if [ -L "$src" ]; then 285 - # Copy as symlink 286 287 tgt=$(readlink "$src") 287 288 ln -sf "$tgt" "${INITRAMFS_DIR}/lib64/dri/${drv}" 288 289 elif [ -f "$src" ]; then ··· 297 298 done 298 299 299 300 GPU_SIZE=$(du -sh "${INITRAMFS_DIR}/lib64/libgallium"* "${INITRAMFS_DIR}/lib64/libSDL"* "${INITRAMFS_DIR}/lib64/libEGL"* "${INITRAMFS_DIR}/lib64/dri/" 2>/dev/null | tail -1 | cut -f1) 300 - log "SDL2 + Mesa GPU stack bundled (gallium: $(du -sh "${GALLIUM}" 2>/dev/null | cut -f1))" 301 + log "SDL3 + Mesa GPU stack bundled (gallium: $(du -sh "${GALLIUM}" 2>/dev/null | cut -f1))" 301 302 fi 302 303 303 304 # Copy ALSA config files (required for snd_pcm_open to resolve device names)
+13 -1
fedac/native/src/ac-native.c
··· 1579 1579 char ver[64]; 1580 1580 char bts[64]; 1581 1581 char bname[64] = ""; 1582 + char ddrv[64] = ""; 1582 1583 snprintf(ver, sizeof(ver), "version %s", AC_GIT_HASH); 1583 1584 #ifdef AC_BUILD_TS 1584 1585 snprintf(bts, sizeof(bts), "%s", AC_BUILD_TS); ··· 1588 1589 #ifdef AC_BUILD_NAME 1589 1590 snprintf(bname, sizeof(bname), "%s", AC_BUILD_NAME); 1590 1591 #endif 1592 + const char *driver = drm_display_driver(display); 1593 + snprintf(ddrv, sizeof(ddrv), "display %s", driver); 1591 1594 int wv = font_measure_matrix(ver, 1); 1592 1595 int wt = font_measure_matrix(bts, 1); 1593 1596 int wn = bname[0] ? font_measure_matrix(bname, 1) : 0; 1597 + int wd = font_measure_matrix(ddrv, 1); 1594 1598 int max_w = wv; 1595 1599 if (wt > max_w) max_w = wt; 1596 1600 if (wn > max_w) max_w = wn; 1601 + if (wd > max_w) max_w = wd; 1597 1602 int panel_w = max_w + 8; 1598 - int panel_h = bname[0] ? 28 : 20; 1603 + int panel_h = (bname[0] ? 28 : 20) + 8; 1599 1604 int panel_x = screen->width - panel_w - 3; 1600 1605 int panel_y = 3; 1601 1606 graph_ink(graph, is_day ··· 1618 1623 ? (ACColor){80, 100, 90, (uint8_t)alpha} 1619 1624 : (ACColor){210, 235, 220, (uint8_t)alpha}); 1620 1625 font_draw_matrix(graph, bts, panel_x + 4, line_y + 8, 1); 1626 + // Display driver line 1627 + graph_ink(graph, is_day 1628 + ? (ACColor){90, 60, 120, (uint8_t)alpha} 1629 + : (ACColor){180, 160, 255, (uint8_t)alpha}); 1630 + font_draw_matrix(graph, ddrv, panel_x + 4, line_y + 16, 1); 1621 1631 // "FRESH" badge when first boot of this version 1622 1632 if (is_new_version) { 1623 1633 graph_ink(graph, is_day ··· 2053 2063 // Init graphics + font 2054 2064 ACGraph graph; 2055 2065 graph_init(&graph, screen); 2066 + if (display) graph_init_gpu(&graph, display); 2056 2067 font_init(); 2057 2068 2058 2069 // Cursor overlay buffer — drawn separately so KidLisp effects don't smear it ··· 2906 2917 fb_destroy(screen); 2907 2918 screen = new_screen; 2908 2919 graph_init(&graph, screen); 2920 + if (display) graph_init_gpu(&graph, display); 2909 2921 input->scale = pixel_scale; 2910 2922 // Update JS runtime's graph reference (holds framebuffer) 2911 2923 if (rt) {
+49 -35
fedac/native/src/drm-display.c
··· 12 12 13 13 #ifdef USE_SDL 14 14 // ============================================================ 15 - // SDL2 GPU-accelerated display (uses KMSDRM backend on bare metal) 15 + // SDL3 GPU-accelerated display (uses KMSDRM backend on bare metal) 16 16 // ============================================================ 17 17 18 18 static ACDisplay *sdl_init(void) { 19 19 // Set KMSDRM hints for bare metal (no X11/Wayland) 20 20 if (getpid() == 1) { 21 - setenv("SDL_VIDEODRIVER", "kmsdrm", 0); 21 + setenv("SDL_VIDEO_DRIVER", "kmsdrm", 0); 22 22 } 23 23 24 - if (SDL_Init(SDL_INIT_VIDEO) < 0) { 25 - fprintf(stderr, "[sdl] SDL_Init failed: %s\n", SDL_GetError()); 24 + if (!SDL_Init(SDL_INIT_VIDEO)) { 25 + fprintf(stderr, "[sdl3] SDL_Init failed: %s\n", SDL_GetError()); 26 26 return NULL; 27 27 } 28 28 29 - // Get display mode to know the resolution 30 - SDL_DisplayMode dm; 31 - if (SDL_GetDesktopDisplayMode(0, &dm) < 0) { 32 - fprintf(stderr, "[sdl] GetDesktopDisplayMode failed: %s\n", SDL_GetError()); 29 + // Get primary display and its mode 30 + SDL_DisplayID primary = SDL_GetPrimaryDisplay(); 31 + if (!primary) { 32 + fprintf(stderr, "[sdl3] No primary display: %s\n", SDL_GetError()); 33 + SDL_Quit(); 34 + return NULL; 35 + } 36 + const SDL_DisplayMode *dm = SDL_GetDesktopDisplayMode(primary); 37 + if (!dm) { 38 + fprintf(stderr, "[sdl3] GetDesktopDisplayMode failed: %s\n", SDL_GetError()); 33 39 SDL_Quit(); 34 40 return NULL; 35 41 } 36 42 37 - SDL_Window *win = SDL_CreateWindow("ac-native", 38 - SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 39 - dm.w, dm.h, 40 - SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_SHOWN); 43 + SDL_Window *win = SDL_CreateWindow("ac-native", dm->w, dm->h, 44 + SDL_WINDOW_FULLSCREEN); 41 45 if (!win) { 42 - fprintf(stderr, "[sdl] CreateWindow failed: %s\n", SDL_GetError()); 46 + fprintf(stderr, "[sdl3] CreateWindow failed: %s\n", SDL_GetError()); 43 47 SDL_Quit(); 44 48 return NULL; 45 49 } 46 50 47 - SDL_ShowCursor(SDL_DISABLE); 51 + SDL_HideCursor(); 48 52 49 - SDL_Renderer *ren = SDL_CreateRenderer(win, -1, 50 - SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); 51 - if (!ren) { 52 - fprintf(stderr, "[sdl] CreateRenderer (accel) failed: %s, trying software\n", SDL_GetError()); 53 - ren = SDL_CreateRenderer(win, -1, SDL_RENDERER_SOFTWARE); 54 - } 53 + SDL_Renderer *ren = SDL_CreateRenderer(win, NULL); 55 54 if (!ren) { 56 - fprintf(stderr, "[sdl] CreateRenderer failed: %s\n", SDL_GetError()); 55 + fprintf(stderr, "[sdl3] CreateRenderer failed: %s\n", SDL_GetError()); 57 56 SDL_DestroyWindow(win); 58 57 SDL_Quit(); 59 58 return NULL; 60 59 } 61 60 62 - // Log renderer info 63 - SDL_RendererInfo info; 64 - SDL_GetRendererInfo(ren, &info); 65 - fprintf(stderr, "[sdl] Renderer: %s (flags: 0x%x)\n", info.name, info.flags); 66 - if (info.flags & SDL_RENDERER_ACCELERATED) 67 - fprintf(stderr, "[sdl] GPU-accelerated rendering active\n"); 61 + // Enable vsync 62 + SDL_SetRenderVSync(ren, 1); 68 63 69 - // Set nearest-neighbor scaling for pixel-art look 70 - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); 64 + // Log renderer info 65 + const char *ren_name = SDL_GetRendererName(ren); 66 + fprintf(stderr, "[sdl3] Renderer: %s\n", ren_name ? ren_name : "unknown"); 71 67 72 68 ACDisplay *d = calloc(1, sizeof(ACDisplay)); 73 69 d->fd = -1; 74 70 d->is_sdl = 1; 75 - d->width = dm.w; 76 - d->height = dm.h; 71 + d->width = dm->w; 72 + d->height = dm->h; 77 73 d->sdl_window = win; 78 74 d->sdl_renderer = ren; 79 75 // Texture created lazily in display_present (needs framebuffer dimensions) 80 76 d->sdl_texture = NULL; 81 77 d->sdl_tex_w = 0; 82 78 d->sdl_tex_h = 0; 79 + snprintf(d->sdl_renderer_name, sizeof(d->sdl_renderer_name), "%s", 80 + ren_name ? ren_name : "unknown"); 83 81 84 - fprintf(stderr, "[sdl] Ready (%dx%d)\n", d->width, d->height); 82 + fprintf(stderr, "[sdl3] Ready (%dx%d)\n", d->width, d->height); 85 83 return d; 86 84 } 87 85 #endif /* USE_SDL */ ··· 259 257 #ifdef USE_SDL 260 258 ACDisplay *sdl = sdl_init(); 261 259 if (sdl) return sdl; 262 - fprintf(stderr, "[drm] SDL2 failed, falling back to DRM dumb buffers\n"); 260 + fprintf(stderr, "[drm] SDL3 failed, falling back to DRM dumb buffers\n"); 263 261 #endif 264 262 265 263 ACDisplay *d = calloc(1, sizeof(ACDisplay)); ··· 469 467 d->sdl_texture = SDL_CreateTexture(d->sdl_renderer, 470 468 SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 471 469 screen->width, screen->height); 470 + if (d->sdl_texture) { 471 + SDL_SetTextureScaleMode(d->sdl_texture, SDL_SCALEMODE_NEAREST); 472 + } 472 473 d->sdl_tex_w = screen->width; 473 474 d->sdl_tex_h = screen->height; 474 - fprintf(stderr, "[sdl] Created texture %dx%d\n", screen->width, screen->height); 475 + fprintf(stderr, "[sdl3] Created texture %dx%d\n", screen->width, screen->height); 475 476 } 476 477 // Upload pixels to GPU texture 477 478 SDL_UpdateTexture(d->sdl_texture, NULL, 478 479 screen->pixels, screen->stride * (int)sizeof(uint32_t)); 479 480 // GPU-accelerated scale to fullscreen 480 481 SDL_RenderClear(d->sdl_renderer); 481 - SDL_RenderCopy(d->sdl_renderer, d->sdl_texture, NULL, NULL); 482 + SDL_RenderTexture(d->sdl_renderer, d->sdl_texture, NULL, NULL); 482 483 SDL_RenderPresent(d->sdl_renderer); 483 484 return; 484 485 } ··· 757 758 } 758 759 } 759 760 free(s); 761 + } 762 + 763 + const char *drm_display_driver(ACDisplay *d) { 764 + if (!d) return "none"; 765 + #ifdef USE_SDL 766 + if (d->is_sdl) { 767 + static char buf[48]; 768 + snprintf(buf, sizeof(buf), "sdl3:%s", d->sdl_renderer_name); 769 + return buf; 770 + } 771 + #endif 772 + if (d->is_fbdev) return "fbdev"; 773 + return "drm"; 760 774 } 761 775 762 776 void drm_destroy(ACDisplay *d) {
+9 -5
fedac/native/src/drm-display.h
··· 8 8 #include "graph.h" 9 9 10 10 #ifdef USE_SDL 11 - #include <SDL2/SDL.h> 11 + #include <SDL3/SDL.h> 12 12 #endif 13 13 14 14 typedef struct { ··· 39 39 int fbdev_stride; // fbdev line length in pixels 40 40 int fbdev_swap_rb; // 1 if need to swap R and B channels (BGR format) 41 41 42 - // SDL2 GPU-accelerated display 42 + // SDL3 GPU-accelerated display 43 43 #ifdef USE_SDL 44 - int is_sdl; // 1 if using SDL2 backend 44 + int is_sdl; // 1 if using SDL3 backend 45 45 SDL_Window *sdl_window; 46 46 SDL_Renderer *sdl_renderer; 47 47 SDL_Texture *sdl_texture; 48 48 int sdl_tex_w, sdl_tex_h; // texture dimensions (matches small framebuffer) 49 + char sdl_renderer_name[32]; // renderer driver name (e.g. "opengl", "vulkan") 49 50 #endif 50 51 } ACDisplay; 51 52 52 - // Initialize display (tries SDL2 GPU first if available, then DRM, then fbdev) 53 + // Get display driver name ("sdl3:opengl", "drm", "fbdev", "wayland") 54 + const char *drm_display_driver(ACDisplay *d); 55 + 56 + // Initialize display (tries SDL3 GPU first if available, then DRM, then fbdev) 53 57 ACDisplay *drm_init(void); 54 58 55 59 // Present the small framebuffer scaled up to the display 56 60 // This is the primary display function — handles GPU texture upload or CPU scaling 57 61 void display_present(ACDisplay *d, ACFramebuffer *screen, int scale); 58 62 59 - // Flip to the back buffer (makes it visible) — used by non-SDL paths 63 + // Flip to the back buffer (makes it visible) — used by non-SDL3 paths 60 64 void drm_flip(ACDisplay *d); 61 65 62 66 // Get pointer to the back buffer (the one to draw into)
+179 -2
fedac/native/src/graph.c
··· 4 4 #include <string.h> 5 5 #include <math.h> 6 6 7 + #ifdef USE_SDL 8 + #include "drm-display.h" 9 + #endif 10 + 7 11 static inline uint8_t clamp_u8(int v) { 8 12 if (v < 0) return 0; 9 13 if (v > 255) return 255; ··· 15 19 g->screen = screen; 16 20 g->ink = (ACColor){255, 255, 255, 255}; 17 21 g->ink_packed = 0xFFFFFFFF; 22 + g->gpu_display = NULL; 23 + } 24 + 25 + void graph_init_gpu(ACGraph *g, void *display) { 26 + g->gpu_display = display; 18 27 } 19 28 20 29 void graph_wipe(ACGraph *g, ACColor color) { ··· 158 167 ACFramebuffer *fb = g->fb; 159 168 if (strength <= 0) return; 160 169 170 + #ifdef USE_SDL 171 + // GPU path: downscale → upscale with bilinear filtering = fast blur 172 + if (g->gpu_display) { 173 + ACDisplay *d = (ACDisplay *)g->gpu_display; 174 + if (d->is_sdl && d->sdl_renderer) { 175 + // Divisor controls blur amount: higher = blurrier 176 + int divisor = strength + 1; 177 + if (divisor > 8) divisor = 8; 178 + int small_w = fb->width / divisor; 179 + int small_h = fb->height / divisor; 180 + if (small_w < 1) small_w = 1; 181 + if (small_h < 1) small_h = 1; 182 + 183 + SDL_Texture *src = SDL_CreateTexture(d->sdl_renderer, 184 + SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 185 + fb->width, fb->height); 186 + SDL_Texture *small = SDL_CreateTexture(d->sdl_renderer, 187 + SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, 188 + small_w, small_h); 189 + SDL_Texture *result = SDL_CreateTexture(d->sdl_renderer, 190 + SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, 191 + fb->width, fb->height); 192 + 193 + if (src && small && result) { 194 + // Upload framebuffer → source texture 195 + SDL_UpdateTexture(src, NULL, fb->pixels, 196 + fb->stride * (int)sizeof(uint32_t)); 197 + // Set bilinear filtering for smooth blur 198 + SDL_SetTextureScaleMode(src, SDL_SCALEMODE_LINEAR); 199 + SDL_SetTextureScaleMode(small, SDL_SCALEMODE_LINEAR); 200 + 201 + // Pass 1: downscale (src → small) 202 + SDL_SetRenderTarget(d->sdl_renderer, small); 203 + SDL_RenderTexture(d->sdl_renderer, src, NULL, NULL); 204 + 205 + // Pass 2: upscale (small → result) 206 + SDL_SetRenderTarget(d->sdl_renderer, result); 207 + SDL_RenderTexture(d->sdl_renderer, small, NULL, NULL); 208 + 209 + // Read back from result render target 210 + SDL_Rect full = {0, 0, fb->width, fb->height}; 211 + SDL_Surface *rsurf = SDL_RenderReadPixels(d->sdl_renderer, &full); 212 + SDL_SetRenderTarget(d->sdl_renderer, NULL); 213 + if (rsurf) { 214 + const uint32_t *sp = (const uint32_t *)rsurf->pixels; 215 + int spitch = rsurf->pitch / 4; 216 + for (int y = 0; y < fb->height && y < rsurf->h; y++) { 217 + memcpy(&fb->pixels[y * fb->stride], 218 + &sp[y * spitch], 219 + (size_t)fb->width * sizeof(uint32_t)); 220 + } 221 + SDL_DestroySurface(rsurf); 222 + } 223 + } 224 + if (src) SDL_DestroyTexture(src); 225 + if (small) SDL_DestroyTexture(small); 226 + if (result) SDL_DestroyTexture(result); 227 + return; 228 + } 229 + } 230 + #endif 231 + 232 + // CPU fallback: box blur 161 233 size_t buf_size = (size_t)fb->width * fb->height * sizeof(uint32_t); 162 234 uint32_t *tmp = malloc(buf_size); 163 235 if (!tmp) return; 164 236 memcpy(tmp, fb->pixels, buf_size); 165 237 166 238 int radius = strength; 167 - // Simple box blur 168 239 for (int y = 0; y < fb->height; y++) { 169 240 for (int x = 0; x < fb->width; x++) { 170 241 int r = 0, gr = 0, b = 0, count = 0; ··· 193 264 ACFramebuffer *fb = g->fb; 194 265 if (!fb || level <= 0.0 || fabs(level - 1.0) < 1e-6) return; 195 266 267 + #ifdef USE_SDL 268 + // GPU path: render texture with scaled src rect for zoom 269 + if (g->gpu_display) { 270 + ACDisplay *d = (ACDisplay *)g->gpu_display; 271 + if (d->is_sdl && d->sdl_renderer) { 272 + const int w = fb->width; 273 + const int h = fb->height; 274 + const float inv = (float)(1.0 / level); 275 + 276 + SDL_Texture *src = SDL_CreateTexture(d->sdl_renderer, 277 + SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, w, h); 278 + SDL_Texture *dst = SDL_CreateTexture(d->sdl_renderer, 279 + SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, w, h); 280 + 281 + if (src && dst) { 282 + SDL_UpdateTexture(src, NULL, fb->pixels, 283 + fb->stride * (int)sizeof(uint32_t)); 284 + SDL_SetTextureScaleMode(src, SDL_SCALEMODE_NEAREST); 285 + 286 + // Source rect: the portion of the texture visible after zoom 287 + float crop_w = w * inv; 288 + float crop_h = h * inv; 289 + SDL_FRect srcrect = { 290 + (w - crop_w) * 0.5f, (h - crop_h) * 0.5f, 291 + crop_w, crop_h 292 + }; 293 + SDL_FRect dstrect = {0, 0, (float)w, (float)h}; 294 + 295 + SDL_SetRenderTarget(d->sdl_renderer, dst); 296 + SDL_SetRenderDrawColor(d->sdl_renderer, 0, 0, 0, 255); 297 + SDL_RenderClear(d->sdl_renderer); 298 + SDL_RenderTexture(d->sdl_renderer, src, &srcrect, &dstrect); 299 + 300 + // Read back 301 + SDL_Rect full = {0, 0, w, h}; 302 + SDL_Surface *rsurf = SDL_RenderReadPixels(d->sdl_renderer, &full); 303 + SDL_SetRenderTarget(d->sdl_renderer, NULL); 304 + if (rsurf) { 305 + const uint32_t *sp = (const uint32_t *)rsurf->pixels; 306 + int spitch = rsurf->pitch / 4; 307 + for (int y = 0; y < h && y < rsurf->h; y++) { 308 + memcpy(&fb->pixels[y * fb->stride], 309 + &sp[y * spitch], 310 + (size_t)w * sizeof(uint32_t)); 311 + } 312 + SDL_DestroySurface(rsurf); 313 + } 314 + } 315 + if (src) SDL_DestroyTexture(src); 316 + if (dst) SDL_DestroyTexture(dst); 317 + return; 318 + } 319 + } 320 + #endif 321 + 322 + // CPU fallback 196 323 const int w = fb->width; 197 324 const int h = fb->height; 198 325 size_t buf_size = (size_t)w * h * sizeof(uint32_t); ··· 246 373 ACFramebuffer *fb = g->fb; 247 374 if (!fb || fabs(angle_radians) < 1e-6) return; 248 375 376 + #ifdef USE_SDL 377 + // GPU path: SDL3 texture rotation 378 + if (g->gpu_display) { 379 + ACDisplay *d = (ACDisplay *)g->gpu_display; 380 + if (d->is_sdl && d->sdl_renderer) { 381 + const int w = fb->width; 382 + const int h = fb->height; 383 + double angle_degrees = angle_radians * (180.0 / M_PI); 384 + 385 + SDL_Texture *src = SDL_CreateTexture(d->sdl_renderer, 386 + SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, w, h); 387 + SDL_Texture *dst = SDL_CreateTexture(d->sdl_renderer, 388 + SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, w, h); 389 + 390 + if (src && dst) { 391 + SDL_UpdateTexture(src, NULL, fb->pixels, 392 + fb->stride * (int)sizeof(uint32_t)); 393 + SDL_SetTextureScaleMode(src, SDL_SCALEMODE_NEAREST); 394 + 395 + SDL_FRect dstrect = {0, 0, (float)w, (float)h}; 396 + SDL_FPoint center = {w * 0.5f, h * 0.5f}; 397 + 398 + SDL_SetRenderTarget(d->sdl_renderer, dst); 399 + SDL_SetRenderDrawColor(d->sdl_renderer, 0, 0, 0, 255); 400 + SDL_RenderClear(d->sdl_renderer); 401 + SDL_RenderTextureRotated(d->sdl_renderer, src, NULL, &dstrect, 402 + angle_degrees, &center, SDL_FLIP_NONE); 403 + 404 + // Read back 405 + SDL_Rect full = {0, 0, w, h}; 406 + SDL_Surface *rsurf = SDL_RenderReadPixels(d->sdl_renderer, &full); 407 + SDL_SetRenderTarget(d->sdl_renderer, NULL); 408 + if (rsurf) { 409 + const uint32_t *sp = (const uint32_t *)rsurf->pixels; 410 + int spitch = rsurf->pitch / 4; 411 + for (int y = 0; y < h && y < rsurf->h; y++) { 412 + memcpy(&fb->pixels[y * fb->stride], 413 + &sp[y * spitch], 414 + (size_t)w * sizeof(uint32_t)); 415 + } 416 + SDL_DestroySurface(rsurf); 417 + } 418 + } 419 + if (src) SDL_DestroyTexture(src); 420 + if (dst) SDL_DestroyTexture(dst); 421 + return; 422 + } 423 + } 424 + #endif 425 + 426 + // CPU fallback 249 427 size_t buf_size = (size_t)fb->width * fb->height * sizeof(uint32_t); 250 428 uint32_t *tmp = malloc(buf_size); 251 429 if (!tmp) return; ··· 258 436 const double c = cos(angle_radians); 259 437 const double s = sin(angle_radians); 260 438 261 - // Inverse mapping: destination -> source with toroidal wrapping. 262 439 for (int y = 0; y < h; y++) { 263 440 for (int x = 0; x < w; x++) { 264 441 const double dx = x - cx;
+2
fedac/native/src/graph.h
··· 10 10 ACFramebuffer *screen; // Primary screen buffer 11 11 ACColor ink; // Current drawing color 12 12 uint32_t ink_packed; // Pre-packed ARGB32 13 + void *gpu_display; // ACDisplay* when SDL3 GPU is available (NULL = CPU only) 13 14 } ACGraph; 14 15 15 16 void graph_init(ACGraph *g, ACFramebuffer *screen); 17 + void graph_init_gpu(ACGraph *g, void *display); // Enable GPU-accelerated effects 16 18 17 19 // Core primitives (matching AC API) 18 20 void graph_wipe(ACGraph *g, ACColor color);
+31
fedac/native/src/js-bindings.c
··· 4297 4297 #ifdef AC_BUILD_NAME 4298 4298 JS_SetPropertyStr(ctx, hw, "buildName", JS_NewString(ctx, AC_BUILD_NAME)); 4299 4299 #endif 4300 + #ifdef AC_GIT_HASH 4301 + JS_SetPropertyStr(ctx, hw, "gitHash", JS_NewString(ctx, AC_GIT_HASH)); 4302 + #endif 4303 + #ifdef AC_BUILD_TS 4304 + JS_SetPropertyStr(ctx, hw, "buildTs", JS_NewString(ctx, AC_BUILD_TS)); 4305 + #endif 4306 + 4307 + // Display driver and GPU info 4308 + { 4309 + extern void *g_display; 4310 + if (g_display) { 4311 + const char *drv = drm_display_driver((ACDisplay *)g_display); 4312 + JS_SetPropertyStr(ctx, hw, "displayDriver", JS_NewString(ctx, drv)); 4313 + } else { 4314 + JS_SetPropertyStr(ctx, hw, "displayDriver", JS_NewString(ctx, "wayland")); 4315 + } 4316 + // Read GPU renderer from sysfs (Mesa exposes via DRI) 4317 + char gpu_name[128] = "unknown"; 4318 + FILE *fp = popen("cat /sys/kernel/debug/dri/0/name 2>/dev/null || " 4319 + "cat /sys/class/drm/card0/device/label 2>/dev/null || " 4320 + "echo unknown", "r"); 4321 + if (fp) { 4322 + if (fgets(gpu_name, sizeof(gpu_name), fp)) { 4323 + // Strip trailing newline 4324 + char *nl = strchr(gpu_name, '\n'); 4325 + if (nl) *nl = '\0'; 4326 + } 4327 + pclose(fp); 4328 + } 4329 + JS_SetPropertyStr(ctx, hw, "gpu", JS_NewString(ctx, gpu_name)); 4330 + } 4300 4331 4301 4332 // Audio diagnostics 4302 4333 if (current_rt->audio) {
+21 -2
fedac/native/src/machines.c
··· 157 157 char hostname[64] = ""; 158 158 read_file("/etc/hostname", hostname, sizeof(hostname)); 159 159 160 + // Display driver 161 + extern void *g_display; 162 + const char *drv = "unknown"; 163 + if (g_display) { 164 + drv = drm_display_driver((ACDisplay *)g_display); 165 + } else if (getenv("WAYLAND_DISPLAY")) { 166 + drv = "wayland"; 167 + } 168 + 169 + // GPU name from sysfs 170 + char gpu_name[128] = "unknown"; 171 + { 172 + char tmp[128] = ""; 173 + if (read_file("/sys/kernel/debug/dri/0/name", tmp, sizeof(tmp)) > 0 || 174 + read_file("/sys/class/drm/card0/device/label", tmp, sizeof(tmp)) > 0) { 175 + snprintf(gpu_name, sizeof(gpu_name), "%s", tmp); 176 + } 177 + } 178 + 160 179 char msg[2048]; 161 180 snprintf(msg, sizeof(msg), 162 181 "{\"type\":\"register\"," ··· 170 189 "\"battery\":%d," 171 190 "\"charging\":%s," 172 191 "\"hostname\":\"%s\"," 173 - "\"hw\":{\"display\":\"%s\"}}", 192 + "\"hw\":{\"display\":\"%s\",\"displayDriver\":\"%s\",\"gpu\":\"%s\"}}", 174 193 AC_BUILD_NAME, AC_GIT_HASH, AC_BUILD_TS, 175 194 AC_BUILD_NAME, AC_GIT_HASH, AC_BUILD_TS, 176 195 m->current_piece, ··· 179 198 bat_pct, 180 199 bat_chg ? "true" : "false", 181 200 hostname, 182 - hw); 201 + hw, drv, gpu_name); 183 202 sq_push(m, msg); 184 203 ac_log("[machines] registered\n"); 185 204 }
+2
system/public/aesthetic.computer/disks/machines.mjs
··· 428 428 429 429 if (m.hw) { 430 430 if (m.hw.display) info.push(["Display", m.hw.display]); 431 + if (m.hw.displayDriver) info.push(["Driver", m.hw.displayDriver]); 432 + if (m.hw.gpu && m.hw.gpu !== "unknown") info.push(["GPU", m.hw.gpu]); 431 433 if (m.hw.sampleRate) info.push(["Audio", `${m.hw.sampleRate / 1000}kHz`]); 432 434 if (m.hw.keyboard) info.push(["Keyboard", m.hw.keyboard]); 433 435 }