Commit graph

87 commits

Author SHA1 Message Date
Kevin Boos
e0a5a23f2f
Splash improvements for running untrusted mini-apps (#1139)
* fix a pile of splash script-vm bugs: newline statements, short-circuit args, tail calls

went through the script VM and parser and fixed a batch of correctness bugs
that were biting the launcher's mini-apps:

- newline-delimited statements: a `(` or `[` at the start of the next line no
  longer greedily glues onto the previous value as a call/index. leading infix
  operators and `.` still continue the expression (the shader DSL needs that),
  and the divert is suppressed inside ()/[] groupings.
- short-circuit `&&`/`||` used as a call argument no longer loses its value to
  nil when the jump skips a multi-op right-hand side.
- a call as the very last statement of a script actually executes now, in both
  end-of-parse unwind loops (also patched a zero-offset ShortCircuitEnd).
- custom widgets that deref to a base with a #[source] field now forward
  script_source, so script_apply_eval works on them instead of silently no-op'ing.

plus regression tests for the newline and short-circuit cases.

* harden splash isolates: scoped timers, net gating, effective-visibility snapshots

isolate-safety work so mini-apps can't reach outside their sandbox:

- isolate-safe script-timer dispatch hook + gc for stale timers
- gate net.socket_stream on the net runtime being present
- widget-tree snapshot reports effective visibility (a widget counts as hidden
  if any ancestor is hidden)
- macos_activate_app (plus a headless no-op) so the launcher can focus itself

* widen the host->splash surface: splash setters, view/glassbutton script calls

everything the host needs to poke into a running mini-app's script:

- Splash: call_script_fn, set_script_global, set_allow_net, and a cached body id
  so host->script calls don't rescan for the body every time
- View.set_visible and GlassButton set_text/text are callable from script now
- makepad_test learned right-click (secondary button) so the headless tests can
  exercise long-press / context menus

* fix small-size glass lens + sdf box degeneration, warn on missing glyphs

visual correctness fixes we kept tripping over:

- cap the gauss lens band at 35% of the surface's smaller side so tiny discs
  degrade gracefully instead of smearing
- clamp the Sdf2d.box (and box_x/box_y/box_all) radius so an oversized radius
  saturates at a circle instead of collapsing into a rotated diamond
- log once per codepoint when no loaded font can render it (was silently
  drawing .notdef boxes)

* guard stale rect areas in clipped_rect/abs_to_rel/set_rect against out-of-bounds panics

* add switch_finger_capture to hand a live finger capture between widgets mid-drag

* add promote_finger_capture_over: hand a child-grabbed finger up to a co-capturing container

* splash: add validate_splash_body, a dry-run eval for externally-sourced scripts

evaluates a body in a throwaway isolate with the exact prefix/limits the
Splash widget uses and returns the captured script errors instead of logging
them. lets hosts that install source from outside (downloads, AI generation,
user input) reject bad scripts with real errors to show or feed back, where
the widget's own eval silently keeps the old view.

* strip mod.res from splash isolates; document validate_splash_body caveats

the res module's handles reach both the filesystem (abs_path loads) and the
network (web_url / http resources) without going through the gated net
runtime, so a 'no-net' isolate could still fetch and exfiltrate. found by an
adversarial review of AI-generated app installs, but it applies to any
untrusted splash source.

also note on validate_splash_body that the instruction limit bounds compute,
not heap growth, and that top-level timers live until isolate reclamation --
same exposure as actually installing the source, so validation adds nothing
new.

* splash: jailed per-app file storage (mod.fs inside isolates)

mini-apps get an OS-style private data directory, like an android app's
internal storage or an iOS container: the app sees a filesystem rooted at
"/", and that root IS its host-assigned sandbox directory
(Splash::set_sandbox_dir / SplashRef forwarder). registered as mod.fs in
isolates -- deliberately shadowing the stripped real fs module, so inside
an app "the filesystem" simply is the jail:

  fs.read fs.write fs.append fs.exists fs.remove fs.mkdir fs.list

containment lives entirely in the host layer:
- lexical path resolution against the root; `..` above the root, NUL, deep
  or overlong paths are errors before any I/O
- the per-VM root is rust state keyed by the isolate's heap -- script code
  can neither read nor retarget it
- symlink defense in depth: nothing here can create links, and every
  existing component under the root is verified non-symlink before use
- quotas: 1MB/file, 16MB/jail, 256 entries
- no root assigned (previews) -> every call errors cleanly

validate_splash_body gives dry runs a throwaway jail (temp dir, removed
after) so top-level fs.read boot loads validate instead of erroring. roots
are dropped with their isolates in the gc.

unit tests cover the containment: traversal/absolute/backslash escapes,
depth/name caps, and the symlink block.

* splash: put the jailed fs module in scope as a bare name

app scripts say fs.read("/x"), but the eval prefix only used the widgets
prelude, so bare fs resolved to a not-found error value and every storage
call failed silently. bind it in the prefix (let fs = mod.fs) for both the
plain and net variants; a script reassigning fs only shadows its own name,
the jail stays host-side.

* splash storage: quota + boundary hardening from adversarial review

three confirmed jail findings:
- mkdir bypassed every quota (target + create_dir_all, no jail_usage check)
  -> unbounded inode/dir-metadata exhaustion on the shared host volume.
  now charges new dirs against MAX_ENTRIES via missing_entries(); write's
  entry check does the same so a deep write can't overshoot the cap either.
- write/append/mkdir lacked remove's root guard: fs.write("/", data)
  resolved real == root and reached create_dir_all(root.parent()) -- one
  dir above the jail (the shared app_data/). now rejected like remove does.
- validate_splash_body's scratch jail used a predictable temp name created
  with create_dir_all (would follow a planted symlink out of temp). now an
  exclusive create_dir on a per-process+vm name (EEXIST-safe against a
  planted entry), reclaimed via gc before the dir is removed so a top-level
  timer can't resurrect it.

unit tests added for missing_entries; the containment tests still pass.

* splash: empty set_text tears down the isolate instead of no-oping

set_text("") was a silent no-op (eval_body early-returns on an empty body),
so a reused Splash that goes back to empty -- the widget-gallery live preview
on Back -- left its old isolate running its timers (and holding a storage-jail
binding) behind a blank view. now an empty body reclaims the isolate: the
isolate-minted view is replaced with a fresh empty one built in the main vm
BEFORE the isolate heap is freed, then the isolate is gc'd (stopping its
timers, dropping its jail root); vm_id resets to MAIN so a later non-empty
set_text allocs a fresh isolate as before. the existing host_launcher
teardown call sites (widget picker back()/reset()) become correct unchanged.

* overlay: composite glass in draw order, not creation order

every gauss/glass surface opens its own draw list and registers it in the
window's single Overlay via store_sub_list, which hands out the first free
slot and keeps it for the life of the process. renderers walk that table in
index order, so the paint order of all glass in an app was the order the
surfaces were first *created* — permanently, with freed slots reused by
whatever registered next. draw order never came into it, so a widget rebuilt
after a layout change, or a panel opened later, could land on top of anything
drawn after it. the only workarounds available to apps were "don't draw the
thing that's winning", which looks like a bug.

the hook for fixing it was already there and unused: CxDrawList's
draw_item_reorder, honoured by every backend (metal, d3d11, opengl, vulkan,
web_gl, headless raster). so stamp each overlay sub-list with the position it
was begun in this frame (Cx2d::overlay_seq, reset in Overlay::begin) and have
Overlay::end stable-sort the table by that stamp.

this also gets parent-then-child right without special cases, which matters
because glass.GlassButton / glass.GlassSegmented call begin_overlay_reuse
unconditionally instead of checking is_drawing_overlay(), so they hold their
own slots rather than riding their parent's.

* glass.GlassSegmented: size segments to their labels, add set_selected

three things, all of them things that looked broken to a user:

- segments were width/count, so "Max" got the same room as "Default": the long
  word crowded, the short one floated. each segment is now measured (DrawText
  layout size_in_lpxs) and gets its text plus padding, with leftover width
  shared equally so every label keeps the same margin. if the labels don't fit,
  the padding shrinks (never the text) to a floor. the pill's x/width are
  computed in rust and passed as uniforms since they can't come from a segment
  count any more, and hit-testing is a boundary lookup rather than a division.

- `selected` was public but the pill is drawn from a private sel_pos that only
  followed it via the click animation, so restoring a saved value from code
  left the control showing one segment while reporting another — and a click on
  the segment it really held was then ignored as "already selected". that reads
  as the control eating your clicks. set_selected keeps both in step.

- the travel easing was 0.30, which arrived before the eye could follow it.
  0.16.

* text_input: re-layout when max_lines changes

the laidout text was cached on width alone, so flipping draw_text.max_lines
at runtime (collapsing a composer to one line) kept the old multi-row layout
and the field never shrank. make max_lines part of the cache key.

* text_input: add set_max_lines instead of making callers script it

applying script to a TextInput re-applies its #[live] fields, and text is
one of them, so toggling max_lines through script_apply_eval! silently
wiped whatever the user had typed. give it a typed setter.

* text_input: don't drop the layout in set_max_lines

clearing laidout_text there leaves the field with no layout for the rest
of the event batch, so every cursor op in that window bails out with
"can't move cursor because layout was invalidated by an earlier event".
since set_max_lines gets called from focus/blur handling, that window is
exactly when you're clicking into the field — so the click placed no
caret at all. max_lines is already part of the layout cache key, so the
next draw re-lays out on its own.

* text_input: add scroll_to_top

for a field that folds to a fixed height when it loses focus: the scroll
offset survives the blur, so a draft last edited near its end folds
showing whichever line the caret had scrolled to rather than its first.
leaves laidout_text alone — scrolling doesn't change the layout, and
dropping it would break every cursor op for the rest of the event batch,
same trap as set_max_lines.

