Skip to content

Linux port - #28

Merged
oneup03 merged 29 commits into
direct_experimentfrom
linux_port
Jul 12, 2026
Merged

Linux port#28
oneup03 merged 29 commits into
direct_experimentfrom
linux_port

Conversation

@oneup03

@oneup03 oneup03 commented Jul 11, 2026

Copy link
Copy Markdown
Owner

No description provided.

oneup03 and others added 29 commits July 3, 2026 22:37
Probe driver proves Linux vrcompositor drives IVRDriverDirectModeComponent:
NewSharedVulkanImage swap sets accepted, SubmitLayer/Present at display
cadence, dmabuf import viable on RADV. See vrto3d/probe/RESULTS.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…shaders, CMake split

- Shared driver sources (hmd_device_driver, device_provider, osd_menu,
  json_manager, debug_log, uevr_receiver, app_id_mgr) gated for Linux;
  Windows code paths unchanged.
- VkRenderer + DirectModeComponentVk: dmabuf swap-texture exchange via
  IVRIPCResourceManagerClient (single-layer Linux model per M0 findings),
  blit+OSD+repack pipeline, stb PNG screenshots, vsync tick thread.
- Portable keybinds: new Key_*/Pad_* vocabulary, legacy VK_*/XINPUT_*
  parsed + migrated on save; evdev global input backend (kb/mouse/pad,
  xkbcommon text) behind vrto3dlib/input_state.h.
- ImGui Vulkan OSD backend + evdev OSD input.
- GLSL repack shader (all output modes + display correction) compiled to
  SPIR-V headers; wayland protocol sources generated and vendored.
- linux_helper.hpp mirrors win32_helper surface (/proc, SIGTERM shutdown,
  steam path, OpenXR runtime json, hotkey evaluator).
- XRandR frame-packed modeline helper + EDID firmware generator + docs +
  Linux CI workflow. CMake platform split with bin/linux64 staging.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…; frame-packed modeline wired into X11 presenter

- WaylandPresenter: layer-shell overlay surface (topmost + output pick on
  KDE/wlroots) with xdg_toplevel set_fullscreen fallback (GNOME).
- X11Presenter: XRandR output selection, borderless FULLSCREEN+ABOVE
  window, DualDisplay spanning via _NET_WM_FULLSCREEN_MONITORS, runtime
  frame-packed modelines via x11_modeline (restored on shutdown).
- Shared SwapchainBundle (FIFO, OUT_OF_DATE recreation) used by both.
- platform_linux: XRandR/wl_output monitor enumeration, /proc helpers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Without LANGUAGES C the generated xdg-shell/layer-shell .c files were
silently dropped from the build, leaving xdg_surface_interface undefined
and vrserver failing to dlopen the driver.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Import compositor sRGB images with their UNORM twin and prefer a UNORM
swapchain, so no implicit sRGB decode/encode happens anywhere — bytes in
equal bytes out, matching the Windows D3D11 path (whose repack math and
Dubois anaglyph matrices operate on gamma-encoded values).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ver)

New WibbleWobblePresenter (IVkPresenter) selected by output_mode==WibbleWobble
or VRTO3D_PRESENTER=wibblewobble. Renders the canonical side-by-side into a
ring of exportable dmabuf images and hands them to a running wwserver over
libwwclient (dlopen'd — optional runtime dep), which does the frame-sequential
presentation + shutter-glasses sync. Blit-to-linear-dmabuf for portable
cross-process import; implicit dmabuf sync orders wwserver's sampling.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The X11 output window was a normal managed InputOutput window, so a WM could
park keyboard focus on it — starving a flat game underneath of keys, with no
give-back (request_game_focus is a no-op on Linux). Set InputHint+input=False
so the WM never focuses our pure display surface. OSD clicks/hotkeys are
unaffected — Linux OSD input comes from the global evdev backend, not X11
window focus. Mirrors the Wayland layer surface's keyboard_interactivity=NONE.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…op peek, hide dead OSD controls

- Presenters: overlay is click-through by default (X11 empty XShape input
  region + input=False; Wayland empty wl_surface input region + kb NONE) so
  mouse+keyboard reach the flat game. SetInputCapture(true) on OSD-open takes
  pointer+keyboard so GUI clicks/typing don't leak to the game (OSD still
  reads evdev). SetAlwaysOnTop(false) lowers the overlay (X11 XLowerWindow +
  drop _NET_WM_STATE_ABOVE; Wayland layer BACKGROUND) to peek at the game.
- VkRenderer edge-tracks menu-visible + man_on_top on the present thread and
  drives the two presenter calls; on-top defaults true on Linux so the OSD
  'Always on Top' checkbox is meaningful (uncheck = peek).
- Gamepad GUI nav confirmed already wired (buttons/dpad/sticks/triggers ->
  ImGui nav, NavEnableGamepad + HasGamepad).
- OSD: hide Linux-inert controls (Auto Focus, Hide/Lock Cursor, LeiaSR head-
  tracking section) and filter LeiaSR/NvidiaDX9 out of the output-mode
  dropdown while preserving enum mapping. Link libXext for XShape.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…onnect)

