diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h index 2dea63ef0db..a5225eaeedf 100644 --- a/intern/ghost/GHOST_C-api.h +++ b/intern/ghost/GHOST_C-api.h @@ -1007,6 +1007,13 @@ extern GHOST_TCapabilityFlag GHOST_GetCapabilities(void); */ extern void GHOST_SetBacktraceHandler(GHOST_TBacktraceFn backtrace_fn); +/** + * When `use_window_frame` is false, don't show window frames. + * + * \note This must run before the system is created. + */ +extern void GHOST_UseWindowFrame(bool use_window_frame); + /** * Focus window after opening, or put them in the background. */ diff --git a/intern/ghost/GHOST_ISystem.hh b/intern/ghost/GHOST_ISystem.hh index 810b4d0418e..cc018139146 100644 --- a/intern/ghost/GHOST_ISystem.hh +++ b/intern/ghost/GHOST_ISystem.hh @@ -150,6 +150,9 @@ class GHOST_ISystem { static GHOST_TBacktraceFn getBacktraceFn(); static void setBacktraceFn(GHOST_TBacktraceFn backtrace_fn); + static bool getUseWindowFrame(); + static void setUseWindowFrame(bool use_window_frame); + protected: /** * Constructor. @@ -541,5 +544,14 @@ class GHOST_ISystem { /** Function to call that sets the back-trace. */ static GHOST_TBacktraceFn backtrace_fn_; + /** + * When false, don't use window frame. + * + * \note This needs to be set before system initialization + * to avoid loading LIBDECOR libraries (which can crash). + * If LIBDECOR is removed, this could be set on window creation instead. + */ + static bool use_window_frame_; + MEM_CXX_CLASS_ALLOC_FUNCS("GHOST:GHOST_ISystem") }; diff --git a/intern/ghost/intern/GHOST_C-api.cc b/intern/ghost/intern/GHOST_C-api.cc index 913e0f069d4..325cb02d41c 100644 --- a/intern/ghost/intern/GHOST_C-api.cc +++ b/intern/ghost/intern/GHOST_C-api.cc @@ -965,6 +965,11 @@ void GHOST_SetBacktraceHandler(GHOST_TBacktraceFn backtrace_fn) GHOST_ISystem::setBacktraceFn(backtrace_fn); } +void GHOST_UseWindowFrame(bool use_window_frame) +{ + GHOST_ISystem::setUseWindowFrame(use_window_frame); +} + void GHOST_UseWindowFocus(bool use_focus) { GHOST_ISystem *system = GHOST_ISystem::getSystem(); diff --git a/intern/ghost/intern/GHOST_ISystem.cc b/intern/ghost/intern/GHOST_ISystem.cc index 83bb833fbdc..81a340335e3 100644 --- a/intern/ghost/intern/GHOST_ISystem.cc +++ b/intern/ghost/intern/GHOST_ISystem.cc @@ -39,6 +39,8 @@ const char *GHOST_ISystem::system_backend_id_ = nullptr; GHOST_TBacktraceFn GHOST_ISystem::backtrace_fn_ = nullptr; +bool GHOST_ISystem::use_window_frame_ = true; + GHOST_TSuccess GHOST_ISystem::createSystem(bool verbose, [[maybe_unused]] bool background) { @@ -53,12 +55,13 @@ GHOST_TSuccess GHOST_ISystem::createSystem(bool verbose, [[maybe_unused]] bool b GHOST_TSuccess success; if (!system_) { + const bool use_window_frame = GHOST_ISystem::getUseWindowFrame(); #if defined(WITH_HEADLESS) /* Pass. */ #elif defined(WITH_GHOST_WAYLAND) # if defined(WITH_GHOST_WAYLAND_DYNLOAD) - const bool has_wayland_libraries = ghost_wl_dynload_libraries_init(); + const bool has_wayland_libraries = ghost_wl_dynload_libraries_init(use_window_frame); # else const bool has_wayland_libraries = true; # endif @@ -261,3 +264,13 @@ void GHOST_ISystem::setBacktraceFn(GHOST_TBacktraceFn backtrace_fn) { GHOST_ISystem::backtrace_fn_ = backtrace_fn; } + +bool GHOST_ISystem::getUseWindowFrame() +{ + return GHOST_ISystem::use_window_frame_; +} + +void GHOST_ISystem::setUseWindowFrame(bool use_window_frame) +{ + GHOST_ISystem::use_window_frame_ = use_window_frame; +} diff --git a/intern/ghost/intern/GHOST_SystemWayland.cc b/intern/ghost/intern/GHOST_SystemWayland.cc index 1221a57e34f..10fb059df8b 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cc +++ b/intern/ghost/intern/GHOST_SystemWayland.cc @@ -1548,6 +1548,11 @@ struct GWL_Display { */ bool background = false; + /** + * Show window decorations, otherwise all windows are frame-less. + */ + bool use_window_frame = false; + /* Threaded event handling. */ #ifdef USE_EVENT_BACKGROUND_THREAD /** @@ -7609,8 +7614,12 @@ GHOST_SystemWayland::GHOST_SystemWayland(const bool background) wl_log_set_handler_client(background ? ghost_wayland_log_handler_background : ghost_wayland_log_handler); + const bool use_window_frame = GHOST_ISystem::getUseWindowFrame(); + display_->system = this; display_->background = background; + display_->use_window_frame = use_window_frame; + /* Connect to the Wayland server. */ display_->wl.display = wl_display_connect(nullptr); @@ -7641,7 +7650,7 @@ GHOST_SystemWayland::GHOST_SystemWayland(const bool background) #ifdef WITH_GHOST_WAYLAND_LIBDECOR bool libdecor_required = false; - { + if (use_window_frame) { const char *xdg_current_desktop = [] { /* Account for VSCode overriding this value (TSK!), see: #133921. */ const char *key = "ORIGINAL_XDG_CURRENT_DESKTOP"; @@ -9359,6 +9368,11 @@ GHOST_TimerManager *GHOST_SystemWayland::ghost_timer_manager() } #endif +bool GHOST_SystemWayland::use_window_frame_get() +{ + return display_->use_window_frame; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -9870,7 +9884,7 @@ bool GHOST_SystemWayland::use_libdecor_runtime() #endif #ifdef WITH_GHOST_WAYLAND_DYNLOAD -bool ghost_wl_dynload_libraries_init() +bool ghost_wl_dynload_libraries_init(const bool use_window_frame) { # ifdef WITH_GHOST_X11 /* When running in WAYLAND, let the user know when a missing library is the only reason @@ -9891,7 +9905,11 @@ bool ghost_wl_dynload_libraries_init() ) { # ifdef WITH_GHOST_WAYLAND_LIBDECOR - has_libdecor = wayland_dynload_libdecor_init(verbose); /* `libdecor-0`. */ + if (use_window_frame) { + has_libdecor = wayland_dynload_libdecor_init(verbose); /* `libdecor-0`. */ + } +# else + (void)use_window_frame; # endif return true; } diff --git a/intern/ghost/intern/GHOST_SystemWayland.hh b/intern/ghost/intern/GHOST_SystemWayland.hh index 42a840c8ce1..a60698c1832 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.hh +++ b/intern/ghost/intern/GHOST_SystemWayland.hh @@ -86,7 +86,7 @@ int gwl_window_scale_int_from(const GWL_WindowScaleParams &scale_params, int val * Return true when all required WAYLAND libraries are present, * Performs dynamic loading when `WITH_GHOST_WAYLAND_DYNLOAD` is in use. */ -bool ghost_wl_dynload_libraries_init(); +bool ghost_wl_dynload_libraries_init(bool use_window_frame); void ghost_wl_dynload_libraries_exit(); #endif @@ -280,6 +280,8 @@ class GHOST_SystemWayland : public GHOST_System { bool completed) const; void ime_end(const GHOST_WindowWayland *win) const; + bool use_window_frame_get(); + static const char *xdg_app_id_get(); /* WAYLAND utility functions. */ diff --git a/intern/ghost/intern/GHOST_WindowWayland.cc b/intern/ghost/intern/GHOST_WindowWayland.cc index 2e39b4c79ec..f51c9fca75f 100644 --- a/intern/ghost/intern/GHOST_WindowWayland.cc +++ b/intern/ghost/intern/GHOST_WindowWayland.cc @@ -1867,12 +1867,16 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system, GWL_XDG_Decor_Window &decor = *window_->xdg_decor; if (system_->xdg_decor_manager_get()) { + const bool use_window_frame = system_->use_window_frame_get(); decor.toplevel_decor = zxdg_decoration_manager_v1_get_toplevel_decoration( system_->xdg_decor_manager_get(), decor.toplevel); zxdg_toplevel_decoration_v1_add_listener( decor.toplevel_decor, &xdg_toplevel_decoration_v1_listener, window_); + /* Request client side decorations as a way of disabling decorations. */ zxdg_toplevel_decoration_v1_set_mode(decor.toplevel_decor, - ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + use_window_frame ? + ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE : + ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE); } /* Commit needed to so configure callback runs. */ diff --git a/source/blender/windowmanager/WM_api.hh b/source/blender/windowmanager/WM_api.hh index bb7ab80a4eb..ec22bd092ef 100644 --- a/source/blender/windowmanager/WM_api.hh +++ b/source/blender/windowmanager/WM_api.hh @@ -103,6 +103,8 @@ void WM_init_state_normal_set(); void WM_init_state_maximized_set(); void WM_init_state_start_with_console_set(bool value); void WM_init_window_focus_set(bool do_it); +bool WM_init_window_frame_get(); +void WM_init_window_frame_set(bool do_it); void WM_init_native_pixels(bool do_it); void WM_init_input_devices(); diff --git a/source/blender/windowmanager/intern/wm_playanim.cc b/source/blender/windowmanager/intern/wm_playanim.cc index bf885d49ef1..bb219a82477 100644 --- a/source/blender/windowmanager/intern/wm_playanim.cc +++ b/source/blender/windowmanager/intern/wm_playanim.cc @@ -1862,6 +1862,7 @@ static std::optional wm_main_playanim_intern(int argc, const char **argv, P /* Init GHOST and open window. */ GHOST_SetBacktraceHandler((GHOST_TBacktraceFn)BLI_system_backtrace); + GHOST_UseWindowFrame(WM_init_window_frame_get()); ps.ghost_data.system = GHOST_CreateSystem(); if (UNLIKELY(ps.ghost_data.system == nullptr)) { diff --git a/source/blender/windowmanager/intern/wm_window.cc b/source/blender/windowmanager/intern/wm_window.cc index 184f3388e4d..8c360da2619 100644 --- a/source/blender/windowmanager/intern/wm_window.cc +++ b/source/blender/windowmanager/intern/wm_window.cc @@ -132,6 +132,7 @@ static struct WMInitStruct { GHOST_TWindowState windowstate = GHOST_WINDOW_STATE_DEFAULT; eWinOverrideFlag override_flag; + bool window_frame = true; bool window_focus = true; bool native_pixels = true; } wm_init_state; @@ -2116,6 +2117,7 @@ void wm_ghost_init(bContext *C) consumer = GHOST_CreateEventConsumer(ghost_event_proc, C); GHOST_SetBacktraceHandler((GHOST_TBacktraceFn)BLI_system_backtrace); + GHOST_UseWindowFrame(wm_init_state.window_frame); g_system = GHOST_CreateSystem(); GPU_backend_ghost_system_set(g_system); @@ -2814,6 +2816,16 @@ void WM_init_state_maximized_set() wm_init_state.override_flag |= WIN_OVERRIDE_WINSTATE; } +bool WM_init_window_frame_get() +{ + return wm_init_state.window_frame; +} + +void WM_init_window_frame_set(bool do_it) +{ + wm_init_state.window_frame = do_it; +} + void WM_init_window_focus_set(bool do_it) { wm_init_state.window_focus = do_it; diff --git a/source/creator/creator_args.cc b/source/creator/creator_args.cc index 817276d3446..28c8486bbad 100644 --- a/source/creator/creator_args.cc +++ b/source/creator/creator_args.cc @@ -702,6 +702,7 @@ static void print_help(bArgs *ba, bool all) BLI_args_print_arg_doc(ba, "--window-geometry"); BLI_args_print_arg_doc(ba, "--start-console"); BLI_args_print_arg_doc(ba, "--no-native-pixels"); + BLI_args_print_arg_doc(ba, "--no-window-frame"); BLI_args_print_arg_doc(ba, "--no-window-focus"); PRINT("\n"); @@ -1889,6 +1890,15 @@ static int arg_handle_window_maximized(int /*argc*/, const char ** /*argv*/, voi return 0; } +static const char arg_handle_no_window_frame_doc[] = + "\n\t" + "Disable all window decorations (Wayland only)."; +static int arg_handle_no_window_frame(int /*argc*/, const char ** /*argv*/, void * /*data*/) +{ + WM_init_window_frame_set(false); + return 0; +} + static const char arg_handle_no_window_focus_doc[] = "\n\t" "Open behind other windows and without taking focus."; @@ -3087,6 +3097,7 @@ void main_args_setup(bContext *C, bArgs *ba, bool all) BLI_args_add(ba, "-w", "--window-border", CB(arg_handle_window_border), nullptr); BLI_args_add(ba, "-W", "--window-fullscreen", CB(arg_handle_window_fullscreen), nullptr); BLI_args_add(ba, "-M", "--window-maximized", CB(arg_handle_window_maximized), nullptr); + BLI_args_add(ba, nullptr, "--no-window-frame", CB(arg_handle_no_window_frame), nullptr); BLI_args_add(ba, nullptr, "--no-window-focus", CB(arg_handle_no_window_focus), nullptr); BLI_args_add(ba, "-con", "--start-console", CB(arg_handle_start_with_console), nullptr); BLI_args_add(ba, "-r", "--register", CB(arg_handle_register_extension), nullptr); diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index 9dabaccff6b..27dda2c6bad 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -121,6 +121,10 @@ if(WITH_UI_TESTS) # while this could be investigated, use windowed mode instead. # Use a window size that balances software GPU rendering with enough room to use the UI. --factory-startup + # Used so GHOST/Wayland doesn't attempt to load LIBDECOR + # which can fail on some systems, causing tests to fail. + # On other systems this is harmless. + --no-window-frame -p 0 0 800 600 "${EXE_PARAMS}" "${ARGN}"