Commit graph

560 commits

Author SHA1 Message Date
Admin
303945ea57 Studio: forward game controllers to the hosted app
A game was playable standalone and completely dead under Studio — which is
exactly where it gets developed. The cause is structural, not a binding: an
app hosted by Studio is a child process with no window of its own, so the
OS hands controller input to Studio and never to it. On macOS the child
never even reaches `init_cx_os`, so `apple_game_input` is None and
`game_input_states()` returns an empty slice forever.

So controllers now travel the same road mouse and key events already do.
`StudioToApp::GameInput` carries the whole controller set; the run view
polls its own `game_input_states` on the tick it already uses to batch
messages, and the app lands them in `Cx::game_input_remote`, which
`game_input_states()` reads whenever `in_makepad_studio` is set.

Details worth knowing:

- Sent on change only. A pad at rest reports identical level state every
  tick, and that is not worth the wire. The memo drops on focus loss so
  regaining focus resends rather than assuming the app still holds it.
- Only the focused run view forwards, so two open run views cannot both be
  driven by one stick. That is the rule the keyboard already follows.
- The set is replaced wholesale rather than merged, so a pad that unplugs
  disappears by being absent instead of freezing at its last state.
- Wheels ride along with gamepads. Forwarding only pads would have left a
  wheel silently dead under Studio while a pad worked, which is a worse
  failure than both being dead.
- Linux gains controllers under Studio that it does not have standalone:
  its native backend is a stub, but the forwarded path is platform-neutral.

The 21-field mapping lives in ONE place with both directions together.
Written per-side it would have drifted the first time a button was added —
one end gaining a field and the other quietly reporting zero for it. The
round-trip tests give every field a distinct value, because a fixture of
uniform numbers cannot detect two crossed wires, and the sticks are pinned
separately since flattening a Vec2 to scalars is where a swap would hide.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 20:30:29 +02:00
Admin
d933cc242a platform: fix the perf_monitor doctest
An indented code block in module docs is a doctest, and this one contained
a bare `...`, so `cargo test -p makepad-platform` has been failing on a
snippet that was only ever meant to be read. Fenced as `ignore`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 20:30:12 +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
88183a924b Fix http_server dropping pipelined body bytes; add Intent::Authoring to game_net
from_tcp_stream wrapped the socket in a BufReader that was dropped on
return, so body bytes read ahead into it vanished and handle_post blocked
forever on bytes that no longer existed — one wedged thread per request
that sent headers and body in the same TCP segment. Browsers split the two,
which is why nothing noticed. The function now owns its buffer, reads to
\r\n\r\n, and returns the remainder alongside the headers for handle_post
to consume first. The websocket upgrade path had the identical exposure (a
frame pipelined with the upgrade was silently dropped) and consumes the
same prefix now; EOF mid-head returns instead of spinning to the 4096-line
guard. New tests cover headers+body in ONE write (the case that hung, with
the connection held open afterwards so a regression blocks rather than
passing on EOF), the split case, and a plain GET.

game_net: Intent::Authoring{text} + MAX_AUTHORING_TEXT so a keyless client
in a hosted room can route a creation request to the host's agent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 02:20:54 +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
ee96a23a3e macOS: present gate at 3 in flight — the >=2 gate single-buffered the pipeline (50ms lock)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:43 +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
c073c08596 Revert to known-good: pass batching opt-in (MAKEPAD_BATCH_PASSES=1), AI loading opt-in (MAKEPAD_AI=1)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:43 +02:00
Admin
b387a6d5be Metal: flush batched offscreen buffer at present-pass ENTRY — restore GPU/CPU pipelining
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:43 +02:00
Admin
c59fe0b0eb Metal: frame-batched command buffer for offscreen passes + big benchmark window
Texture-mode passes append to one retained command buffer flushed at the
window pass (safety flush at repaint start for texture-only frames) —
the 12-pass gauss pyramid paid ~1ms commit/schedule latency PER PASS.
Profiling mode (MAKEPAD_GPU_PROFILE) keeps per-pass buffers so spans
stay attributable. Startup window 3400x2050 for pixel-heavy iteration.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:43 +02:00
Admin
097a57ab70 Metal: MAKEPAD_GPU_PROFILE=1 per-pass GPU profiler + gpu_ms in map frame log
Per command buffer: pass debug name, GPU start->end interval, draw calls,
vertices, instances, upload bytes — accumulated and printed as a 1Hz
table from the completion threads. Map frame stats gain gpu_ms/gpu_max
from the existing PERF_CHANNEL_GPU ring.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:43 +02:00
Admin
b1329124eb Map: layer-skip, keep-stale fixes, tilt priority, cancellation, budgets
- Regression attributed: the combined archive's six osm_* detail layers
  were wholesale-ingested and STYLED by the base parse (roads double-
  styled via streets + osm_lines: union input 946->3940 rings, stroke
  prep 26x) -> 2.1-5.5x slower than the old two-archive pair. Layer-level
  lazy skip (MvtSink::wants_layer): unconsumed layers skip as raw bytes
  in both passes; all 24 profile cells improve 1.3-2.3x.