* text_input: add set_height

for a composer that folds to one line when it loses focus. pinning the
height is the safe way to fold — unlike clamping max_lines it leaves the
laid-out text alone, and the laid-out text is what maps a click to a
caret position. fold by re-layout and the press that re-focuses the
field resolves against the folded layout while the expanded one is on
screen, so the caret and any drag-selection land on the wrong text.

* text_input: add take_key_focus, which actually shows the caret

the caret draws as (1.0 - blink) * focus, and both come from animators
that only move when the widget is dealt a Hit::KeyFocus. setting key
focus on a field that ALREADY holds it dispatches no hit — so a field
that was focused, then hidden (hiding doesn't clear Cx's key focus) and
shown again comes back typable but with no caret and no selection
highlight, animators still parked where the last focus-lost left them.

plays focus.on unconditionally rather than only when focus changed:
repairing the case where it did NOT change is the entire point.

* splash: name scripts in errors, and stop using line as an identity slot

a runtime error from a Splash app logged `:1804943384:12 - widget has
no uid`: empty file, and a "line" that is really a pointer address.
the format is {file}:{line}:{col}, and both fields were casualties of
the same hack — ScriptMod.line carried self_id so the body could be
found again (m.line == self_id && m.file.is_empty()), while ip_to_loc
adds that same field to the script's real line when reporting. so every
location came out as real_line + a pointer, and nothing said WHICH app.

identity moves to module_path, which nothing else reads for these
bodies, freeing line to be a line. file gets a real name via a new
set_debug_name the host calls with the mini-app's id.

the validator's ScriptMod gets the same treatment — its errors are
shown to the user AND fed back to the agent as repair input, so a
location offset by a vm id was actively misleading there.

* splash: document the constant offset in reported script lines

the host prefix is two lines, so a reported line is two ahead of the
app's own file. it can't be zero — a zero-line prefix would share line 1
with the app's first line, and that line is always the // name: header,
which would comment the prefix out.

* script: stop silently losing widgets emitted from branches and loops

Splash mini-apps kept rendering nothing from on_render closures with zero
errors logged. Bisected live and in pure-VM probes, this was a pile of
separate bugs in the same corner:

- an if/else whose branch emits a widget lost the taken true branch: the
  statement's POP_TO_ME got fused onto the else tail, which the IF_ELSE
  jump skips. Generalized last_short_circuit_target into last_jump_target
  and record every branch join (if/elif/else, match, try/err), so the
  commit lands standalone AT the join and every path runs it. Same disease
  as the short-circuit-argument bug, new jump sites.
- elif never patched its arm's IF_ELSE jump (relative 0), so a taken arm
  spun the interpreter until the instruction limit killed the whole entry.
  elif now desugars into else { if ... } via IfElseExpr, which also gives
  its arms the join treatment.
- for x in <non-iterable> silently skipped the body where the equivalent
  while errored; now raises "for loop source is not iterable" (nil and
  empty sources stay silent). for k v in <number> passed key/index swapped
  and lost k after the first iteration (the advance only rebound v).
