From a788a3babcd2076c0652ec6bb35c5016b0c99ce8 Mon Sep 17 00:00:00 2001
From: Filippo Luca Ferretti <102977828+flferretti@users.noreply.github.com>
Date: Fri, 10 Jan 2025 15:45:41 +0100
Subject: [PATCH]  Enable runtime window system selection on Linux in
 `iDynTree::Visualizer` (#1223)

Co-authored-by: Silvio Traversaro <silvio@traversaro.it>
---
 src/visualization/CMakeLists.txt     | 19 ++++++++++++++++++
 src/visualization/src/Visualizer.cpp | 29 +++++++++++++++++++++++++---
 2 files changed, 45 insertions(+), 3 deletions(-)

diff --git a/src/visualization/CMakeLists.txt b/src/visualization/CMakeLists.txt
index 7907eb7c71..ed2e6ed184 100644
--- a/src/visualization/CMakeLists.txt
+++ b/src/visualization/CMakeLists.txt
@@ -97,6 +97,25 @@ if(IDYNTREE_USES_IRRLICHT)
         find_library(IOKIT_LIBRARY IOKit)
         target_link_libraries(${libraryname} LINK_PRIVATE ${CARBON_LIBRARY} ${COCOA_LIBRARY} ${IOKIT_LIBRARY})
     endif ()
+
+    # On Linux, in some system for some reason creating a windows with X does not work, but creating it with Wayland
+    # yes. For this reason, we expose an option to permit to try to create a glfw window via wayland. This is an
+    # option and is not enabled by default as the glfw version shipped via apt with Ubuntu version before 24.10, so
+    # we expose this as an option and we enable it by default only if we are configuring inside a conda environment
+    # Once we drop support for apt dependencies on Ubuntu 24.04, we will be able to remove this code and always
+    # try wayland first
+    if(NOT WIN32 AND NOT APPLE)
+        if(DEFINED ENV{CONDA_PREFIX})
+            set(IDYNTREE_GLFW_TRY_WAYLAND_FIRST_DEFAULT_VALUE ON)
+        else()
+            set(IDYNTREE_GLFW_TRY_WAYLAND_FIRST_DEFAULT_VALUE OFF)
+        endif()
+        option(IDYNTREE_GLFW_TRY_WAYLAND_FIRST "If enabled, when creating a window iDynTree will try first to use wayland and only on failure X11" ${IDYNTREE_GLFW_TRY_WAYLAND_FIRST_DEFAULT_VALUE})
+        mark_as_advanced(IDYNTREE_GLFW_TRY_WAYLAND_FIRST)
+        if(IDYNTREE_GLFW_TRY_WAYLAND_FIRST)
+            add_definitions(-DIDYNTREE_GLFW_TRY_WAYLAND_FIRST)
+        endif()
+    endif()
 endif()
 
 if(IDYNTREE_USES_MESHCATCPP)
diff --git a/src/visualization/src/Visualizer.cpp b/src/visualization/src/Visualizer.cpp
index d76f7311f0..302ff639f9 100644
--- a/src/visualization/src/Visualizer.cpp
+++ b/src/visualization/src/Visualizer.cpp
@@ -26,6 +26,9 @@
  #define GLFW_EXPOSE_NATIVE_NSGL
 #elif defined(__linux__)
  #define GLFW_EXPOSE_NATIVE_X11
+ #if defined(IDYNTREE_GLFW_TRY_WAYLAND_FIRST)
+   #define GLFW_EXPOSE_NATIVE_WAYLAND
+ #endif
  #define GLFW_EXPOSE_NATIVE_GLX
 #endif
 
@@ -162,7 +165,7 @@ struct Visualizer::VisualizerPimpl
 #elif defined(__APPLE__)
     id m_windowId;
 #elif defined(__linux__)
-    Window m_windowId;
+    void* m_windowId; // Pointer to either wl_surface* or X11 Window
 #endif
 #endif
 
@@ -505,8 +508,28 @@ bool Visualizer::init(const VisualizerOptions &visualizerOptions)
     pimpl->m_windowId = glfwGetCocoaWindow(pimpl->m_window);
     irrDevParams.WindowId = (void*)(pimpl->m_windowId);
 #elif defined(__linux__)
-    pimpl->m_windowId = glfwGetX11Window(pimpl->m_window);
-    irrDevParams.WindowId = (void*)(pimpl->m_windowId);
+
+    void* nativeWindow = nullptr;
+
+    #ifdef IDYNTREE_GLFW_TRY_WAYLAND_FIRST
+        // Try Wayland first
+        struct wl_surface* waylandWindow = glfwGetWaylandWindow(pimpl->m_window);
+    #else
+        void* waylandWindow = nullptr;
+    #endif
+
+    if (waylandWindow)
+    {
+        nativeWindow = static_cast<void*>(waylandWindow);
+    }
+    else
+    {
+        // Fallback to X11
+        Window x11Window = glfwGetX11Window(pimpl->m_window);
+        if (x11Window)
+            nativeWindow = static_cast<void*>(reinterpret_cast<void*>(x11Window));
+    }
+    irrDevParams.WindowId = nativeWindow;
 #endif
 
     irrDevParams.DeviceType = irr::EIDT_SDL;