Files
OrcaSlicer/src/slic3r/Utils/EmbossStyleManager.hpp
SoftFever 3e4af2c723 WIP: Add native Wayland support for Linux (#13197)
* Add runtime display backend detection for Wayland support

Add LinuxDisplayBackend utility to detect X11 vs Wayland at runtime
using GDK_IS_X11_DISPLAY / GDK_IS_WAYLAND_DISPLAY macros. This is
the foundation for removing the forced GDK_BACKEND=x11 and enabling
native Wayland support.

- New files: LinuxDisplayBackend.hpp/.cpp with get_linux_display_backend(),
  is_running_on_wayland(), and is_running_on_x11()
- Propagate wxHAVE_GDK_X11 / wxHAVE_GDK_WAYLAND from FindGTK3.cmake
  as compile definitions to libslic3r_gui
- No-op on non-Linux platforms (returns Unknown / false)

* Fix Phase 1 code quality: pragma once, source ordering, static cache

* Make X11 initialization conditional for Wayland support

Remove the unconditional GDK_BACKEND=x11 force that blocked native
Wayland. Replace with conditional logic:

- EGL safety fallback: re-force X11 only when wxUSE_GLCANVAS_EGL is
  off and WAYLAND_DISPLAY is set, with a warning log
- XInitThreads() only called when DISPLAY is set (X11 in use)
- __GLX_VENDOR_LIBRARY_NAME only set when DISPLAY is present (GLX-specific)
- WEBKIT_DISABLE_COMPOSITING_MODE only set under XWayland (both
  DISPLAY and WAYLAND_DISPLAY present)
- Guard X11/Xlib.h include with __has_include for robustness
- Restore display validation to accept either DISPLAY or WAYLAND_DISPLAY

This is Phase 2 of the Wayland support plan.

* Fix Phase 2: safer EGL macro check, add clarifying comments

* Add GLAD2 library and replace GLEW linkage in build system

Set up GLAD2 as a static library to replace GLEW for OpenGL loading.
GLAD2 supports both GLX and EGL, which is required for Wayland support.

- Create src/glad/ with pre-generated GLAD2 sources (GL 4.6 compat)
- Add src/glad/CMakeLists.txt building glad as a static library
- Wire glad into src/CMakeLists.txt before libvgcode
- Modify libvgcode to use shared glad for GL path (keeps local copy
  only for GLES2/Emscripten) to avoid duplicate symbol conflicts
- Replace GLEW::GLEW with glad in libslic3r_gui link libraries

Note: GLEW is kept in deps for OpenCSG. Code migration from GL/glew.h
to glad/gl.h headers will follow in Phase 3B+3C.

* Fix Phase 3A+3D: libvgcode GLAD include, dead files, dlopen dep, OpenGL link var

* Migrate from GLEW to GLAD: replace headers and API calls across codebase

Replace all #include <GL/glew.h> with <glad/gl.h> across 49 source files.
Migrate GLEW API calls to GLAD equivalents:
- glewInit/glewExperimental -> gladLoaderLoadGL()
- GLEW_EXT_* / GLEW_ARB_* extension checks -> GLAD_GL_EXT_* / GLAD_GL_ARB_*
- Remove GLEW-specific EGL/GLX mismatch #error guards (not needed with GLAD)
- Replace unavailable EXT symbols with core GL equivalents in
  GLCanvas3D.cpp (GL_MAX_SAMPLES, glRenderbufferStorageMultisample,
  glBlitFramebuffer, GL_READ/DRAW_FRAMEBUFFER)
- Update log messages from glewInit to gladLoadGL

* Fix Phase 3B+3C: remove GLEW find, clean EXT symbols, update attribution

- Remove find_package(GLEW) block from root CMakeLists.txt since GLEW
  is no longer linked by any main application code
- Remove "glew" from SLIC3R_STATIC option description
- Replace all remaining EXT framebuffer symbols with core equivalents
  in render_thumbnail_framebuffer_ext and _rectangular_selection_picking_pass
- Update AboutDialog credits from GLEW to GLAD

* Enable EGL in wxWidgets and add runtime GLX/EGL selection for Wayland

- Set wxUSE_GLCANVAS_EGL=ON in wxWidgets build and Flatpak manifest
- Add PreferGLX() call on X11 sessions for driver compatibility
- Remove Phase 2 safety fallback (EGL is now always compiled in)
- Guard SwapBuffers against hidden canvases to prevent Wayland stalls

* Fix Phase 4: move PreferGLX to app startup, fix FPS counter guard

Move wxGLCanvas::PreferGLX() from OpenGLManager::create_wxglcanvas()
(static initializer) to GUI_App::on_init_inner() before any wxGLCanvas
is constructed. This prevents a race where SkipPartCanvas could trigger
wxGLBackend::Init() before the GLX preference is set. The new location
also adds explicit is_running_on_wayland() detection with a warning for
unknown backends.

Move increment_fps_counter() inside the IsShownOnScreen() guard so FPS
is only counted when a frame is actually swapped.

* Update GLFW from 3.3.7 to 3.4 for runtime Wayland/X11 backend selection

Replace the compile-time GLFW_USE_WAYLAND flag (which locked to a single
backend) with GLFW 3.4's GLFW_BUILD_WAYLAND + GLFW_BUILD_X11 flags that
build both backends and auto-select at runtime based on the available
display server. This enables the CLI thumbnail renderer to work on both
Wayland and X11 sessions without separate builds.

* wayland: Fix UI call sites that rely on global screen coordinates

On Wayland, wxGetMousePosition() returns (0,0) and SetPosition() is a
no-op for top-level windows. Fix the highest-impact call sites:

- GLCanvas3D: Use cached m_mouse.position from event handlers instead
  of wxGetMousePosition() + ScreenToClient() in get_local_mouse_position()
- Plater: Use event-relative coords via ClientToScreen(e.GetPosition())
  instead of wxGetMousePosition() in 3 leave-window handlers
- BBLTopbar: Use event.GetPosition() and FindToolByPosition() directly
  in mouse handlers instead of wxGetMousePosition()/FindToolByCurrentPosition()
- Search: Use focus-based dismiss logic on Wayland instead of
  wxGetMousePosition()-based rect checks in SearchDialog and
  SearchObjectDialog
- GUI_App: Skip SetPosition() in window_pos_restore() on Wayland where
  it is a no-op; still restore size and maximize state
- Button: Position tooltip relative to button widget via ClientToScreen
  instead of wxGetMousePosition()

* Fix SearchDialog Wayland dismiss: guard against search_line focus

* flatpak: Add Wayland socket permission for native Wayland support

* spec

* Fix crash on Wayland when wxWidgets lacks EGL support

Restore the safety fallback that forces GDK_BACKEND=x11 when wxWidgets
was not built with wxUSE_GLCANVAS_EGL=ON. Without this, the GLX backend
tries to access a non-existent X11 display on native Wayland, crashing
in wxGLCanvas::IsDisplaySupported() with SIGSEGV at offset 0xe4.

Also add a defense-in-depth guard in detect_multisample() that skips
the IsDisplaySupported call entirely on Wayland without EGL.

Root cause: deps/wxWidgets must be rebuilt after enabling EGL. The
compile-time check in OrcaSlicer.cpp detects the mismatch and falls
back safely.

* Fix EGL detection: use wxHAS_EGL instead of wxUSE_GLCANVAS_EGL

wxUSE_GLCANVAS_EGL is a CMake build option, NOT a C++ preprocessor
macro. The actual macro defined in wxWidgets setup.h is wxHAS_EGL.
All compile-time EGL checks were using the wrong macro, causing
the safety fallback to always trigger even with a properly built
EGL-enabled wxWidgets.

* Fix GL function pointers invalidated on Wayland/EGL

gladLoaderLoadGL() dlopen's libGL.so.1 to resolve GL function pointers
via dlsym, then immediately dlclose's the handle. On X11/GLX this is
fine because the GLX context keeps libGL.so mapped. On Wayland/EGL,
nothing else holds libGL.so open, so dlclose unmaps it and all function
pointers become dangling — causing SIGSEGV on the first GL call.

Fix: on Wayland, use gladLoadGL(eglGetProcAddress) which resolves
function pointers through the EGL loader without opening/closing
libGL.so.

* fix crash on start and various rendering issues

* fix crash on close

* small refactor

* move GPU selection to desktop file

* clean up a bit

* clean up more

* fix appimage error
2026-04-13 19:45:39 +08:00

322 lines
11 KiB
C++

#ifndef slic3r_EmbossStyleManager_hpp_
#define slic3r_EmbossStyleManager_hpp_
#include <memory>
#include <optional>
#include <string>
#include <functional>
#include <imgui/imgui.h>
#include <wx/font.h>
#include <glad/gl.h>
#include <libslic3r/BoundingBox.hpp>
#include <libslic3r/Emboss.hpp>
#include <libslic3r/TextConfiguration.hpp>
#include <libslic3r/EmbossShape.hpp>
#include <libslic3r/AppConfig.hpp>
namespace Slic3r::GUI::Emboss {
/// <summary>
/// Manage Emboss text styles
/// Cache actual state of style
/// + imgui font
/// + wx font
/// </summary>
class StyleManager
{
friend class CreateFontStyleImagesJob; // access to StyleImagesData
public:
/// <param name="language_glyph_range">Character to load for imgui when initialize imgui font</param>
/// <param name="create_default_styles">Function to create default styles</param>
StyleManager(const ImWchar *language_glyph_range, const std::function<EmbossStyles()>& create_default_styles);
/// <summary>
/// Release imgui font and style images from GPU
/// </summary>
~StyleManager();
/// <summary>
/// Load font style list from config
/// Also select actual activ font
/// </summary>
/// <param name="app_config">Application configuration loaded from file "PrusaSlicer.ini"
/// + cfg is stored to privat variable</param>
void init(AppConfig *app_config);
/// <summary>
/// Write font list into AppConfig
/// </summary>
/// <param name="item_to_store">Configuration</param>
/// <param name="use_modification">When true cache state will be used for store</param>
/// <param name="use_modification">When true store activ index into configuration</param>
/// <returns>True on succes otherwise False.</returns>
bool store_styles_to_app_config(bool use_modification = true, bool store_active_index = true);
/// <summary>
/// Append actual style to style list
/// </summary>
/// <param name="name">New name for style</param>
void add_style(const std::string& name);
/// <summary>
/// Change order of style item in m_styles.
/// Fix selected font index when (i1 || i2) == m_font_selected
/// </summary>
/// <param name="i1">First index to m_styles</param>
/// <param name="i2">Second index to m_styles</param>
void swap(size_t i1, size_t i2);
/// <summary>
/// Discard changes in activ style
/// When no activ style use last used OR first loadable
/// </summary>
void discard_style_changes();
/// <summary>
/// Remove style from m_styles.
/// Fix selected font index when index is under m_font_selected
/// </summary>
/// <param name="index">Index of style to be removed</param>
void erase(size_t index);
/// <summary>
/// Rename actual selected font item
/// </summary>
/// <param name="name">New name</param>
void rename(const std::string &name);
/// <summary>
/// load some valid style
/// </summary>
void load_valid_style();
/// <summary>
/// Change active font
/// When font not loaded roll back activ font
/// </summary>
/// <param name="font_index">New font index(from m_styles range)</param>
/// <returns>True on succes. False on fail load font</returns>
bool load_style(size_t font_index);
// load font style not stored in list
struct Style;
bool load_style(const Style &style);
// fastering load font on index by wxFont, ignore type and descriptor
bool load_style(const Style &style, const wxFont &font);
// clear actual selected glyphs cache
void clear_glyphs_cache();
// remove cached imgui font for actual selected font
void clear_imgui_font();
// getters for private data
const Style *get_stored_style() const;
const Style &get_style() const { return m_style_cache.style; }
Style &get_style() { return m_style_cache.style; }
size_t get_style_index() const { return m_style_cache.style_index; }
std::string &get_truncated_name() { return m_style_cache.truncated_name; }
const ImFontAtlas &get_atlas() const { return m_style_cache.atlas; }
const FontProp &get_font_prop() const { return get_style().prop; }
FontProp &get_font_prop() { return get_style().prop; }
const wxFont &get_wx_font() const { return m_style_cache.wx_font; }
const wxFont &get_stored_wx_font() const { return m_style_cache.stored_wx_font; }
Slic3r::Emboss::FontFileWithCache &get_font_file_with_cache() { return m_style_cache.font_file; }
bool has_collections() const { return m_style_cache.font_file.font_file != nullptr &&
m_style_cache.font_file.font_file->infos.size() > 1; }
// True when activ style has same name as some of stored style
bool exist_stored_style() const { return m_style_cache.style_index != std::numeric_limits<size_t>::max(); }
/// <summary>
/// check whether current style differ to selected
/// </summary>
/// <returns></returns>
bool is_font_changed() const;
bool is_unique_style_name(const std::string &name) const;
/// <summary>
/// Setter on wx_font when changed
/// </summary>
/// <param name="wx_font">new wx font</param>
/// <returns>True on success set otherwise FALSE</returns>
bool set_wx_font(const wxFont &wx_font);
/// <summary>
/// Faster way of set wx_font when font file is known(do not load font file twice)
/// When you not sure that wx_font is made by font_file use only set_wx_font(wx_font)
/// </summary>
/// <param name="wx_font">Must be source of font file</param>
/// <param name="font_file">font file created by WxFontUtils::create_font_file(wx_font)</param>
/// <returns>True on success otherwise false</returns>
bool set_wx_font(const wxFont &wx_font, std::unique_ptr<Slic3r::Emboss::FontFile> font_file);
// Getter on acitve font pointer for imgui
// Initialize imgui font(generate texture) when doesn't exist yet.
// Extend font atlas when not in glyph range
ImFont *get_imgui_font();
// initialize font range by unique symbols in text
ImFont *create_imgui_font(const std::string& text, double scale);
// init truncated names of styles
void init_trunc_names(float max_width);
/// <summary>
/// Initialization texture with rendered font style
/// </summary>
/// <param name="max_size">Maximal width and height of one style texture</param>
/// <param name="text">Text to render by style</param>
void init_style_images(const Vec2i32& max_size, const std::string &text);
void free_style_images();
// access to all managed font styles
const std::vector<Style> &get_styles() const;
/// <summary>
/// Describe image in GPU to show settings of style
/// </summary>
struct StyleImage
{
void* texture_id = nullptr; // GLuint
BoundingBox bounding_box;
ImVec2 tex_size;
ImVec2 uv0;
ImVec2 uv1;
Point offset = Point(0, 0);
};
/// <summary>
/// All connected with one style
/// keep temporary data and caches for style
/// </summary>
struct Style : public EmbossStyle
{
// Define how to emboss shape
EmbossProjection projection;
// distance from surface point
// used for move over model surface
// When not set value is zero and is not stored
std::optional<float> distance; // [in mm]
// Angle of rotation around emboss direction (Z axis)
// It is calculate on the fly from volume world transformation
// only StyleManager keep actual value for comparision with style
// When not set value is zero and is not stored
std::optional<float> angle; // [in radians] form -Pi to Pi
bool operator==(const Style &other) const
{
return EmbossStyle::operator==(other) &&
projection == other.projection &&
distance == other.distance &&
angle == other.angle;
}
// cache for view font name with maximal width in imgui
std::string truncated_name;
// visualization of style
std::optional<StyleImage> image;
};
using Styles = std::vector<Style>;
// check if exist selected font style in manager
bool is_active_font();
// Limits for imgui loaded font size
// Value out of limits is crop
static float min_imgui_font_size;
static float max_imgui_font_size;
static float get_imgui_font_size(const FontProp &prop, const Slic3r::Emboss::FontFile &file, double scale);
private:
// function to create default style list
std::function<EmbossStyles()> m_create_default_styles;
// keep language dependent glyph range
const ImWchar *m_imgui_init_glyph_range;
/// <summary>
/// Cache data from style to reduce amount of:
/// 1) loading font from file
/// 2) Create atlas of symbols for imgui
/// 3) Keep loaded(and modified by style) glyphs from font
/// </summary>
struct StyleCache
{
// share font file data with emboss job thread
Slic3r::Emboss::FontFileWithCache font_file = {};
// must live same as imgui_font inside of atlas
ImVector<ImWchar> ranges = {};
// Keep only actual style in atlas
ImFontAtlas atlas = {};
// wx widget font
wxFont wx_font = {};
// cache for view font name with maximal width in imgui
std::string truncated_name;
// actual used font item
Style style = {};
// cache for stored wx font to not create every frame
wxFont stored_wx_font = {};
// index into m_styles
size_t style_index = std::numeric_limits<size_t>::max();
} m_style_cache;
// Privat member
Styles m_styles;
AppConfig *m_app_config = nullptr;
size_t m_last_style_index = std::numeric_limits<size_t>::max();
/// <summary>
/// Keep data needed to create Font Style Images in Job
/// </summary>
struct StyleImagesData
{
struct Item
{
Slic3r::Emboss::FontFileWithCache font;
std::string text;
FontProp prop;
};
using Items = std::vector<Item>;
// Keep styles to render
Items styles;
// Maximal width and height in pixels of image
Vec2i32 max_size;
// Text to render
std::string text;
/// <summary>
/// Result of job
/// </summary>
struct StyleImages
{
// vector of inputs
StyleImagesData::Items styles;
// job output
std::vector<StyleImage> images;
};
// place to store result in main thread in Finalize
std::shared_ptr<StyleImages> result;
// pixel per milimeter (scaled DPI)
double ppm;
};
std::shared_ptr<StyleImagesData::StyleImages> m_temp_style_images = nullptr;
bool m_exist_style_images = false;
};
} // namespace Slic3r
#endif // slic3r_EmbossStyleManager_hpp_