- Gray-tile keep-stale: three kill paths fixed — mark_tile_failed no
  longer replaces drawable Ready entries with gray placeholders on batch
  errors (archive rewrites), transient absent reads no longer evict
  Ready meshes + 30s blacklist, retry backoff moved to TileEntry.
- Tilt-aware request priority (camera-projected screen distance with
  toward-camera bias) — the near field builds first under tilt.
- Restyle bursts lift the 4-slot gesture throttle to 12; queued jobs for
  dead zoom/bucket/mode are pruned (TagThreadPool::retain_queued), stale
  results dropped pre-upload; uploads byte-budgeted (24MB/frame).
- Stage matrix: painter-order union cascade = 59-77% of every cell
  (~320-350ms worst key) — per-bucket face bake is the path to <200ms.
- Baked-fill strips: A/B shows no measurable win post fill-tessellator
  optimization -> removal recommended (dissolve stays).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:42 +02:00
Admin
419d77c652 Audio: half-duplex echo control — duck only while the assistant speaks
Idle listening now runs PLAIN capture (no VPIO, zero ducking — music
untouched); the app arms the voice-processing unit only while its own
TTS is audibly playing (+0.8s tail), which is the only window where echo
cancellation matters. Options changes rebuild running input units
(AudioUnitAccess.last_input_options); WindowVoiceInput/VoiceWave gain
set_echo_cancellation; route app polls speech playback at 4Hz.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:41 +02:00
Admin
1288ed7471 Audio: gentler VPIO ducking — standard mode at Min level, env-tunable
Advanced ducking dips other audio hard on voice activity even at Min;
standard ducking at Min is a light constant reduction. Also fixes the
IO-unit instantiation double-retain (av retained twice, au never) that
made every unit immortal — mic-off now really ends the voice session
and un-ducks. MAKEPAD_VPIO_DUCKING=min|mid|max|default and
MAKEPAD_VPIO_ADVANCED=1 for experimentation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:41 +02:00
Admin
cff99f0a22 Audio: fix VPIO for real — native format, retain balance, error release
Standalone harness (scratchpad/vpio_test.m) proved macOS VoiceProcessingIO
rejects ANY custom bus format with -10875 FailedInitialization; with the
native format untouched it allocates and accepts the min-ducking config.
- VoiceInput skips the bus-format override and reads back the native
  sample rate/channel count after allocation (input handler now sizes its
  buffer from input_channels instead of hardcoded 2ch).
- Retain balance fixed: the AU handle is retained once on SUCCESS (the
  historic double av-retain made every IO unit immortal), and a FAILED
  instantiation is released in the error path — leaked half-born VPIO
  units were keeping system-wide ducking engaged until process exit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:41 +02:00
Admin
72c0330d95 Audio: fully tear down VPIO on mic-off; set ducking config post-allocate
release_audio_unit now stops hardware, deallocates render resources and
releases the retained render_block — the leaked VoiceProcessingIO unit
kept the system voice-chat session (and its ducking of all other app
audio) alive until process exit. The other-audio ducking configuration
(advanced + minimum) is applied after allocateRenderResources and logs
whether it took effect.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:41 +02:00
Admin
aad72dfe2b Audio: never auto-select loopback; fix VPIO init + minimize ducking
- The SCK loopback device claimed is_default and default_input() fell
  through to it when the real mic errored — the voice pipeline silently
  became SYSTEM-AUDIO capture behind screen-recording privileges. Loopback
  is no longer default and default_input() only auto-picks real Input
  devices; system-audio capture is explicit-opt-in by device id.
- VoiceProcessingIO input failed FailedInitialization on macOS when pinned
  via setDeviceID (it aggregates its own devices; follows system default
  input now). If VPIO still fails, degrade to plain capture instead of
  marking the DEVICE failed (voice_input_unusable flag + device-change
  re-arm); input error log no longer says "output".
