Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
229da8d
feat(wuss): add an animated Porter-Duff demo task to the wuss test
dpt Aug 28, 2026
2e67c4b
feat(wuss): scroll the opposite way on an Adjust-clicked scroll arrow
dpt Aug 28, 2026
fa65ea3
docs(wuss): rewrap the window and wuss headers, retitle the README se…
dpt Aug 28, 2026
bdb8288
fix(wuss)!: make mouse buttons flags so chords can be reported
dpt Aug 28, 2026
6e2c57f
docs(wuss): move wuss.md into a windowing directory
dpt Aug 28, 2026
8c0509d
docs(wuss): add a glossary
dpt Aug 28, 2026
5fa615d
feat(wuss)!: let a window set a minimum content extent
dpt Aug 28, 2026
61e1f7a
fix(wuss): track launcher running state in a separate array
dpt Aug 28, 2026
9c468a4
refactor(geom): add POINT and SIZE2D compound-literal macros
dpt Aug 28, 2026
fa735d7
fix(wuss): preserve pointer offset in resize-corner drag
dpt Aug 29, 2026
d99aa4e
docs: trim derivable build and module-layout notes from CLAUDE.md
dpt Aug 29, 2026
131d0e5
feat(wuss): add work-area icons
dpt Aug 29, 2026
43af586
fix(wuss): scroll work-area grid and axis rulers with the document
dpt Aug 29, 2026
63a6fde
feat(wuss): add wuss_icon_create_array for bulk icon creation
dpt Aug 29, 2026
85a004d
test(wuss): pad the image task with a border and min-doc floor
dpt Aug 29, 2026
ac5e5cc
refactor(wuss): pass the image path into image_create
dpt Aug 29, 2026
0b3bff9
feat(wuss): add wuss_window_create_placed for wuss-chosen positions
dpt Aug 29, 2026
3232d2d
refactor(wuss): let test tasks self-place via wuss_window_create_placed
dpt Aug 29, 2026
34962d7
feat(packer): add packer_set_gutter for spacing between located boxes
dpt Aug 29, 2026
316fd6d
feat(wuss): mark sofa-task vertices with white dots
dpt Aug 30, 2026
cbd4397
fix(screen): remove signed-overflow and negative-shift UB in wu fix8 …
dpt Aug 30, 2026
d6cf9d1
refactor(wuss): make launcher rows work-area button icons
dpt Aug 30, 2026
bac930d
feat(wuss): set an overall screen margin in window packer
dpt Aug 30, 2026
1afeb0d
refactor(wuss): give each launcher task per-instance heap state
dpt Aug 30, 2026
d6a6103
build(tools): add wrap_protos.py and rewrap overlong prototypes
dpt Aug 30, 2026
f679894
feat(screen): add screen_draw_ninepatch for resizable 9-patch frames
dpt Aug 30, 2026
52bc62f
docs: update CLAUDE.md build/test/style guidance
dpt Aug 30, 2026
7a0d2f6
feat(screen): add screen_NINEPATCH_NO_CENTRE flag
dpt Aug 30, 2026
8a741b0
docs: start CHANGELOG.md
dpt Aug 30, 2026
1d7d305
fix(wuss): release held icon when a window opens over it
dpt Aug 30, 2026
74faa65
fix(wuss): don't repaint blitted pixels when a drag slides one clean …
dpt Aug 30, 2026
277b4e8
docs: fill in CHANGELOG for changes since 62a9193
dpt Aug 30, 2026
b860d83
fix(wuss): don't raise window when dragging a scrollbar well
dpt Aug 30, 2026
834fa55
feat(wuss): make furniture and icon subsystems compile-time optional
dpt Aug 30, 2026
587bcc4
feat(wuss): pattern-fill work-area icons
dpt Aug 30, 2026
3a9a4bf
perf(framebuf): expand screen_fill_pattern tile rows to colour runs
dpt Aug 30, 2026
e00cabf
docs: fill in CHANGELOG for changes since 277b4e8
dpt Aug 30, 2026
fcdeb88
fix(screen): clamp ninepatch corners so they don't overlap on tiny boxes
dpt Aug 30, 2026
4d561b2
style(screen): group screen_fill_pattern declarations at top of scope
dpt Aug 30, 2026
57e3c71
fix(wuss): don't wedge oversized auto-placed windows at the origin
dpt Aug 30, 2026
55dc77d
refactor(wuss): share bevel/backdrop colour validation in wuss_create
dpt Aug 30, 2026
e82cb39
refactor(wuss): replace dead wuss__icon_screen_box with a shared tran…
dpt Aug 30, 2026
1ca951a
docs(wuss): correct the screen_copy_rect failure comment in window_move
dpt Aug 30, 2026
fea7015
docs: note the ninepatch corner-clamp fix in CHANGELOG
dpt Aug 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Changelog

