Wuss snagglepuss - #13
Merged
Merged
Conversation
dpt
commented
Sep 16, 2026
Owner
Rename the bundled bitmap fonts to carry a provenance prefix: DPT- for the in-house fonts (Digits-Regular, Digits-Bold, Henry, Daydream, CookeTall) and ZX- for GliderRider. Update the bmfont test table, the wuss demo font list and the bmfont doc image links to match. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ypes Rename wuss_ICON_TYPE_BUTTON to wuss_ICON_TYPE_ACTION throughout the icon API, implementation, tests and docs. Add six reserved icon type constants after wuss_ICON_TYPE_RULE: DISPLAY, WRITABLE, NUMBER, STRING_SET, SLIDER and DRAGGABLE. The enum values and from-spec validation exist but no rendering, hit-testing or event routing is wired up yet; a spec using one is accepted and currently draws as a plain wuss_ICON_TYPE_LABEL. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The icon struct no longer stores a back-pointer to its window. The four public mutators that need the window (delete, set_text, set_hidden, set_selected) now take it as an explicit first argument; getters are unchanged. wuss_icon_get_window is removed. wuss_t gains pressed_window and hover_window companions to pressed_icon / hover_icon so window close/hide ownership checks and the hover prev-invalidate path have a window handle without icon->window. The internal helpers wuss__icon_invalidate / _select / _draw / _set_hover take the window explicitly. struct wuss_icon's mutually-exclusive per-type fields (label border, pattern tile, bitmap image, radio group, menu-entry swatch) move into a named union u, one sub-struct per arm, zeroed then filled by type in wuss__icon_from_spec. TODO: note the gadget taxonomy. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The spec's mutually-exclusive per-type fields (border, pattern, bitmap + icon_set, group, swatch) move into a named union u mirroring wuss_icon_data: u.label.border, u.pattern.tile, u.bitmap.image / u.bitmap.set, u.radio.group, u.menu_entry.swatch. The flat fields are gone; bbox, type, text, fg, bg and flags stay common. icon_set is renamed to bitmap.set. wuss__icon_from_spec reads the arms; the five in-tree call sites and the docs are updated. info.c's label specs never set a u arm, so it now zeroes the whole specs array before filling it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The label was pinned to the window's content corner, so it stayed put while the gradient scrolled under it. Anchor it at document (2, 2) instead: screen pos = content top-left - scroll + offset. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
curve's type label was pinned to the window corner like gradient's; anchor it at document (2, 2) instead. porter-duff ignored scroll entirely: the checkerboard was phased to the window corner and the composited bitmap and rule label were drawn at raw bounds->x0/y0. Phase the checkerboard with the scroll offset and draw the bitmap and label at bounds->x0 - scroll, so the pane tracks the document when the window is shrunk below the 256x256 content size. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
struct wuss_icon becomes { wuss_icon_spec_t spec; wuss_icon_state_t state; }
and the internal wuss_icon_data union is removed. wuss__icon_from_spec now
copies the whole spec into out->spec, resolving fg/bg/swatch to palette
indices and a BITMAP's u.bitmap.image from u.bitmap.set in place. The label
text lives in spec.text (const char *), owned as before by wuss_icon_create
and freed with a const cast.
All icon->FIELD accesses become icon->spec.FIELD; runtime state stays on
icon->state via the existing wuss__icon_* accessors.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Ran tools/wrap_doxygen.py across the headers; two blocks were over 77 cols and reflowed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The region core delegated furniture, icons and menus but kept font storage and text rendering as loose fields and files. Collect them into libraries/wuss/font/: struct wuss_fontset (the slot arrays plus count), wuss__fontset_init/_height, the wuss_get_font* accessors (was core/get-font.c) and the centralised wuss__text_* renderer (was core/text.c). struct wuss's four loose font fields collapse to one embedded fontset. create.c drops its inline slot-fill loop and sizes the titlebar from wuss__fontset_height. Furniture, menu and icon draw sites move from wuss->fonts[n] / wuss->nfonts to the fontset members. Fonts stay a core service consumed by furniture/menu/icons, not an icon concern. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add a Fonts subsection under Setup covering libraries/wuss/font/: the slot array, wuss_font_desc_t, per-slot roles (system / bold / symbol), the empty-slot fallbacks and titlebar sizing, and the readback accessors. Update the stale wuss_create signature to the current fonts-array / alloc form and add a Font slot glossary entry. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
screen_copy_bitmap_dithered() blits a bitmap onto a paletted screen with per-pixel 8x8 Bayer ordered dithering before the nearest-palette-entry lookup, so a smooth gradient stipples between the available entries instead of banding at each quantisation step. The dither is phased to screen coordinates so it stays put across redraws. On 32bpp screens and for RLE sources it is identical to screen_copy_bitmap. pattern_bayer_threshold() exposes the recursive 8x8 Bayer matrix (0..63) that the screen_PATTERN_BAYER* tiles are derived from, for callers that dither a continuous value rather than fill with a fixed-coverage tile. The dither amplitude is half the mean palette-level gap, a heuristic that assumes a roughly even greyish palette. The wuss image task gains a 'Dithering' menu toggle: it seeds the row tick from the task state on open, flips the state and redraws on select, and re-ticks the row in place when the menu is kept open after the pick. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
wuss_mouse_move early-returns without delivering any event once the pointer is over window furniture, so a menu chain never heard about the pointer leaving a row for the parent menu's title bar and the submenu stayed open. Add a second chain walk in the wuss__menu_handle IDLE branch: for any menu that has a child open and is not mid-flash, if the pointer is now over that menu's title bar (inside window->visible but above the content box), close the child chain and clear the parent row's hovered state. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Window-scoped events fired when the pointer crosses onto or off a window's whole on-screen footprint -- content or furniture alike. Exactly one ENTER is outstanding per window, always balanced by a later EXIT; moving between content and furniture within one window does not re-fire them. Carry no data. Reusable infrastructure for tooltips, per-window cursors, and hover affordances. struct wuss gains a pointer_window tracker. wuss__pointer_set_window moves it and emits EXIT-then-ENTER on the crossing edge; it is called from wuss_mouse_move and wuss_mouse_click right after the window hit-test, ahead of the furniture / no-handler early returns. wuss__pointer_forget_window drops a window from the tracker without an EXIT for the close and hide paths, where wuss_EVENT_CLOSE (or the window simply going off the mouse) already covers it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The RADIO and OPTION icon glyph square was always font-height and the blit was clipped to it, so an icon-set sprite (radon/radoff/opton/optoff) larger than the font was centred in that square and truncated. The label offset ignored the real sprite width too. Resolve the state bitmap first and, when present, take the glyph box and label offset from its own size, vertically centred in the icon box. Drop the clip-to-glyph-square intersection; screen_copy_bitmap already honours the screen clip. Vector fallback keeps the font-height square. Also refresh libraries/wuss/TODO.txt. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
dither_channel recomputed 255 / (nlevels - 1) and a divide-by-64 for every channel of every pixel -- nine integer divides per dithered pixel, all by values constant for the whole blit. Precompute a 64-entry signed bias table, one per Bayer cell, once per blit (dither_bias_build: a single divide plus a 64-iteration fill). The inner loop drops to a table lookup and an add per channel, gated behind one do_dither branch. dither_channel now just applies the pre-built bias for a pixel position. No behavioural change; all 26 tests pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The ordered-dither Bayer cell was selected from the destination screen coordinate, so moving a sprite's window shifted every pixel onto a different cell and the dither pattern crawled across the sprite as it moved. Key it on the sprite-local pixel (screen coord minus the blit origin) instead, so the pattern is fixed to the sprite. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
screen_copy_rect only handled 4bpp and the whole-byte formats; 1bpp and 2bpp screens fell through to result_NOT_SUPPORTED, so every window move or scroll on a low-bpp screen failed the wuss blit fast path and repainted the entire footprint. Generalise the 4bpp packed-pixel copy (screen_copy_rect_p4) to any sub-byte depth (screen_copy_rect_packed, bpp_bits 1/2/4), keeping the two-axis blit-direction trick that makes the in-place copy safe under self-overlap. Dispatch log2bpp 0 and 1 to it alongside 2. Add test_copy_rect_packed covering p1/p2/p4: a non-byte-aligned copy lands at the destination, does not smear past it, and leaves rows outside the source untouched. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…t_packed The generalised packed copy used LSB-first indexing for every sub-byte depth, but the rest of the screen code packs p1 and p2 MSB-first (bit 7 / bits 7..6 leftmost, per screen_set_pixel_p1/p2 and screen_copy_bitmap_p1/ p2) -- only p4 is LSB-first. On a 2bpp screen a non-byte-aligned window move therefore picked the wrong pixel within each byte and shuffled the copied row. Carry an msb_first flag (set for bpp_bits 1 and 2) and flip the within-byte shift accordingly. Strengthen test_copy_rect_packed: lay the source down with screen_set_pixel, give each column a distinct index, and read back with a house-order reader -- it now fails if the copy uses the wrong endianness. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A borrowed-window menu item was positioned off the parent menu's right edge by wuss__submenu_anchor with no on-screen clamp; wuss_window_move deliberately does not clamp (drags rely on it), so a parent menu near the right screen edge left the window opening off-screen. Extract wuss_window_create's inline on-screen nudge into a shared wuss__nudge_visible_onscreen helper in core/impl.h and call it from wuss__menu_open_window after the anchor move. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
wuss__icon_draw_radio_option only repainted its box when the icon carried an explicit bg. With bg unset (the icon blending against the window backdrop) a state change redrew the new glyph over the old one with no clear, so a shrinking radon->radoff swap or a removed tick/centre left stale pixels. Clear the glyph box to the blend ground in that case. Separately, wuss__icon_from_spec copied the caller's bbox verbatim. The state bitmap (radon/radoff, opton/optoff) is drawn centred in the bbox with a top-left offset, so a bitmap larger than the bbox overhangs it; a select-time invalidate covers only the bbox and leaves the overhanging edges stale. Grow a RADIO/OPTION bbox to the largest of its two state bitmaps so the drawn glyph always fits the box invalidate, hit-test and layout use. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The IDLE tick closes a submenu when the pointer is over its parent's titlebar, so the parent can be grabbed and dragged. wuss__pointer_over_ titlebar only checked the parent's visible box and y, not whether the parent was the frontmost window at the pointer, so dragging the child menu back over the parent's titlebar -- child window then on top there -- was read as a parent-titlebar hover and closed the child mid-drag. Require wuss__window_at at the pointer to be the parent itself. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
chars_set_font resized the window but not its document extent, so the scroll range stayed sized to the font the window was created with; switching to a larger font put the far grid cells past the scroll range and out of reach, and a smaller font left dead scroll space. Call wuss_window_set_doc with the new grid size alongside the resize. Also hoist the duplicated chars_window_size call in chars_create into a local. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds pixelfmt_p8 across the framebuf/wuss/image-io stack, mirroring the existing p4 paletted path throughout: - span_p8 (copy/fill/blendconst), registered in the span registry - screen: blend_pixel, copy_bitmap (+ ordered dither), fill_pattern, and the RLE-blit destination path all gain p8 branches - bmfont: p8 glyph drawing (byte-per-pixel, no nibble packing) - bitmap_convert: p8 -> bgrx8888 - bitmap_load_png: a palette-type PNG is now kept as pixelfmt_p8 (PLTE and any tRNS copied into bm->palette) instead of being expanded to RGB/RGBA - bitmap_save_png: pixelfmt_p8 is written back out as a palette-type PNG - wuss SDL frontend: --depth 8 Fixes found along the way: - apps/wuss/main.c: the UI palette and the physical screen palette were the same fixed-size array; growing it for p8 leaked screen_copy_ninepatch its own gain (256 entries needed for a paletted screen) into wuss_create's npalette, corrupting the 16-colour UI palette lookups. Split into a 16-entry UI palette and a separately-sized screen palette. - bitmap_load_png: an OOM from the new p8 palette allocation inside bitmap_init was silently dropped; now checked and propagated. - libraries/wuss/test/tasks/image.c: screen_copy_bitmap/ninepatch assume a deep 32bpp source, an assumption bitmap_load_png's new p8-preserving behaviour breaks for palette-type PNG fixtures (ninepatch.png, jessica.png). Added load_png_deep() to convert a p8 load result back to bgrx8888 before it reaches the blitters. 26/26 DPTLibTest tests pass; wuss (build-sdl) builds clean. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… swap palette task gains a second 'Screen' window showing the physical screen bitmap's own palette (scr->palette, sized per 1/2/4/8bpp) rather than wuss's 16-entry UI palette; 32bpp has no palette, shown as a label. Mirrors checker.c's two-window-per-task pattern. load_png_deep (image.c) converted a p8-loaded PNG to bgrx8888 before handing it to screen_copy_bitmap/screen_copy_ninepatch, but that whole family assumes source pixels are rgba8888 byte order (see bitmap_load_png's documented invariant) and reads them as such unconditionally. bgrx8888 swaps red and blue against that, producing dull/wrong-hue composites even when the source PNG's palette exactly matches the screen's. Added bmconv_p8_to_rgbx8888 (mirrors bmconv_p8_to_bgrx8888) and switched load_png_deep to it. 26/26 DPTLibTest tests pass; wuss (build-sdl) builds clean. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
scr_palette's 240 slots beyond the 16 UI colours were left black. Fill 232 of them with the web-safe (6x6x6, step 0x33) cube instead, so a p8 screen has real colour range beyond the UI palette for nearest-match; the remaining 24 stay black. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
task_handle_event's wuss_EVENT_PALETTE handler passed wuss's bare 16-entry UI palette straight to bitmap_set_palette, which reads however many entries the screen bitmap's own format needs (256 for p8) -- heap-buffer-overflow on any palette pick while running p8. Factored the pad-to-screen-size logic (16 UI colours, then the web-safe 216, then black) out of run_wuss into tasks_build_screen_palette (tasks.c/.h), shared by both startup and the live palette-change handler, sized via pixelfmt_paletted_nentries(g.bm->format). 26/26 DPTLibTest passes (build-asan and an ASan/UBSan build-sdl); wuss builds clean; a standalone ASan/UBSan harness exercising the fixed pad-then-bitmap_set_palette call shape runs clean. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ry count
dither_bias_build took pixelfmt_paletted_nentries() (raw palette size: 2/4/16/256) as its level count, but the pixelmap quantises R/G/B independently -- 4:4:4 for p1/p2/p4, 5:6:5 for p8 -- so the real per-channel level count never matches the total entry count. For p8 this made the bias gap 255/(256-1) = 1, collapsing the Bayer bias table to {-1,0} and making dithering invisible; p1/p2 were technically wrong too (coarser gap than the real channel depth), just not visibly broken.
Add dither_levels_for(pm), keyed on the worst of pm->rbits/gbits/bbits, and reorder each screen_copy_bitmap_p{1,2,4,8} to fetch the pixelmap before building the bias table so it can use the real channel depth.
screen_copy_bitmap_i assumed every source bitmap is 32bpp RGBA/RGBX and cast src->base straight to pixelfmt_rgba8888_t*. resources/wuss/ninepatch.png is a colortype-3 (PLTE) PNG, so bitmap_load_png decodes it to pixelfmt_p8 (1 byte/pixel); the 32bpp stride then read past the 729-byte (27x27) buffer -- ASan heap-buffer-overflow plus a UBSan misaligned load, via wuss__icon_draw_bitmap -> screen_copy_bitmap -> screen_copy_bitmap_p4. Reject any source whose format isn't 32bpp (pixelfmt_log2bpp != 5) with result_NOT_SUPPORTED before the format switch, rather than reading it as one. Existing callers already ignore or tolerate a non-OK return (icons_create's has_sprite gate), so ninepatch decoration just stops drawing rather than crashing; a real paletted-source blit path is future work if it's needed back. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Replace the previous commit's reject-only stub with real support for blitting a paletted (p1/p2/p4/p8) source bitmap -- e.g. a PNG saved with a PLTE chunk -- onto a screen of any format. A shared src_fetch_rgba() helper decodes a source pixel (packed index or plain 32bpp) into an rgba8888 colour_t on the fly, reusing the same paletted->deep pixelmap_get() table the RLE decoder already relies on. Every screen_copy_bitmap_p1/p2/p4/p8/32 helper now reads through it directly instead of the source being unpacked into a malloc'd rgba8888 scratch bitmap up front -- no extra pass or allocation per blit, and the per-pixel cost matches the existing deep-source paths. A paletted source's tRNS-derived alpha (baked into its palette entries by bitmap_load_png) is carried through and alpha-tested exactly like a real rgba8888/bgra8888 source pixel would be. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…lpha The rgba8888/bgra8888 check in screen_copy_bitmap_i's has_alpha computation is a general pixel-format property, not something specific to that call site. Move it to a pixelfmt_has_alpha() macro alongside pixelfmt_is_rle/pixelfmt_paletted_nentries, covering all four 32bpp alpha formats (bgra8888/rgba8888/abgr8888/argb8888). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds tools/realign_decls.py, which re-pads the type-to-name column of contiguous same-indent variable declaration blocks so it sits exactly one space past the block's own longest type token, matching the tighter style already adopted in geom/stack. Handles pointer '*' columns, array subscripts, blank/comment lines within an otherwise-continuous run, and call-bearing initializers (counted for width, never rewritten). Skips struct/enum/union bodies. Run across all of libraries/ (excluding fortify/ and test/); build and full test suite verified passing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Groups realign_decls.py, wrap_protos.py and wrap_doxygen.py under tools/fmt/ and updates CLAUDE.md's references. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Moves test_wrap_protos.py and test_wrap_doxygen.py alongside their scripts. Adds test_realign_decls.py covering the star-column padding, struct-body exclusion, array subscripts, blank/comment-line bridging, and call-initializer/has_init group-break rules worked out while debugging the script. Factors process_file()'s in-memory logic out into a pure reformat_text(text) helper so the tests don't need to touch disk. Also adds a standing rule in CLAUDE.md to run realign_decls.py alongside wrap_protos.py/wrap_doxygen.py before committing .c/.h changes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Ran realign_decls.py, wrap_protos.py and wrap_doxygen.py across all DPTLib sources (excluding fortify/ and test/). realign_decls and wrap_doxygen made no changes (already clean); wrap_protos rewrapped three prototypes whose parameter columns had drifted: path_leaf_strip_ext (now one param per line), wuss_slider_row_event and wuss__fontset_init (star-to-name column tightened). Build and full test suite verified passing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…asks Each *_create now takes a trailing X_task_t **out so callers who need the allocated block can get it back; tasks.c passes NULL everywhere since none of its spawn_* wrappers need it. Each module also gains an X_destroy paired with X_create, called from the module's own wuss_EVENT_QUIT handler instead of an inline free -- modules that own extra heap state (colourmenus, fontmenus, menus, proginfo, bitmaps, sprites) tear that down in X_destroy before freeing the block, matching what their QUIT handlers already did. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
wuss_create now takes a resources root path, stored on wuss_t and retrievable via wuss_get_resources(). text, chars, palette, image, icons, and porter-duff task modules no longer take their own resources parameter -- they fetch it from wuss directly, so callers stop threading the path through every _create call. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
image_create now locates its own ninepatch and initial image via wuss_get_resources instead of taking them as args. clock, minesweeper, saturn, palette, porter-duff and icons drop their remaining font/palette/config/startup-name params, sourcing them from wuss_get_font_n/wuss_get_palette/a hardcoded default/self-comparison against the live system palette. Every demo X_create now shares the shape (wuss_t *, X_task_t **out), so apps/wuss/tasks.c collapses its 18 hand-written spawn_* wrappers into one spawn_generic plus a {name, create} table. Dead g.palette/g.npalette/g.palette_name/g.daydream_font/g.bold_font globals removed.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
g -> g_tasks avoids shadowing the g/b loop locals in tasks_build_screen_palette. saturn_create at startup fixes a use-after-free in saturn_destroy: wuss_destroy sweeps tasks (freeing the dialogue window borrowed onto the Configuration menu leaf) before closing any leftover open chain, so saturn_destroy now closes its own menu_handle first if still open. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The stars/ring exclusion thresholds were literal constants tuned for size==256; since p's range grows linearly with size, a larger window shrank the exclusion disc relative to half and let stars/ring points land inside the planet body. Scale them by size/SATURN_SIZE_DEFAULT. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Resets task->config to SATURN_CONFIG_DEFAULT, refills the sliders, and applies it; Select also closes the menu chain, Adjust leaves the dialogue open showing the reset values. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The flat Launch submenu had grown to 18 entries; group by kind and hang the category submenus directly off the top-level task menu instead of behind an extra Launch level. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…o text demo text.c's menu gains a Spacing submenu (letter/word presets), Foreground/Background colourmenus, and a No Background toggle that draws glyphs transparently over the window's content. bmtext_layout/bmtext_draw now take an optional spacing parameter so the word-wrap and draw paths honour the picked preset. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
wuss__slider_row_snap's CLAMP(v, min, max) assumed min <= max, but a reversed slider (documented fill-backwards case, min > max) made every snapped value collapse to max regardless of drag position. Clamp against the ordered [lo, hi] pair instead. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
wuss_colourmenu_create built one row per palette entry with swatch = i and no upper bound, but wuss_colour_t indices >= wuss_COLOUR_SYMBOLIC (128) are reserved for the symbolic/chrome-role namespace, not real palette slots. A palette with more than 128 entries produced rows whose swatch value aliased wuss_COLOUR_BLACK, wuss_COLOUR_GREY, etc, rendering and reporting the wrong colour. Cap the row count at wuss_COLOUR_SYMBOLIC. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The DISABLED-item early return in wuss__menu_handle fired before the MOUSE_MOVE child-close logic, so moving the pointer from an open-submenu row onto a disabled sibling left the stale submenu window open and the old row highlighted. Move the disabled check so it only suppresses a disabled row opening its own submenu/pick, not the close-on-move-away path; MOUSE_UP keeps its own disabled guard so a click still can't select a disabled row. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Unlike the structurally identical wuss_fontmenu_selected and wuss_colourmenu_selected, wuss_dialogue_handle_icon dereferenced dialogue and event with no NULL check, crashing instead of returning not-mine for a task multiplexing ICON dispatch across an optionally-created dialogue. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
wuss__order_pieces always zeroed a full WUSS_MAX_INVALIDATE_PIECES^2 adjacency matrix and ran the O(n^2) intersection loop even for the common single-piece case (no occlusion), on every window move/resize/scroll blit. A single piece can never clobber itself, so skip straight to a trivial order. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
wuss__clip_to_visible and wuss__subtract_boxes each implemented the same ping-pong carve-by-cuts loop with a different cut source. Extract the shared logic into carve_by_cuts, driven by a get_cut callback, with zorder_get_cut and array_get_cut adapters for the two call sites. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
wuss_window_move and wuss_window_resize each filtered their clean (occlusion-clear) pieces against wuss->dirty[] with the same per-piece subtract-and-collect loop. Extract wuss__filter_settled to share it; resize.c keeps its own pre-filter of dirty by intersection with before_content, then hands the result to the shared helper. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…an it CLAMP(sausage_px, WUSS_MIN_SAUSAGE, track_px) silently returns track_px when track_px < WUSS_MIN_SAUSAGE, dropping below the minimum sausage size instead of enforcing it. Clamp the upper bound itself against WUSS_MIN_SAUSAGE so the floor always wins. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Matches the NULL-guard idiom used elsewhere in this file and in dialogue.c/fontmenu.c/colourmenu.c -- a borrowed const char* dereferenced with no check is a crash waiting for a live caller. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
old_scroll was declared after before_content/carve despite being assigned first. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
MSVC has no VLA support (C99 6.7.5.2 is optional and unimplemented there), so stack_smallest()'s size2d_t mins[n] and stack__place_container()'s stack__child_t children[n] failed Windows CI with C2057/C2466/C2133. Both are now fixed-size arrays capped at the new STACK_MAX_ITEMS (64), with stack_solve()/stack_smallest() rejecting n > STACK_MAX_ITEMS as result_STACK_BAD_TREE. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.