Repository navigation
v0.12.0
teksilo-scene becomes an editor. A selection can be moved, resized and
rotated — with a pointer, from the keyboard and through a screen reader — where
a heavyweight item could not be dragged with a pointer at all. SceneCard is
the container a note lives in, and a card can hand its height to its own words.
One picker now answers every "what did the pointer hit?", over one geometry
descriptor that also brings Qt's four selection modes and a lasso. A pan over
fifty thousand lightweight items costs what a pan over an empty scene costs,
and the cards nobody can see cost no accessibility node and no Tab stop. Around
that: a third
paint band and a surface for ink that is still wet, every position the OS
batched, a seam a data layer can reverse a scene edit through, and a finger that
can pan a SceneView with selection or magnetism switched on.
Breaking, pre-1.0: PathItem::new takes one argument, SceneItem loses two
methods and gains one, Path::commands is a method, item_change_signal
carries an envelope, and several types the crate hands to consumer code are
now #[non_exhaustive]. Each one is listed under Breaking changes below
with what to do about it.
Added
Moving, resizing and rotating a selection
- A selection transform controller, opted into per view with
SceneView::transform_controller(TransformConfig): a frame around the
selection, eight resize handles, a rotate handle and a body drag. Knobs for
aspect lock, centred scaling, rotation snaps and their tolerance, a minimum
size, handle and padding sizes, edge auto-pan distance and speed, and a
chromeclosure for painting the whole thing yourself.on_start/
on_change/on_endreport the gesture;LivePreviewdecides whether the
content moves under the frame or only the frame does. - The model is written once, when the gesture ends, through
Scene::apply_transform_delta. One gesture is one step to reverse,Esc
cancels with nothing to roll back, and a sample costs a relayout rather than
one model write per selected item. - Keyboard: the transform key (
tby default) enters the controller,Tab
cycles the handles, the arrows move or resize by a step,Esccancels. SceneView::transform_session_signal()publishes the live gesture, for an
app-owned inspector, status bar or size readout.- Assistive technology: every handle is published with a role, a name and —
where it drives a single number — a value.IncrementandDecrementmove
the coordinates that handle actually drives, and each two-dimensional handle
also advertises a named action per direction (TransformStep), so a width can
be changed without a height.TransformLabelstakestr!like every other
label. - Both tiers move. A heavyweight card is draggable with a pointer at last;
the lightweight drag, theAlt+arrow nudge and the controller all go through
one door. SceneView::transform_enabled_signal()andmagnetism_enabled_signal()for a
toolbar to bind. Turning either off takes its chrome off the screen and out
of the published accessibility tree.
SceneCard, and a card that sizes itself to its content
SceneCard— the container a heavyweight item usually wants: a surface, a
header that is the grab handle, a selection ring, threeCardModes
(Idle/Selected/Editing) and one namedRole::Group, with no opinion
about what is inside it. Dragging the title strip moves the card; dragging the
prose inside it selects text; a trailing header slot holds buttons that can be
clicked — even with the jitter a real click carries — without starting the
drag. Slots take a widget, a boxed widget or an id, andsurface(..)replaces
the chrome entirely.SizePolicyon a heavyweight entry —Fixed(today's behaviour and the
default),HeightForWidth(the width is authored, the height follows the
words) andIntrinsic(the entry shrink-wraps its widget). Set it with
Scene::set_size_policy/SceneModel::set_size_policy, or with
SceneCard::height_for_width(). Only the cards a pass lays out are measured,
so an off-screen card keeps the estimate it was created with and a pan still
costs the viewport.Scene::set_measured_sizeis there for an app measuring
something the framework cannot.- A
SceneViewanswersScrollIntoView. A caret moving inside an embedded
editor pans the camera to follow it, with no app wiring, and an outer
ScrollAreais re-targeted to where the card will land. It honours reduced
motion, which the publicSceneView::ensure_visiblecannot. - Double-clicking a card puts the caret in its body even when that body is a
Switcherthat swaps the editor in on the next pass.
One geometry, one picker
SceneItem::shape() -> ItemShapeis how an item describes its geometry: a
rectangle, a rounded rectangle, an ellipse or a path, with a fill rule and an
optional stroke band in local or screen units. It replacesshape_contains
andclone_shape_test, which had to be kept in agreement by hand.- Qt's four
ItemSelectionModes —IntersectsItemShape(the default),
ContainsItemShape,IntersectsItemBoundingRectand
ContainsItemBoundingRect— chosen per view with
SceneView::marquee_selection_mode, or per call onScene::items_in_region
andcolliding_items_with. SceneRegionis a rectangle or a path, so a lasso is expressible:
SceneRegion::lasso(path),::stroke(path, width),::from_screen_rect(..).
A rotated marquee stops over-selecting.- One resolver decides every pick.
PaintKey— band, thenz, then
insertion order — is the order the tap, the drag, the hover, the cursor, the
tooltip, the hold andScene::item_atall read.claims_pressand
hit_testableare public for a consumer running its own pass. PathItemcarries a realfill_rule, used by painting and hit-testing alike,
andhit_stroke_widthfor a hairline that should still be clickable.- On
Path:flatten(tolerance)(a polyline per subpath),
exact_bounds(tolerance)(a tight box, wherebounds()returns the
control-point hull and so over-reports every curve),
contains_point(p, rule, tolerance), theSubpathtype, andarc_to_cubics
shared rather than duplicated inside the path atlas.
Ink
- A third paint band.
SceneLayer::Interleavedorders a lightweight item
against the heavyweight cards byz, so a dried stroke can sit above note A
and below note B — which two bands could not express. The pointer still
resolves through the lightweight hit snapshot, the item keeps its own
accessibility node, and the Tab ring does not move. WetLayer— a surface for content being authored right now, installed
withSceneView::wet_layer(..). It repaints on its own without re-running the
band beneath it, and always sits above every card and interleaved item and
below theOverband.WetLayer::request_repaint(ctx)from a pointer
handler;WetNodenames the nodes it owns.EventContext::coalesced()— the positions the OS batched into the packet
being dispatched, oldest first, each with its own timestamp and its own
pressure and tilt. A drawing surface fans out over it and then over
pointer_position(). Opt the producer in with
TeksiloAppBuilder::pen_batching(PenBatching::Coalesce), which emits one
dispatch per pen drain instead of one per packet and never folds a transition.
Noton_drag— the drag recogniser swallows every move inside its slop and
reports the press position, so the start of a stroke never arrives there.- docs/ink.md — the shape of a drawing tool on a scene page:
what to read a stroke from, what to put on which band, and what a wet stroke
costs.
A seam a data layer can reverse a scene edit through
Undo itself stays in the data layer; this crate ships no stack and no undo().
What it ships is a record complete enough to invert.
- Every notification carries a transaction id, a
ChangeSource
(User/Programmatic/Remote), aHistoryMode
(Record/RecordPreserveRedo/Ignore) and anephemeralflag. The
framework stamps its own gesture commitsUser, so an app can tell a finished
drag from a programmatic move. - One write scope is one transaction, so removing a subtree is one change to
reverse rather than N. Group several calls withSceneModel::transaction/
user_edit; nesting joins rather than splits.SceneTransaction::abandon
tags a cancelled interaction, andsquash(off by default) folds a
transaction's repeated writes to one quantity into its endpoints. Scene::take/Scene::restore— an owning salvage door.takehands
back the item box, its magnets (ids included) and its whole slice of the
logical accessibility tree;restoreputs it back at the sameItemId
and the same place in the reading order, which is what selection, magnets,
relations and every app side-map are keyed on.Scene::removeis the same
call with the salvage routed to the edit sink, andRemovedItem::detach()
forgets the recorded parent so a salvage restores at root level — or into a
different scene.SceneModel::set_edit_sinkreceives one owningSceneTransactionRecord
per committed transaction, with the scene unborrowed so the sink may read and
write it; the sink's own writes are journaled in turn.transaction_signal
fires once per transaction, after the sink.Scene::replace_itemswaps a lightweight item's box while keeping the entry,
the id and everything keyed on it.Placement { parent, z, local_pos, transform }is written as one property withset_placement, plus
reparent_keeping_scene_posfor a drag into a group that must not move the
item, andz_between, which reports whenf32precision has run out at a
locus.ItemChange::is_edit()separates the scene's edits from the derived
notifications emitted beside them, so an app counting the changes in an edit
gets the number the transaction record has.- Accessibility readers to match the writers:
Scene::a11y_live_of/
a11y_landmark_of, anda11y_relations/a11y_live_of/
a11y_landmark_of/a11y_categories_ofonSceneModel.
Saying what a change should become before it is applied
- A geometry constraint on
SceneModel— one closure that rewrites a
gesture's proposed geometry before anything is applied, so snap-to-grid,
axis lock and page clamping reach the drag ghost instead of correcting it a
frame late. Install it withSceneModel::set_geometry_constraint; it is handed
aProposedChange(the scene read-only, the roots the gesture moves, and the
gesture's start and proposed frames in scene coordinates) and returns a
ChangeVerdict—Accept,Adjust(frame)orReject. - It governs the transform controller on both tiers, the lightweight item drag
and theAlt+arrow nudge, for pointer, keyboard and assistive technology
alike. An app driving its own drag callsSceneModel::constrain_move/
constrain_frame. Programmatic mutators are never constrained. SceneModel::downgrade()andWeakSceneModel— a non-owning handle. A
constraint normally needs no handle at all (ProposedChange::sceneis the
whole read surface); this is for a policy object that holds one for its other
work, and it is what keeps such a closure from leaking the scene that owns it.
Core, platform and canvas
Widget::accepts_child_hit(child, point)— a parent can veto one child for
one point, which is the per-point questionhit_transparent(a per-node
declaration) cannot ask.EventContext::dispatch_target()— the arena's own answer to who the press
landed on, so a container need not guess it from a rectangle.EventContext::release_cursor()— withdraws a handler's cursor override so
the node-declared one resolves again.- A pan claimant may carry its own drag. A node that declares a
PanClaim
and also installson_dragnow competes as both: the pan wins atpan_slop,
and a hold arms the node's own drag.LongPressRole::DragHandledeclares that
the hold inside a subtree belongs to that drag, and applies to a direct
pointer only — a mouse enrols no pan member at all, so nothing about it
changes. - Every pen sample carries the device's own time.
PenPacket::device_time_ms
plus a sharedback_daterule that places a drained batch on the tree's
timeline, wrapped 32-bit counters and missing clocks included. Windows now
drainsGetPointerPenInfoHistory, so a digitizer running above the message
rate delivers every sample with its own pressure and tilt rather than one per
message. AccessNodeBuilder::set_custom_actionsadvertisesAction::CustomAction
itself, so a node's custom actions are reachable rather than decorative.- A stroke carrying a
StrokeStyle— dashed, dotted, or a custom cap or join —
reaches the screen from every canvas stroke primitive, not onlystroke_path.
Accessibility
SceneMinimapis operable. Given anon_clickit is focusable, publishes
aRole::Groupnode, advertisesClickand the four scroll actions, pans with
the arrow keys and recentres onHome/Enter/Space.access_readout
supplies the phrasing for where the viewport sits, as aMinimapReadout, so it
can be localised.
Demos
cargo run -p scene-ink— every pen sample throughctx.coalesced(), a
WetLayerthat repaints alone, and a dried stroke interleaved between two
notes.cargo run -p scene-corkboard— beats areSceneCards with real editors:
drag the title strip to move one, drag its prose to select text, pan a beat
off-screen and keep typing (the camera follows the caret), and "Fit to text"
hands every beat's height to its words. Both panes carry a transform
controller, and one "Snap to grid" toggle drives a single geometry constraint
shared by both.
Changed
Breaking changes
PathItem::new(path)takes one argument and derives its own bounds. Drop
thelocal_boundsyou were passing; if you were widening it to make a hairline
clickable, usehit_stroke_widthinstead.ItemFlags::NEGATIVE_Z_BEHIND_PARENTis removed. It was declared and
documented but read by nothing, andPaintKeyis flat, so it could not have
been honoured without making the key hierarchical. Nothing observable changes;
delete the reference.PointerSequence::has_deferred_grab()is nowhas_deferred_grab_for(id).
A deferred grab is a statement about the node it arms, not about the press:
read sequence-wide it suppressed every descendant's touch long-press and
context menu. Pass the node you are asking about.SceneItem::shape_containsandclone_shape_testare gone, replaced by
shape() -> ItemShape. An item that overrode either overridesshape()now;
the default returns the item'slocal_bounds, which is what an item that
overrode neither already got.SceneItem::set_fill/set_strokereturnAppearanceWrite<T>instead of
bool, carrying the value they overwrote — the only place a journal can get it
from.AppearanceWrite::Refusedis the default, and is what an item with no
such slot should keep returning.SceneItemA11yContextpublishes scene coordinates.screen_bounds,
local_to_screen,advertised_boundsandbounds_spaceare replaced by
scene_boundsandlocal_to_scene; the camera is declared once as an
AccessKit transform above the whole subtree. An item emitting its own
sub-element geometry stops projecting through the view transform and publishes
in scene space.A11yBoundsSpaceis removed with them.item_change_signalcarries aSceneChange— the change plus its
transaction envelope — rather than a bareItemChange. Observers read
notification.change.ItemChangeis no longerCopyorPartialEq(it carries owned values
now) and gainsPlacementChanged,ItemReplaced,HandlersChanged,
MeasuredSizeChangedandSizePolicyChanged.TransformChanged,
PayloadChangedandAppearanceChangedcarry both sides of the value they
replaced.Path::commandsis a method. Read withcommands(), append withpush,
build from a vector withPath::from_commands. The field backs a rolling
content stamp that a publicVeccould not be kept in step with.PointerSample::coalescedis aVec<CoalescedSample>, not a tuple — each
batched position keeps its own axes, which the tuple dropped.PenPacket::time: EventTimeis replaced bydevice_time_ms: Option<u32>,
the device's own counter rather than a stamp with an unknown epoch, and
ToolEvent::Framecarriestime_ms. A backend reports what the device said;
back_dateplaces the batch on the tree's timeline.#[non_exhaustive]on the types the crate hands to consumer code, so a
new field is not a source break next time:ItemChange,SceneLayer,
MagnetVisualState,MagnetRef,MagnetConnection,MagnetSnap,
MagnetMarker,MagnetFeedback,SceneItemPaintContextand
SceneItemA11yContext. Amatchgains a_arm; a struct literal becomes the
type'snewconstructor, which every one of them now has, with its fields
still public.WidgetPlacementgained adormantfield and is now#[non_exhaustive].
Aplace_childrenimplementation reads and writes the slice it is handed and
is unaffected. What breaks is a struct literal —WidgetPlacement { id, origin, size }— which becomesWidgetPlacement::new(id, origin, size). The field
arrives pre-set to the child's current state, so a parent that ignores it
changes nothing; it is read only for a parent whoseculls_childrenreturns
true.SceneCardhas one method per slot.header_id,header_boxed,
header_trailing_id,header_trailing_boxed,body_idandbody_boxedare
gone;header,header_trailingandbodyeach take a widget, a
Box<dyn Widget>or aWidgetId. Drop the suffix —.body_id(id)becomes
.body(id). This follows the slot-twin purge the widget catalog went through
in 0.11.0, so a card addresses its slots the way every other container does,
and last call wins where before an id set after a widget left both stored.
Performance
- A pan costs the viewport, not the model. One pan sample over a scene with a
50 000-item lightweight off-screen tail went from 16 ms to 1.8 µs —
what a pan over an empty scene costs. The two hit snapshots are pure
functions of the model, so they are built once and patched per item from the
change stream instead of being rebuilt on every sample.
(crates/teksilo-scene/tests/pan_scaling_probe.rs.)
A pan over off-screen heavyweight cards is cheaper than it was but is not
flat: the cards are parked rather than laid out, and what remains is the
framework's own walk over their arena nodes.
(crates/teksilo-scene/tests/heavyweight_retention_probe.rsprints both.) - Off-screen cards park. At 50 000 of them the published accessibility node
count and the Tab-stop count equal the on-screen counts exactly, and the
accessibility walk no longer grows with the tail. A card carrying focus or a
live pointer is pinned wherever the camera goes.
(crates/teksilo-scene/tests/heavyweight_retention_probe.rs.) - Moving one item costs the moved subtree, not the scene.
Scene::set_local_poswalks a kept adjacency list instead of rebuilding a
scene-wide parent map on every move.
(crates/teksilo-scene/tests/mutation_scaling_probe.rs.) - A hover no longer costs the selection.
Scene::selection_rootsis linear
rather than quadratic in the number of selected items, and the transform
controller resolves it once per hover instead of thirteen times per pointer
move. With the whole of a 5 000-item scene selected, oneselection_roots
call went from 177 ms to a figure that no longer grows with the selection, and
a drag sample in a scene with no constraint installed no longer pays for the
selection at all.
(crates/teksilo-scene/tests/selection_roots_scaling_probe.rs,
transform_scaling_probe.rsprint the tables.) - A growing wet stroke is no longer quadratic. The renderer's path-mask cache
keys on a rolling content stamp, so a cache hit does not scale with the path's
length. (crates/teksilo-render/tests/wet_stroke_cost.rs.)
Scene
- An
item_change_signalobserver may read and write the scene. Changes are
queued and drained after the mutation's borrow drops — theteksilo-data
mutate-then-notify discipline — so the snap-to-grid pattern the docs describe
is implementable at last. An observer that panics mid-flush costs that
observer's delivery and nothing else. ACascadeBudgetbounds one drain's
observer-generated deliveries (100 000 by default, flat, a knob on
SceneModel::set_cascade_budget), and its diagnostic names the subject that
piled up. SceneListAdapterkeeps each row'sItemId. A row whose content changed,
and every row an insert or a removal shifted, keeps the id it had — so
selection, magnets and accessibility parenting survive a data change instead of
being retired with it. Each source change is one transaction.ItemChange::VisibilityChangedis emitted by every door that flips
IS_VISIBLE, not onlyset_flag, and is a derived notification rather than an
edit. Hiding a card is one recorded edit and two notifications whichever door
it went through; it used to be two edits through one and one through the other.Scene::set_zignores a write only when the entry already holds that exact
value. Its oldf32::EPSILONtest never fired at 1e6 and swallowed millions of
distinct floats near zero — including the midpointsz_betweenhands out, so a
caller was told there was room and then got a silent no-op.- A rotation applied by
apply_transform_deltathat also moves the item now
emitsTransformChangedas well asLocalPosChanged;set_transformignores
a write that changes nothing. ItemChange::HandlersChangedcarries the handler sets it replaced.
handlers_mutcannot know what the caller does with the&mutit hands out,
so it announces without recording.- A geometry constraint returning
Adjustwith a frame that cannot be applied —
non-finite, or a negative extent — is refused rather than stored, and a debug
build panics naming the frame. ANaNframe used to be kept verbatim, which
removed the item from hit-testing, from the marquee and from every spatial
query for good, with nothing raised to say so. SceneModel::constrain_move/constrain_framecalled inside an open write
scope panic naming the rule instead of reportingRefCell already mutably borrowed.- An
Overitem occludes a card only if it would act on a press. A
decorative halo over an embedded note takes nothing and focus stays in the
note; a hover affordance is not a press claim, and neither is accepting a drop. - A marquee no longer takes screen-pinned items.
commit_marqueeruns
throughScene::items_in_region, which answers in scene coordinates, and a
pinned item does not live there — it holds a fixed place in the viewport, so
the scene rectangle a rubber band describes says nothing about whether the
pointer crossed it. Dragging a band across the page therefore leaves a pinned
badge alone where it used to select it. Selecting one deliberately still works
by click. There is no screen-space region query yet; the deferral is recorded
at the query's own site. SceneView::retention_margin(px)sets how far outside the viewport a
heavyweight card stays mounted — the band that keeps a fling from mounting and
unmounting cards under the finger. Default 96 screen px; read it back with
current_retention_margin.
Platform
PlatformWindow::reconfigure_surfaceanswersboolrather than():
falsemeans the display server is gone and the caller should wind down
instead of asking for another frame, which would return to the same place.FrameOutcomegainsDisplayLost, distinct fromErrorand
NeedsReconfigurebecause it is terminal: reconfiguring or redrawing after
it spins the loop.
Fixed
Scene
- A
SceneViewwith selection or magnetism switched on can be panned with a
finger. The view's own drag was enrolled in a way that was never resolved, so
its recogniser latched at the mouse slop and the pan was never evaluated. - A won pan no longer fires the claimant's own
on_tap— panning a list with
a finger used to tap a row on the way past. - A revoked drag no longer leaves the item displaced. The scene had no
PointerCancelarm at all, so a cancelled touch or a lost capture left the
item wherever the last sample put it. - Leaving a
SceneViewclears what leaving it should clear. Its
PointerLeavearm had never run, so an item stayed hovered for ever, the
tooltip stayed armed, and the cursor stayed onGrabwith the pointer outside
the view. - Two
ItemFlagsdo what their documentation says. An item without
IS_VISIBLEis no longer clickable or draggable, and one withoutIS_ENABLED
no longer eats a click. - Paint order and hit order agree. Equal-
zties were resolved to the
bottom-most item, exactly inverting paint, and the scene's two pickers were
wired to opposite halves of the dispatch pipeline — so a tap beat every card
and a drag lost to every card. - A card's published rectangle tracks the camera. A heavyweight item's
accessibility bounds were its arena rectangle in scene coordinates while a
lightweight item's were projected to the screen, so the two tiers disagreed and
a pan, zoom or rotation moved neither. Both are published in scene space now,
under one declared camera transform, and an accessibility hit-test at the
painted position finds the card. - A selection change no pointer made — "select all", a search result, or the
other pane of a sharedSceneSelection— reaches the published accessibility
tree. Both panes kept describing the previous selection, at its previous
position. - An item's height can be changed from assistive technology.
Incrementon a
"Resize top" or "Resize bottom" slider reported the action handled, announced
an unchanged number and moved nothing. Esccancels a transform the pointer started, not only one begun from the
keyboard.- A revoked contact ends the gesture through the cancel path:
on_endfires
withTransformOutcome::Cancelledand the edge auto-pan stops. The pan used to
keep tweening for up to thirty seconds with no input, and the hook an app tears
its overlay down on never fired. TransformConfig::min_sizefloors only the axes the dragged handle drives.
Dragging the bottom edge of a 2 × 100 hairline used to widen it to 4.- A geometry constraint that accepts a change leaves a magnetised drag alone on a
rotated item; the comparison deciding whether the rule had overruled the
magnet was exact across a rotation round-trip that is not bit-exact. - A banded region — a lasso drawn as a stroke — is answered in the frame where
its band is round, so an anisotropic scale no longer lets a containment query
pick an item the matching shape query then rejects. A zero-length segment, and
an open polyline's phantom closing chord, are both handled.
The minimap
- It stays inside its own frame. Zooming in projected the viewport rectangle
outside the widget, and nothing clipped the result, so the minimap painted over
its neighbours — a caller-supplied border by half its own width, unclamped. The
projection now runs through the union of content and viewport, and the widget
clips its own paint. - Its content extent is computed in one place; paint and click used to carry
separate copies of the same formula.
Rendering and canvas
- A dashed or dotted stroke reaches the screen.
stroke_rect,
stroke_rounded_rect,stroke_circle,stroke_ellipseanddraw_lineall
accepted aStrokeStyleand dropped it, along withline_capandline_join.
A styled stroke now routes to the tier that carries the pattern, and a solid
one keeps its cheaper tier. - The renderer no longer paints what hit-testing cannot see. A path opening
with an arc was rasterised with an injectedMoveToat the bitmap origin,
drawing a long spoke from there to the shape — visibly painted, and a click
inside it hit nothing. The atlas opens each implicit subpath where the
flattener opens it. Path::transformedandPath::flattenagree about where a subpath begins.
They did not for an arc following aClose, so rotating a two-loop lasso
changed what it selected.
Input
- Pen samples are spaced by the device's own clock. Both pen backends stamped
zero and the translator then stamped one instant across a whole drained batch,
so velocity, smoothing and prediction were all computed from zero spacing. On
Windows the batch was also genuinely one packet per message — the history
buffer was never drained. PointerSample::coalescedreaches a handler. It was populated and then
discarded, with no accessor and two constructors writing it out empty, so a
tool written the obvious way drew at frame rate on a 200–360 Hz digitizer.
Platform
- A compositor that exits no longer takes the application down as a crash.
When the display server goes away, the window surface stops answering for the
adapter it was matched against, and the nextSurface::configurefailed
inside wgpu's default error handler, which panics. What the user saw was
wgpu error: Validation Error / In Surface::configure / Surface does not support the adapter's queue family, and what that reads like is a GPU
mismatch. It is not one:request_adapterfilters candidates on the very
query that fails there, so the adapter had answered yes to the same question
moments earlier. The message cost a real investigation, and a bug report
aimed at the renderer for a compositor crash.
EverySurface::configurenow runs inside an error scope, so the failure is
returned rather than thrown. A surface with no formats left for the adapter
it was matched against is reported as a lost display server, in those words;
anything else keeps wgpu's own message, so a genuine mistake is not
relabelled as a dead compositor. - The same event reaching winit first is no longer a panic either. Both Linux
backends report a failed dispatch or flush asExitFailure(errno), and
runtreated every error alike. A lost display connection now exits
cleanly with one line naming the cause;NotSupported,Osand
RecreationAttemptstay fatal, since those are genuine startup faults where
a backtrace is the useful answer. - Opening a window no longer kills a GNOME session on a machine with no
touchscreen. The Wayland drag-and-drop backend asked the seat for its
pointer and its touch as soon as a window attached, on the reasoning that a
seat lacking the capability would hand back a proxy that never emits. The
protocol says otherwise:wl_seat::get_touchwithout the touch capability is
amissing_capabilityerror, and the client is the one at fault. Mutter
(GNOME 50) does not answer with that error — it serves the request out of a
MetaWaylandTouchwhose list head is still all-zero, because thewl_list
is initialised only when a touch device appears, and writes through it.
gnome-shell takes SIGSEGV, and since the compositor is the session, every
window on the desktop dies with it, a second or two after the app opened
one. KWin is unaffected, which is what made it read as a compositor bug
rather than ours. Both objects are now bound from thewl_seat::capabilities
event instead, and released when a capability is withdrawn — the press
serialsstart_dragneeds arrive on the same terms as before, since a seat
that can start a drag is by definition one that announced the capability.
(This is the cause of the lost display server the two entries above learned
to survive.)
Known limitations
- An assistive-technology probe and the pointer still disagree over an
interactive scene overlay.accesskit_consumerresolves a hit by walking
a node's children in reverse, and the scene emits its children in band order,
so the heavyweight tier wins an explore-by-touch probe whatever band the
overlay is in and whatever it claims. The press-claiming rule below fixes the
pointer answer, not this one. Closing it means reordering an AccessKit child
list, which is also the reading order — an accessibility-tree decision rather
than a hit-test one. Documented indocs/teksilo-scene-a11y.mdand pinned by
what_the_press_claiming_rule_does_and_does_not_reconcile, so it cannot drift
silently.
Full Changelog: v0.11.0...v0.12.0