Skip to content

Wuss platypus - #7

Merged
dpt merged 44 commits into
developfrom
wuss-platypus
Aug 30, 2026
Merged

dpt merged 44 commits into
developfrom
wuss-platypus

Conversation

@dpt

@dpt dpt commented Aug 30, 2026

Copy link
Copy Markdown
Owner

No description provided.

dpt and others added 30 commits August 28, 2026 16:22
Adds an eleventh launcher task to the interactive wuss test: the two
bundled composite demo images (resources/composite/A.png and B.png)
composited under a Porter-Duff rule that cycles through all twelve
operators, over an alpha checkerboard so transparency reads as
transparent rather than white.

composite() takes no offset or alpha parameter, so the animation is a
per-frame edit on scratch copies: the destination is restored from a
pristine B and the source's alpha channel is scaled by a triangle ramp
(0 to 255 and back across each rule's turn). Alpha is non-premultiplied,
so only the alpha byte is touched. Pacing is a per-idle frame counter,
as in ball.c; clicking advances the rule and the wheel adjusts the
cycle speed.

The bitmap clone/convert helpers are copied from composite-test.c,
where they are statics rather than library functions.

Widens the launcher window to fit the longer entry name.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adjust-clicking a scroll arrow now steps against the direction the arrow
points, so a single arrow can be worked both ways without moving the
pointer. Toggle-size remains Select-only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ction

Reflows the Doxygen parameter blocks in window.h and wuss.h, and renames
the README's "Wuss" section to "Windowing".
Select/Menu/Adjust were consecutive enumerated values, so a chord such as
Select+Adjust could not be expressed at all. They become flags -- Select
4, Menu 2, Adjust 1, matching the RISC OS button order -- with a
wuss_BUTTON_NONE of zero, and every internal comparison switches from
equality to a bit test.

Where a chord is ambiguous Select wins: a Select+Adjust click on a scroll
arrow scrolls the way the arrow points rather than backwards, and on the
back icon sends the window to the back.

BREAKING CHANGE: wuss_button_t's values have changed, and client code
comparing the reported button for equality must now test with '&' or it
will fail to match a chord.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Matches the per-area layout of the other docs (databases, datastruct,
framebuf, geom, io).
Names the terms the document already leans on: the three coordinate
spaces (screen, window-local, virtual content), content area versus
visible bounds, furniture and its parts, and the RISC OS button
conventions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
doc already caps how large a resize-drag or toggle-size can make a
window; the floor was the fixed WUSS_MIN_CONTENT, so a task whose
content stops making sense below some size had no way to say so.

wuss_window_create gains a min_doc alongside doc, set once at creation.
wuss__min_content clamps it up to WUSS_MIN_CONTENT, so a window can
never be squeezed too small to grab, and down to doc, so a minimum can
never demand a window larger than the document it shows. Pass (0,0) for
the built-in floor.

BREAKING CHANGE: wuss_window_create takes a min_doc argument between doc
and window.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The launcher entry table is now const, so the per-row "running" flag can no
longer live in it. Move it into launcher_task as a fixed-size bool array
indexed by entry, capped at LAUNCHER_MAX_ENTRIES.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Replace inline (point_t) { x, y } and (size2d_t) { w, h } compound
literals across the tree with POINT(x, y) / SIZE2D(w, h) helpers defined
alongside their types.

Also folds in a pre-existing null-pointer guard in bmfont_get_info and
minor alignment tidy-ups that were already in the working tree.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Resize-drag recomputed width/height as pointer minus content origin on
every move, discarding where within the resize icon the mouse-down
click landed. The window's corner snapped to the raw pointer position
on the first move, jumping if the click wasn't at the icon's exact
corner pixel.

Store the pointer's offset from the content box's bottom-right corner
at mouse-down (mirroring how a titlebar drag already stores its
content-relative offset), and subtract it on each move, so the grabbed
point stays under the pointer through the drag.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TWwV3bFEi9z5MZc5wqiJzZ
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add a work-area icon subsystem: static labels and clickable bevelled
buttons that Wuss draws inside a window's content area and hit-tests
before the content task sees a click. Icon boxes are in virtual document
space so they scroll with the content; button clicks and hovers reach the
task as wuss_EVENT_ICON, while labels and hidden or disabled icons fall
through as wuss_EVENT_MOUSE.

Includes the public wuss/icon.h API, per-window icon storage, drawing,
mouse routing integration, and an icons test task.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The icons task drew its backdrop grid and x/y axis rulers pinned to the
window edge, so Wuss's scroll blit displaced the painted pixels without
repainting them, smearing the labels. Anchor all task drawing to document
space so it scrolls rigidly with the content, as the blit assumes; add a
bmfont-drawn coordinate ruler along the document x=0 and y=0 lines.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Loops wuss_icon_create over a spec array with all-or-nothing rollback:
on the first failure any icons already created by the call are destroyed
and no handles are written. Converts the icons test task to use it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a BORDER margin around the bitmap, a min-doc resize floor, and a
pink background so the border and transparent pixels are visible.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
image_create took the repo root and built the PNG path itself; it now
takes the full filename, with path assembly moved to the spawn_image
caller in wuss-test.c.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
New entry point taking a content size instead of a box: wuss packs the
window (furniture included) into the first free screen region via a
packer_t it owns, top-left order, tracking occupied area across calls so
successive auto-placed windows tile. Cascades from the previous
placement when no region fits.

The slot is released back to the pool on close, and on the first
wuss_window_move / wuss_window_resize (a titlebar drag counts as a
move), after which wuss stops tracking the window's position.

Adds packer_release() to geom/packer as the inverse of packer_place_*;
released areas are not coalesced, which is sufficient for whole-window
placement.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Switch every test task from a hardcoded BOX_POS_SIZE origin to
wuss_window_create_placed, passing just the content size. Windows now
tile from the top-left instead of landing at fixed coordinates.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
packer_place_by now searches for the box plus a configurable gutter strip
along its two inner edges, so boxes placed by location never end up flush.
The gutter defaults to 0, leaving every existing caller unchanged. The
returned position is still the un-inflated box.

wuss_window_create_placed sets a fixed WUSS_PLACE_GUTTER and stores the
gutter-inflated region in wuss_window::packed so the whole reservation,
not just the footprint, is handed back on close/move.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Each wireframe model in the sofa test task now draws a small white square
at every projected vertex, on top of the edges. Also adds a Cobra Mk III
model alongside the existing ship.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…lines

The anti-aliased fixed-point line rasteriser had three undefined behaviours,
all reachable with long or off-screen endpoints:

- FIX16_ONE * dy_f8 (and grad_f16 * dx) overflowed 32-bit int once a line
  exceeded ~32k fixed-point units; compute those in long long.
- INT_TO_FIX8(iy) left-shifted pixel Y coordinates that can be negative;
  use a multiply instead.
- the yf recurrence left-shifted a possibly-negative fix8 value before
  shifting back; form the fix16 sum by multiply, then arithmetic-shift down.

Adds test_wu_fix8_extreme_coords covering large and off-screen endpoints,
and fixes the test draw() helper's own negative left shift.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Replace the launcher's hand-drawn text rows and bespoke mouse hit-testing
with wuss_ICON_TYPE_BUTTON icons created via wuss_icon_create_array; wuss
now paints and hit-tests them and delivers clicks as wuss_EVENT_ICON.

Each click spawns a fresh instance of the row's task, so a row can be
clicked any number of times. Drops launcher_redraw/launcher_mouse, the
running[] tint array, and the now-unused font and palette parameters.
Shutdown calls every entry's destroy() unconditionally (NULL-safe).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Reuses WUSS_PLACE_GUTTER to pad the whole screen.
Every launcher task backed its state with a single file-scope static in
wuss-test.c, so spawning a task twice made both windows share one state
block and only the latest window's animation ran.

Each spawn_* now calloc's a fresh per-instance block, passes it to the
task's *_create, and frees it again if create fails or opens no window.
Each task's wuss_EVENT_CLOSE handler frees its own block (and any owned
bitmaps); the checker task, whose two windows share one block, frees it
once both have closed. The now-redundant *_destroy functions, the twelve
g_*_task statics, the destroy_* wrappers and the launcher_entry_t.destroy
column are removed.

