Conversation
fschutt
added a commit
that referenced
this pull request
Sep 28, 2026
…2026-09-19 Brings PR #476 up to date with master and the documentation branch, so the PR is mergeable again. Conflicts resolved: - api.json: a three-way JSON merge against the merge base (every key changed on one side only; 0 true conflicts), re-saved by `azul-doc normalize`. - common/event.rs: master only rewrapped these regions (rustfmt); the app-cursor paths, live focus resolution and the removed scrollbar drag keep this branch's refactors (add_app_cursor, resolve_focus_target_live, the press router - which already reads get_scroll_node_info). - cpurender/raster.rs: master's "the masked region is the element" and this branch's half-pixel coverage fix combined - both the element box and the mask rect are pixel-aligned bounding boxes (floor/ceil). - radio_group.rs / segmented.rs: master's `callback.invoke(..)` (managed hosts get their context) with this branch's `&mut CallbackInfo`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Cd9Y37iWnwHFPHYfneLQfi
fschutt
added a commit
that referenced
this pull request
Oct 2, 2026
… is gone after a frame (RED) PR #476 ledger, engine backlog 4: video decode / camera / screen capture / microphone workers keep running after their node leaves the DOM - run_all_threads only reaps FINISHED threads, and a capture or decode loop never finishes. Headless test (dll/src/desktop/shell2/headless/mod.rs): a node whose AfterMount starts a capture-style worker (runs until TerminateThread, bounded to 4 s) is dropped by the next DOM. Asserts the worker is told to stop, and once it returned, ONE thread-poll frame removes it from the window and the thread poll stops. (The shells' remove_threads now go through LayoutWindow::remove_thread, which already existed; the next commit makes it forget the owner too.) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fschutt
added a commit
that referenced
this pull request
Oct 2, 2026
… (RED) PR #476 ledger, idle leftovers: AzWidgets at rest held 476 FLIP moves whose values had converged (translate 15 -> 0.016) but never finished, so needs_animation_frame stayed true and the window never went idle. core/src/animation_test.rs: - the converged state (0.016, v -0.2) is settled, and one tick at it - even a zero-length one - finishes the channel on its target; - a spring crossing its target fast is NOT settled (no mid-flight stop); - a state the criterion accepts never moves further than EPSILON_VALUE again (SMOOTH / GENTLE / SNAPPY); - a 300 px FLIP move settles within 1.1 s at 60 / 120 / 240 Hz. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fschutt
added a commit
that referenced
this pull request
Oct 2, 2026
…ange (112cf83..53c978b) Three read-only reviews: EDITORS (Mail, Notes, Writer, Tasks, Review, Contacts, Calendar - one shared RichTextEditor from AzNotes), OFFICE (Sheets, Show, Photo, VideoCut, Paint, Drive, Meet, Maps, Calculator, Setup, Builder, Shells, Widgets, Storage, AppKit), WIDGETS_API (widgets, core/css/dll, api.json, autofix). Every finding is a next-wave item in the PR #476 ledger. Top: deterministic Uuid::v4 ids overwrite saved documents (Notes, Photo, Show, VideoCut). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WyGP18BMbfHvnTRYqNVjbP
…ser decision) A udmabuf is only sampled in place when the GPU accepts its LINEAR pitch; 256 bytes is the multiple every common GPU accepts (what Qt picked). The test states pitch = align_up(width * 4, 256), padding in whole pixels (`padding_px` / `pitch_px` added: the renderer views a slot as a pixmap `pitch / 4` pixels wide). Fails against the tight pitch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… for its strut CSS 2.1 s10.8.1: every line box of a block container starts with a strut of the container's own first available font. collect_font_stacks_from_styled_dom keyed only TEXT nodes (and placeholders), so a block whose inline content was only boxes (an icon row of inline-blocks or images) never loaded its face: fc.rs took the 0.8em / 0.2em strut guess and line-height normal as 1em (a 10px inline-block in 16px Arial: a 16px line, Chrome 18). Phase 1 now also keys the PARENT of every inline-level box (display inline / inline-block / inline-flex / inline-grid / inline-table) - the container its line boxes belong to (an inline box with no glyphs holds a strut of its own font, keyed the same way). Keys are deduplicated by (family, weight, style), so a document whose containers share their text's font resolves no new chain. A minimal solver3 edit (MAILREF8 owns solver3 sizing). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ares, not their order NodeData's Hash folded the inline declarations' discriminants in order. An imperative patch (`set_css_property` -> `upsert_inline_css_property`) moves its declaration to the end of the inline style, so the patched node hashed unlike a fresh build of the same widget in the same state - and exactly like an untouched twin elsewhere. reconcile_dom's subtree pass (A2) then gave the rebuilt widget the twin's identity: in AzWidgets the clicked Switch matched the ShellSettingsDialog's switch 7400 px further down (the Dialogs section, 2026-10-01, put a second switch on the page), its track got a FLIP slide from there and its transitions / overrides were remapped onto the other switch - "the toggle immediately transitions". The inline part of the hash is now a count plus a wrapping sum of per-property hashes: order-free, and a property declared twice still counts twice. Values stay out of the hash as before (a toggle is the same node). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…he main checkout of a worktree) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pool_layout: stride = align_up(width * 4, 256). WIRING PENDING in the next commits (the native slot target, the popup copy, the tooltip blit still assume tight rows until then). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mounts (RED) THREADS8. The map tile sweep (map_on_after_mount) is a 250 ms timer that never ends itself: a map that left the DOM kept waking the app four times a second and kept its tile cache alive, and a remount added a second sweep. The rule the node threads follow (managers::thread_owner) is stated for timers: unit tests of the manager (stopped once, follows a moved node, child DOM of an unmounted VirtualView, a removed timer is never stopped for its old node) and a headless test (AfterMount timer gone after the unmount, the app timer untouched, the shell drained the stop list). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
THREADS8, GREEN part a for 5c7e4e4: ThreadOwnerManager gains timer owners (bind_timer / timer_owner / forget_timer) and a stop list (take_timers_to_stop / timers_to_stop). remap_node_ids moves a timer owner with its node and puts the timer of an unmounted node (or of a node in the child DOM of an unmounted VirtualView) on the stop list; threads and timers share one `follow` rule. Not wired yet: the window drop and the dispatcher binding come next. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the unmount THREADS8, GREEN part b: LayoutWindow::remap_node_ids classifies `timers` as node-keyed through thread_owners and removes the timers the manager put on its stop list, so they never fire again; the shell stops the OS timers from the same list (next commit). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New CpuBackend contract field `native_target_row_padding_px` (0 everywhere until a shell sets it). The test arms a slot whose rows are 13 px longer than the frame (a 256-byte-pitch wl_shm slot in miniature) and asserts the frame is rendered INTO it (no owned-path fallback) and every row matches the owned render, full and incremental. Fails today: render_frame refuses any target whose width differs from the frame. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…started to the node THREADS8, GREEN part c1: dispatch_events_propagated collects the AddTimer of an AfterMount / NodeResized / Updated callback (binds_threads_to_node) and binds the timer to the node once the changes are applied (if it is still running); applying AddTimer / RemoveTimer forgets an old owner of the id. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…_target_row_padding_px The armed target may be `frame width + padding` pixels wide (its width is the slot pitch); the frame lands in columns 0..frame width of every row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
azul.h's load-time check is a GCC / Clang constructor or, in C++, a static object. MSVC compiling C - the C compiler of Windows - gets neither: a C program built with cl.exe runs unchecked unless it calls AzAbi_check() itself. The test asks for an `#elif defined(_MSC_VER)` arm after the C++ one: an entry in the CRT initializer table `.CRT$XCU` (selectany, one per program), kept by `/include:` with the 32-bit x86 underscore. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
THREADS8, GREEN part c2 for 5c7e4e4: a provided PlatformWindow::stop_timers_of_unmounted_nodes drains the thread-owner manager stop list into stop_timer (NSTimer / SetTimer / timerfd / headless). It runs at the start of dispatch_pending_lifecycle_events (after every rebuild pass of regenerate_layout) and of invoke_expired_timers (for a rebuild outside regenerate_layout, e.g. a VirtualView re-render). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…adding The native path hands render_frame a pixmap `stride / 4` pixels wide over the slot and sets native_target_row_padding_px = pitch - frame width, so the renderer writes rows at the slot pitch (256-byte multiple). Present damage, the commit swizzle and the catch-up copies already address rows by `cpu_state.stride`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…; middle raises CSS 2.1 s10.8 (user ruling 2026-10-03, Chrome is the reference): - vertical-align: middle put the box's midpoint at the baseline PLUS half the x-height, i.e. moved it down; the spec raises it (y = baseline - xh/2). A 10px box sat at 11.8 (Chrome 4.84), a 24px middle icon in 16px text at 16 - below its own line (Chrome 0). - calculate_line_metrics counted every baseline-relative box by its unshifted ascent / descent, so a shifted box (middle, text-top, text-bottom, a length) was placed partly outside the line box it was counted in; and the strut joined the line box only AFTER the top / bottom pass, so a 24px bottom-aligned box in an 18px strut line made a 27.2px line with the box 3.2px down (Chrome 24 / 0). One helper, baseline_shift(align, ascent, descent, constraints), is the rule both the line box and the placement read. The strut starts pass 1. sub / super keep their line-ascent-relative shift and their old contribution (unchanged; Chrome's font-size/5+1 and /3+1 shifts are left - see the report). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…open on Apple opus_apple.rs: AudioToolbox dlopen'd (AudioConverterNew / Dispose / FillComplexBuffer / Get/SetProperty / GetPropertyInfo - the VideoToolbox rule: no build-time link, a missing symbol leaves the handles closed). An encoder is one converter float PCM (the app's rate, 1-2 channels) -> Opus 48 kHz, 960 frames a packet, at the asked bit rate; its input callback hands out everything queued in one buffer kept alive until the next callback, and answers 'nodt' when dry (ends the call, not the stream). A decoder is the converter back, one packet per decode, with an encoder's magic cookie for the same format where the converter wants one. codec.rs: the handles hold an EncoderInner / DecoderInner only with a live engine (honest handles); frames in another rate or channel count are refused; closed off Apple with the reason said once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New last arm of azul.h's load-time check, after the GCC / Clang constructor
and the C++ static object:
#elif defined(_MSC_VER)
#pragma section(".CRT$XCU", read)
static void __cdecl AzAbi_checkAtLoad(void) { AzAbi_check(); }
__declspec(selectany) __declspec(allocate(".CRT$XCU")) void (__cdecl *AzAbi_checkAtLoadEntry)(void) = AzAbi_checkAtLoad;
#if defined(_M_IX86)
#pragma comment(linker, "/include:_AzAbi_checkAtLoadEntry")
#else
#pragma comment(linker, "/include:AzAbi_checkAtLoadEntry")
#endif
`selectany` (as guiddef.h's DEFINE_GUID uses in C) makes the entry one per
program though every translation unit defines it; `/include:` keeps it with
/OPT:REF and /GL. Checked with clang in MSVC mode (x86_64- and
i686-pc-windows-msvc, -fms-extensions -fms-compatibility, -O2): the object
has the `.CRT$XCU` entry and the right `/include:` directive. NOT verified
with cl.exe (no Windows here): the post-release Windows C leg is the first
real compile.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…window teardown Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mn width MAILREF8 group C (mailgun billing, 26 boxes 90px low): a nested 80%-wide table is laid out at its min-content in the cell's min-content measurement, and layout_cell_for_height kept max(final layout, measurement). Chrome 154 numbers in the file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ot the measurement's MAILREF8 group C: layout_cell_for_height (block branch) took max(final layout at the column width, the min/max-content measurement's used_size). The measurement lays a nested percentage-width table out at its min-content, so Mailgun billing's invoice row was 412px for Chrome's 323 and everything below it 90px low. The cell's own height is read by cell_specified_border_box_height, which the measured term stood in for. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…choCanceller) - module exception + the tool's move Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… newtype The generated EchoCanceller handle is a raw pointer, so the Arc<Mutex<Playout>> holding it was not Send. The library keeps the canceller's state behind a Mutex and AzMeet only reaches it under the Playout mutex (SAFETY comment). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…4 boxes mismatched) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fschutt
added a commit
that referenced
this pull request
Oct 3, 2026
…minate THREADS8 (PR #476 engine backlog 4). Root cause: both workers took their receiver as `_recv` and never read it, so the TerminateThread an unmounted <video> (managers::thread_owner) or a closing window sends was never seen: the worker ran until the grace period detached it, and its Tick / resize messages piled up unread. Each frame now starts with the shared capture_common::terminate_requested (the camera / screencap / mic workers use it already), which also drains the other messages. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…RED)
The wave-8 build still re-laid out ~10% of AzWidgets per switch-knob frame
(coordinator: 55-58 ms, root_layout_pass 19 ms, 288 text re-flows, 618 taffy
misses). AZ_TAFFY_DEBUG + AZ_RECON_DEBUG on the prebuilt app: 607 of the 618
misses are in one region - the form whose column ends with its "Send the raw
form" button (DOM 3459-3461) - and the reconcile builds that button, its <p>
and its text FRESH on every tick ("COUNT MISMATCH parent dom 3459: old 0 new
1", recon_old_idx_none 3, fp_new_node 3), dirtying every ancestor to the body.
- text_after_a_block_is_carried_over_by_the_next_layout: a box with a block
and then text and a span; a relayout of the unchanged page must build 0
nodes fresh and find 0 dirty. RED: the trailing inline run is fresh each
pass.
- The AzWidgets-like card of a_knob_frame_costs_the_same_on_a_page_twice_as_long
gains the shape LAYOUTPERF8's page lacked: inline content AFTER a block
("More about note i" + a span). RED again: every card's trailing run is
rebuilt each frame, so the cost doubles with the page.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s old self (GREEN) reconcile_recursive looked up the inline children of an anonymous block's run in the old tree two ways: a run BEFORE a block fell back from the parent's direct children to the whole old tree (`dom_to_layout`), the run that ENDS the box did not - and those children are never direct children of the parent, they sit in the anonymous block. So text, a span or a button ending a box after a block was rebuilt FRESH by every reconcile (with its subtree), was intrinsic-dirty every pass, and cleared the per-node and flex caches of every ancestor up to the root. AzWidgets: the form column ends with its "Send the raw form" button (DOM 3459-3461); every switch-knob frame of the wave-8 build rebuilt it and re-laid out the form - 607 of the frame's 618 taffy misses, the 288 text re-flows, most of root_layout_pass's 19 ms. Any page whose boxes end with text after a block (mail HTML, documents) relaid out from the root on every relayout, even with nothing changed. One lookup now, `old_layout_index_of`, for all three sites that match a child built inside a box the parent's layout made around it: both inline-run branches and `reconcile_child_under` (anonymous table boxes), which held the same fallback inline. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The font manager keeps one chain cache and one signature of the stacks it was resolved for; a view's child DOM, laid out inside its host's pass, replaces both, so the host never matches its own signature again. Prebuilt AzWidgets (three views): every pass - every switch-knob frame - resolves the page's fonts again (font_chain_resolve 2.2 ms of a tick; each view pass resolves its own, ~20 us). RED: after a pass the cache holds no chain for the host's serif stack, and a relayout of the unchanged host resolves twice (font_load_missing 2). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…heir signature in place (GREEN) layout_dom_recursive_with_viewport already swaps the host's layout cache out for a child DOM's pass; the font manager's chain cache and the signature of the stacks it was resolved for now get the same treatment. The child resolves against an empty slot; afterwards the host's chains and signature are back, with the child's chains for the stacks only it uses kept beside them (text edited inside the view still finds its chains). AzWidgets (three views): the page's fonts were resolved again on every pass - every switch-knob frame paid font_chain_resolve 2.2 ms for a page whose font stacks never changed. Expected after: the host skips (its signature matches), each view resolves its own small DOM (~20 us). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ler findings Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hains, VirtualView passes, expected numbers Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the reconcile (no fresh rebuild every layout), VirtualView passes keep the host's font chains Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eal app; 8C investigates on the build Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tripped DYLD_LIBRARY_PATH The LAYOUTPERF8B re-measure on the 19:04 build was UNCHANGED because it never ran that build. run_capped exports DYLD_LIBRARY_PATH=target/azul-lib and then starts `env AZ_BACKEND=headless ... App`; /usr/bin/env is SIP-protected, so the kernel strips every DYLD_* variable when it starts it, and App loaded the libazul.dylib its install name points at instead: target/release/build/azul-dll-78dff1e65276e337/out/libazul.dylib, dated 18:38 (target/azul-lib/libazul.dylib is 19:04 - the build refreshed that copy, not the build-script one). Shown with a non-SIP python: through the old runner DYLD_LIBRARY_PATH is None, through this one it is target/azul-lib. Re-run correctly (no env in between), the same tick on the same build: solver3 20.0 ms (was 33.6), root_layout_pass 5.8 ms (19.4), text_layout_flow 76 (288), taffy misses 184 (618), no font_chain_resolve in the root pass, no fresh nodes in the root reconcile, 38.8-43 ms wall profiled (55-58). The documented `-- env A=B App` form now works: the runner consumes the env assignments with `export` and starts the program directly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…age column measured twice, cards whose height depends on it (RED) Measured on the 8B build (with the right library - see 653a502): the knob tick still lays out 184 flex items again and re-flows 76 text runs, all but 11 in the form section. AZ_TAFFY_DEBUG shows why: the page column (layout node 27, a flex item with a visible overflow) is measured by its container twice per pass - its basis under a max-content height and its automatic minimum under a min-content height - and in each run it asks the form section for its width at the height the section gets in THAT run: MISS n2038 kd=(None,Some(938.0)) avail=(Definite(852.0),Definite(12693.0)) MISS n2038 kd=(None,Some(906.0)) avail=(Definite(852.0),Definite(12661.0)) Two keys, one measurement slot of taffy's cache: each run evicts the other's entry, so every tick misses both - and every level below repeats it. The test page gets the same shape: the cards sit in a page column with a visible overflow inside the scrolling one, and each card holds a group in wrapping columns (as tall as its options stacked at max-content, as one option at min-content). a_knob_frame_costs_the_same_on_a_page_twice_as_long is RED again: every card misses twice per frame, so the cost doubles with the page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… measured under two keys of one class keeps both (GREEN) taffy's Cache keeps one measurement per slot class. A flex container measured twice per pass under different available main sizes (its basis at max-content, its automatic minimum at min-content - every flex item with a visible overflow) asks each item for its cross size at the main size the item got in that run; an item whose size depends on it (wrapping lines) gets two keys of one class, each run evicts the other's entry, and every pass misses both with the item's whole subtree. Now that clean nodes keep their caches across passes (LAYOUTPERF8), that thrash is what the AzWidgets knob tick still paid: 173 of its 184 taffy misses and its 76 text re-flows, all in the form section the page column asked for (None, 938) and (None, 906). TaffyMeasureSpill (taffy_bridge.rs) keeps the last two distinct keys per class, with taffy's own match rule. It lives in the node's NodeCache (which follows the node across passes) and is allocated only at a node's first eviction - a second store into a class, which can only follow a miss (NodeCache::taffy_slots_stored). It is part of the taffy cache for validity: never read while the primary is empty, dropped by the first store after the primary was emptied (every clear - the dirty closure, a restyle, a clone that cannot keep it - empties the primary), and with NodeCache::clear. Counted in the memory report. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…wo ways per class Counted on the AzWidgets tick (AZ_TAFFY_DEBUG, the right library): the form section's descendants are asked up to FOUR distinct keys of one slot class per pass and up to seven in all. Two ways per class would cycle through four keys and miss every one (LRU with fewer ways than a cyclic set). A ring of 12 measurements of any class, searched with taffy's own rule (its Cache::get searches all slots too), holds every key such a node is asked; from the second pass on all of them hit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… thrash, expected numbers Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nodes measured at two heights per pass; run_capped starts the app itself (SIP strips DYLD_* through /usr/bin/env) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nges build_link.rs copies the AZ_LINK_PATH library into the build script's OUT_DIR and sets that copy's absolute path as the install name, but emitted no rerun-if-changed for it (the static path does). After a dylib rebuild the apps kept loading the old copy unless DYLD_LIBRARY_PATH reached them, which SIP prevents through /usr/bin/env - a perf fix measured as 'no change' was the old library (2026-10-03). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s 157-309) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
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.
Summary
Bug-hunting follow-up to the 2026-09-16 handoff, driven by a real-input self-test of AzWidgets on KDE Plasma
Wayland (ydotool for the pointer and keys, Spectacle for what the user actually sees). Every engine and
shell fix has a test that was seen RED before the change. The Wayland protocol fixes (right-click, menu
measurement, anchor edge and size rounding, decoration mode) have no compositor in the test harness: they
carry unit tests of the pure decisions they now make and were verified live with real input.
Fixed
fix(scroll)thumb heldfix(focus)ring after relayoutfix(events)FocusLost targeton_focus_losthooks never ranBluragainst the node focused nowfix(events)focus-only changefix(text)reveal across a wrapfix(layout)abspos + opacityfix(layout)abspos in flexfix(cpurender)empty framefix(wayland)right-clickfix(wayland)menu measuredfix(wayland)edge + roundingfix(wayland)decorationsNoTitlerequested server-side decorationsfix(wayland)refusal readfix(menu)font sizeA disjoint-rect split of the damaged raster (for a related swizzle leak) was tried and reverted in this
branch: it seams box-shadows and anti-aliasing across the pieces (the slider test caught 4 stale pixels).
Verification: layout integration battery 1114 passed, layout unit tests 7749 passed, shell unit tests 2379
passed. Live on KDE Plasma 5.27 Wayland with real input: 40 one-click wheel scrolls with the native
backbuffer hit 15 empty-damage frames without a colour swap (before: swapped within 3-9 clicks); the
context menu opens at the pointer at its measured size.
Unverified commit:
7b0f0b635(decoration refusal) was committed when its RED/GREEN build was stopped forthe wrap-up. Its test
a_granted_client_side_request_is_not_a_refusalruns with the shell unit tests.Plus tooling:
scripts/ydotool_start.sh(daemon + socket permissions + flat pointer acceleration for thevirtual device, without which every "go to x,y" pinned the cursor in a screen corner) and
scripts/ydotool_input.py(press, hold, drag, wheel and keys through the daemon's socket), and theAZ_BB_PROBEnative-backbuffer pixel probe that found the orange frame, andAZ_RECONCILE_DEBUG, which namesevery node a DOM rebuild failed to match.
Investigated, not a bug
select and paint correctly with real input.
Slider alike); the colour change seen while sweeping was the orange-frame bug.
Still open (recorded, not in this PR)
rebuild with the new flags; a real refusal still reshapes the DOM at the next app rebuild.
including focusable widget roots, so focus is dropped.
exists, but the root box has
height:autoand never overflows itself; with the injected menu bar andthe 8px body margin the demo's page bottom is cut off by 42px. Needs the viewport, not the root box, as
the scroll container.
NoTitlewindows have no window controls on Wayland (and X11). With the double titlebar fixed,nobody draws minimize/maximize/close; the app's own title row needs azul-drawn, KDE-like controls.
bounding box but reports the separate rects (the fix belongs in the report, a raster split seams
box-shadows); overlapping moves in one frame unswizzle twice; popups write into a buffer the compositor
may hold. Longer term: render B,G,R,A natively like Qt/GTK instead of swizzling in place.
Breeze's rounded corners, border, shadow and taller rows. The software menu bar and the fake titlebar
do not look like KDE's yet.
reached in this round.
azul-corefindingsderivable_impls/implicit_clonepre-date the branch and are already fixed on master.Full status, bug ledger and the real-input self-test driver: private gist
https://gist.github.com/fschutt/ea8988a6b9494763a122bc8e94e94d22
🤖 Generated with Claude Code
https://claude.ai/code/session_01ETMF11HyFDjLsvwEckEx7x