- VPIO voice-chat mode ducks all other app audio to a whisper: request
  advanced ducking at minimum level (macOS 14+/iOS 17+) so music stays
  near full volume while AEC keeps running. Links AudioToolbox.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:41 +02:00
Admin
a17f745ccb Add missing platform/src/event/location.rs
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 12:56:19 +02:00
Admin
1afcedd8f6 NL-wide bridge-dz overlay + mbtiles-merge; fix VoiceInput unit handling
- bridge-dz baked for the entire Netherlands (17026 tiles, 29MB):
  AHN-refined over the 8 Amsterdam sheet pairs, solver-only elsewhere.
  Baked as 4 north-south strips with 2-tile overlap — the full-bbox
  global solve peaked past 60GB RSS — merged with the new mbtiles-merge
  subcommand (later input wins on overlaps, block-major write order).
  Route app bridge_dz path ams -> nl.
- audio_unit: the new VoiceInput (VoiceProcessingIO) kind takes the input
  setup/handler paths (set_input_handler panicked and aborted the app);
  VPIO capture forced mono on every platform.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 12:38:48 +02:00
Admin
22316f9b8c Audio: echo-cancelled capture option (Apple VoiceProcessingIO)
use_audio_inputs_with_options(devices, AudioInputOptions{echo_cancellation})
— platform-neutral trait default ignores the flag; Apple swaps the input
unit HAL->VoiceProcessingIO (system-wide AEC, the unit iOS input already
uses), so the assistant's own TTS no longer feeds back into the mic.
Voice capture (WindowVoiceInput) requests it always.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 12:16:29 +02:00
Admin
723b7928bb Route assistant: mmap LLM weights, fix batched-attention corruption, kokoro voice out
- ggml: read-only mmap module (unix-gated) + two-region Context (mapped
  weights / dirty caches) + segmented Metal buffer binding; llama loads
  GGUF weights as file-backed clean pages (jetsam-exempt) with owned-arena
  fallback (MAKEPAD_LLAMA_NO_MMAP=1). Route app dirty footprint 12GB -> 4-7GB;
  model load becomes lazy page-in; A/B byte-identical on 4B + 9B.
- llama: fix graph-cache keying corruption — a graph keyed wider than the
  KV cache corrupted attention for any prefill batch >= 2 (flash op reads
  permute-node dims baked at build; view reconfigure never reached the
  kernel; masks were written cache-narrow). Masks now always fill the full
  graph key width and graphs key by 1024-buckets; reconfigure path removed.
  Verified byte-exact vs per-length reference across batch 1/2/8/64/512,
  short+long prompts, mmap on/off, plus a two-session concurrency probe.
- voice: passive VoiceWaves no longer register the global audio-input
  callback (the invisible caption-bar wave stole mic audio — last
  registrant wins — and spawned duplicate whisper workers); whisper back
  to F16 default (voice Metal library has no quantized kernels; q5_0
  failed every GPU matmul); raw transcripts render immediately.
- route: kokoro TTS voice output (speaker toggle; streams reply sentences,
  announces nav maneuvers + arrival) with barge-in — voice activity on the
  mic stops playback instantly; dispatcher context 8k -> 32k (hybrid KV is
  12/48 layers, ~48KB/token); window caption bar suppressed under studio.
- llama-generate: --max-context/--prefill-batch-size + state fingerprints;
  new llama_concurrent_probe bin.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 12:13:31 +02:00
Admin
676255dfc2 Land route app + platform geo/permission APIs, map nav layer, geodata radar
apps/route: AI trip planner on MapView — tool broker + local/cloud agent
dispatch, nav session + simulated drive, layers/theme state, DDG image
search, trip history, tilt-shift DOF layer (linear circle-of-confusion:
level = log2 of radius, constant growth rate, tilt raises only the
ceiling; tilt-shift on by default).

Platform: Cx geo location API (macOS/iOS/Android/web) + permission
plumbing, memory watchdog, headless build cfg. Map: nav layer M0 API,
landcover drape, bridge dz. Geodata: 250m dual-radar compositor
(radar_volume) + KNMI sync. Docs for the route/glass/blur/shiny work.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 00:10:52 +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
Kevin Boos
8fa33c86fc fix(linux): don't abort on machines with no usable audio device (#1154)
* fix(linux): survive machines with no usable audio device

