Commit Graph
2581 Commits
Author SHA1 Message Date
Zane Schepke e01e71027f fix(linux): address review feedback on install/remove scripts
Per kdroidFilter's review on #664:

- Drop jdk.jlink filtering from AbstractJLinkTask (JEP 493 fix) — unrelated
  to this PR, belongs in its own change.
- Drop nativeDistributions.appContent / jpackage --app-content — incomplete
  API (no KDoc, no tests, JVM-only, flattens subdirectories) and redundant
  with appResourcesRootDir + afterInstall for the systemd-unit use case this
  was meant for.
- Expand KDoc on the four linux { afterInstall/afterRemove/beforeInstall/
  beforeRemove } properties to document, on the properties themselves
  rather than only in CLAUDE.md: electron-builder's single-quote-only macro
  substitution for afterInstall/afterRemove, that beforeInstall/beforeRemove
  get no macro substitution at all (raw fpm args), and the Pacman pre_remove
  upgrade-detection caveat.
2026-09-17 13:51:47 -04:00
Zane Schepke 6a81fcb9e6 Merge remote-tracking branch 'upstream/main' into feat/linux-install-scripts
# Conflicts:
#	CLAUDE.md
2026-09-17 13:47:19 -04:00
Elie Gambache e28fe04f85 docs: add ZonePane to Used by
Closed-source desktop client on Nucleus 2.5 with the Tao backend;
link the homepage rather than a GitHub repo, as requested in #596.
2026-09-17 16:27:08 +03:00
Elie Gambache 351bff8ce6 docs: add live GitHub star badges on the larger Used-by apps
Shields.io github/stars badges, so the counts stay current without
hardcoding them.
2026-09-17 13:11:53 +03:00
Elie Gambache e2f2ab7eb1 docs: list the exact Nucleus modules each Used-by app depends on
Call out the Tao backend when decorated-window-tao is on the classpath,
and the legacy JNI window backend for Flare.
2026-09-17 13:04:12 +03:00
Elie Gambache 67c78809c5 docs: expand Used by with more Nucleus consumers
Include apps that take a single module (notifications, tray, packaging)
alongside full-stack desktop apps, so the list shows the runtime is usable
piecewise.
2026-09-17 12:57:14 +03:00
Elie Gambache b60f23dbfc Merge pull request #665 from wgtunnel/feat/graalvm-headless-awt-skip
feat: graalvm headless optimization
2026-09-17 12:22:29 +03:00
Elie Gambache e3246b526d Merge pull request #672 from NucleusFramework/fix/670-egl-gl33-fallback
fix(tao): core-profile GL 3.3 then llvmpipe fallback on Linux EGL (#670)
2026-09-17 12:19:51 +03:00
Elie Gambache 0e49b42b3d fix(tao): fall back to a core-profile GL 3.3 context, then llvmpipe, on Linux EGL
Closes #670.

The X11 and Wayland attach paths asked for one thing only — a GL 3.3
compatibility-profile context — and returned 0 when the driver refused, which
aborts the window on the JVM side. Mesa r600 on an AMD RV610 (Debian 13) caps
the compat profile at 3.0, so the app crashed on launch.

- `create_gl_context()` (shared by both paths) asks for 3.3 compat, then 3.3
  core. Core is strictly more available (a 3.2+ driver must offer it, compat
  is optional), nothing here or in Skia needs the legacy entry points, and
  r600 reaches 3.3 in core — so this GPU stays hardware-accelerated.
- When neither profile is available, `fall_back_to_software()` sets
  `LIBGL_ALWAYS_SOFTWARE=1`, terminates the display and the attach re-enters
  itself once from `eglGetPlatformDisplay`: Mesa reads the variable in
  `eglInitialize` (driver load), not at context creation, so a bare retry of
  `eglCreateContext` would not pick llvmpipe up. Process-wide and one-shot;
  skipped when the user already set the variable.
- Wayland tears its subsurface chain down before retrying (flag consumed at
  `fail_after_subsurface`).

Both branches log one line on stderr so the chosen path is visible in a
bug report. Existing hardware that accepts the compat request is untouched.
2026-09-17 11:49:30 +03:00
Zane Schepke 525cbabeea feat(linux): after/before install/remove scripts
Wire the after-install/after-remove/before-install/before-remove scripts
for the GraalVM native-image packaging path too, matching the JVM/jpackage
path (previously only JVM was wired).
2026-09-08 03:13:17 -04:00
Zane Schepke 076291b0df feat: graalvm headless optimization
Add docs for changes
2026-09-08 03:12:51 -04:00
Elie Gambache 6b7a1092e8 Merge pull request #656 from NucleusFramework/fix/652-654-macos-trackpad-scroll
fix(tao): match AWT trackpad scrolling on macOS and surface Pan events
2026-09-07 07:06:57 +03:00
Elie Gambache 412ab0d490 feat(demo): Trackpad Lab tab — manual rig for #652 / #653 / #654
One screen in nucleus-demo to test the macOS trackpad work by hand:
a root inspector (every Scroll / PanStart / PanMove / PanEnd with gaps,
counters and one summary per gesture incl. how long after the last move
the PanEnd came), vertical and horizontal strips for sign and magnitude,
a map canvas that pans on Pan and zooms on Scroll, a scrollable
DropdownMenu (inline, or NSPanel via "Open with native popup layers"),
and an embedded WKWebView whose page draws its own scrollY / wheel HUD.
The header shows density, px per wheel unit and the trackpadPanEvents
flag. NUCLEUS_DEMO_TAB=<tab> opens the demo straight on a tab.

Adds the composewebview dependency to nucleus-demo (in-tree Nucleus
modules excluded, as in tao-demo).
2026-09-06 15:02:44 +03:00
Elie Gambache 1ffdf8c7fd fix(tao): one timer per pan, each AppKit delta to a native view once (review 5)
- native_view.m: an AppKit event is marked spent only once its delta has
  actually travelled (replayed, or synthesised by the step that carries it),
  so an Ended-with-delta arriving with no pan open (PanStart + PanMove from
  one event) no longer loses the finger movement; per-child gesture state,
  keyed on the child handle rather than a raw NSView*, keeps a deferred
  PanEnd from sending a second terminal phase and resets the sub-point
  residue at gesture boundaries
- NativeView hands pan offsets over in scene px; the macOS host converts
  them back with its own scale, so an app-level LocalDensity override no
  longer skews the embedded view's distance. dispatchScrollToNative is back
  to its original signature (Windows / Linux untouched)
- TaoTrackpadPanRouter: the pan end is a deadline, one timer in flight;
  finger and momentum steps only move the deadline, the grace re-schedules
  once (earlier), the timer re-arms for the remainder — no coroutine, wake
  and cancel per 120 Hz step
- TaoSceneScrollRouter: a zero-delta orphaned tail end is not turned into a
  Scroll (AWT drops zero deltas); once-only announce on a plain volatile;
  the redundant sceneBundle guard in onPointerScroll is gone (the router's
  cancelled flag is the contract)
- TaoPopupSceneLayer documents why the pan uses the layer density while the
  surface uses host.scale; TaoWindow uses import aliases for the ABI alias
  constants; the injector checks the main thread before its static init
- headful: shared ScrollableColumn / ScrollableRow fixtures and a harness
  awaitUntilOrTimeout replace the copies; the wheel-notch baseline is taken
  right before the injection; the library drift test no longer reads the
  demo source
2026-09-06 14:13:48 +03:00
Elie Gambache 72ba91bca9 fix(tao): deliver each AppKit scroll event to a native view once, carry an orphaned tail (review 4)
- native_view.m remembers the AppKit event it last handed to a child
  (NSApp.currentEvent is not cleared while the app idles, and one event can
  yield two Compose steps), so a deferred PanEnd or a PanStart+PanMove pair
  never applies a delta twice; the synthesised fallback keeps a per-view
  sub-point residue instead of rounding slow drags to zero
- popup_panel.m and the vendored view.rs test NSEventPhase bits instead of
  switching on the NS_OPTIONS mask
- TaoSceneScrollRouter leaves the pan position alone for MayBegin (its
  PanEnd belongs to the previous gesture); an orphaned momentum tail is
  delivered as AWT-shaped Scroll instead of dropped; once-only CAS behind a
  plain read; scrollRouter declared with the rest of the host state
- TaoStandalonePopupHostMac.dispose() is idempotent on the invalid path too
- TaoWindow keeps AWT_PIXEL_TO_ROTATION / MACOS_AWT_SCROLL_AMOUNT as
  aliases of the shared constants (public statics in the validated ABI)
- demo ScrollTestScreen finalises a gesture inline on PanEnd / PanStart
- TaoScrollWireDriftTest also guards the kNv* native-view wire and the
  10-units-per-wheel factor in events.rs, native_view.m and the demo;
  TaoSceneScrollTest pins MacOSCocoaConfig's 10 dp per unit on macOS;
  harness routers get one timer slot each
2026-09-06 13:10:02 +03:00
Elie Gambache f93cc8cbdb fix(tao): give native views the whole pan, never stack a late momentum tail (review 3)
- NativeView forwards PanStart / PanMove / PanEnd with a phase; native_view.m
  replays the live AppKit event only when its phase class matches and
  otherwise synthesises a phased scroll, so an embedded scroll view sees the
  gesture begin and end (rubber-band, scroller fade) — Windows / Linux ignore
  the phase
- popups keep the gesture phase whatever hasPreciseScrollingDeltas says,
  like the Rust window path
- TaoTrackpadPanRouter: momentum steps only continue an open pan; a tail
  arriving after the grace already closed it is dropped instead of stacked on
  Compose's fling
- TaoSceneScrollRouter: lazily created timer scope with
  TaoNonFatalCoroutineExceptionHandler (a broken deferred PanEnd costs one
  gesture, not the app); TaoScrollGesturePhase.fromWire is a map lookup
- TaoStandalonePopupHostMac: finishPan() runs inside the frame pump; the
  invalid-host dispose path also shuts the render executor down
- nativeDiagInjectScrollWheel targets the NSView it is handed, is nil-safe on
  the primary screen, inlined; documented as whole-point only (CGEvent delta
  fields are integers — verified) and the headful momentum tail uses whole
  points
- demo ScrollTestScreen closes a gesture on PanEnd / next PanStart and names
  the 10 dp factor; harness closes the pan after the cursor-move guard like
  the host; TaoScrollWireDriftTest resolves its sources per test with a
  readable failure; stray @Suppress dropped
2026-09-06 12:45:36 +03:00
Elie Gambache 980eb8890d fix(tao): bound every trackpad pan, keep popups and native views in step (review 2)
- TaoTrackpadPanRouter: an open pan always has an end timer (150 ms grace
  after the finger Ended, 1 s stall watchdog otherwise); MayBegin during the
  momentum tail closes the pan at once; finishNow() for clicks / wheel
  notches; TaoScrollGesturePhase is now an enum (wire codes attached),
  distinct from the public TaoTrackpadPhase, unknown codes degrade to a
  plain precise scroll
- loop: the phase decides the route for the whole gesture — a step that
  arrives in lines is scaled to its point equivalent instead of being
  dropped as a stray wheel Scroll; dispatch_scroll_gesture loses its bogus
  dead_code allow; view.rs drops the dead ViewState binding
- TaoSceneScrollRouter: pan position / modifiers come from gesture steps
  only (PanEnd must hit the node that got the PanMoves), a click or a wheel
  notch closes the pan, cancel() makes late events no-ops, the timer scope
  carries TaoFatalCoroutineExceptionHandler; TaoPopupSceneLayer reads the
  live layer density; TaoStandalonePopupHostMac drops the native callback
  before cancelling the router; first Pan routing logs one CONFIG line
- NativeView consumes PanStart / PanEnd over an embedded view so the
  ancestor scrollable never opens a session of its own
- popup_panel.m: NucleusScrollGesture enum instead of literals;
  TaoScrollWireDriftTest compares Rust, ObjC and Kotlin wire codes plus the
  JNI descriptors
- nativeDiagInjectScrollWheel is inert unless NUCLEUS_TAO_INPUT_INJECTION=1
  (set by taoHeadfulTest) and main-thread only
- README / CLAUDE.md document the Pan behaviour and
  -Dnucleus.tao.trackpadPanEvents=false
2026-09-06 11:53:30 +03:00
Elie Gambache 79d91fe7d9 fix(tao): route macOS scroll input through one router, keep popups in step (review)
Review follow-ups on #652 / #653 / #654:

- TaoSceneScrollRouter: single front door for wheel / trackpad input shared
  by the window host and both NSPanel popup hosts; popup_panel.m now forwards
  the AppKit phase so a two-finger swipe over a popup list behaves like the
  window behind it. `-Dnucleus.tao.trackpadPanEvents=false` restores
  AWT-style Scroll events for handlers that only know PointerEventType.Scroll
- tao patch 0007: PixelDelta carries AppKit's logical points, so the loop no
  longer locks WINDOWS per event nor round-trips through two scale caches
- TaoTrackpadPanRouter: terminal steps with a delta still pan when no gesture
  is open; momentum grace 150 ms, tunable via
  -Dnucleus.tao.trackpadMomentumGraceMillis; isPanning removed
- NativeView forwards only non-zero PanMove (PanStart / PanEnd replayed
  NSApp.currentEvent, stale for the deferred end)
- host detach cancels the router's timer scope and ignores scrolls after
  the scene is gone
- TaoWindow: one preciseScrollEvent() shaping helper, shared AWT constants
  (apiDump: the two leaked private-companion fields are gone)
- headful cases: recorder reset per run, display-mode restore even when the
  window never reports the new scale; JNI descriptor drift guard for
  popup_panel.m; demo ScrollTestScreen logs Pan steps too
2026-09-06 10:45:13 +03:00
Elie Gambache 5b04066e7c fix(tao): match AWT trackpad scrolling on macOS and surface Pan events (#652, #653, #654)
Horizontal trackpad scrolling was reversed and precise deltas were scaled
by the display factor, both relative to the AWT backend; trackpad gestures
were indistinguishable from wheel notches.

- vendored tao (patch 0007): stop negating scrollingDeltaX in the macOS
  scroll_wheel (AppKit already follows the MouseScrollDelta convention, as
  winit does) and carry the full AppKit phase / momentumPhase on
  WindowEvent::MouseWheel as `scroll_phase: ScrollPhase`
- loop: hand SCROLL_PIXEL over in logical points (AWT never applies the
  display scale to preciseWheelRotation) and route phased steps to a new
  EventCallback.onScrollGesture
- host: TaoTrackpadPanRouter turns the gesture stream into Compose
  PanStart / PanMove / PanEnd with panOffset = AWT delta x 10 dp, keeping
  the pan open across AppKit's momentum tail (deferred PanEnd) so Compose
  does not stack its own fling; wheel notches and phase-less precise
  scrolls stay Scroll events; NativeView forwards Pan to the native view
- popups: appKitWheelToAwtScrollDelta flips both axes and drops the scale
- headful e2e: MacOsTrackpadScrollHeadfulCases inject real scrollWheel:
  NSEvents into the tao content view (nativeDiagInjectScrollWheel), the
  #653 case flipping the display to its HiDPI twin; plus router, scene and
  wire unit tests
2026-09-06 10:04:04 +03:00
Elie Gambache 9d9ce7fef5 Merge pull request #648 from NucleusFramework/fix/636-composable-target-window-scope
fix(application): keep window content on the UI applier (#636)
2026-09-04 17:57:00 +03:00
Elie G. 412666372e fix(application): keep window content on the UI applier (#636)
The window and dialog openers had their composition target inferred, so a
single non-UI-targeted composable called in the `nucleusApplication` scope
(the Compose compiler bakes `@ComposableTarget` onto any unmarked factory
forwarding a target-marked content lambda) reclassified the whole scope —
nested windows included — and every `@UiComposable` call in it warned, which
is fatal under `-Werror`.

Declare each opener `@ComposableOpenTarget(-1)` with a `@UiComposable`
content lambda, the way Compose Desktop's own `Window`/`Dialog` are open:
they are callable from any applier and always compose UI content in the new
window's own composition, so neither direction of the applier leak survives.
Covers `DecoratedWindow`/`DecoratedDialog`, the `NucleusWindowHost` /
`NucleusDialogHost` interfaces with their default implementations,
`HostedWindow`/`HostedDialog`, and Tao's `DecoratedWindow`,
`DecoratedDialog` and `TaoStandalonePopup`. The Material/Jewel wrappers
inherit the same scheme through inference (`[_[UiComposable]]`).

`COMPOSE_APPLIER_CALL_MISMATCH` is only a warning, so both test compilations
escalate it to an error and a fixture per module compiles the reported shape
(non-UI factory in the application scope, UI content in the windows).
2026-09-04 15:34:10 +03:00
Elie Gambache 15f0028219 Merge pull request #645 from NucleusFramework/fix/644-windows-ime-taskbar-reentrancy
fix(tao): release Windows input locks before pumping calls (#644)
2026-09-04 14:13:27 +03:00
Elie G. 3c105d2de7 fix(tao): release Windows input locks before pumping calls (#644)
Two more instances of the #640 bug class, both fixed upstream and ported by
hand — a non-reentrant lock held across a call that pumps or synchronously
sends messages, so a nested window-procedure dispatch re-locks it and the
event-loop thread parks in `WaitOnAddress` for good.

- IME: the window-state lock was held across `ImeHandler::process_message`,
  which peeks the queue on `WM_IME_ENDCOMPOSITION` (the Korean IME sends the
  committed string after the end message, so the queue decides whether to
  defer). The peek now happens in `ime::peek_commit_queued` before the lock is
  taken, and the result is passed in — mirroring upstream, where "IME
  character-pending detection happens before acquiring window state locks".
  Wider blast radius than #640: there only the keyboard arm took the lock,
  here nearly every arm does, so any nested message deadlocked.
  Upstream: tauri-apps/tao#1215.
- `TaskbarCreated`: the window-state lock was held across `set_skip_taskbar`,
  which goes through `CoCreateInstance` + `ITaskbarList`. An STA COM call pumps
  the message queue while it waits, so the window procedure is re-entered.
  The flag is now read out before the call. Upstream: tauri-apps/tao#1264.

`Imm32Source` carries the precomputed flag instead of peeking on demand;
`ImeSource` is unchanged, so the IME state-machine tests keep driving the same
sequences through their stub.
2026-09-04 13:58:26 +03:00
Elie Gambache 9fc67f0892 Merge pull request #642 from NucleusFramework/fix/640-windows-keyboard-layout-cache-reentrancy
fix(tao): never hold key-event locks across PeekMessageW on Windows (#640)
2026-09-04 13:48:59 +03:00
Elie G. c20018a511 fix(tao): never hold key-event locks across PeekMessageW on Windows (#640)
`PeekMessageW` delivers pending cross-thread *sent* messages inline: the
kernel re-enters the window procedure through `KiUserCallbackDispatcher`
before the peek returns. Two non-reentrant `parking_lot::Mutex`es were held
across such a peek, so the nested dispatch deadlocked the event-loop thread
against itself — it parked in `WaitOnAddress` and never pumped a message
again, leaving the window permanently "Not Responding".

- `event_loop.rs`: the keyboard callback held the global `KEY_EVENT_BUILDERS`
  map across `KeyEventBuilder::process_message`, which peeks. The builder is
  now taken out of the map for the duration and put back afterwards (only if
  the slot still exists, so a window dropped re-entrantly is not resurrected).
  Its map value became an `Option` slot to make that take/put-back possible.
- `keyboard.rs`: the key-press and key-release arms held `LAYOUT_CACHE` across
  their peek, while every other arm — and `update_modifiers`, reached from any
  mouse or focus message — locks it too. The guard is now scoped to end before
  the peek and re-acquired after it.

Reported as a freeze when moving a window between Windows 11 virtual
desktops: that path is keyboard-triggered and makes the shell send messages
to the window mid-peek. The reporter's stack shows the two nested window
procedure chains around `NtUserPeekMessage` and the park in `WaitOnAddress`.

Only injected or sent key messages can nest here — real keyboard input is
posted to the queue rather than sent, and the peeks use `PM_NOREMOVE` — so a
nested key message now finds an empty builder slot and is dropped instead of
hanging the process.
2026-09-04 12:57:15 +03:00