- a line-leading { after a value-ending line glued onto the previous
  expression as a proto instantiation; it now starts a new statement,
  same divert rule the ( and [ newline fix added.
- int literals were second-class numbers: U40/I32/F32/F16/U32 didn't
  collapse to the number bucket in to_redux, so 6 .is_number() missed
  method dispatch entirely and an int arg against a float default failed
  with "expected number, got number". They're all just numbers now.

Regression tests in tests/on_render_emission.rs cover each shape, incl.
the exact calendar/weather patterns that were blank in the launcher.

* splash: keep an on_render closure's final widget, and say when a render fails

Two host-side halves of the emission-loss story:

- the parser turns a closure's last statement into its return value, so a
  render closure that ENDS with a widget literal built it and then threw it
  away (this is why wrapping a whole render in one extra View{} produced
  nothing). script_result now pushes a returned widget object into me as
  the last child; non-widget returns still get skipped downstream.
- a render closure that errors mid-run used to have its output discarded
  with no diagnostics at all, which is what made this whole bug family
  cost days to find. Now it logs the error before dropping the result.

* script: auto-close still-open fn and let states at end of source

Both parse drivers dropped EndFnExpr/EndFnBlock/EmitLetDyn through their
auto-close catch-all when the source ended with them still open. A module
whose FINAL statement was let c = <lambda> got a body whose jump-over
stayed 0 — FN_BODY_DYN re-ran, found its me already popped, logged
"me stack is empty" and fell straight INTO the body, running it inline
at definition time and ending the module eval early — and the let itself
never emitted, so the binding silently didn't exist (same for
let c = <call>). Now those states close the way the live handlers do:
return + jump patch for the body, LET_DYN/LET_TYPED for the binding, and
the let's own EndStmt no longer marks a statement value (LET consumed
it; the final RETURN would pop an empty stack).

Fun consequence: the old test idiom of reading a result via
  let out = r
  out
only ever worked BECAUSE the trailing let was dropped — RETURN popped
the naked value off the stack. With the let actually binding, scripts
must end on a call (echo(r)); the emission tests are updated to do that.

Regression tests in tests/auto_close_eof.rs, including the exact
deferred-boot-timer closure shape the launcher apps use (which was
already fine — it just LOOKED guilty, see the splash commit).

* splash: probe optional script hooks without spamming the error log

call_script_fn checks whether the fn exists and bails quietly — but it
probed with a trapping scope_value, which had already queued a NotFound
by the time the miss was handled. Every host broadcast of an optional
hook (on_app_resize, on_widget_resize) against a script that doesn't
define it logged
  variable 00001e93e419c77c not found in scope
— maximally misleading: the hex is just id!(on_app_resize) (Rust-side
ids aren't in the reverse-lookup table so they print raw), and the
line:col is the stale ip from the end of that script's eval, which
pointed at whatever closure happened to be compiled last. In the
launcher that was the boot-timer line of every generated app, sending
the investigation down a deferred-closure rabbit hole the pure-VM tests
then cleared. Probe with NoTrap.

* script derive: don't name the eval values vec 'v'

script_apply_eval!'s generated values block bound 'let mut v' and then
spliced #(expr) interpolations in verbatim — so a caller interpolating a
variable that happened to be called v got the macro's own half-built Vec
(borrow errors if you were lucky, the wrong value if not). Obscure name
instead.

* headless: don't compile the Apple video path

Upstream's zero-copy video work put CoreVideo/Metal code in
gpu_texture.rs (plus two consumers) behind cfg(target_os = "macos")
alone. Under cfg(headless) the apple backend isn't built at all, so
every one of those symbols — ObjcId, msg_send!, CVPixelBufferRef,
CVMetalTextureCache* — is missing and makepad-platform fails to
compile with 94 errors. That takes the headless harness down with it,
which is what host_launcher's UI tests run on.

Gate the Apple blocks on not(headless) too. Nothing is lost: headless
has no Metal device to import a CVPixelBuffer into, so the whole path
is inapplicable there.

Not caused by the rebase — pristine dev has it: its headless
CxOsTexture is an empty struct while gpu_texture.rs reads .os.texture.
2026-08-12 01:55:46 +02:00
Admin
ff604e53a3 splash: string-compare early-out in deep_eq, thread stack prealloc, bench additions
- deep_eq returns false immediately when both sides are string-like and
  the bit-compare failed (strings are interned, so bit-equality IS string
  equality) — trims the type-check chain on the hot failed-compare path
  of string-tag dispatch (a.kind == "...")
- pre-reserve thread stacks (value stack, scopes, calls, mes, loops)
- splash_bench: string_cmp workload, arith_hostmode (instruction limit +
  run budget installed, the game-host configuration; measured: the two
  per-instruction counters cost ~nothing, branch prediction hides them),
  BENCH_ONLY profiler filter

Tried and rejected: batching consecutive value-pushes in run_core into
one dispatch iteration — the scan overhead outweighed the per-value
limit/budget checks it skipped (they were already free); reverted.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 15:59:05 +02:00
Admin
0cd882f793 splash optimisation work
Interpreter perf pass on platform/script, semantics-preserving:

- ValueMap hybrid small-map: object maps linear-scan a vec below 16
  entries and spill to a HashMap above (scopes/call-args/small objects
  never hash)
- compound scope assigns (+=, -=, ...) locate their slot with ONE
  proto-chain walk instead of a read walk plus a write walk
- for-loop iterations clear and reuse the iteration scope in place when
  nothing captured it (closure capture -> REFFED -> fresh scope); loop
  scopes no longer copy the parent scope vec
- 'for i in a..b' captures end/step at loop entry when the range object
  is un-REFFED; stored (mutable) ranges keep live per-iteration lookups
- trap error-queue + trap.on polled via one Cell<u8> bitfield per
  instruction instead of a RefCell borrow + Option<enum> Cell read
- ADD checks the number fast path before the string check

Measured (splash_bench, medians of 3 A/B runs): array_iter -27%,
method_call -21%, array_index -21%, for-range loops -19%, object_churn
and field r/w -8..9%, fib -7%, sandbox-style composite tick -6%.

Adds platform/script/test/src/bin/splash_bench.rs (run with
cargo run -p makepad-script-test --bin splash_bench --release,
BENCH_ONLY=<name> loops one workload for profilers) and parity asserts
for the touched edges: mid-loop range end/step mutation stays live on
stored ranges, closures pin their iteration's scope, selective capture
does not leak across reused scopes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-10 13:07:40 +02:00
Jason Yau
947f815181
Video playback fixes and improvements (#1155)
* android oes video zero-copy and gles shader fixes

* regenerate windows-rs by windows-strip

* fix windows video freezes with MF on an MTA worker

* regenerate windows-rs by windows-strip

* MTA MF video, COM notify, YUV texture reuse

* add local video file playback support to video-player example

* NV12 Metal present, seek warm-up gate, YUV full range

* fix Linux GStreamer video: A/V mute, HLS prepare, async teardown

* no-op SelectVideoTrack/SelectAudioTrack on non-Linux backends

* Linux ALSA/Pulse: bigger buffers, fix resample setup, report device rate

* fix some warnings

* Move XInput/DirectInput device discovery off the UI thread

* fix GLSL unpack4u8 for GLES 3.0 (#version 300 es)

* Linux GStreamer: DMA-Buf NV12 OES zero-copy, optional GLMemory path

* Linux MediaPlugin: DMA-Buf NV12 and GLMemory zero-copy present APIs

* Add Windows SourceReader DXGI NV12 zero-copy video path

* fix build error

* fix GLES: add highp precision for sampler2DArray in Linux shaders

* playback speed support for android

---------

Co-authored-by: jasonqiu <jasonqiuchen@outlook.com>
Co-authored-by: jasonqiu <jasonqiu@futunn.com>
2026-08-10 12:29:33 +02:00
Sabin Regmi
105158e08c
Shader: update unpacking function for better precision in GLSL shaders (#1162) 2026-08-05 19:34:39 +02:00
Admin
362c96d63b Fireworks: parametric GPU sparks with a splash-side style seam (gated off)
CPU launches shells; the GPU animates every spark from a closed form. Once
a shell bursts nothing in the world can influence a spark — it flies from a
known point, at a known time, along a fixed arc — so position is a function
of (spark index, seed, age) and there is no state to step and nothing to
upload per frame. Twelve shells of 320 sparks is 3,840 particles for TWELVE
instances of CPU work, against 3,840 simulation updates and a 3,840-instance
upload for the stepped path.

**The style seam is the point.** Rust owns structure — trajectory, lifetime,
billboarding — and exposes four hooks a splash script overrides by
inheritance: `spark_motion` (swirl, fizzle, drift), `spark_size`,
`spark_color`, and `spark_pixel` (the sprite program). Arcade's own styling
lives in its `script_mod!`, not in Rust: a three-stage temperature burn,
hotter on the fast outer shell than the slow core, with sparse glitter
strobing. A generated game can restyle the sky without being able to reach
the simulation.

Two real bugs fixed on the way:

- **Vec3f instance fields silently misalign.** A `Vec3f` is tightly packed in
  Rust but a `vec3` obeys 16-byte alignment in the shader ABI, so the burst
  origin read back as zero and every shell rendered at the world origin, on
  the ground. The other shaders here get away with `Vec3f` because theirs are
  `uniform`, not instance. Everything is packed into `vec4`s now, which is
  the shape the hardware wants anyway and removes the class of bug.
- **Eight bare `panic!()`s in the shader backend** now name the backend, the
  stage and the IO type, and the commonest case — reading a geometry
  attribute from `pixel:` — gets a message saying so and showing the varying
  that fixes it. Previously a shader that tripped it aborted with no text at
  all, which is not a barrier, it is a wall in the dark.

**Gated off by default.** The spark SIZE instance is not reaching the shader:
scaling it 25x changes nothing on screen, so every spark draws as a
screen-filling blob and the sky whites out. That is the same class as the
Vec3f bug above and I have not found the second instance of it. The launcher,
the trajectory, the placement annulus and the whole hook surface are finished
and tested; this is one plumbing bug from working. ARCADE_FIREWORKS=1 turns
it on to work on it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 16:59:45 +02:00
Admin
0c8b5357cf Shader compiler: bound emitted loops and inlining — the AI-shader security barrier
The compiler is where hostile shaders get stopped, because this engine lowers
the script shader language to Metal/HLSL/GLSL/WGSL rather than passing source
to a driver. Two genuinely exploitable holes found and closed:

- Loop bounds were arbitrary EXPRESSION STRINGS written straight into the
  emitted shader, so `for i in 0..some_uniform` compiled to an unbounded GPU
  loop and `loop{}` emitted a bare `while(true)`. A hang triggers a driver
  device reset that kills the app — ugly anywhere, worst in a headset. Rather
  than reject (which would break legitimate code), the bound is now EMITTED: a
  provably-small integer literal compiles unchanged, anything else gets a hard
  65536-iteration cap. literal_bound() is deliberately conservative — a
  uniform, arithmetic, a call, hex or a negative all count as unprovable
- compile_fn INLINES at every call site, so a branching call graph expands
  exponentially with depth (recur_block stops self-recursion, but not f1
  calling f2 twice calling f3 twice). MAX_EMITTED_BYTES (1 MB) bounds what was
  an unbounded compile-time DoS

Recursion was already safe (recur_block errors); nothing added there.
Honest remaining gap: the cap bounds ITERATIONS, not cost per iteration — a
shader doing 65536 heavy texture samples is legal and slow. Bounding real GPU
time needs a cost model or driver watchdog; neither exists here and this does
not claim otherwise.

Verified empirically, not just by compiling: all 24 loop{} constructs in the
built-in shaders are guarded (48 emitted lines, 4 unique guard names per
shader confirming no shadowing), zero for( loops exist in any built-in, and
the capture shows rendering intact — including text, which is exactly where
those guarded loops live.

MEASURED FIRST, then declined to build: 34 shaders compile at Arcade boot in
~15 ms total on Metal (first 4.47 ms cold, rest 0.10-0.74 ms). A persistent
shader cache is NOT worth its invalidation-and-staleness surface to save 15 ms
of one-time boot, so it wasn't built. Vulkan/GLES on Quest may differ — that
needs on-device measurement before anyone builds speculatively. Benchmark
retained behind MAKEPAD_SHADER_BENCH (it was logging every boot);
MAKEPAD_SHADER_DUMP=<dir> writes generated source, which is what an AI has to
debug and was otherwise invisible.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 09:19:01 +02:00
Admin
d41b6e43ec Arcade M0r: parity-neutral sim internals cleanup
- Entity lookup O(log n): shared entity_index_sorted helper, single
  push_entity spawn path with ascending-id assert + per-tick sorted
  debug_assert — the sorted-Vec invariant is now enforced, not hoped
- WorldSnapshot captures next_id: failed-eval rollback can no longer mint
  colliding ids under surviving entities (review defect fixed)
- game.log buffered (1s/16KiB/eval-boundary flush), agent RPC poll gated
  on one .agent dir-mtime stat — no per-tick file I/O left
- Camera rig authoritative on GameWorld (orbit/chase state); widget keeps
  only device-input accumulation; mailbox drain order unchanged
- Per-tick cumulative script budget: with_instruction_limit reports
  consumption (incl. trap-wipe subtlety), gamemaker tick holds ONE 500k
  pool across on_tick + timers + touch events
- Tape probe verified BYTE-IDENTICAL vs pre-cleanup reference

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 23:34:45 +02:00
Admin
0ebe270c20 Script VM GC campaign: isolate GC, host-transient release, cross-VM resource handle fix
- Escape barrier: every checked store funnel tags stored objects REFFED
  (stored => REFFED structural), ret==args guards at native completion,
  fn-arg bind-time barrier (closure-captured scopes retained args untagged
  -> latent use-after-free under eager release)
- vm.release_transient() for host per-call objects; *_unchecked pushes are
  the releasable-container path; call_with_scope native branch no longer
  leaks one scope object per Rust->native vm.call()
- pump_widget_async: dead-isolate reclaim every pump + needs_gc-gated
  round-robin mark/sweep — isolate heaps were never collected before
- res.rs: resource path cache was Cx-global and cached handle VALUES across
  VMs, so isolate heaps held main-heap handle indices (exposed by the first
  isolate mark pass ever to run). Now: data shared globally, handles minted
  per-heap, cache keyed (heap_key, path), per-heap detach on GC
- vm.gc() no longer shrink_to_fits every run; explicit gc_and_compact()
- script-test: GC campaign suite (flat-heap tick loops, retained-input
  survival, escaped-scope capture, isolate churn collection)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 22:27:27 +02:00
Admin
292a47d7e6 Shader: register _mp_unpack ids in the GLSL/WGSL luts — Linux vertex compile fix
The wrapper names printed as hex LiveId hashes on non-Metal backends
(identifiers starting with digits — GLSL compile failure at startup on
Linux/GLES). Metal had the lut entries; Glsl/Wgsl now do too.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 19:53:19 +02:00
Admin
882cf6e440 Shader: unpack2f16/unpack4u8 on all backends — GLSL ES3 built-ins, WGSL mapping, headless Rust impls
GLSL emits _mp_ wrappers over unpackHalf2x16/unpackUnorm4x8 (WebGL2
baseline has them native); WGSL names mapped for the future backend;
the headless runtime gets bit-exact CPU decodes. The packed map vertex
format is now portable to the wasm target.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:44 +02:00
Admin
1c563efc20 Shader: unpack2f16/unpack4u8 registered in shader scope + CPU impls (bit-exact)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:43 +02:00
Admin
196539995c Shader compiler: unpack2f16/unpack4u8 intrinsics (Metal) — packed map vertex format foundation
One f32 geometry slot carries two f16s or four unorm8s; the vertex fn
unpacks via _mp_ helpers in the MSL preamble. Type checker: float ->
vec2f/vec4f. GLSL/WGSL mappings + headless Rust-backend impl to follow
with the packed layout itself.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:43 +02:00
Admin
0d8c4773d8 Glass blur: 1/8-res floor for deep mips, bicubic sampling, headless JIT fixes
Deep gauss pyramid levels (3-5) are re-homed to 1/8 resolution via
progressive half-texel tent upsamples so no on-screen sample comes from
a texture coarser than 8 device px/texel; half-texel offsets keep the
exposed sigma ladder at ratio-2 per level (full-texel tents inflate deep
levels ~35% and open a visible blur band at the raw->re-homed boundary).
Per-pixel sampling replaces the 13-tap cross cascade with 4-tap bicubic
B-spline reconstruction: C2-smooth (kills the bilinear texel lattice)
and cheaper (glass 26->8 taps, chromatic variant 78->24).

Headless shader JIT fixes found while verifying: RenderCx always emits
vtx_pos (vertex fns that return the position have no VertexPosition io),
vec*vec MulAssign/DivAssign impls, and discard returns a value.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 00:10:52 +02:00
Admin
691021129b map: rebuild MapView to openstreetmap-carto quality
- semantic fill paint order (land/sites/water/buildings/street areas);
  raw MVT layer order was painting land over 6,880 buildings per tile
- tile-local f64 coordinates + per-tile offsets; fixes f32 web-mercator
  quantization (0.25px vertex, 2px shader ULP at z17)
- casing/center stroke buffers split; fills -> all casings -> all centers
  across tiles (carto roads-casing/roads-fill order)
- per-view-zoom-bucket restyling with carto width stops; stale buckets
  stay drawable during rebuild; screen-space AA/tolerance
- carto palette, building outlines z15+, bridge decks, tram/rail from
  streets layer above road centers, thin-path slow width growth
- house numbers (addresses) and shop names (pois) as point labels z16+
- MVT-correct absolute ring winding for multipolygon classification
- visible_tile_keys divides viewport by overzoom (was 64x over-request)
- text: prepare_single_line_run scales glyph size+raster with font_scale;
  draw_path_glyphs pins ambient font_scale (letter-spaced labels bug)
- script derive: cast numeric field defaults instead of .into() fallback

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 15:34:44 +02:00
Kevin Boos
d1efac1892 New unified kinetic scrolling. Vastly improve draw shaprness on low-res screens. Fix rendering and text perf issues on CPU+GPU (#1127)
* Fix rendering, gradient, sampling, etc issues on older GPUs

* Fix bug in `box_y` sdf function, which caused gradients to be split
  into two bands incorrectly. Mostly a problem on lower-res screens.
* Use per-texture filtering instead of GL sampler objects on Linux,
  especially for Mesa drivers that ignore min filter samplers.

This should help prevent blocky/pixellated things like emoji/avatars

* Improve rendering sharpness on low-DPI screens (icons, emoji, images)

On 1.0-DPI screens, emoji/SVG-icons/avatars were minified without
adequate sampling and SVG AA was sub-pixel, producing blocky/aliased
output. This reworks each path and adds optional full-window SSAA.

SVG icons (device-aware AA + round caps):
- draw_svg.rs/draw_vector.rs/render.rs: size the fill & stroke AA
  fringe and the curve-flatten tolerance in DEVICE pixels (≈constant
  regardless of icon size), so edges resolve via the analytic
  d/fwidth coverage and curves stay smooth at any scale; re-tessellate
  on scale change.
- triangulate.rs: thread the flatten tolerance through path fill/stroke.
- tessellate.rs: emit round caps as a solid disc (u=0.5) instead of a
  radial fade that collapsed to a square at small sizes.
- widgets/icon.rs: don't clip the Icon to its Fit bounds, so round
  caps that extend past the box render fully instead of being sheared.

Emoji:
- glyph_raster_image.rs/rasterizer.rs: rasterize color emoji near the
  on-screen size with an alpha-weighted box downscale (geometric-mean
  scale factor) instead of the font's native PNG strike.

Images / avatars (mipmaps):
- image_cache.rs/texture.rs/draw_list.rs/lib.rs: optionally emit a CPU
  mipmap chain (VecMipBGRAu8_32) for non-animated images so minified
  avatars/thumbnails sample cleanly. Env-gated MAKEPAD_IMAGE_MIPMAPS;
  default on for GL on Linux.
- metal.rs: real per-level mip upload. d3d11.rs/vulkan.rs/web_gl.rs:
  safe single-level fallback (no crash; real mips TODO).

Full-window supersampling (optional):
- window.rs: render the whole UI into an offscreen target at
  MAKEPAD_SUPERSAMPLE× device resolution and downscale-resolve into the
  window. Default 2×, env-tunable, 1× disables. Modeled on the existing
  GaussStack render-to-texture path.

* don't unconditionally enable supersampling SSAA of 2x by default

it's too expensive and too slow for most older devices

* cleanup, reduce comment verbosity

* improve SVG anti aliasing

* Windows: fix laggy/juddery scroll performance

- Pace the render loop to the display refresh using a DXGI frame-latency
  waitable object and present with vsync, replacing the free-spinning,
  uncapped Poll loop that caused uneven scroll cadence.
- Coalesce consecutive WM_MOUSEMOVE messages and paint once per loop pass
  to stop the judder when moving the mouse during fling deceleration.
- Cache get_dpi_factor() and the WM_NCHITTEST WindowDragQuery result to
  avoid per-mouse-move GetDeviceCaps syscalls and widget-tree hit-tests.
- Throttle XInput/DirectInput polling of empty/disconnected controller
  slots, which was stalling the UI thread.
- Rework the momentum fling to a native exponential model with a
  frame-interval EMA, and stop the tail auto-scroll from fighting an
  active fling/drag.
- D3D11: update the glyph atlas and image textures in place via
  UpdateSubresource instead of recreating them on every change, and
  spread D3D11 shader-object creation across frames.
- Slug atlas: only force a full re-layout on a width change; append rows
  on height growth.

* Windows: correctness fixes from review (off the scroll hot path)

- d3d11: close the DXGI frame-latency waitable HANDLE in Drop (it was
  leaked once per main-window lifecycle and the field comment was wrong);
  keep popup swap chains at frame-latency 1; track the waitable-swapchain
  flag for ResizeBuffers instead of inferring it from the handle; present
  without the vsync interval during a live resize.
- windows.rs: poll game input on the idle signal tick so a gamepad button
  can be serviced while the app is otherwise idle.
- win32_window / window: invalidate the WM_NCHITTEST / WindowDragQuery
  caches on window move and on a caption relayout, with a generation
  counter guarding against a reentrant invalidation being clobbered.
- windows_game_input: detect a controller already plugged in at launch via
  a one-shot full scan on the first poll, probe slot 0 (Player 1) first,
  and offset the DirectInput enumeration so it never stacks with the
  XInput probe.
- comment/doc corrections.

* Image cache: accept any Arc<D: AsRef<[u8]> + ?Sized> for async image data

The load_image_from_data_async family required Arc<Vec<u8>>, forcing callers
that already hold the bytes as Arc<[u8]> (e.g. a content-addressed media cache)
to copy the whole buffer via .to_vec() just to satisfy the type. Generalize the
data parameter to Arc<D> where D: AsRef<[u8]> + ?Sized, so those callers can pass
their existing Arc by refcount-clone with no byte copy. The decode path only ever
borrowed the bytes (&[u8]), so this is purely a signature relaxation; existing
Arc<Vec<u8>> callers are unaffected (D = Vec<u8>).

* Linux: GL glyph-atlas in-place texture update + X11/Wayland mouse-move coalescing

* Scroll: unified fling model + native trackpad momentum deceleration

Share one kinetic-scroll model between PortalList and ScrollBar (and thus
ScrollXView/ScrollYView/ScrollXYView) via a new widgets/src/scroll_motion.rs:

- Touch-drag flicks use an iOS-style exponential self-decay, frame-rate
  independent via a per-frame integrator with dt smoothing.
- Trackpad scrolling applies the OS momentum directly while fast (responsive,
  full native speed), then hands off to a gentler self-decaying tail once it
  slows past a threshold, so the deceleration is longer and smoother than the
  OS's short, choppy tail. Handoff is seeded at the current speed for a
  continuous transition; the seed is clamped against degenerate event timing.
- Add ScrollPhase to scroll events, mapped from NSEventPhase/momentumPhase on
  macOS and wl_pointer AxisStop on Wayland; None elsewhere (wheels/X11/Windows
  behave as before). MAKEPAD_RAW_TRACKPAD_MOMENTUM=1 bypasses the smoothed tail.
- A press catches an in-progress fling (stops the scroll, consumes the press so
  it doesn't also activate a child), matching iOS/Android/macOS.

* Shader codegen: prefix Metal/WGSL locals to avoid reserved-word collisions

The Metal/WGSL backends emitted user-declared shader locals verbatim, so a
local named after a reserved type keyword (e.g. `half`) produced invalid
shader source and failed to compile at runtime. Prefix them with `l_` like the
HLSL/GLSL backends already do.

* TextFlow: don't panic on unbalanced HTML close tags

end_code/end_quote unwrapped the area stack, so a stray `</pre>` or
`</blockquote>` in untrusted content (e.g. a chat message) panicked. Return
early instead, and drop a vestigial per-list-item area-stack push that leaked
an entry and could hand a stray close tag the wrong block's area.

* Scroll: expose fling decel, handoff threshold, & tail-decel as `#[live]` fields

This allows app devs to override the scroll feel per-widget in the DSL,
or globally by overriding the base widget's defaults — verified that a DSL
override takes effect.

* Windows: fix frame pacing, paste crash, and wheel input backlog

- Wait on the frame-latency waitable right before each window's vsync
  present instead of on every Paint, so input no longer stalls behind
  waits that have no matching present. Drain leftover credits after a
  live resize.
- Pasting when the clipboard has no text no longer panics.
- The poll loop now handles up to 32 messages (2 ms) per frame and
  merges consecutive mouse-wheel messages, so fast wheels can't build a
  backlog that keeps scrolling after the gesture ends. Sleep 1 ms when a
  frame presents nothing so animation polling doesn't spin a core.

* Image: don't build unused mip chains; fix stale images in recycled widgets

- Only build the CPU mip chain when a backend actually uploads it
  (Metal, behind its env var), and build it on the decode thread instead
  of the UI thread. Linux GL still gets its mipmaps via glGenerateMipmap
  and now retains less CPU memory per image.
- Recycled Image widgets no longer show the previous item's image or
  apply an old decode result. Placeholder textures set via set_texture
  (like blurhashes) stay visible while the real image decodes, and a
  failed load clears the widget instead of leaving old content up.

* Widgets: avoid needless caption redraws; cheaper PortalList height tracking

- Label::set_text does nothing when the text hasn't changed, and the
  window caption title is only synced when it actually changes, so mouse
  moves and animation ticks no longer redraw the whole window every
  event. The caption centering padding requests its own redraw now.
- PortalList records item heights only when new or changed, and only
  re-applies the default height after it drifts by half a pixel, so big
  lists don't walk every unmeasured item on every scroll frame.

* Text: cache layouts of long texts; stop cloning glyph outlines every frame

- The layout cache now accepts texts of any length (long messages and
  code blocks used to re-layout on every scroll frame). It is a real LRU
  with a byte budget on top of the entry cap, and texts drawn in the
  current frame are never evicted, so one heavy frame can't thrash the
  cache into a permanent miss cycle. The shaper cache is LRU now too.
- Glyph outlines are shared via Rc, so drawing a cached glyph no longer
  copies its command list, and outline complexity is computed once when
  the outline is built instead of every frame.

* Html: fix stale links/spans in recycled widgets and <details> renumbering

- set_text only rebuilds when the content actually changed, so a
  recycled link can't open the previous message's URL, and re-setting
  identical content keeps the user's <details> open/closed state.
- Custom widgets and <details> are keyed by their node index instead of
  a visit-order counter, so toggling a collapsed <details> can't
  renumber the widgets after it and rebind them to the wrong nodes.
  item_with_scope also recreates its widget when the template changes.

* Linux: fixed-distance wheel scrolling; Wayland frame-callback pacing

- Wheel scrolling moves a fixed 60 px per detent on X11 and Wayland
  instead of a timing-based guess that flipped between 12 px and 240 px
  depending on how events batched. Wayland reads real detent counts via
  AxisValue120 (wl_seat v9, with AxisDiscrete as the older fallback) and
  maps keymaps MAP_PRIVATE as v7+ requires. Touchpads are unchanged.
- Wayland frames are paced with wl_surface frame callbacks and swap
  interval 0, so redrawing a hidden or minimized window can no longer
  hang the whole app inside eglSwapBuffers (compositors withhold frame
  callbacks for hidden windows). Windows with a callback in flight skip
  presenting and stay dirty; X11 keeps vsync exactly as before.

* Text: bigger layout cache budget, reclaimed at the end of each frame

A maximal ~60 KB message lays out to roughly 4 MB of glyphs, so the 4 MB
budget couldn't hold even one alongside a normal screen. Raise it to
16 MB, and run eviction at the end of every frame so memory over the
budget is freed one frame after its content leaves the screen, instead
of lingering until some later layout happens to insert a new entry.

* Fix oversized uniform slices: the array lengths were in bytes, not f32 elements

* Wayland: flush buffered mouse motion before scroll events, and drop motion for closed windows

* GL: fall back to non-mipmapped filtering when glGenerateMipmap fails on strict GLES3 drivers

* Text: bucket emoji raster scales so zooming reuses atlas slots instead of re-decoding every step

* Image: add has_content() and record texture provenance on cache-hit loads too

* Scroll: native trackpad momentum, Chrome-model bounce; presses catch motion, never click children

* Dock: redraw the newly selected tab immediately when the active tab is closed

* Html: standard link colors with pressed precedence; skip re-parsing unchanged text; color setters

* Image: don't redraw on cache-hit loads of the already-bound image (per-draw reloaders looped forever)

* Scroll: log macOS momentum-end phase bits to check Cancelled (touch-cut) vs Ended (natural fade)

* Scroll: momentum state machine; flicks survive pagination; edge sentinels & once-per-frame actions

* Scroll: remove the MAKEPAD_SCROLL_DEBUG diagnostics

* Scroll: time-based fling velocity window; pointer fan-out guard; parked flings survive pagination

* PortalList: optional reached-start/end margins (Some(0) default); repositioning re-announces the edges

* Linux/Wayland: honor UI zoom across window resizes; scale caption bar with zoom

Keep the wayland-side window geom in native units so the zoomed dpi isn't
read back as "native" on the next Configure — UI zoom no longer resets or
flickers on maximize/tile. Also let the caption bar height scale with the
zoom (pin to native only on macOS, where the buttons are OS traffic lights).

* Windows: D3D11 fixes for drawlist mgmt

trying to help with the `new_batch` bugs

* draw_list.rs — `set_zbias` now returns whether it changed
* d3d11.rs — uploads draw_call_uniforms on the given condition:
  `uniforms_dirty || zbias_changed || buffer.is_none()`
  and the zbias advance is hoisted above the early-continues

* Scroll: hard flicks carry farther and boost on re-flick; iOS-style touch rubber band with per-edge bounce gating; Android fling spline behind a flag

* Splitter: redraw both pane subtrees on drag; cached views keep fresh fixed sizes in the dirty check

* iOS: re-deliver window-geometry changes dropped by re-entrant UIKit callbacks; Init only ever from the first draw

* final cleanup fixes. Ensure all examples, experiments, `studio` all work

* Image: skip re-parsing an SVG that is already shown, keyed on the caller's shared bytes

* CachedView: fix upside-down offscreen texture on GL/GL-ES

GL/GL-ES store offscreen FBOs bottom-up, unlike Metal/D3D/WGSL.
The DSL->script-shader migration switched the CachedView composite to plain
.sample() (non-flipping sample2d on GL), so cached views rendered
vertically mirrored on GL/GL-ES; Metal was fine on macOS.

Add a `sample_rt` script sampler (emits the V-flipping sample2d_rt on
GLSL, plain no-flip sample elsewhere) and use it in the CachedView and
CachedRoundedView composites.

* Fix `AdaptiveView::redraw()` to actually do something
2026-07-21 09:59:12 +02:00
Admin
59b7d2712f gamemaker: full engine round — chase camera rig, racing-game APIs, script stdlib math, real error line numbers
Engine (examples/gamemaker): chase camera rig (camera({chase}) — ease-behind
with mouse-wins/recenter authority), writable camera + look deltas, spatial
queries (raycast/overlap_sphere/ground_normal), save/load, sustained tones,
rot_y + collide:false spawnables, HUD slots/bars, terrain noise shaping +
height bands, per-shape instanced render batching with static slabs (3.2x),
error push-loop into the agent chat, unknown-verb/option diagnostics with
game.splash:line:col positions, streaming tail-statement finalization, quiet
toolbar UI, Shh voice hush, fable voice.

Platform: runtime vector methods (.length/.normalized/.dot/.cross), scalar+
vector lerp, TAU; ScriptVm error capture sink; window frame capture API; four
headless-JIT fixes (scalar casts, mat4 mul, Id-arg expansion, commuted
scalar-vec ops) with regression test stages. Widgets: single-line TextInput
baseline centering, TextFlow inline-code baseline alignment, glass button
corner_radius uniform. Voice/ggml Metal backends: debug logs behind
GGML_METAL_TRACE. splashgame.md: the runtime-loaded game API contract.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 14:20:03 +02:00
Admin
ef0514a9aa hypothetical heap access fix 2026-07-01 14:04:23 +02:00
Admin
29932cb008 parse {}{} as {},{} 2026-06-26 13:37:17 +02:00
Kevin Boos
662545be44 Limit cache growth for text/script, reclaim memory after gc (#1123)
* Run script-VM gc in the desktop and mobile event loops, not just macOS

Only macOS was calling the script VM's garbage collector.
Now we call it everywhere, based on the original implementation in macOS.

* Limit cache growth for text/script, reclaim memory after gc

This PR includes several misc improvements to reduce memory usage and/or
return unused memory to the OS properly.

- slug atlas: reset the append-only curve buffer past a cap (mirrors the raster
  atlas reset: cleared at the prepare_textures boundary, forcing a rebuild), so
  it no longer accumulates every distinct large glyph ever rendered.
- script heap: in gc(), truncate the String reuse pool and shrink over-allocated
  free-list/slot capacity (never moves a live slot, so all refs stay valid).
- font outline cache: cap per-font distinct-glyph entries (clear-on-exceed).
- image cache: evict Loaded entries past a cap; widgets keep their own texture
  clones so displayed images are unaffected, and in-flight loads are preserved.

The string intern table is intentionally left unbounded: it backs stable
pointer-based FontId/FontFamilyId, and is bounded by the few distinct font names.
2026-06-16 09:07:53 +02:00
Kevin Boos
1f1c9178a7 Ensure inline composition is still shown in TextInput on all platforms (#1109)
* Support standard keyboard navg shortcuts/keys in TextInput

Implement platform-standard TextInput navigation and deletion behavior,
including Home, End, PageUp, PageDown, word movement, line/document
boundaries, and Shift-based selection.

* Use Apple Option/Cmd conventions on Apple targets
* Use Ctrl conventions on non-Apple targets
* Web accepts both shortcut styles for now, since we don't have a way
  to query the host OS from within a makepad web env.

Also, be extremely careful to ensure that we respect Unicode grapheme boundaries
when doing all the selection/navigation logic.

Fix `Delete`, which was erroneously handled before.

Add lots of missing keys in Linux X11 & Wayland backends, e.g.,
Home, End, Delete, Insert, PageUp/PageDown, and arrow keys

* Add `CropToFill` image fit variant, improve ImageFit docs

This allows you to easily achieve the "centered cropped fit" that most apps
want for things like avatars or small thubmnails that get masked.

* Detect and support hardware keyboards, distinguish from soft/virtual kbd

Mimic desktop behavior on mobile systems as much as possible.
This is esp important for tablets like iPad OS where you're more likely
to have a real physical keyboard attached.

For iOS:
* Arrow keys and Home/End/PageUp/PageDown navigate and auto-repeat
  at the system-defined rate (connected via `UIKeyCommand`)
* Cmd+Enter to submit a `TextInput` and Cmd+C/X/V clipboard shortcuts now work.
* Ensure the pop-up diacritic/accent menu is properly placed using a hidden
  `UITextInput` native widget, which acts as a sort of "proxy"
* Proactively drain `ShowTextIME` after each draw so the IME position will be
  properly updated after each keystroke.
* Importnatly, don't mark the IME dismissed when a hardware keyboard is attached.

For both iOS & Android:
* Add a `has_physical_keyboard()` detection mechanism across both backends,
  and fix platform-specific key repeat behavior

For Android:
* Ensure clipboard cut/copy works using the same Ctrl shortcuts (API 26+)

Soft/virtual keyboard/IME changes:
* For multiline TExtInputs, a soft keyboard Enter/Return key will always just
  insert a new line, to avoid complexity with keyboard shortcut cfgs.
* CJK keyboard character selection should also be properly positioned now

* minor optimization to avoid re-setting IME pos if it didn't change

* Ensure inline composition is still shown in TextInput on all platforms

This is mostly relevant when using CJK and other similar IMEs.
Previously Makepad didn't shown any "echoes" of the single latin chars
that the user would type, but it would correctly input the selected CJK
glyph. So if you typed `nihao` and then selected `你哈`, then you would
see the proper chinese characters but not the latin "nihao".
Tha't s a bit confusing while typing.

Summary of the fixes per platform:

macOS:
- `set_marked_text` now forwards the marked (composition) text to the
  focused TextInput with `replace_last = true`; previously it only stored
  it in an ivar. `unmark_text` clears the preview, and both it and
  `insert_text` share a new `clear_marked_text_ivar` helper so a commit
  doesn't emit a second, destructive text-input event.

Windows:
- Add `WM_IME_COMPOSITION` handling: commit `GCS_RESULTSTR`
  (`replace_last = false`) and show `GCS_COMPSTR` inline
  (`replace_last = true`), and clear on `WM_IME_ENDCOMPOSITION`. The
  message is consumed so DefWindowProc neither draws its own composition
  window nor synthesizes a duplicate WM_CHAR for the result.
- Extend the vendored `windows` binding with `ImmGetCompositionStringW`,
  `GCS_COMPSTR`/`GCS_RESULTSTR`, and `WM_IME_COMPOSITION`/
  `WM_IME_ENDCOMPOSITION`, which it didn't previously generate.

Linux (Wayland):
- Handle the `zwp_text_input_v3` `PreeditString` event (previously empty)
  and apply the double-buffered preedit/commit state on `Done`, in the
  protocol-mandated order (commit, then preedit), clearing the preview
  when a cycle carries no preedit.

Linux (X11):
- Create the input context with XIM on-the-spot (`XIMPreeditCallbacks`)
  and forward the preedit string from the draw/start/done callbacks,
  falling back to `XIMPreeditNothing` if the IM server doesn't support
  callbacks. Callbacks run inside `XFilterEvent`, so they only mutate a
  thread-local that the event loop drains into the widget afterward,
  avoiding re-entrant access to the app.

Android:
- Don't mark the IME dismissed when a physical keyboard is attached
  (mirrors the iOS guard). Android was unconditionally calling
  `text_ime_was_dismissed()` on soft-keyboard hide, which tore down the
  IME connection that hardware-key composition relies on.

* iOS: replace custom `UITextInput` with a native `UITextView`

`UITextView` is a full system-native keyboard client, so we get all the
major features for free: language HUD pill and the complete globe/Ctrl+Space
shortcut to cycle between IMEs/languages.

Makepad basically just mirrors the state of the system native text view,
via the `full_state_sync`, but the actual native text view is kept invisible
so it doesn't interfere with what we render in Makepad's TextInput.
Notably, the Full Keyboard Accessibility setting now does work properly,
whereas it did not before with our UITextInput-based approach.

We also make sure that arrow keys, nav keys, auto-repeat, and modifiers
are properly hanlded so we can retain the expected kbd shortcuts,
like other desktop platforms.

* iOS: remove the old `UITextInput` connection with the Makepad TextInput

We've now switched to the native UITextview, so we don't need this any more.

* iOS: fix desync during fast typing

Ensure there's no race between the native UITextView and
Makepad's TextInput, as the Enter/REturn key needs special handling
w.r.t. how `pressesBegan` gets it (From a real hardware kbd).

* TextInput: more iOS integration, and text input types

more native integration for things like username/password,
new password fields, email, address, URLs, etc.
These tell iOS to change the keyboard layout/type for the text input.

* iOS: don't let Full Keyboard Access focus on our hidden native cursor

* cleanup

* iOS/TextInput: fix perf issues

* iOS TextInput: more fixes for read-only efficiency, and filtered input

Also port some of these fixes to Android's IME integration layer

* iOS/TextInput: hide the native caret iOS draws during autocorrect

but still allow the "decline autocorrect" bubble to popup where that
hidden caret is located (and the CJK candidate window in the same spot)

* Avoid script VM re-entrant panic: defer animator_cut/play if script VM is held

`animator_cut` / `animator_play` call `cx.with_vm`, which panics
(*"Script VM swapped off"*) when invoked during an apply walk — e.g. a
widget's `on_after_apply` on `ScriptReapply` / `Reload` — because the VM
is already taken for the duration of that walk's enclosing `cx.with_vm`.

- The derive macro's `animator_cut_scoped` / `animator_play_scoped` now
  check `cx.is_script_vm_held()`; when held, they queue the op
  (`defer_cut` / `defer_play`) and return instead of re-entering the VM.
- `animator_handle_event_scoped` replays the queue via `flush_deferred`
  on the next frame, once the VM is free.

The defer path runs **only** in the formerly-panicking case, so VM-free
animations are byte-for-byte unchanged.

Also adds a re-entrancy-naming panic (`VmHolderGuard`) plus
`Cx::try_with_vm` / `Cx::is_script_vm_held` for diagnosing and handling
this class of bug.

* Better spacing/positioning for IME popups like the CJK candidate menu

applied to all platforms, but primarily an issue on macOS/iOS.

The candidate/conversion window (e.g. CJK pinyin) was covering the line of
text being composed. Carry the caret-line rect (not just a point) through
ShowTextIME and feed each backend its native "keep clear of this line" API,
so the OS places the candidate directly above/below the line with a small gap:

- macOS: firstRectForCharacterRange returns the line rect via AppKit
  convertRect:toView:nil + convertRectToScreen (drops the hand-rolled
  screen-coord math + fudge offsets); invalidate on caret move.
- Windows: ImmSetCandidateWindow with a CFS_EXCLUDE line rect.
- Wayland: set_cursor_rectangle with the real line rect.
- X11: XNSpotLocation/XNArea at the line.
- iOS: return the true composing-line box from firstRectForRange so iOS flips
  around the real edges (consistent at any screen position) instead of a
  degenerate point; only while marked text is active, to avoid an oversized
  autocorrect highlight when typing normally.

* Fix Linux X11 behavior: Ctrl-based kbd shortcuts didn't work in TextInput

also trying to fix X11 behavior for positioning the CJK candidate window,
turns out there was an X11 bug for Ubuntu 22 and older so it's not always
possible, but we can attempt a workaround if errors occur (based on that,
we try to auto-detect the version of X11)

* fix X11 event loop latency by draining only a max of 64 events before redrawing

still working on X11 CJK candidate window positioning...

* add logs to X11 ime to figure out wtf is going on

* more robust fallbacks for X11 CJK candidate window positioning... grr

* maybe try to set the XFontSet attribute? for CJK candidate positioning

* positioning works now but there is a bit of overlap still

* now that X11 CJK candidate positioning works in some cases,
we need to pass the full rectangle containing the current line of text
to the X11 library so that it can position the window both on top
and beneath the current line of text, if needed.

* tweaking X11 CJK candidate positioning

* abandon the screen-positioning heuristic

Instead, we just send the bounding rect of the current text character
and hopefully let the X11 platform libs decide where to put the
CJK candidate popup

* add more spacing to the bounding rect on X11

* tweak for a bit more space between CJK candidate window

* more tweaks, rect height isn't being respected for some reason...

* attempting to add more instrumentation to figure out wtf is going on with X11 CJK positioning

* remove bad instrumentation that was causing freezes. ugh

* different approach for IME placement on X11

* previous positioning attempts for X11 didn't work.

New strategy: let it be positioned, and then try to move it

* still trying to fix X11 CJK candidate window positoining...

* trying to find CJK candidate window with X11 queries (To move it)

* abandon window scanning approach

* better approach, now just tweaking it

* fix one case where the candidate window was flipped but it pointing too low

* tweaking more

* trying to fix above-text line positioning

* still trying to tweak CJK candidates ABOVE the text line

* be more conservative when guessing whether X11 will show the CJK candidate above or below

* improve size heuristic for CJK candidate height

* calling X11 as complete now. jfc. Cleanup, remove debug logs, etc
2026-06-16 09:07:32 +02:00
Admin
f91eec1b7f isolates 2026-06-02 18:51:17 +02:00
admin
b94ab6e985 splash md for calculator 2026-05-27 07:39:41 +02:00
Kevin Boos
2f59cee02b Separate apply-reload and script-reapply into different concepts (#1069)
* Simplify tooltip logic, fix positioning to respect safe inset areas

And other misc positioning/formatting fixes, like wrapping
after a hard line break, as well as ensuring that the callout
arrow thing itself is centered (to the fullest extent possible)
w.r.t. the rest of the tooltip body.

Basically, now it looks good again.

* Fix CheckBox/Toggle `set_active` to animate like others

All other widgets allow you to pass an `animate` arg when setting
them as active, except CheckBox (and by proxy, its wrapper Toggle).
This is necessary for proper non-animated thigns like restoring the
state of a toggle from persistent storage, or other similar examples
where you don't really want the animation to occur (because that'll
look like the user did it accidentally or some kind of phantom movement).

* PortalList: pass "touch stop" (FingerUp) events to children, always

I had recently implemented a feature where PortalList would not pass
events down to its children if those events were being direclty handled
by the PortalList as part of its scroll-action. That was generally correct,
but it missed one rare case where a child widget was waiting on a
finger up (touch stop/release) for something like stopping a hover/down
animation.

So the child, like a button, could capture the initial FingerDown
but never the FingerUp, so they'd get stuck on the hover or down
animation. This fixes that issue by passing FingerUp-causing events
down to the child widgets.

Note that the children must use `was_tap()` on the FingerUp in order
to handle a regular click -- but they should have already been doing that.
So this doesn't break anything, it's just strictly a proper fix
for something that i should've covered previously.

* Separate apply-reload and script-reapply into different concepts

The goal here is to differentiate between "applies" that change the actual
Splash script "DSL" (i.e., the template) from a "re-apply" that doesn't
change the DSL template but does change runtime heap objects.

Mostly, we want to ensure that LiveEdit (the former) is different from
things that require heap updates (the latter), such as changing a theme
value or doing screen rotation that changes safe inset areas on mobile.

I don't know that I love this approach as a permanent solution,
but it's a good stepping stone until we can redesign LiveEdit/Apply
to funnel all of these various events through the same singular system.
We probably want to use different attributes on widget fields in order
to have more fine-grained control over what happens on an Apply action.

Generated list of brief details here:

* `Apply::ScriptReapply` variant + `is_script_reapply` /
  `is_live_edit_reload` predicates; `Event::ScriptReapply` now applies
  via `Apply::ScriptReapply`.
* `String` / `ArcStringMut` `script_apply` early-return on
  `ScriptReapply`.
* Codegen: `#[deref]` runs before `#[apply_default]`'s recursive call
  so animator state wins over template defaults.
* `Animator::script_apply_default` returns `state_object` on
  `ScriptReapply`; new `current_state_apply()` helper for
  `on_after_apply` hooks.

* `Cx::request_live_edit()` + `pending_live_edit_request` for primitive
  heap mutations (safe-area insets baked into `script_mod!`
  expressions).
* `handle_live_edit()` returns `LiveEditTrigger {None, FileChange,
  Manual}`; `run_live_edit_if_needed` skips shader-cache reset and
  same-tick `ScriptReapply` follow-up for `Manual` (fixes ~1s rotation
  lag).
* iOS/Android post-event hook now drains both flags via
  `run_live_edit_if_needed` (was firing `LiveEdit` indiscriminately).
* `Window` `WindowGeomChange` uses `request_live_edit()` for
  safe-area.

* `StackNavigationView` gains `runtime_title` field re-asserted in
  `on_after_apply`; new `StackNavigation::set_title` API.
* `app_main!` collapses 4 duplicate platform branches into a shared
  `_app_main_event_closure!` macro.
2026-04-26 22:11:31 +02:00
Kevin Boos
5dc5c265a5 Switch to SLUG/DrawGlyph font drawing stack (#1042)
* Switch to SLUG/DrawGlyph font drawing stack (via Codex)

* Improve same-frame new glyph caching and SLUG packed instances

* avoid performance regression by batch updating slug atlas cache

* TextFlow: separate SLUG glyph batches to avoid interleaving HTML text drawing

* SLUG optimization: append instead of a full clone each generation

* trying out codex perf fix for linux wayland

* Disable SLUG glyps on Linux for now

* Fix Linux SLUG rendering, warmup, and promotion behavior

- re-enable Linux SLUG through a separate Linux-only DrawText helper
  instead of bloating the normal DrawText shader path
- preserve widget text styling on Linux SLUG by syncing common DrawText
  state into the helper, including base colors, gradients, and interactive
  states such as hover, focus, down, active, pressed, drag, empty, and
  disabled
- keep normal Linux UI text on the raster/MSDF path and only switch to
  SLUG above the Linux cutoff, while still falling back cleanly when SLUG
  data is unavailable
- fix Linux SLUG glyph placement so promoted text uses the correct glyph
  origin, layout position, and atlas packing
- add progressive SLUG warmup on Linux by budgeting glyph generation and
  uploads across redraws and falling back to raster/MSDF until SLUG data
  is actually ready
- lazily register and prewarm the shared Linux SLUG helper so app startup
  and first-use latency stay low
- opt only the Linux SLUG helper into async GL shader compilation and use
  parallel shader compile support when available, avoiding the large
  startup and first-tab stalls seen before
- fix Linux runtime shader issues caused by copied widget text shaders and
  custom get_color logic by using a shared helper shader with the expected
  text-state inputs
- stabilize Linux SLUG promotion by preventing stale retained areas,
  cleaning up raster/helper ownership correctly during draw, and
  shadow-promoting the first ready SLUG frame before making it visible
- eliminate the visible SLUG handoff flicker so Linux text now switches
  from raster/MSDF to SLUG without freezes or noticeable visual artifacts
- add a dedicated UIZoo SLUG tab with side-by-side below-cutoff and
  above-cutoff examples, plus diagnostic cases for plain labels,
  gradients, custom text shaders, and glyph/color probes
- keep the final diff focused by removing unrelated formatting-only churn
  from the worktree during cleanup

* Tighten Linux SLUG promotion and helper sync

- make Linux SLUG promotion state local to each DrawText instance
  instead of using a global per-redraw gate
- cache Linux SLUG helper shader field intersections so helper state
  syncing avoids repeated per-draw allocations and linear membership checks
- harden the shadow-promotion fallback path so failed helper batching
  only takes a single raster fallback path
- preserve DrawText memory alignment after adding Linux SLUG bookkeeping

* try to fix emoji on Android

* emoji fix take 2

* uizoo example: allow touch/drag scroll. Don't panic in FileTree demo

* Fix emoji on Android

* Windows: async HLSL shader compile + extend SLUG helper path to Windows

Fixes two major performance issues on Windows that made uizoo unusable on
first launch:

1. **60-75s startup stall** caused by synchronous `D3DCompile` of ~30+
   SLUG-bearing text shader variants on the UI thread before the window
   could present.
2. **3-4s hang when opening the SLUG tab** caused by synchronous compile
   of the fat DrawTextSlug helper shader the first time a SLUG glyph was
   needed.

Also fixes a latent HLSL-only `CreateInputLayout` E_INVALIDARG crash
triggered by shaders with >26 instance inputs (exposed by DrawTextSlug).

`DrawTextLinuxSlug` → `DrawTextSlug` and all `linux_slug_*` /
`LinuxSlug*` symbols dropped their `Linux` prefix. Cfg guards expanded
from `target_os = "linux"` to `any(target_os = "linux", target_os = "windows")`
so Windows now:

- uses the same lean base `DrawText` shader (SDF/MSDF only, no SLUG
  curve-solver HLSL inlined)
- uses a separate `DrawTextSlug` helper shader for the SLUG path
- has the same progressive glyph-build budget and DPI cutoff
  (`default_slug_new_glyphs_per_redraw`, `default_slug_min_dpxs_per_em`
  in `fonts.rs` now match `OsType::Windows` alongside the Linux variants)
- falls back to raster/MSDF while the SLUG helper shader or its glyph
  data isn't yet ready

macOS/iOS/Android/WASM paths are untouched — they still use the fat
all-in-one `DrawText` shader via `cfg(not(any(linux, windows)))`.

Added `AsyncHlslCompile` in `d3d11.rs` and an `async_hlsl_compile` field
on `CxOs`. Shaders flagged `async_compile: true` (the SLUG helper) now
dispatch to a background thread per shader via `std:🧵:Builder`
(named `hlsl-compile-<id>` for debugging). Workers call `D3DCompile`
off the UI thread, write the resulting DXBC to the on-disk cache, and
send only a status result (not the bytes themselves — SLUG is ~240 KB
and ferrying it through the channel is wasteful) back via an mpsc
channel guarded by a `Mutex`.

`hlsl_compile_shaders` drains completed results at the top of each
call, constructs `CxOsDrawShader` objects on the main thread (the
bytes come from the cache-hit path in `CxOsDrawShader::new`), and
triggers `redraw_all()` so widgets whose shaders just became ready
get re-rendered. The existing `sh.os_shader_id.is_none()` guard in
`render_view` handles skipping the draw call while a shader is
pending.

Added `Cx::is_draw_shader_window_ready()` on Windows for the SLUG
helper's readiness check; on Windows it's simply
`os_shader_id.is_some()` since HLSL compile is either synchronous
(cache hit) or tracked via the async path.

Cold-start still compiled 30+ shaders synchronously (parallelized via
`std:🧵:scope`) which took ~5-10s because FXC's per-call speed is
the bottleneck and parallelism helps less than expected. Now
`hlsl_compile_shaders` partitions queued shaders by cache state:

- cache hit → sync path: disk read + D3D11 object creation, a few ms
- cache miss OR `async_compile: true` → async path: worker thread

On a fully cold cache, every shader is a cache miss → the window
presents on the first frame with no compile work on the UI thread.
Widgets fill in over the next ~1-2s as their shaders become ready.
On a warm cache every shader is a hit → instant startup as before.

Added `shader_bytes_cached()` for cheap existence checking of the
cache entries.

`d3d_compile_hlsl` now passes `D3DCOMPILE_SKIP_OPTIMIZATION`. FXC's
optimizer is what makes individual compiles burn hundreds of ms to
seconds on text shaders with loops; UI shaders don't benefit enough
from it to justify the cold-cache cost. If a specific shader is later
shown to be a runtime hotspot, the fix is to recompile it optimized
on a background thread and hot-swap, not to pay the cost upfront for
every shader.

Bumped `CACHE_KEY_VERSION` to 2 so pre-existing `.dxbc` blobs compiled
with the old flags are invalidated cleanly on upgrade.

`d3d11.rs` used its own `index_to_char(i) = i + 'A'` which produced
invalid HLSL semantic names (`[`, `\`, `]`, …) past 26 inputs, while
the HLSL generator in `shader_hlsl.rs` already used a correct
multi-character scheme (`A..Z, AA..AZ, BA..`). `CreateInputLayout`
returned E_INVALIDARG because the names didn't match. Replaced with
`makepad_script::shader_hlsl::index_to_semantic` so both sides of
the binding agree.

This was a latent bug — no existing shader had >26 instance inputs
until `DrawTextSlug` (which inherits many interactive-state fields
from Label-derived shaders). Linux/GLSL is unaffected because GLSL
binds by identifier, not semantic name.

- Hoisted `d3d_compile_hlsl`, `hlsl_cache_key`, and
  `get_or_compile_shader_bytes` out of `CxOsDrawShader::new` to module
  scope so they can be shared with the async worker.
- Compute `hlsl_cache_key` once per shader during partition and reuse
  at dispatch instead of recomputing.
- Scoped borrows in the async drain loop eliminate mapping/bindings
  clones.

- `platform/src/os/windows/d3d11.rs` — compile pipeline, async infra,
  semantic-name fix
- `platform/src/os/windows/windows.rs` — `async_hlsl_compile` field on
  `CxOs`
- `draw/src/shader/draw_text.rs` — rename `LinuxSlug*` → `Slug*`,
  expand cfg gates
- `draw/src/text/fonts.rs` — extend SLUG cutoff/budget defaults to
  Windows

No changes to macOS, iOS, Android, or WASM paths.
2026-04-16 22:50:48 +02:00
Admin
69aa615947 mlx 5x->2x 2026-04-10 14:46:22 +02:00
Admin
d134df9f06 xr llama and slug 2026-04-01 12:19:03 +02:00
Admin
cf46675a32 xr room mapping 2026-03-27 13:51:54 +01:00
Admin
9b9a35ea7c better physics 2026-03-25 17:39:00 +01:00
Admin
5bf2f03a97 multiview 2026-03-25 17:38:59 +01:00
Admin
1bbc4b6791 finally 2026-03-25 17:38:59 +01:00
Kevin Boos
2b787c6851 Fix shader compilation bug on linux and window positioning (#979)
TL;DR: the shader compiler needed explicit casts in for loop bounds,
and the window positioning was messed up, causing the app-level title bar
to overlap with the native OS-level title bar (which means you couldn't
see or press the window chrome buttons)

--------

here's an AI-generated summary of the changes, for more details:

On OpenGL ES 3.0 targets (Linux/EGL, Android), shaders containing `for` loops
over `uint` variables failed to compile with a GLSL type-error. The shader
compiler emitted the loop header as:

```glsl
for(uint i = 0; i < 4; i++) { … }
```

The integer literals `0` and `4` are of type `int` in GLSL. GLSL ES 3.0 forbids
implicit casts between `int` and `uint`, so the initialiser and the comparison
both produce a compile error. The other shader backends (Metal/WGSL/Rust) do not
have this restriction, so no corresponding arm existed for GLSL.

**`platform/script/src/shader_control.rs`** — Add a `ShaderBackend::Glsl` arm to
`handle_for_1` that wraps both loop bounds in an explicit constructor call for the
loop variable's type:

```glsl
for(uint i = uint(0); i < uint(4); i++) { … }
```

This satisfies GLSL ES 3.0's strict no-implicit-cast rule and matches the
behavior already implemented for the WGSL backend (which uses typed variable
declarations for the same reason).

**`platform/src/os/linux/opengl.rs`** — Gate the helper functions
`shader_source_hash` and `shader_source_preview` (and their call-sites) behind
`#[cfg(target_os = "android")]`. These functions are only referenced from
Android-specific shader-cache code paths; without the attribute the compiler
emits dead-code warnings on every other Linux/OpenGL build.

- `platform/script/src/shader_control.rs`
- `platform/src/os/linux/opengl.rs`

----------------------------------------------------------------------------

On GNOME (and likely other modern WMs), restoring a saved window position would
consistently produce two visual artifacts:

1. **No WM title bar visible** — the client area was rendered where the title bar
   should appear.
2. **Black bar at the bottom** — the bottom portion of the window surface was not
   covered by rendered content.

The root cause was two related issues in `xlib_window.rs`:

**Issue 1 — `XMoveWindow` called after `XMapWindow` (races with WM reparenting)**

After `XMapWindow`, the window manager asynchronously reparents the client window
into a decoration frame. If `XMoveWindow` is called after reparenting has occurred,
the coordinates are interpreted relative to the WM frame rather than the root
window. For example, calling `XMoveWindow(client, 23, 89)` after GNOME reparents
places the client 89 px from the top of the WM frame. Since the title bar is only
~37 px tall, the client ends up 52 px below the title bar, and its bottom edge
extends 52 px *beyond* the bottom of the WM frame. GNOME responds by resizing or
repositioning the client, producing the rendering artifacts described above.

**Issue 2 — No `USPosition` hint set**

Without the `USPosition` flag in `WM_NORMAL_HINTS`, GNOME ignores the position
provided to `XCreateWindow` and applies its own smart-placement algorithm. This
meant the application relied entirely on the post-map `XMoveWindow` call described
above, which was itself broken.

**`platform/src/os/linux/x11/x11_sys.rs`** — Add the standard `XSizeHints` flag
constants:

- `USPosition` (`1 << 0`) — user-specified x, y
- `USSize` (`1 << 1`) — user-specified width, height
- `PPosition` (`1 << 2`) — program-specified position
- `PSize` (`1 << 3`) — program-specified size

**`platform/src/os/linux/x11/xlib_window.rs`** — Two changes in `XlibWindow::init()`:

1. Before calling `Xutf8SetWMProperties`, populate an `XSizeHints` struct with
   `flags = USPosition | PPosition` (and `x`/`y` set to the requested coordinates)
   when a position was provided. Pass this struct as the `WM_NORMAL_HINTS` argument
   instead of the previous `ptr::null_mut()`. This tells GNOME/Mutter to honor the
   requested position rather than running its own placement heuristic.

2. Move the `XMoveWindow` call to *before* `XMapWindow`. At that point the window is
   still a direct child of the root window, so the coordinates are unambiguously
   root-relative. This eliminates the race with WM reparenting entirely.
2026-03-24 19:52:21 +01:00
Admin
0b4ee8860d fix android screencap to studio 2026-03-20 09:03:50 +01:00
Admin
8393979f7b remove dep 2026-03-18 01:02:57 +01:00
Admin
62a7fd33f4 cef 2026-03-17 19:00:28 +01:00
Admin
c4064cef6a vulkan video textures 2026-03-17 19:00:28 +01:00
Admin
1a24696c26 vulkan video textures 2026-03-17 19:00:28 +01:00
Admin
98bc76b705 vulkan back 2026-03-15 13:20:49 +01:00
Admin
0cfd9f36e0 otw 2026-03-15 09:52:45 +01:00
Admin
bdbb889cab zbuf back 2026-03-15 09:52:44 +01:00
Admin
695403ef72 xr otw 2026-03-12 15:51:30 +01:00
Admin
6679035b4a hiccup 2026-03-11 10:19:58 +01:00
Admin
73d997258c mipmapping 2026-03-09 09:18:33 +01:00
Admin
cf22b515c8 fix script mods 2026-03-08 18:20:07 +01:00
Admin
eba48ce826 live reloading 2026-03-08 17:25:27 +01:00
Admin
c1780ca9ef undo integer attribute change 2026-03-08 13:11:57 +01:00
Lutz
619f364d20 fix: Single-slot UInt/SInt instance attributes render incorrectly (#922)
Co-authored-by: Lutz Paelike <lutz.paelike@ehealthafrica.org>
2026-03-08 11:26:52 +01:00
offline-ant
dd83d4b759 platform: small fixes and quality improvements (#928)
- Android log levels: map log! macro levels to Android log priorities
  (ERROR=6, WARN=5, INFO=4). Some devices suppress DEBUG by default.
- cargo-makepad android: show help text instead of panicking on missing
  or invalid subcommand.
- Android build: discover .class files dynamically instead of hardcoding
  ~25 individual paths. Add Java 8 source/target flags.
- Remove deprecated AsyncTask import from MakepadNetwork.java.
- Studio stdout: add newline after JSON messages for JSON-lines parsing.
- Studio stdout: skip profiler timing in stdout mode to avoid overhead.
- Script thread: fix panic on empty call stack in call_has_me/call_has_try.
- Cursor: reset to Default on FingerHoverOut in TextFlow and TextInput.

Co-authored-by: ant <ant@offline.click>
2026-03-08 11:24:18 +01:00
Admin
8c5987039d fix shader compiler 2026-03-06 18:49:24 +01:00
Admin
2aad6a6aa9 drumroll 2026-03-06 17:21:17 +01:00