wuss_idle already walks every window, so tasks now animate independently
in as many windows as are open.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
New tools/wrap_protos.py rewraps C function prototypes and definitions
onto one parameter per line, declarators aligned in a column (the style
in screen-copy-rect.c), but only when the one-line form exceeds 80
columns. Conservative guards keep it off call statements, control-flow,
macro bodies and comment text. Handles function-pointer and array
parameters. Self-check in tools/test_wrap_protos.py.

Applied across the tree: 35 files rewrapped. Builds clean (nosdl, sdl,
asan); DPTLibTest 24/24.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Draw a 3x3-grid source image into a destination box: corners at natural
size, edges and centre tiled. Clipped to the destination and the screen
clip, which is restored on return. Inherits pixel-format support and
blending from screen_draw_bitmap.

Exercised by a new screen test and by the wuss SDL image task, which now
tiles resources/wuss/9tile.png behind the loaded PNG.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
screen_draw_ninepatch gains a flags argument; passing
screen_NINEPATCH_NO_CENTRE draws only the border and leaves the interior
untouched. Existing callers pass 0.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A button icon pressed on MOUSE_DOWN stayed pressed if the client's ICON
handler opened a window covering the icon's owner: the later MOUSE_UP
resolved via wuss__window_at to the new window and never reached the
pressed icon.

Track the held icon on struct wuss as pressed_icon (mirroring
furniture.dragging) and release it on any MOUSE_UP before the window
hit-test. Also clear it on pointer-leave in mouse-move, and in
wuss_window_close and wuss_icon_delete so it can't dangle.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
dpt and others added 14 commits August 30, 2026 18:01
…piece onto another's vacated ground