Makepad aborted inside libpulse on any machine without a working sound
server: get_updated_descs passed the null pa_operation returned by a
context that is not READY straight to pa_operation_get_state, which is
an assert in libpulse ("Assertion 'o' failed at pulse/operation.c:136").
Headless boxes and containers died on startup. Nearby paths were no
better - a sound server that advertises a sink it cannot open hung the
UI thread for good, and a device that failed to open was re-opened on
every device change, forever.

PulseAudio:
 - never hand a null pa_operation to libpulse, and give up on a context
   that is not ready instead of calling into it
 - PulseAudioAccess::new returns None rather than panicking, so a
   machine without a server simply runs on ALSA
 - PA_CONTEXT_NOAUTOSPAWN: without it pa_context_connect forks and execs
   a sound daemon synchronously, before the mainloop exists, blocking
   the UI thread for as long as the spawn takes (measured 7s) and
   leaving a daemon running on a machine that deliberately had none
 - every wait is bounded by a mainloop timer, so a server that never
   answers cannot freeze the app
 - the context state callback no longer takes the access mutex, which
   the UI thread holds while waiting on the mainloop (a deadlock)
 - null info pointers, eol < 0 and absent default sink/source names are
   handled instead of dereferenced
 - streams still being created cannot be disconnected, so they are
   parked and disconnected once the server answers rather than left
   attached to the microphone for the life of the process
 - callbacks no longer panic across the FFI boundary, and a stream the
   server tears down is reaped and reported instead of going silent

ALSA:
 - track input and output failures apart: both directions of one pcm
   share a device id, so a card with no microphone disabled playback
 - publish a device before opening it, not after. Opening takes ~200ms
   and the check that decides to spawn reads that same list, so two
   device changes inside the window - the normal startup - spawned a
   second thread whose EBUSY marked a device that was playing fine as
   failed
 - a stream that dies mid-playback is reported, so the app can move to
   another device instead of silently losing audio

Both backends:
 - a device that failed to open is not retried on every device change.
   It is retried when the device list changes, or when the app asks for
   a different set than last time, so an explicit request - a user
   picking a device, a widget toggling its microphone - is still
   honoured
 - AudioDevicesEvent::default_output falls back to another working
   device instead of handing back one already known to have failed,
   which is what made apps ask for it again on every event.
   default_input deliberately does not: the next input in the list is
   typically a monitor source, and silently recording the machine's
   own output instead of a microphone would be a privacy breach

* fix(linux): enumerate and prefer the generic ALSA "default" pcm

Device enumeration called snd_device_name_hint once per card. Passing a
card number only returns that card's raw pcms, so the generic ones -
"default", "pipewire", "pulse" - were invisible to makepad, and it then
picked the first plughw: node as its default device. Raw nodes demand
exclusive access to the card, so makepad's chosen device failed with
EBUSY whenever anything else was playing, which on a desktop running
PipeWire is most of the time.

Ask for the whole system instead (card -1), which is what every other
alsa client does and what "aplay -L" prints. It returns the generic pcms
as well as every card's own, so nothing is lost. "default" is now
preferred when picking the default device, for both directions; the old
plughw:/dmix: chain remains for a system with no alsa configuration.

"null" is skipped: it accepts and discards audio and always opens, so
offering it would let the automatic fallback land on a device that looks
like working audio and is silent.

Also frees the hint array, which was leaked on every enumeration.

Measured with the raw card held by another process: before, makepad's
first pick failed; now it opens "default" and audio flows immediately.
2026-07-30 21:25:38 +02:00
Admin
7f3f568933 map: opacity-aware AA fringes, single-level translucent rings; macos: skip foreign windows in scroll path
- Opaque faces get boundary-straddling AA skirts (coverage 50% at the
  edge, legacy convention); translucent faces ramp outward only and their
  straddling rings pick one level part — premultiplied translucent color
  must never paint over itself (double-blend darkened plaza boundaries).