Corrects the earlier always-on-top-by-default. VkRenderer's focus block now
replicates WindowPresenter::FocusThreadLoop: overlay starts lowered; when an
app connects to SteamVR it auto-raises (once per app PID, latching
is_on_top+man_on_top so Ctrl+F8 can still drop it); man_on_top overrides; OSD
open forces on-top+capture. Linux Activate no longer defaults on-top true.
Dropped the unconditional BringToTop at present-thread start.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Byte-preserving UNORM-everywhere was still dark: the SteamVR Linux compositor
hands direct-mode eye images as LINEAR data, so presenting them raw through a
UNORM swapchain makes KWin interpret linear values as sRGB and darken them.
Prefer an _SRGB swapchain so the final present store sRGB-encodes exactly once
(imports/out_sbs stay UNORM/raw). Needs on-hardware visual confirmation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Converted all 108 vrto3d_profiles/*_config.json from legacy VK_*/XINPUT_*
to the portable Key_*/Pad_* vocabulary via tools/migrate_profiles.cpp (reuses
the loader's vrto3d::keys::MigrateName, handles + combos), matching the
migrated default_config. No behavioral change — the loader already accepted
both spellings; this makes the shipped canonical format consistent and avoids
a silent rewrite on each user's first save.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
#1: Wayland/X11 presenters had identical AcquireNext/Present/RenderPass/
Extent/Format delegations + ctx_/swapchain_ members. Hoisted into
SwapchainPresenterBase; each presenter now implements only its native
windowing surface. WibbleWobblePresenter stays a direct IVkPresenter (no
swapchain).

#3: The want-on-top state machine existed in both WindowPresenter::
FocusThreadLoop (Windows) and VkRenderer's Linux focus block and had to be
hand-synced. Extracted to focus_policy.h ComputeWantOnTop() (pure, per-caller
FocusLatchState); both call it. Behavior identical.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Refactor #2: hoist the repeated 'create image -> get mem reqs -> find
memory type -> allocate -> bind -> make view' sequence into vk_context:
Image2D + AllocateBindImageView (with alloc_pnext / extra_type_bits for
dmabuf import/export chains) + CreateImage2D for the plain device-local
case. vk_renderer's out_sbs target and the wibblewobble render target
now use CreateImage2D; the dmabuf import/export paths keep their bespoke
memory-type logic and call AllocateBindImageView directly.

Also bumps the VRto3DLib submodule to pick up the ApplyUserSettingsHotkeys
dedup (refactor #4).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Steam Deck usability (no keyboard):
- Add an always-polled Start+DPad-Up gamepad chord mirroring Ctrl+F8
  (toggle always-on-top). This is the direct fix for a lowered/buried
  overlay 'disappearing' with no keyboard way back — it counterparts the
  existing Start+DPad-Down menu-open chord and is polled regardless of
  disable_hotkeys so recovery always works.
- Harden the X11 re-raise: SetAlwaysOnTop(true) now also sends a
  _NET_ACTIVE_WINDOW request with source=pager, which KWin honors even
  from a non-focusable background window (a plain XRaiseWindow can be
  swallowed by focus-stealing prevention, leaving the overlay stuck
  behind the Plasma desktop). input=False keeps it from grabbing keys.
- Add a 'Reset Defaults' button in System > Misc that restores VRto3D's
  built-in factory stereo/shader/tracking tunables while preserving the
  display/output hardware fields. Distinct from 'Reload Default Cfg',
  which re-reads the user's possibly-customized default_config.json.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The runtime keybind loader already migrates legacy VK_*/XINPUT_* names
to the portable vocabulary transparently and canonicalizes them in place
on the next profile save (VRto3DLib key_names MigrateName + ParseBindName).
That makes this dev-only bulk converter — used once to migrate the 108
shipped profiles (commit 0d1395b) — redundant. Dropping it keeps the
migration story to a single self-healing path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-top)

Investigating 'overlay stays on top after the game exits' when SteamVR
survives the game (returns to Home/void). The disconnect->lower path is
correct and fires (Unload logged), but nothing logs the actual focus
raise/lower, and only accepted app connects are logged — so we can't see
what becomes the scene app after the game exits and whether we spuriously
auto-raise for it (on Linux the game filter is just 'non-empty name').

- vk_renderer focus block: log every RAISE/LOWER with the FocusInputs
  snapshot (is_on_top/man/auto/pid/running/menu).
- device_provider: include event type + pid on accepted connects, and log
  connect/scene-app-change events that are ignored (skip-list or filter)
  with the resolved process name — this reveals the fallback scene app.

Diagnostic only; no behavior change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e gate

Root cause of 'went on top for the game but never lowered after exit':
the present-thread focus/z-order block sat *below* the 'if (!have_frame)
continue;' gate. On app disconnect the driver both queues the LOWER
(LoadSettings clears is_on_top_/man_on_top_) and pauses the renderer
(OnAppDisconnect -> paused_for_disconnect_ -> OnDirectModeFrame drops all
frames). With no frames, have_frame stays false and the loop continues
before ever reaching the focus block, so SetAlwaysOnTop(false) is never
called and the window stays in its last state (raised).

Move the focus/z-order/input-capture block above the have_frame gate so
it runs every loop iteration (incl. the 50ms no-frame timeouts while
paused), applying the queued lower promptly. The block touches only the
display connection and focus atoms — no frame data — so it's safe to run
frameless. Also makes 'start lowered' apply before the first frame.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@oneup03
oneup03 merged commit 268a79f into direct_experiment Jul 12, 2026
1 check passed
@oneup03
oneup03 deleted the linux_port branch July 12, 2026 23:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant