From 3f7f94317d7dca31d9db9d213cece626b1b74d9e Mon Sep 17 00:00:00 2001 From: Lionel Gulich Date: Wed, 7 Oct 2026 19:21:42 +0200 Subject: [PATCH 1/2] Prefer GLFW X11 backend when no Wayland display is set GLFW >= 3.4 built with both Wayland and X11 probes Wayland first when XDG_SESSION_TYPE is unset. Inside Docker this makes libwayland print "error: XDG_RUNTIME_DIR is invalid or not set in the environment." before GLFW falls back to X11. Hint the X11 platform before glfwInit() when WAYLAND_DISPLAY is unset so the probe is skipped. Co-Authored-By: Claude Opus 5.5 --- mujoco_ros2_control/src/mujoco_simulation.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/mujoco_ros2_control/src/mujoco_simulation.cpp b/mujoco_ros2_control/src/mujoco_simulation.cpp index bb0f75ed..b966c908 100644 --- a/mujoco_ros2_control/src/mujoco_simulation.cpp +++ b/mujoco_ros2_control/src/mujoco_simulation.cpp @@ -527,6 +527,21 @@ MujocoSimulation::~MujocoSimulation() } } +/** + * GLFW >= 3.4 built with both Wayland and X11 probes Wayland first unless XDG_SESSION_TYPE says otherwise. Without + * a Wayland compositor (e.g. inside Docker) libwayland prints "XDG_RUNTIME_DIR is invalid or not set" before GLFW + * falls back to X11. Skip the probe when no Wayland display is advertised. Must run before any glfwInit() call. + */ +static void preferX11WithoutWaylandDisplay() +{ +#if GLFW_VERSION_MAJOR > 3 || (GLFW_VERSION_MAJOR == 3 && GLFW_VERSION_MINOR >= 4) + if (std::getenv("WAYLAND_DISPLAY") == nullptr && glfwPlatformSupported(GLFW_PLATFORM_X11)) + { + glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_X11); + } +#endif +} + bool MujocoSimulation::initialize(rclcpp::Node::SharedPtr node, const std::string& model_path, const std::string& mujoco_model_topic, double sim_speed_factor, bool headless) { @@ -536,6 +551,8 @@ bool MujocoSimulation::initialize(rclcpp::Node::SharedPtr node, const std::strin sim_speed_factor_ = sim_speed_factor; headless_ = headless; + preferX11WithoutWaylandDisplay(); + if (sim_speed_factor_ > 0) { RCLCPP_INFO(get_logger(), "Running the simulation at %.2f percent speed", sim_speed_factor_ * 100.0); From 8d40cf154b169ec21cd612e04933f136d7c5e684 Mon Sep 17 00:00:00 2001 From: Lionel Gulich Date: Wed, 7 Oct 2026 19:35:18 +0200 Subject: [PATCH 2/2] Apply GLFW X11 hint in camera plugin as well The core library and libmujoco_ros2_control_plugins_impl.so can each carry their own statically linked GLFW (e.g. Bazel builds against BCR glfw 3.4), so the init hint set in MujocoSimulation does not reach the camera plugin's glfwInit() and the XDG_RUNTIME_DIR error is printed a second time. Move the helper into a shared header and call it before both glfwInit() sites. Co-Authored-By: Claude Opus 5.5 --- mujoco_ros2_control/src/mujoco_simulation.cpp | 18 +------- .../glfw_platform.hpp | 42 +++++++++++++++++++ .../src/camera_plugin.cpp | 3 ++ 3 files changed, 47 insertions(+), 16 deletions(-) create mode 100644 mujoco_ros2_control_plugins/include/mujoco_ros2_control_plugins/glfw_platform.hpp diff --git a/mujoco_ros2_control/src/mujoco_simulation.cpp b/mujoco_ros2_control/src/mujoco_simulation.cpp index b966c908..d88a97e2 100644 --- a/mujoco_ros2_control/src/mujoco_simulation.cpp +++ b/mujoco_ros2_control/src/mujoco_simulation.cpp @@ -20,6 +20,7 @@ #include "mujoco_ros2_control/mujoco_simulation.hpp" #include "array_safety.h" #include "mujoco_ros2_control/sim_display_text.hpp" +#include "mujoco_ros2_control_plugins/glfw_platform.hpp" #include #include @@ -527,21 +528,6 @@ MujocoSimulation::~MujocoSimulation() } } -/** - * GLFW >= 3.4 built with both Wayland and X11 probes Wayland first unless XDG_SESSION_TYPE says otherwise. Without - * a Wayland compositor (e.g. inside Docker) libwayland prints "XDG_RUNTIME_DIR is invalid or not set" before GLFW - * falls back to X11. Skip the probe when no Wayland display is advertised. Must run before any glfwInit() call. - */ -static void preferX11WithoutWaylandDisplay() -{ -#if GLFW_VERSION_MAJOR > 3 || (GLFW_VERSION_MAJOR == 3 && GLFW_VERSION_MINOR >= 4) - if (std::getenv("WAYLAND_DISPLAY") == nullptr && glfwPlatformSupported(GLFW_PLATFORM_X11)) - { - glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_X11); - } -#endif -} - bool MujocoSimulation::initialize(rclcpp::Node::SharedPtr node, const std::string& model_path, const std::string& mujoco_model_topic, double sim_speed_factor, bool headless) { @@ -551,7 +537,7 @@ bool MujocoSimulation::initialize(rclcpp::Node::SharedPtr node, const std::strin sim_speed_factor_ = sim_speed_factor; headless_ = headless; - preferX11WithoutWaylandDisplay(); + mujoco_ros2_control_plugins::prefer_x11_without_wayland_display(); if (sim_speed_factor_ > 0) { diff --git a/mujoco_ros2_control_plugins/include/mujoco_ros2_control_plugins/glfw_platform.hpp b/mujoco_ros2_control_plugins/include/mujoco_ros2_control_plugins/glfw_platform.hpp new file mode 100644 index 00000000..dd9f068d --- /dev/null +++ b/mujoco_ros2_control_plugins/include/mujoco_ros2_control_plugins/glfw_platform.hpp @@ -0,0 +1,42 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef MUJOCO_ROS2_CONTROL_PLUGINS__GLFW_PLATFORM_HPP_ +#define MUJOCO_ROS2_CONTROL_PLUGINS__GLFW_PLATFORM_HPP_ + +#include + +#include + +namespace mujoco_ros2_control_plugins +{ + +/** + * GLFW >= 3.4 built with both Wayland and X11 probes Wayland first unless XDG_SESSION_TYPE says otherwise. Without + * a Wayland compositor (e.g. inside Docker) libwayland prints "XDG_RUNTIME_DIR is invalid or not set" before GLFW + * falls back to X11. Skip the probe when no Wayland display is advertised. Must run before glfwInit(). + * + * Static so that each shared library calls the GLFW it links against; a statically linked GLFW has per-library state. + */ +static inline void prefer_x11_without_wayland_display() +{ +#if GLFW_VERSION_MAJOR > 3 || (GLFW_VERSION_MAJOR == 3 && GLFW_VERSION_MINOR >= 4) + if (std::getenv("WAYLAND_DISPLAY") == nullptr && glfwPlatformSupported(GLFW_PLATFORM_X11)) + { + glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_X11); + } +#endif +} + +} // namespace mujoco_ros2_control_plugins + +#endif // MUJOCO_ROS2_CONTROL_PLUGINS__GLFW_PLATFORM_HPP_ diff --git a/mujoco_ros2_control_plugins/src/camera_plugin.cpp b/mujoco_ros2_control_plugins/src/camera_plugin.cpp index 3a20e2f9..2e1f38da 100644 --- a/mujoco_ros2_control_plugins/src/camera_plugin.cpp +++ b/mujoco_ros2_control_plugins/src/camera_plugin.cpp @@ -19,6 +19,8 @@ #include "camera_plugin.hpp" +#include "mujoco_ros2_control_plugins/glfw_platform.hpp" + namespace mujoco_ros2_control_plugins { @@ -51,6 +53,7 @@ bool CameraPlugin::init(rclcpp::Node::SharedPtr node, const mjModel* model, mjDa // Start the rendering thread process // Try GLFW first, fall back to EGL for headless environments + prefer_x11_without_wayland_display(); if (glfw_init_fn()) { use_egl_ = false;