With an occluder biting a corner out of a window's pre-move footprint,
the clean (non-occluded) pieces can overlap in destination: on a
downward drag a full-width bottom band slides straight into the
destination of the right-side band. The vacated-sliver invalidate only
subtracted each piece's own destination, so that shared ground -- which
the other piece's blit had already filled with valid pixels -- was
invalidated and repainted across the full window width for nothing.

Compute each sliver as clean[i] minus every clean piece's destination
via wuss__subtract_boxes, not just full_dest[i].

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The resize icon and scrollbar wells shared a restack block, so grabbing a
scroll well on Select popped the window to the front. Restrict the raise to
FURNITURE_RESIZE.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Two CMake options, WUSS_FURNITURE and WUSS_ICONS (both default ON, so
current behaviour is unchanged), drop the respective furniture/*.c and
icon/*.c files from the build and #ifdef-guard every core call site,
struct field and inline helper that references them.

With WUSS_FURNITURE off every window is chromeless: content box == visible
box, no furniture drawn, hit-tested or dragged. wuss__scroll_clamp and
wuss__scroll_step move to a new core file scroll-step.c so programmatic and
wheel scrolling survive. With WUSS_ICONS off the wuss_icon_* API is not
compiled and struct wuss/wuss_window lose their icon fields.

The icon typedef moves to wuss.h (forward declaration) so wuss_event_t can
name it regardless of the option; icon.h's body is wrapped in #ifdef
WUSS_ICONS and dropped from PUBLIC_HEADERS when off.

Tests stay ON-only: tasks/icons.c, tasks/launcher.c and the SDL
interactive driver build only when both options are on. The core wuss_test
is split - the existing body is gated on both options, with a compact
core-only replacement exercising the chromeless window path (create, move,
z-order, doc coordinates, scroll, invalidate, close) for the off configs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add screen_fill_pattern(), an 8x8 two-colour tile fill primitive in
framebuf/screen with eight built-in patterns (solid, grey50, stripes,
diagonal, dots, grid, crosshatch), phase-locked to a caller-supplied
origin so a scrolling fill stays put.

Add wuss_ICON_TYPE_PATTERN: a non-interactive icon whose bbox is filled
with one of those patterns in fg/bg, aligned to document space. Clicks
fall through as wuss_EVENT_MOUSE; disabled swatches fold fg into bg.

Cover the primitive in screen-test.c (phase, clipping) and show every
pattern in the interactive icons task.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The tile is 8x8 and repeats, so a fill has only eight distinct pixel
rows. Expand each to a phase-shifted colour run once up front; the
scanline loops then index the run with no per-pixel bit test. The
32bpp path memcpy's whole 8-pixel runs instead of storing pixel by
pixel.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Covers screen_fill_pattern(), wuss_ICON_TYPE_PATTERN, the WUSS_FURNITURE/WUSS_ICONS compile-time options and the scrollbar-well raise fix.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
When dst was narrower or shorter than two patch cells the near and far corner boxes overlapped and each drew its cell full-size across the shared band, so the last one won. Clamp the corner column/row boundaries to the destination midpoint: the near corner keeps the near half, the far corner the far half, and the edge/centre runs between collapse. Also assert log2bpp >= 3 in ninepatch_cell so a sub-8bpp source aborts cleanly instead of hitting a negative shift, and hoist a mid-scope loop var.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
next_cascade had a single wrap check and no loop, so a footprint wider or taller than the screen made the wrap condition permanently true and every oversized auto-placed window landed at (0,0). Pin an over-screen footprint at the top-left and return without advancing the cascade counter, so later normal-sized windows still cascade.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The backdrop and bevel-colour range check plus its free/free/return cleanup were duplicated across the WUSS_FURNITURE and WUSS_ICONS-only branches and had drifted: the two paths defaulted the bevel colours differently for a NULL config. Extract validate_bevel_backdrop() and default both bevels to 0.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…sform

wuss__icon_screen_box was declared, compiled and linked with zero callers, re-deriving the content-to-screen bbox transform that wuss__icon_draw open-codes. Drop it for wuss__icon_box_to_screen(content, scroll, bbox, out), which wuss__icon_draw now routes through, so the transform exists once.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
screen_copy_rect also returns 0 for an off-screen source or destination, and the blit pieces are only occlusion-clipped, so a later piece can fail after earlier pieces have already moved pixels. The frame still self-heals via the union fallback; the comment now says so instead of claiming the blit fails identically on the first piece.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Covers fcdeb88; the other commits since e00cabf are docs/refactor/style with no user-facing change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @dpt, your pull request is larger than the review limit of 150,000 diff characters

@dpt
dpt changed the base branch from master to develop August 30, 2026 20:50
@dpt
dpt merged commit 671cb53 into develop Aug 30, 2026
11 checks passed
@dpt
dpt deleted the wuss-platypus branch August 30, 2026 20:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant