Skip to content

feat: xquartz forwarding - #78

Merged
matejstastny merged 40 commits into
mainfrom
feat/xquartz-forwarding
Aug 7, 2026
Merged

feat: xquartz forwarding#78
matejstastny merged 40 commits into
mainfrom
feat/xquartz-forwarding

Conversation

@CPrutean

@CPrutean CPrutean commented Aug 5, 2026

Copy link
Copy Markdown
Member

Modified the workflow for the container to forward X11 windows through Xquartz VirtualGL. Additionally fixed a few small issues in chrono but the rest of the work is to be done in a seperate branch.

The DISPLAY normalization prepended ':' to anything not already starting
with one. That was meant for environments exporting a bare number ("1"),
but it also mangled hostname-qualified specs such as
"host.docker.internal:0" into ":host.docker.internal:0", breaking X11
forwarding to a remote X server.

Narrow the match to bare numbers so remote specs are left alone.
Docker Desktop cannot bind-mount the host's X11/Wayland socket into the
container on macOS or on Windows without WSL2/WSLg, so GUI windows had no
way to reach the host and fell back to the internal VNC stack.

Forward them over the X11 protocol's TCP transport instead, reachable at
host.docker.internal, with new devcontainer variants mirroring the
existing nvidia one.

To support this, generalize x_server.sh's Wayland-only passthrough check
into try_display_passthrough(), which also tries a generic X11 display
before falling back to Xvfb/VNC. As a side effect this also makes native
Linux X11-without-Wayland hosts work.

Also start VirtualGL's 3D X server when passthrough lands on a remote
display: XQuartz and VcXsrv can show windows but cannot provide a usable
OpenGL context, so GL has to be rendered container-side instead. It
degrades gracefully when VirtualGL isn't installed.

Two bugs this surfaced:
  - Xvfb cannot bind a remote spec like "host.docker.internal:0", so
    normalize DISPLAY back to ":0" before starting the internal server.
  - The "display already in use" guard fired on the host's own display
    and aborted startup; passthrough already proves nothing local is
    listening, so drop it.
Forwarding X11 to XQuartz/VcXsrv gets windows onto the host, but not
rendering: those servers cannot hand back a usable OpenGL context. Their
indirect GLX is deprecated and unmaintained upstream, so OGRE2 - used by
both Gazebo and RViz - failed at glXMakeCurrent with GLXBadContext and
never created a renderer. Gazebo showed a blank window and rviz2 died
with "Unable to create the rendering window".

Neither Mesa fallback helps: forcing indirect GLX hits the same dead end,
and the software/direct path cannot match XQuartz's GLX fbconfigs at all
("failed to create drisw screen").

Install VirtualGL and route rendering through it instead. GL runs against
a container-local headless X server (Mesa llvmpipe, OpenGL 4.5) and only
finished frames go to the host as ordinary X11 images, which XQuartz and
VcXsrv handle reliably. Using VirtualGL's X11 transport keeps this free
of any host-side install beyond the X server itself, and needs no extra
ports. `sim gazebo` applies the vglrun prefix automatically; LD_PRELOAD
propagates it to the Gazebo server, Gazebo GUI and RViz processes.

Rendering stays software-only - there is no GPU passthrough on these
hosts - so this buys correctness and native windows, not speed.

Verified on Apple Silicon with XQuartz: Gazebo and RViz both render, with
IGLX left disabled.
Cover the XQuartz and VcXsrv/X410 setup steps, how GL rendering is routed
through VirtualGL, and how to verify it. Note two gotchas found while
setting this up: `xhost -display :0 + ...` does not do what it looks like
(its own man page documents this as a bug), and GL apps launched by hand
on these hosts need the `vglrun` prefix that `sim gazebo` adds for you.
@CPrutean CPrutean self-assigned this Aug 5, 2026
@CPrutean CPrutean added the feature New feature or request label Aug 5, 2026
@CPrutean
CPrutean requested a review from matejstastny August 5, 2026 21:34
@matejstastny matejstastny changed the title Feat/xquartz forwarding feat: xquartz forwarding Aug 5, 2026
CPrutean and others added 19 commits August 5, 2026 16:27
sim chrono run builds chrono/ locally via make+cmake+ninja, but the
5d31df8 prebuilt-image refactor dropped those tools from the dev
image. Add them back.

Also patch FindSIMD.cmake in the vsg-chrono base image build: Chrono
unconditionally compiles with -march=native, baking in whatever SIMD
extensions the CI build machine has (SVE on GitHub's arm64 runners).
Since the image is built once and pulled onto arbitrary hardware,
this crashes with SIGILL on CPUs without that exact ISA, e.g. Apple
Silicon which has no SVE support. Skip the native shortcut on
aarch64 so it falls back to Chrono's portable -march=armv8-a NEON
detection.

This comment was marked as resolved.

@matejstastny matejstastny left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I worked on this and refined it in #85 I think it works good. Some things to test (fine after merge):

  • NVIDIA GPU detection working in the host script
  • Vanilla Windows directions working
  • WSL working (propably is just to check)

@matejstastny matejstastny added documentation Improvements or additions to documentation refactor Big structural change labels Aug 7, 2026
@matejstastny
matejstastny merged commit d1e106d into main Aug 7, 2026
6 checks passed
@matejstastny
matejstastny deleted the feat/xquartz-forwarding branch August 7, 2026 05:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation feature New feature or request refactor Big structural change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants