feat: xquartz forwarding - #78
Merged
Merged
Conversation
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.
…iner cli centric dev container
…iner cli centric dev container
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.
XQuartz forwarding: env/compose cleanup + docs restructure
matejstastny
approved these changes
Aug 7, 2026
matejstastny
left a comment
Member
There was a problem hiding this comment.
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)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.