- macos event loop: a foreign window class in the scroll-wheel path (the
  screen-capture overlay's TUINSWindow) has no macos_window_ptr ivar —
  skip instead of panicking. This was the random studio death whenever a
  system screenshot overlay was active.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 10:42:24 +02:00
Kevin Boos
1b558cefd9 Linux: derive window app id from the binary name, or app override (#1151)
Every window hardcoded `create_app_id = "Makepad"`. GNOME Shell matches Wayland
toplevels to their `.desktop` file by app id, so packaged apps looked for
`Makepad.desktop`, missed, and lost their icon. X11 was unaffected, having always
used argv[0] for WM_CLASS.

* Default `create_app_id` to the argv[0] basename via `window::default_app_id()`.
* Add a `window.app_id` DSL field to override it, for rDNS packaging like Flatpak.
* Route X11's WM_CLASS through `create_app_id` too, replacing its own inline copy
  of the argv[0] chain, so both identities come from one field.
2026-07-30 08:51:06 +02:00
Kevin Boos
1de784dd0e PortalList: allow setting layout flow at runtime; AdaptiveView fixes (#1144)
* PortalList: add `set_flow(cx, flow)` to switch a list between vertical and
  horizontal layout flow at runtime. Much faster than using `script_apply_eval`,
  and always fully correct because it updates the `vec_index` axis.
  It also avoids a full ScriptReapply sequence, which is potentially expensive
  across all of an app's widgets.
* AdaptiveView: add `active_variant()` getter so that widgets using AdaptiveView   do not have to separately track which variant it should be in.   Removes all ambiguity and possibility of divergence... finally!
  * Also fixes long-standing TODO item in AdaptiveView about properly handling
    weird window geom events, e.g., on macOS sometimes it spits out a 0-width
    window update which is total b.s.
* DisplayContext: add `is_desktop_width()` helper.
* Window: ignore spurious zero-size window geom events.
2026-07-28 19:31:42 +02:00
Jason Yau
ccd1adbaf6 Fix Windows WASAPI open failures and infinite retry spam (#1148)
Co-authored-by: jasonqiu <jasonqiuchen@outlook.com>
2026-07-28 18:46:52 +02:00
Admin
16a5f507bb map: transit lines/labels, districts, 3D signals+trees, label flip invariant
- transit routes draw as per-line colored strokes (stable ref-hash
  palette) on white casings, labeled "Tram 7"/"Metro 52"; stops get
  name labels (stations z13+, local z15+)
- districts layer: tiered admin boundaries (gemeente/wijk/buurt) and
  centroid name labels staged by zoom, muted admin purple
- upside-down labels fixed structurally: post-placement invariant
  re-places any glyph run that reads net-leftward (hairpin ramps fooled
  every pre-placement chord heuristic); vertical dead band ±4°
- overlay charger pins hold collision priority over base charging_station
  icons, which are suppressed while a charger overlay is active; pins
  reserve their bubble box so POI text places beside them
- per-icon zoom floors: overlay pins use kW tier floors (8/10/12) —
  micro floor 16.5 no longer hides them; 0.6 zoom grace; fail-open when
  the icon_zoom uniform is unset; oneway arrows from z15
- rail z-fighting: stroke merge drops duplicate quantized segments from
  forked ways (switches drew shared segments twice, shimmering in tilt)
- 3D mode: stoplights (pole + red/amber/green lights) and taller
  ellipsoid trees; live_reload names its override files

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 16:57:07 +02:00
Admin
c0b851a578 macos: present-gated frame pacing — skip the beat instead of blocking
CAMetalLayer with display sync throttles nextDrawable when the
compositor consumes frames unevenly (hardware-mirrored / scaled
displays): the main thread blocks 10-25ms in phases, felt as hiccups
in any sustained-animation app. Track in-flight presents per window
via addPresentedHandler; when two of the three pool drawables have
not reached glass, skip the paint beat and keep the pass dirty — the
next timer beat retries with the pool drained and event handling
never stalls behind vsync.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 21:06:07 +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
Athan
8278f31400 feat : add advance vide player example (#1138)
Co-authored-by: Athan Xiao <Athan.Xiao@one.nz>
2026-07-23 12:16:19 +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
71b93c1ea1 macos: A/B diagnostics — MAKEPAD_NO_VSYNC, MAKEPAD_TIMER_TRACE, MAKEPAD_NO_GAUSS
Verdict on the gamemaker judder (measured, 25s A/B runs, same window size):
  gauss OFF        -> unchanged (25-33ms gaps)   [pyramid exonerated]
  empty game world -> unchanged                  [game exonerated]
  vsync OFF        -> FIXED (119.8fps steady, worst 9ms; slow callbacks 124->2)

The stall is CAMetalLayer display-sync throttling: the compositor (hardware-
mirrored + SwitchResX-scaled 7680x2160) returns drawables unevenly, and
nextDrawable blocks the main thread 10-25ms in phases. The durable fix is
present-gated pacing: track in-flight presents via addPresentedHandler and
skip the paint when the pool is busy instead of blocking the event loop.

MAKEPAD_TIMER_TRACE=1 logs paint-clock fire-to-fire gaps >20ms and slow
callbacks >10ms with a per-step breakdown (live_edit/net/pad/paint/gc).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 15:45:59 +02:00
Admin
d3d110df7b perf 2026-07-10 15:34:38 +02:00
Admin
5d1c228c4b macos: pace paint timer to display refresh; PerfGraph gpu channel + pass trace
The fixed 8ms timer0 presented ~125Hz into a 120Hz vsync queue: the drawable
pool drifted full and nextDrawable blocked the main thread in a ~25-frame
sawtooth (PerfGraph 'wait' ramps). Timer now arms at 1.002/max_fps across
attached NSScreens — the +0.2% makes NSTimer lateness drain the queue instead
of accumulating. Sawtooth verified gone.

perf_monitor: 'gpu' channel — presented-frame GPU interval tapped from the
existing command-buffer completion aggregation (atomic hand-off, folded at
frame_boundary; concurrent with CPU channels, plotted violet).

metal: MAKEPAD_GPU_PASS_TRACE=1 logs per-pass GPU time ([gpu-pass] name ms)
for frame-budget hunts.

Measured (gamemaker, empty world vs full racing game — identical ~4.7ms GPU):
the frame cost is the UI glass pipeline, not the game — scene capture +
6 mip downsamples + 3 smooth upsamples re-run every frame at 120Hz because
the game pane dirties the window; at low GPU clocks the 11-pass chain's
latency swings past the 8.3ms budget in phases.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 15:30:08 +02:00
Admin
2c15305f03 PerfGraph: generic frame profiler widget + Cx perf monitor channels
platform: Cx.perf_monitor — per-frame ring (240) of paint-to-paint gap +
per-channel CPU us. Built-in channels: event dispatch (outermost, minus
app-attributed time), script exec, GC, pass encode, nextDrawable wait.
Apps register custom channels: cx.perf_monitor.channel("physics", rgb).
Off until enabled; hooks in event dispatch, macos repaint, metal draw_pass.

widgets: PerfGraph — corner-pinned live panel (DrawVector strips): frame-gap
bars colored against 120/60Hz budgets with guide lines, stacked per-channel
CPU, legend with averages. Self-positions bottom-right (DrawVector geometry
+ deferred turtle alignment don't mix — no aligning parent).

gamemaker: PerfGraph hovers the game pane (F3 toggles), engine feeds script
+ physics channels (incl. hot-reload evals); engine text overlay moved to
F4; per-phase engine window kept for ag perf / AIGAME_PERF=1; new 'ag perf'
harness verb + template guidance (template CLAUDE.md force-added: runtime
resource, blanket CLAUDE.md gitignore had kept it untracked).

Measured on my-game-5: engine frame CPU ~0.3ms; the hiccup is frame pacing —
the 8ms NSTimer paint clock beats against the 120Hz display, the drawable
pool drifts full and nextDrawable blocks the main thread in a ~25-frame
sawtooth (avg 2-3.4ms, spikes 20-30ms). The graph shows it as red ramps.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 15:10:27 +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
admin2
dfacfb5d51 Clean unused workspace patch warnings 2026-07-08 12:48:33 +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
Admin
56cf3bc2e6 aichat 2026-06-25 18:52:10 +02:00
damien
51f9a78d85 fix: hot reload broken when file contains script_apply_eval! calls (#1124)
script_apply_eval! expands to a ScriptMod{file, line, column, ...} with
its code field prefixed by __script_source__. This adds a runtime body
to the VM just like script_mod! does, so collect_compiled_sites_for_file
counts it toward the compiled-site total for that file.

The file extractor (extract_script_mods_from_rust_file) only scans for
the script_mod! macro pattern, so any file with N script_apply_eval!
calls and M script_mod! calls causes:

  hot reload could not match script_mod! blocks for <file>:
  runtime has N+M, file has M

Skip __script_source__ bodies in collect_compiled_sites_for_file. These
are Rust-driven runtime evals, not static DSL blocks, so the extractor
cannot match them and hot reload cannot update them anyway.

Fixes hot reload for files that mix script_mod! with script_apply_eval!.
2026-06-16 09:09:08 +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
Kevin Boos
e93aba1161 iOS: replace custom UITextInput with a native UITextView (#1121)
* 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)
2026-06-12 09:12:26 +02:00