All notable changes to DPTLib are recorded here.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
This project does not yet publish versioned releases; entries are grouped under
_Unreleased_ until one is cut.

## [Unreleased]

### Added

- `wuss/icon.h` — work-area icons: static labels and clickable bevelled
buttons drawn inside a window's content area. Icon boxes are in virtual
document space, so they scroll with the content. Wuss hit-tests buttons
before the content task sees a click and delivers them as `wuss_EVENT_ICON`;
labels and hidden or disabled icons fall through as `wuss_EVENT_MOUSE`.
- `wuss_icon_create_array()` — creates a batch of icons from 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.
- `wuss_window_create_placed()` — creates a window from a content size instead
of a box, letting Wuss pack it (furniture included) into the first free
screen region. Successive auto-placed windows tile; placement cascades when
no region fits. The slot is released on close and on the first
`wuss_window_move()` / `wuss_window_resize()`. An overall screen margin is
kept around all auto-placed windows.
- `screen_fill_pattern()` — 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. The tile has only eight distinct rows, so each
is expanded to a phase-shifted colour run once up front and the scanline
loops index it with no per-pixel bit test; the 32bpp path memcpy's whole
8-pixel runs.
- `wuss_ICON_TYPE_PATTERN` — a non-interactive work-area icon whose bbox is
filled with a `screen_fill_pattern()` pattern in fg/bg, aligned to document
space. Clicks fall through as `wuss_EVENT_MOUSE`; disabled swatches fold fg
into bg.
- `WUSS_FURNITURE` and `WUSS_ICONS` CMake options (both default ON) drop the
furniture/*.c and icon/*.c files and `#ifdef`-guard every core call site,
struct field and helper that references them. With `WUSS_FURNITURE` off every
window is chromeless (content box == visible box); with `WUSS_ICONS` off the
`wuss_icon_*` API is not compiled. Programmatic and wheel scrolling survive
either off via the new core `scroll-step.c`.
- `screen_draw_ninepatch()` — draws a resizable "9-patch" frame from a source
image that is a 3x3 grid of equal cells: corners at natural size, edges and
centre tiled, clipped to the destination box and the screen clip.
- `screen_NINEPATCH_NO_CENTRE` flag for `screen_draw_ninepatch()` to draw only
the border and leave the interior untouched.
- `packer_set_gutter()` — `packer_place_by()` now reserves a configurable
gutter strip along the box's two inner edges so located boxes are never
flush. Defaults to 0, leaving existing callers unchanged; the returned
position is still the un-inflated box.
- `packer_release()` — inverse of `packer_place_*`, returns an area to the
pool. Released areas are not coalesced.
- `POINT(x, y)` and `SIZE2D(w, h)` compound-literal macros in `geom/point.h`
and `geom/size.h`.

### Changed

- **Breaking:** `wuss_button_t` values are now flags (`wuss_BUTTON_SELECT` 4,
`wuss_BUTTON_MENU` 2, `wuss_BUTTON_ADJUST` 1, `wuss_BUTTON_NONE` 0) so
chords such as Select+Adjust can be reported. Client code comparing a
reported button for equality must now test with `&`.
- **Breaking:** `wuss_window_create()` takes a `min_doc` argument between
`doc` and `window`, the minimum content extent a resize-drag or toggle-size
will shrink to. It is clamped up to the built-in grab floor and down to
`doc`. Pass `(0, 0)` for the built-in floor.
- Adjust-clicking a scroll arrow now steps against the direction the arrow
points, so one arrow can be worked both ways without moving the pointer.
Toggle-size stays Select-only.

### Fixed

- Dragging a scrollbar well with Select no longer raises the window; only a
resize-icon grab restacks it.
- `wuss_window_move()` no longer repaints already-blitted pixels when a drag
past an occluded corner slides one clean piece of the window onto ground
another clean piece just vacated.
- A work-area button held on mouse-down is now released if the click opens a
window that covers the button's owner, instead of staying stuck pressed.
- Resize-corner drag preserves where within the resize icon the mouse-down
landed, so the window's corner no longer jumps to the raw pointer position
on the first move.
- Removed signed-overflow and negative-shift undefined behaviour in the
anti-aliased fixed-point line rasteriser, reachable with long or off-screen
endpoints.
- `screen_draw_ninepatch()` clamps its corner cells so they no longer overlap
and double-draw when the destination box is smaller than the source corners.
52 changes: 37 additions & 15 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,49 @@ DPTLib is a platform-independent C99 library (base, databases, datastruct, frame

## Build

Build directories are per-config and already created; there is **no** plain
`build/` — never invoke `./build/DPTLibTest` or `cmake --build build`.

- `build-asan/` — **default**. Debug, core tests (`BUILD_TESTS=YES`). Use this
unless told otherwise. Despite the name its cache currently has
`USE_ASAN=OFF`; re-run cmake with `-DUSE_ASAN=YES` if you actually need the
sanitisers.
- `build-sdl/` — Release, SDL tests on (`BUILD_SDL_TESTS=ON`). Needed for the
interactive `wuss` driver and anything under `libraries/wuss/test/tasks/`.
- `build-nosdl/` — Release, core tests only.
- `build-riscos/`— GCCSDK cross build. Leave alone unless working on RISC OS.
- `build.xc/` — Xcode generator.

CMake options: `BUILD_TESTS`, `BUILD_SDL_TESTS`, `USE_ASAN` (ASan + UBSan),
`USE_FORTIFY` (bundled Fortify), `DPTLIB_IMAGES_READ_ONLY` (libpng no write).

(Re)configure a dir only if its `CMakeCache.txt` is missing or you are changing
options:
```
mkdir build && cd build
cmake -DBUILD_TESTS=YES ..
make -j4
cmake -B build-asan -G Ninja -DBUILD_TESTS=YES
```

Useful CMake options:
- `BUILD_TESTS=YES` — build the `DPTLibTest` self-test executable.
- `BUILD_SDL_TESTS=YES` — additionally build tests needing SDL2/SDL2_image.
- `USE_FORTIFY=YES` — link the bundled Fortify memory-debugging library.
- `DPTLIB_IMAGES_READ_ONLY=YES` — build libpng without write support.
Build:
```
cmake --build build-asan --target DPTLibTest
```

Requires libpng (`find_package(PNG)` on non-RISC OS). On RISC OS the build fetches and patches zlib/libpng itself via `FetchContent` (see `cmake/*.patch`).

## Testing

Run all tests (needs `-resources` pointing at the repo root, for test fixture files):
Run from the **repo root** so `-resources .` resolves the fixture files:
```
./build/DPTLibTest -resources /path/to/DPTLib
./build-asan/DPTLibTest -resources .
```

Run a subset by naming tests (names come from the `tests[]` table in `apps/test/main.c`, e.g. `atom`, `bitvec`, `curve`, `pickle`, `stream`, `packer`):
Run a subset by naming tests (names come from the `tests[]` table in `apps/test/main.c`, e.g. `atom`, `bitvec`, `curve`, `pickle`, `stream`, `packer`, `wuss`):
```
./build/DPTLibTest -resources /path/to/DPTLib atom bitvec
./build-asan/DPTLibTest -resources . atom bitvec
```

SDL / interactive tests use the `build-sdl` binary instead.

Success prints `++ Tests completed in Ns: N of N tests passed.`

### Adding a new test
Expand All @@ -45,9 +62,7 @@ Success prints `++ Tests completed in Ns: N of N tests passed.`

## Architecture

**Module layout.** Each module lives in two places that must be kept in sync:
- `include/<area>/<module>.h` — the public API, always wrapped in `extern "C"`, documented with Doxygen `\file`/`\param`/`\return` comments.
- `libraries/<area>/<module>/` — implementation `.c` files (often one function per file, e.g. `libraries/datastruct/vector/{create,destroy,insert,...}.c`), plus a private `impl.h` defining the opaque struct behind the public typedef and any internal-only declarations.
**Module layout.** Each module is split between `include/<area>/<module>.h` (public API, `extern "C"`, Doxygen-documented) and `libraries/<area>/<module>/` (implementation `.c` files, often one function per file, plus a private `impl.h` for the opaque struct and internal-only declarations).

New source/header files must be added by hand to the relevant `set(..._SOURCES ...)` list and, for public headers, to `PUBLIC_HEADERS`, in `CMakeLists.txt` — there is no globbing.

Expand All @@ -70,7 +85,14 @@ New source/header files must be added by hand to the relevant `set(..._SOURCES .
- File header comment format: `/* filename.c -- one-line description */`.
- Section breaks within files use `/* ----- ... ----- */` rule comments.
- Public API docs use Doxygen (`\file`, `\param`, `\return`); a `Doxyfile` exists for generating them.
- Edit `.c`/`.h` files with the Edit tool, never `sed -i` line-range splices — they corrupt the Allman/2-space layout and can't be verified without re-reading. To inspect exact bytes or indentation, Read the file; don't shell out to `cat -A`/`cat -v`.
- After editing any `.c`/`.h` function prototype or definition, run `python3 tools/wrap_protos.py <file>`; after editing a header's Doxygen, run `python3 tools/wrap_doxygen.py <file>` (skip vendored headers).

## Commit messages

Use [Conventional Commits](https://www.conventionalcommits.org/): `<type>[optional scope]: <description>`, e.g. `fix(pickle): handle zero-length blobs`. Common types: `feat`, `fix`, `docs`, `refactor`, `test`, `chore`, `ci`, `build`. Add a `!` before the colon (or a `BREAKING CHANGE:` footer) for breaking changes.

When the user says "commit": stage the relevant files and `git commit` with the
message passed via repeated `-m` flags (subject, then body). Do **not** write a
`COMMIT_MSG` / `COMMIT_MSG_TMP` file. Never `git push` unless explicitly asked —
pushing prompts for an SSH key passphrase and will hang.
90 changes: 73 additions & 17 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ endif(CCACHE_FOUND)
option(USE_FORTIFY "Use Fortify" OFF)
option(USE_ASAN "Build with AddressSanitizer and UndefinedBehaviorSanitizer" OFF)
option(DPTLIB_IMAGES_READ_ONLY "Remove libpng write support" OFF)
option(WUSS_FURNITURE "Build the wuss window-furniture subsystem" ON)
option(WUSS_ICONS "Build the wuss in-content icon subsystem" ON)

# Referencing CMAKE_TOOLCHAIN_FILE avoids a warning on rebuilds.
if(NOT ${CMAKE_TOOLCHAIN_FILE} STREQUAL "")
Expand Down Expand Up @@ -116,6 +118,10 @@ set(PUBLIC_HEADERS
include/wuss/window.h
include/wuss/wuss.h)

if(WUSS_ICONS)
list(APPEND PUBLIC_HEADERS include/wuss/icon.h)
endif()

# The public headers must be set as properties of the library, not as
# target_sources. The quoting is essential.
set_target_properties(DPTLib PROPERTIES PUBLIC_HEADER "${PUBLIC_HEADERS}")
Expand Down Expand Up @@ -234,6 +240,8 @@ set(FRAMEBUF_SOURCES
libraries/framebuf/screen/screen.c
libraries/framebuf/screen/screen-copy-rect.c
libraries/framebuf/screen/screen-draw.c
libraries/framebuf/screen/screen-draw-ninepatch.c
libraries/framebuf/screen/screen-fill-pattern.c
libraries/framebuf/span-registry/get.c
libraries/framebuf/span-registry/regdata.h
libraries/framebuf/span/all8888.c
Expand Down Expand Up @@ -317,24 +325,9 @@ set(UTILS_SOURCES
libraries/utils/pack/unpack.c
libraries/utils/primes/primes.c)

set(WUSS_SOURCES
set(WUSS_CORE_SOURCES
libraries/wuss/create.c
libraries/wuss/destroy.c
libraries/wuss/furniture/back-box.c
libraries/wuss/furniture/close-box.c
libraries/wuss/furniture/content-box.c
libraries/wuss/furniture/drag-resize.c
libraries/wuss/furniture/draw.c
libraries/wuss/furniture/hit-test.c
libraries/wuss/furniture/hscroll-box.c
libraries/wuss/furniture/invalidate.c
libraries/wuss/furniture/resize-box.c
libraries/wuss/furniture/scroll-action.c
libraries/wuss/furniture/titlebar-box.c
libraries/wuss/furniture/toggle-action.c
libraries/wuss/furniture/toggle-box.c
libraries/wuss/furniture/vscroll-box.c
libraries/wuss/furniture.h
libraries/wuss/get-font.c
libraries/wuss/idle.c
libraries/wuss/impl.h
Expand All @@ -343,10 +336,12 @@ set(WUSS_SOURCES
libraries/wuss/mouse-move.c
libraries/wuss/redraw.c
libraries/wuss/scroll.c
libraries/wuss/scroll-step.c
libraries/wuss/task/start.c
libraries/wuss/task/stop.c
libraries/wuss/window/at.c
libraries/wuss/window/create.c
libraries/wuss/window/create-placed.c
libraries/wuss/window/close.c
libraries/wuss/window/get-content-bounds.c
libraries/wuss/window/get-scroll.c
Expand All @@ -358,6 +353,48 @@ set(WUSS_SOURCES
libraries/wuss/window/set-background.c
libraries/wuss/window/set-scroll.c)

set(WUSS_FURNITURE_SOURCES
libraries/wuss/furniture/back-box.c
libraries/wuss/furniture/close-box.c
libraries/wuss/furniture/content-box.c
libraries/wuss/furniture/drag-resize.c
libraries/wuss/furniture/draw.c
libraries/wuss/furniture/hit-test.c
libraries/wuss/furniture/hscroll-box.c
libraries/wuss/furniture/invalidate.c
libraries/wuss/furniture/resize-box.c
libraries/wuss/furniture/scroll-action.c
libraries/wuss/furniture/titlebar-box.c
libraries/wuss/furniture/toggle-action.c
libraries/wuss/furniture/toggle-box.c
libraries/wuss/furniture/vscroll-box.c
libraries/wuss/furniture.h)

set(WUSS_ICON_SOURCES
libraries/wuss/icon/create.c
libraries/wuss/icon/create-array.c
libraries/wuss/icon/delete.c
libraries/wuss/icon/draw.c
libraries/wuss/icon/free.c
libraries/wuss/icon/get-bbox.c
libraries/wuss/icon/get-text.c
libraries/wuss/icon/get-type.c
libraries/wuss/icon/get-window.c
libraries/wuss/icon/hit-test.c
libraries/wuss/icon/invalidate.c
libraries/wuss/icon/screen-box.c
libraries/wuss/icon/set-hidden.c
libraries/wuss/icon/set-text.c
libraries/wuss/icon.h)

set(WUSS_SOURCES ${WUSS_CORE_SOURCES})
if(WUSS_FURNITURE)
list(APPEND WUSS_SOURCES ${WUSS_FURNITURE_SOURCES})
endif()
if(WUSS_ICONS)
list(APPEND WUSS_SOURCES ${WUSS_ICON_SOURCES})
endif()

set(ALL_SOURCES
${PUBLIC_HEADERS}
${DATABASE_SOURCES}
Expand Down Expand Up @@ -408,6 +445,13 @@ if(DPTLIB_IMAGES_READ_ONLY)
target_compile_definitions(DPTLib PRIVATE DPTLIB_IMAGES_READ_ONLY)
endif()

if(WUSS_FURNITURE)
target_compile_definitions(DPTLib PUBLIC WUSS_FURNITURE)
endif()
if(WUSS_ICONS)
target_compile_definitions(DPTLib PUBLIC WUSS_ICONS)
endif()

if(NOT TARGET_RISCOS)
set(CMAKE_FIND_FRAMEWORK NEVER)
find_package(PNG REQUIRED MODULE)
Expand Down Expand Up @@ -550,12 +594,20 @@ if(BUILD_TESTS)
libraries/wuss/test/tasks/curve.c
libraries/wuss/test/tasks/gradient.c
libraries/wuss/test/tasks/image.c
libraries/wuss/test/tasks/launcher.c
libraries/wuss/test/tasks/palette.c
libraries/wuss/test/tasks/porter-duff.c
libraries/wuss/test/tasks/sofa.c
libraries/wuss/test/tasks/text.c
libraries/wuss/test/wuss-test.c)

# The icon-driven launcher and the icons task only build when both wuss
# subsystems are present; the SDL interactive driver needs them too.
if(WUSS_FURNITURE AND WUSS_ICONS)
list(APPEND TEST_SOURCES
libraries/wuss/test/tasks/icons.c
libraries/wuss/test/tasks/launcher.c)
endif()

source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${TEST_SOURCES})

# Avoid a warning from CMake
Expand All @@ -579,6 +631,10 @@ if(BUILD_TESTS)
endif()

if(BUILD_SDL_TESTS)
if(NOT (WUSS_FURNITURE AND WUSS_ICONS))
message(FATAL_ERROR "BUILD_SDL_TESTS requires WUSS_FURNITURE and WUSS_ICONS")
endif()

find_package(SDL3 REQUIRED)
find_package(SDL3_image REQUIRED)

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ DPTLib is my platform independent C library. It contains a wide variety of funct
- [`utils/pack.h`](https://github.com/dpt/DPTLib/blob/master/include/utils/pack.h) — structure packing and unpacking helpers
- [`utils/primes.h`](https://github.com/dpt/DPTLib/blob/master/include/utils/primes.h) — cache of prime numbers

### Wuss
### Windowing

- [`wuss/wuss.h`](https://github.com/dpt/DPTLib/blob/master/include/wuss/wuss.h) — minimal window manager {[docs](https://github.com/dpt/DPTLib/blob/master/docs/wuss.md)}
- [`wuss/window.h`](https://github.com/dpt/DPTLib/blob/master/include/wuss/window.h) — window creation, positioning, sizing and client delegation
Expand Down
Loading