diff options
Diffstat (limited to 'intern/ghost/intern/GHOST_WindowWayland.cpp')
-rw-r--r-- | intern/ghost/intern/GHOST_WindowWayland.cpp | 549 |
1 files changed, 325 insertions, 224 deletions
diff --git a/intern/ghost/intern/GHOST_WindowWayland.cpp b/intern/ghost/intern/GHOST_WindowWayland.cpp index 5f3cb3e3f3a..9d62c69edef 100644 --- a/intern/ghost/intern/GHOST_WindowWayland.cpp +++ b/intern/ghost/intern/GHOST_WindowWayland.cpp @@ -31,15 +31,54 @@ # include <libdecor.h> #endif +/* Generated by `wayland-scanner`. */ +#include <xdg-decoration-unstable-v1-client-protocol.h> +#include <xdg-shell-client-protocol.h> + /* Logging, use `ghost.wl.*` prefix. */ #include "CLG_log.h" static constexpr size_t base_dpi = 96; +#ifdef WITH_GHOST_WAYLAND_LIBDECOR +/* Access `use_libdecor` in #GHOST_SystemWayland. */ +# define use_libdecor GHOST_SystemWayland::use_libdecor_runtime() +#endif + static GHOST_WindowManager *window_manager = nullptr; +#ifdef WITH_GHOST_WAYLAND_LIBDECOR +struct WGL_LibDecor_Window { + struct libdecor_frame *frame = nullptr; + bool configured = false; +}; + +static void gwl_libdecor_window_destroy(WGL_LibDecor_Window *decor) +{ + libdecor_frame_unref(decor->frame); + delete decor; +} +#endif /* WITH_GHOST_WAYLAND_LIBDECOR */ + +struct WGL_XDG_Decor_Window { + struct xdg_surface *surface = nullptr; + struct zxdg_toplevel_decoration_v1 *toplevel_decor = nullptr; + struct xdg_toplevel *toplevel = nullptr; + enum zxdg_toplevel_decoration_v1_mode mode = (enum zxdg_toplevel_decoration_v1_mode)0; +}; + +static void gwl_xdg_decor_window_destroy(WGL_XDG_Decor_Window *decor) +{ + if (decor->toplevel_decor) { + zxdg_toplevel_decoration_v1_destroy(decor->toplevel_decor); + } + xdg_toplevel_destroy(decor->toplevel); + xdg_surface_destroy(decor->surface); + delete decor; +} + struct GWL_Window { - GHOST_WindowWayland *w = nullptr; + GHOST_WindowWayland *ghost_window = nullptr; struct wl_surface *wl_surface = nullptr; /** * Outputs on which the window is currently shown on. @@ -52,23 +91,15 @@ struct GWL_Window { /** The scale value written to #wl_surface_set_buffer_scale. */ int scale = 0; /** - * The DPI, either: - * - `scale * base_dpi` - * - `wl_fixed_to_int(scale_fractional * base_dpi)` - * When fractional scaling is available. + * The fractional scale used to calculate the DPI. + * (always set, even when scaling is rounded to whole units). */ - uint32_t dpi = 0; + wl_fixed_t scale_fractional = 0; #ifdef WITH_GHOST_WAYLAND_LIBDECOR - struct libdecor_frame *decor_frame = nullptr; - bool decor_configured = false; -#else - struct xdg_surface *xdg_surface = nullptr; - struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration = nullptr; - struct xdg_toplevel *xdg_toplevel = nullptr; - - enum zxdg_toplevel_decoration_v1_mode decoration_mode = (enum zxdg_toplevel_decoration_v1_mode)0; + WGL_LibDecor_Window *libdecor = nullptr; #endif + WGL_XDG_Decor_Window *xdg_decor = nullptr; wl_egl_window *egl_window = nullptr; bool is_maximised = false; @@ -114,7 +145,7 @@ static int output_scale_cmp(const GWL_Output *output_a, const GWL_Output *output static int outputs_max_scale_or_default(const std::vector<GWL_Output *> &outputs, const int32_t scale_default, - uint32_t *r_dpi) + wl_fixed_t *r_scale_fractional) { const GWL_Output *output_max = nullptr; for (const GWL_Output *reg_output : outputs) { @@ -124,18 +155,16 @@ static int outputs_max_scale_or_default(const std::vector<GWL_Output *> &outputs } if (output_max) { - if (r_dpi) { - *r_dpi = output_max->has_scale_fractional ? - /* Fractional DPI. */ - wl_fixed_to_int(output_max->scale_fractional * base_dpi) : - /* Simple non-fractional DPI. */ - (output_max->scale * base_dpi); + if (r_scale_fractional) { + *r_scale_fractional = output_max->has_scale_fractional ? + output_max->scale_fractional : + wl_fixed_from_int(output_max->scale); } return output_max->scale; } - if (r_dpi) { - *r_dpi = scale_default * base_dpi; + if (r_scale_fractional) { + *r_scale_fractional = wl_fixed_from_int(scale_default); } return scale_default; } @@ -146,10 +175,8 @@ static int outputs_max_scale_or_default(const std::vector<GWL_Output *> &outputs /** \name Listener (XDG Top Level), #xdg_toplevel_listener * \{ */ -#ifndef WITH_GHOST_WAYLAND_LIBDECOR - static CLG_LogRef LOG_WL_XDG_TOPLEVEL = {"ghost.wl.handle.xdg_toplevel"}; -# define LOG (&LOG_WL_XDG_TOPLEVEL) +#define LOG (&LOG_WL_XDG_TOPLEVEL) static void xdg_toplevel_handle_configure(void *data, xdg_toplevel * /*xdg_toplevel*/, @@ -189,17 +216,15 @@ static void xdg_toplevel_handle_configure(void *data, static void xdg_toplevel_handle_close(void *data, xdg_toplevel * /*xdg_toplevel*/) { CLOG_INFO(LOG, 2, "close"); - static_cast<GWL_Window *>(data)->w->close(); + static_cast<GWL_Window *>(data)->ghost_window->close(); } -static const xdg_toplevel_listener toplevel_listener = { +static const xdg_toplevel_listener xdg_toplevel_listener = { xdg_toplevel_handle_configure, xdg_toplevel_handle_close, }; -# undef LOG - -#endif /* !WITH_GHOST_WAYLAND_LIBDECOR. */ +#undef LOG /** \} */ @@ -234,7 +259,7 @@ static void frame_handle_configure(struct libdecor_frame *frame, win->size[1] = win->scale * size_next[1]; wl_egl_window_resize(win->egl_window, UNPACK2(win->size), 0, 0); - win->w->notify_size(); + win->ghost_window->notify_size(); if (!libdecor_configuration_get_window_state(configuration, &window_state)) { window_state = LIBDECOR_WINDOW_STATE_NONE; @@ -244,20 +269,20 @@ static void frame_handle_configure(struct libdecor_frame *frame, win->is_fullscreen = window_state & LIBDECOR_WINDOW_STATE_FULLSCREEN; win->is_active = window_state & LIBDECOR_WINDOW_STATE_ACTIVE; - win->is_active ? win->w->activate() : win->w->deactivate(); + win->is_active ? win->ghost_window->activate() : win->ghost_window->deactivate(); state = libdecor_state_new(UNPACK2(size_next)); libdecor_frame_commit(frame, state, configuration); libdecor_state_free(state); - win->decor_configured = true; + win->libdecor->configured = true; } static void frame_handle_close(struct libdecor_frame * /*frame*/, void *data) { CLOG_INFO(LOG, 2, "close"); - static_cast<GWL_Window *>(data)->w->close(); + static_cast<GWL_Window *>(data)->ghost_window->close(); } static void frame_handle_commit(struct libdecor_frame * /*frame*/, void *data) @@ -265,8 +290,8 @@ static void frame_handle_commit(struct libdecor_frame * /*frame*/, void *data) CLOG_INFO(LOG, 2, "commit"); /* We have to swap twice to keep any pop-up menus alive. */ - static_cast<GWL_Window *>(data)->w->swapBuffers(); - static_cast<GWL_Window *>(data)->w->swapBuffers(); + static_cast<GWL_Window *>(data)->ghost_window->swapBuffers(); + static_cast<GWL_Window *>(data)->ghost_window->swapBuffers(); } static struct libdecor_frame_interface libdecor_frame_iface = { @@ -285,10 +310,8 @@ static struct libdecor_frame_interface libdecor_frame_iface = { /** \name Listener (XDG Decoration Listener), #zxdg_toplevel_decoration_v1_listener * \{ */ -#ifndef WITH_GHOST_WAYLAND_LIBDECOR - static CLG_LogRef LOG_WL_XDG_TOPLEVEL_DECORATION = {"ghost.wl.handle.xdg_toplevel_decoration"}; -# define LOG (&LOG_WL_XDG_TOPLEVEL_DECORATION) +#define LOG (&LOG_WL_XDG_TOPLEVEL_DECORATION) static void xdg_toplevel_decoration_handle_configure( void *data, @@ -296,16 +319,14 @@ static void xdg_toplevel_decoration_handle_configure( const uint32_t mode) { CLOG_INFO(LOG, 2, "configure (mode=%u)", mode); - static_cast<GWL_Window *>(data)->decoration_mode = (zxdg_toplevel_decoration_v1_mode)mode; + static_cast<GWL_Window *>(data)->xdg_decor->mode = (zxdg_toplevel_decoration_v1_mode)mode; } -static const zxdg_toplevel_decoration_v1_listener toplevel_decoration_v1_listener = { +static const zxdg_toplevel_decoration_v1_listener xdg_toplevel_decoration_v1_listener = { xdg_toplevel_decoration_handle_configure, }; -# undef LOG - -#endif /* !WITH_GHOST_WAYLAND_LIBDECOR. */ +#undef LOG /** \} */ @@ -313,10 +334,8 @@ static const zxdg_toplevel_decoration_v1_listener toplevel_decoration_v1_listene /** \name Listener (XDG Surface Handle Configure), #xdg_surface_listener * \{ */ -#ifndef WITH_GHOST_WAYLAND_LIBDECOR - static CLG_LogRef LOG_WL_XDG_SURFACE = {"ghost.wl.handle.xdg_surface"}; -# define LOG (&LOG_WL_XDG_SURFACE) +#define LOG (&LOG_WL_XDG_SURFACE) static void xdg_surface_handle_configure(void *data, xdg_surface *xdg_surface, @@ -324,7 +343,7 @@ static void xdg_surface_handle_configure(void *data, { GWL_Window *win = static_cast<GWL_Window *>(data); - if (win->xdg_surface != xdg_surface) { + if (win->xdg_decor->surface != xdg_surface) { CLOG_INFO(LOG, 2, "configure (skipped)"); return; } @@ -337,14 +356,14 @@ static void xdg_surface_handle_configure(void *data, wl_egl_window_resize(win->egl_window, UNPACK2(win->size), 0, 0); win->size_pending[0] = 0; win->size_pending[1] = 0; - win->w->notify_size(); + win->ghost_window->notify_size(); } if (win->is_active) { - win->w->activate(); + win->ghost_window->activate(); } else { - win->w->deactivate(); + win->ghost_window->deactivate(); } xdg_surface_ack_configure(xdg_surface, serial); @@ -354,9 +373,7 @@ static const xdg_surface_listener xdg_surface_listener = { xdg_surface_handle_configure, }; -# undef LOG - -#endif /* !WITH_GHOST_WAYLAND_LIBDECOR. */ +#undef LOG /** \} */ @@ -401,7 +418,7 @@ static void surface_handle_leave(void *data, } } -static struct wl_surface_listener wl_surface_listener = { +static const struct wl_surface_listener wl_surface_listener = { surface_handle_enter, surface_handle_leave, }; @@ -418,7 +435,7 @@ static struct wl_surface_listener wl_surface_listener = { GHOST_TSuccess GHOST_WindowWayland::hasCursorShape(GHOST_TStandardCursor cursorShape) { - return m_system->hasCursorShape(cursorShape); + return system_->hasCursorShape(cursorShape); } GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system, @@ -434,20 +451,20 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system, const bool stereoVisual, const bool exclusive) : GHOST_Window(width, height, state, stereoVisual, exclusive), - m_system(system), - w(new GWL_Window) + system_(system), + window_(new GWL_Window) { /* Globally store pointer to window manager. */ if (!window_manager) { - window_manager = m_system->getWindowManager(); + window_manager = system_->getWindowManager(); } - w->w = this; + window_->ghost_window = this; - w->size[0] = int32_t(width); - w->size[1] = int32_t(height); + window_->size[0] = int32_t(width); + window_->size[1] = int32_t(height); - w->is_dialog = is_dialog; + window_->is_dialog = is_dialog; /* NOTE(@campbellbarton): The scale set here to avoid flickering on startup. * When all monitors use the same scale (which is quite common) there aren't any problems. @@ -458,75 +475,100 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system, * * Using the maximum scale is best as it results in the window first being smaller, * avoiding a large window flashing before it's made smaller. */ - w->scale = outputs_max_scale_or_default(this->m_system->outputs(), 1, &w->dpi); + window_->scale = outputs_max_scale_or_default(system_->outputs(), 1, &window_->scale_fractional); /* Window surfaces. */ - w->wl_surface = wl_compositor_create_surface(m_system->compositor()); - ghost_wl_surface_tag(w->wl_surface); + window_->wl_surface = wl_compositor_create_surface(system_->wl_compositor()); + ghost_wl_surface_tag(window_->wl_surface); - wl_surface_set_buffer_scale(w->wl_surface, w->scale); + wl_surface_set_buffer_scale(window_->wl_surface, window_->scale); - wl_surface_add_listener(w->wl_surface, &wl_surface_listener, this); + wl_surface_add_listener(window_->wl_surface, &wl_surface_listener, window_); - w->egl_window = wl_egl_window_create(w->wl_surface, int(w->size[0]), int(w->size[1])); + window_->egl_window = wl_egl_window_create( + window_->wl_surface, int(window_->size[0]), int(window_->size[1])); /* NOTE: The limit is in points (not pixels) so Hi-DPI will limit to larger number of pixels. * This has the advantage that the size limit is the same when moving the window between monitors * with different scales set. If it was important to limit in pixels it could be re-calculated - * when the `w->scale` changed. */ + * when the `window_->scale` changed. */ const int32_t size_min[2] = {320, 240}; -#ifdef WITH_GHOST_WAYLAND_LIBDECOR - /* create window decorations */ - w->decor_frame = libdecor_decorate( - m_system->decor_context(), w->wl_surface, &libdecor_frame_iface, w); - libdecor_frame_map(w->decor_frame); - - libdecor_frame_set_min_content_size(w->decor_frame, UNPACK2(size_min)); - - if (parentWindow) { - libdecor_frame_set_parent( - w->decor_frame, dynamic_cast<const GHOST_WindowWayland *>(parentWindow)->w->decor_frame); - } -#else - w->xdg_surface = xdg_wm_base_get_xdg_surface(m_system->xdg_shell(), w->wl_surface); - w->xdg_toplevel = xdg_surface_get_toplevel(w->xdg_surface); - - xdg_toplevel_set_min_size(w->xdg_toplevel, UNPACK2(size_min)); + /* This value is expected to match the base name of the `.desktop` file. see T101805. + * + * NOTE: the XDG desktop-entry-spec defines that this should follow the "reverse DNS" convention. + * For e.g. `org.blender.Blender` - however the `.desktop` file distributed with Blender is + * simply called `blender.desktop`, so the it's important to follow that name. + * Other distributions such as SNAP & FLATPAK may need to change this value T101779. + * Currently there isn't a way to configure this, we may want to support that. */ + const char *xdg_app_id = "blender"; - if (m_system->xdg_decoration_manager()) { - w->xdg_toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration( - m_system->xdg_decoration_manager(), w->xdg_toplevel); - zxdg_toplevel_decoration_v1_add_listener( - w->xdg_toplevel_decoration, &toplevel_decoration_v1_listener, w); - zxdg_toplevel_decoration_v1_set_mode(w->xdg_toplevel_decoration, - ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); +#ifdef WITH_GHOST_WAYLAND_LIBDECOR + if (use_libdecor) { + window_->libdecor = new WGL_LibDecor_Window; + WGL_LibDecor_Window &decor = *window_->libdecor; + + /* create window decorations */ + decor.frame = libdecor_decorate( + system_->libdecor_context(), window_->wl_surface, &libdecor_frame_iface, window_); + libdecor_frame_map(window_->libdecor->frame); + + libdecor_frame_set_min_content_size(decor.frame, UNPACK2(size_min)); + libdecor_frame_set_app_id(decor.frame, xdg_app_id); + + if (parentWindow) { + WGL_LibDecor_Window &decor_parent = + *dynamic_cast<const GHOST_WindowWayland *>(parentWindow)->window_->libdecor; + libdecor_frame_set_parent(decor.frame, decor_parent.frame); + } } + else +#endif + { + window_->xdg_decor = new WGL_XDG_Decor_Window; + WGL_XDG_Decor_Window &decor = *window_->xdg_decor; + decor.surface = xdg_wm_base_get_xdg_surface(system_->xdg_decor_shell(), window_->wl_surface); + decor.toplevel = xdg_surface_get_toplevel(decor.surface); + + xdg_toplevel_set_min_size(decor.toplevel, UNPACK2(size_min)); + xdg_toplevel_set_app_id(decor.toplevel, xdg_app_id); + + if (system_->xdg_decor_manager()) { + decor.toplevel_decor = zxdg_decoration_manager_v1_get_toplevel_decoration( + system_->xdg_decor_manager(), decor.toplevel); + zxdg_toplevel_decoration_v1_add_listener( + decor.toplevel_decor, &xdg_toplevel_decoration_v1_listener, window_); + zxdg_toplevel_decoration_v1_set_mode(decor.toplevel_decor, + ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + } - xdg_surface_add_listener(w->xdg_surface, &xdg_surface_listener, w); - xdg_toplevel_add_listener(w->xdg_toplevel, &toplevel_listener, w); + xdg_surface_add_listener(decor.surface, &xdg_surface_listener, window_); + xdg_toplevel_add_listener(decor.toplevel, &xdg_toplevel_listener, window_); - if (parentWindow && is_dialog) { - xdg_toplevel_set_parent( - w->xdg_toplevel, dynamic_cast<const GHOST_WindowWayland *>(parentWindow)->w->xdg_toplevel); + if (parentWindow && is_dialog) { + WGL_XDG_Decor_Window &decor_parent = + *dynamic_cast<const GHOST_WindowWayland *>(parentWindow)->window_->xdg_decor; + xdg_toplevel_set_parent(decor.toplevel, decor_parent.toplevel); + } } -#endif /* !WITH_GHOST_WAYLAND_LIBDECOR */ - setTitle(title); - wl_surface_set_user_data(w->wl_surface, this); + wl_surface_set_user_data(window_->wl_surface, this); /* Call top-level callbacks. */ - wl_surface_commit(w->wl_surface); - wl_display_roundtrip(m_system->display()); + wl_surface_commit(window_->wl_surface); + wl_display_roundtrip(system_->wl_display()); #ifdef WITH_GHOST_WAYLAND_LIBDECOR - /* It's important not to return until the window is configured or - * calls to `setState` from Blender will crash `libdecor`. */ - while (!w->decor_configured) { - if (libdecor_dispatch(m_system->decor_context(), 0) < 0) { - break; + if (use_libdecor) { + WGL_LibDecor_Window &decor = *window_->libdecor; + /* It's important not to return until the window is configured or + * calls to `setState` from Blender will crash `libdecor`. */ + while (!decor.configured) { + if (libdecor_dispatch(system_->libdecor_context(), 0) < 0) { + break; + } } } #endif @@ -535,9 +577,13 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system, setOpaque(); #endif -#ifndef WITH_GHOST_WAYLAND_LIBDECOR /* Causes a glitch with `libdecor` for some reason. */ - setState(state); + /* Causes a glitch with `libdecor` for some reason. */ +#ifdef WITH_GHOST_WAYLAND_LIBDECOR + if (use_libdecor == false) #endif + { + setState(state); + } /* EGL context. */ if (setDrawingContextType(type) == GHOST_kFailure) { @@ -558,13 +604,13 @@ GHOST_TSuccess GHOST_WindowWayland::setWindowCursorGrab(GHOST_TGrabCursorMode mo } bounds = &bounds_buf; } - if (m_system->window_cursor_grab_set(mode, - m_cursorGrab, - m_cursorGrabInitPos, - bounds, - m_cursorGrabAxis, - w->wl_surface, - w->scale)) { + if (system_->window_cursor_grab_set(mode, + m_cursorGrab, + m_cursorGrabInitPos, + bounds, + m_cursorGrabAxis, + window_->wl_surface, + window_->scale)) { return GHOST_kSuccess; } return GHOST_kFailure; @@ -572,43 +618,47 @@ GHOST_TSuccess GHOST_WindowWayland::setWindowCursorGrab(GHOST_TGrabCursorMode mo GHOST_TSuccess GHOST_WindowWayland::setWindowCursorShape(GHOST_TStandardCursor shape) { - const GHOST_TSuccess ok = m_system->setCursorShape(shape); + const GHOST_TSuccess ok = system_->setCursorShape(shape); m_cursorShape = (ok == GHOST_kSuccess) ? shape : GHOST_kStandardCursorDefault; return ok; } bool GHOST_WindowWayland::getCursorGrabUseSoftwareDisplay() { - return m_system->getCursorGrabUseSoftwareDisplay(m_cursorGrab); + return system_->getCursorGrabUseSoftwareDisplay(m_cursorGrab); } GHOST_TSuccess GHOST_WindowWayland::setWindowCustomCursorShape( uint8_t *bitmap, uint8_t *mask, int sizex, int sizey, int hotX, int hotY, bool canInvertColor) { - return m_system->setCustomCursorShape(bitmap, mask, sizex, sizey, hotX, hotY, canInvertColor); + return system_->setCustomCursorShape(bitmap, mask, sizex, sizey, hotX, hotY, canInvertColor); } GHOST_TSuccess GHOST_WindowWayland::getCursorBitmap(GHOST_CursorBitmapRef *bitmap) { - return m_system->getCursorBitmap(bitmap); + return system_->getCursorBitmap(bitmap); } void GHOST_WindowWayland::setTitle(const char *title) { #ifdef WITH_GHOST_WAYLAND_LIBDECOR - libdecor_frame_set_app_id(w->decor_frame, title); - libdecor_frame_set_title(w->decor_frame, title); -#else - xdg_toplevel_set_title(w->xdg_toplevel, title); - xdg_toplevel_set_app_id(w->xdg_toplevel, title); + if (use_libdecor) { + WGL_LibDecor_Window &decor = *window_->libdecor; + libdecor_frame_set_title(decor.frame, title); + } + else #endif + { + WGL_XDG_Decor_Window &decor = *window_->xdg_decor; + xdg_toplevel_set_title(decor.toplevel, title); + } - this->title = title; + title_ = title; } std::string GHOST_WindowWayland::getTitle() const { - return this->title.empty() ? "untitled" : this->title; + return title_.empty() ? "untitled" : title_; } void GHOST_WindowWayland::getWindowBounds(GHOST_Rect &bounds) const @@ -618,29 +668,29 @@ void GHOST_WindowWayland::getWindowBounds(GHOST_Rect &bounds) const void GHOST_WindowWayland::getClientBounds(GHOST_Rect &bounds) const { - bounds.set(0, 0, UNPACK2(w->size)); + bounds.set(0, 0, UNPACK2(window_->size)); } GHOST_TSuccess GHOST_WindowWayland::setClientWidth(const uint32_t width) { - return setClientSize(width, uint32_t(w->size[1])); + return setClientSize(width, uint32_t(window_->size[1])); } GHOST_TSuccess GHOST_WindowWayland::setClientHeight(const uint32_t height) { - return setClientSize(uint32_t(w->size[0]), height); + return setClientSize(uint32_t(window_->size[0]), height); } GHOST_TSuccess GHOST_WindowWayland::setClientSize(const uint32_t width, const uint32_t height) { - wl_egl_window_resize(w->egl_window, int(width), int(height), 0, 0); + wl_egl_window_resize(window_->egl_window, int(width), int(height), 0, 0); /* Override any pending size that may be set. */ - w->size_pending[0] = 0; - w->size_pending[1] = 0; + window_->size_pending[0] = 0; + window_->size_pending[1] = 0; - w->size[0] = width; - w->size[1] = height; + window_->size[0] = width; + window_->size[1] = height; notify_size(); @@ -669,40 +719,42 @@ GHOST_WindowWayland::~GHOST_WindowWayland() { releaseNativeHandles(); - wl_egl_window_destroy(w->egl_window); + wl_egl_window_destroy(window_->egl_window); #ifdef WITH_GHOST_WAYLAND_LIBDECOR - libdecor_frame_unref(w->decor_frame); -#else - if (w->xdg_toplevel_decoration) { - zxdg_toplevel_decoration_v1_destroy(w->xdg_toplevel_decoration); + if (use_libdecor) { + gwl_libdecor_window_destroy(window_->libdecor); } - xdg_toplevel_destroy(w->xdg_toplevel); - xdg_surface_destroy(w->xdg_surface); + else #endif + { + gwl_xdg_decor_window_destroy(window_->xdg_decor); + } /* Clear any pointers to this window. This is needed because there are no guarantees * that flushing the display will the "leave" handlers before handling events. */ - m_system->window_surface_unref(w->wl_surface); + system_->window_surface_unref(window_->wl_surface); - wl_surface_destroy(w->wl_surface); + wl_surface_destroy(window_->wl_surface); /* NOTE(@campbellbarton): Flushing will often run the appropriate handlers event * (#wl_surface_listener.leave in particular) to avoid attempted access to the freed surfaces. * This is not fool-proof though, hence the call to #window_surface_unref, see: T99078. */ - wl_display_flush(m_system->display()); + wl_display_flush(system_->wl_display()); - delete w; + delete window_; } uint16_t GHOST_WindowWayland::getDPIHint() { - return w->dpi; + /* Using the physical DPI will cause wrong scaling of the UI + * use a multiplier for the default DPI as a workaround. */ + return wl_fixed_to_int(window_->scale_fractional * base_dpi); } GHOST_TSuccess GHOST_WindowWayland::setWindowCursorVisibility(bool visible) { - return m_system->setCursorVisibility(visible); + return system_->setCursorVisibility(visible); } GHOST_TSuccess GHOST_WindowWayland::setState(GHOST_TWindowState state) @@ -711,57 +763,84 @@ GHOST_TSuccess GHOST_WindowWayland::setState(GHOST_TWindowState state) case GHOST_kWindowStateNormal: /* Unset states. */ switch (getState()) { - case GHOST_kWindowStateMaximized: + case GHOST_kWindowStateMaximized: { #ifdef WITH_GHOST_WAYLAND_LIBDECOR - libdecor_frame_unset_maximized(w->decor_frame); -#else - xdg_toplevel_unset_maximized(w->xdg_toplevel); + if (use_libdecor) { + libdecor_frame_unset_maximized(window_->libdecor->frame); + } + else #endif + { + xdg_toplevel_unset_maximized(window_->xdg_decor->toplevel); + } break; - case GHOST_kWindowStateFullScreen: + } + case GHOST_kWindowStateFullScreen: { #ifdef WITH_GHOST_WAYLAND_LIBDECOR - libdecor_frame_unset_fullscreen(w->decor_frame); -#else - xdg_toplevel_unset_fullscreen(w->xdg_toplevel); + if (use_libdecor) { + libdecor_frame_unset_fullscreen(window_->libdecor->frame); + } + else #endif + { + xdg_toplevel_unset_fullscreen(window_->xdg_decor->toplevel); + } break; - default: + } + default: { break; + } } break; - case GHOST_kWindowStateMaximized: + case GHOST_kWindowStateMaximized: { #ifdef WITH_GHOST_WAYLAND_LIBDECOR - libdecor_frame_set_maximized(w->decor_frame); -#else - xdg_toplevel_set_maximized(w->xdg_toplevel); + if (use_libdecor) { + libdecor_frame_set_maximized(window_->libdecor->frame); + } + else #endif + { + xdg_toplevel_set_maximized(window_->xdg_decor->toplevel); + } break; - case GHOST_kWindowStateMinimized: + } + case GHOST_kWindowStateMinimized: { #ifdef WITH_GHOST_WAYLAND_LIBDECOR - libdecor_frame_set_minimized(w->decor_frame); -#else - xdg_toplevel_set_minimized(w->xdg_toplevel); + if (use_libdecor) { + libdecor_frame_set_minimized(window_->libdecor->frame); + } + else #endif + { + xdg_toplevel_set_minimized(window_->xdg_decor->toplevel); + } break; - case GHOST_kWindowStateFullScreen: + } + case GHOST_kWindowStateFullScreen: { #ifdef WITH_GHOST_WAYLAND_LIBDECOR - libdecor_frame_set_fullscreen(w->decor_frame, nullptr); -#else - xdg_toplevel_set_fullscreen(w->xdg_toplevel, nullptr); + if (use_libdecor) { + libdecor_frame_set_fullscreen(window_->libdecor->frame, nullptr); + } + else #endif + { + xdg_toplevel_set_fullscreen(window_->xdg_decor->toplevel, nullptr); + } break; - case GHOST_kWindowStateEmbedded: + } + case GHOST_kWindowStateEmbedded: { return GHOST_kFailure; + } } return GHOST_kSuccess; } GHOST_TWindowState GHOST_WindowWayland::getState() const { - if (w->is_fullscreen) { + if (window_->is_fullscreen) { return GHOST_kWindowStateFullScreen; } - if (w->is_maximised) { + if (window_->is_maximised) { return GHOST_kWindowStateMaximized; } return GHOST_kWindowStateNormal; @@ -780,26 +859,35 @@ GHOST_TSuccess GHOST_WindowWayland::setOrder(GHOST_TWindowOrder /*order*/) GHOST_TSuccess GHOST_WindowWayland::beginFullScreen() const { #ifdef WITH_GHOST_WAYLAND_LIBDECOR - libdecor_frame_set_fullscreen(w->decor_frame, nullptr); -#else - xdg_toplevel_set_fullscreen(w->xdg_toplevel, nullptr); + if (use_libdecor) { + libdecor_frame_set_fullscreen(window_->libdecor->frame, nullptr); + } + else #endif + { + xdg_toplevel_set_fullscreen(window_->xdg_decor->toplevel, nullptr); + } + return GHOST_kSuccess; } GHOST_TSuccess GHOST_WindowWayland::endFullScreen() const { #ifdef WITH_GHOST_WAYLAND_LIBDECOR - libdecor_frame_unset_fullscreen(w->decor_frame); -#else - xdg_toplevel_unset_fullscreen(w->xdg_toplevel); + if (use_libdecor) { + libdecor_frame_unset_fullscreen(window_->libdecor->frame); + } + else #endif + { + xdg_toplevel_unset_fullscreen(window_->xdg_decor->toplevel); + } return GHOST_kSuccess; } bool GHOST_WindowWayland::isDialog() const { - return w->is_dialog; + return window_->is_dialog; } #ifdef GHOST_OPENGL_ALPHA @@ -808,9 +896,9 @@ void GHOST_WindowWayland::setOpaque() const struct wl_region *region; /* Make the window opaque. */ - region = wl_compositor_create_region(m_system->compositor()); - wl_region_add(region, 0, 0, UNPACK2(w->size)); - wl_surface_set_opaque_region(w->surface, region); + region = wl_compositor_create_region(system_->compositor()); + wl_region_add(region, 0, 0, UNPACK2(window_->size)); + wl_surface_set_opaque_region(window_->surface, region); wl_region_destroy(region); } #endif @@ -828,10 +916,10 @@ GHOST_Context *GHOST_WindowWayland::newDrawingContext(GHOST_TDrawingContextType break; case GHOST_kDrawingContextTypeOpenGL: for (int minor = 6; minor >= 0; --minor) { - context = new GHOST_ContextEGL(this->m_system, + context = new GHOST_ContextEGL(system_, m_wantStereoVisual, - EGLNativeWindowType(w->egl_window), - EGLNativeDisplayType(m_system->display()), + EGLNativeWindowType(window_->egl_window), + EGLNativeDisplayType(system_->wl_display()), EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, 4, minor, @@ -844,10 +932,10 @@ GHOST_Context *GHOST_WindowWayland::newDrawingContext(GHOST_TDrawingContextType } delete context; } - context = new GHOST_ContextEGL(this->m_system, + context = new GHOST_ContextEGL(system_, m_wantStereoVisual, - EGLNativeWindowType(w->egl_window), - EGLNativeDisplayType(m_system->display()), + EGLNativeWindowType(window_->egl_window), + EGLNativeDisplayType(system_->wl_display()), EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, 3, 3, @@ -856,7 +944,12 @@ GHOST_Context *GHOST_WindowWayland::newDrawingContext(GHOST_TDrawingContextType EGL_OPENGL_API); } - return (context->initializeDrawingContext() == GHOST_kSuccess) ? context : nullptr; + if (context->initializeDrawingContext()) { + return context; + } + + delete context; + return nullptr; } /** \} */ @@ -867,24 +960,24 @@ GHOST_Context *GHOST_WindowWayland::newDrawingContext(GHOST_TDrawingContextType * Expose some members via methods. * \{ */ -uint16_t GHOST_WindowWayland::dpi() const +int GHOST_WindowWayland::scale() const { - return w->dpi; + return window_->scale; } -int GHOST_WindowWayland::scale() const +wl_fixed_t GHOST_WindowWayland::scale_fractional() const { - return w->scale; + return window_->scale_fractional; } wl_surface *GHOST_WindowWayland::wl_surface() const { - return w->wl_surface; + return window_->wl_surface; } const std::vector<GWL_Output *> &GHOST_WindowWayland::outputs() { - return w->outputs; + return window_->outputs; } /** \} */ @@ -897,24 +990,24 @@ const std::vector<GWL_Output *> &GHOST_WindowWayland::outputs() GHOST_TSuccess GHOST_WindowWayland::close() { - return m_system->pushEvent( - new GHOST_Event(m_system->getMilliSeconds(), GHOST_kEventWindowClose, this)); + return system_->pushEvent( + new GHOST_Event(system_->getMilliSeconds(), GHOST_kEventWindowClose, this)); } GHOST_TSuccess GHOST_WindowWayland::activate() { - if (m_system->getWindowManager()->setActiveWindow(this) == GHOST_kFailure) { + if (system_->getWindowManager()->setActiveWindow(this) == GHOST_kFailure) { return GHOST_kFailure; } - return m_system->pushEvent( - new GHOST_Event(m_system->getMilliSeconds(), GHOST_kEventWindowActivate, this)); + return system_->pushEvent( + new GHOST_Event(system_->getMilliSeconds(), GHOST_kEventWindowActivate, this)); } GHOST_TSuccess GHOST_WindowWayland::deactivate() { - m_system->getWindowManager()->setWindowInactive(this); - return m_system->pushEvent( - new GHOST_Event(m_system->getMilliSeconds(), GHOST_kEventWindowDeactivate, this)); + system_->getWindowManager()->setWindowInactive(this); + return system_->pushEvent( + new GHOST_Event(system_->getMilliSeconds(), GHOST_kEventWindowDeactivate, this)); } GHOST_TSuccess GHOST_WindowWayland::notify_size() @@ -923,8 +1016,8 @@ GHOST_TSuccess GHOST_WindowWayland::notify_size() setOpaque(); #endif - return m_system->pushEvent( - new GHOST_Event(m_system->getMilliSeconds(), GHOST_kEventWindowSize, this)); + return system_->pushEvent( + new GHOST_Event(system_->getMilliSeconds(), GHOST_kEventWindowSize, this)); } /** \} */ @@ -940,31 +1033,39 @@ GHOST_TSuccess GHOST_WindowWayland::notify_size() */ bool GHOST_WindowWayland::outputs_changed_update_scale() { - uint32_t dpi_next; - const int scale_next = outputs_max_scale_or_default(this->outputs(), 0, &dpi_next); + wl_fixed_t scale_fractional_next = 0; + const int scale_next = outputs_max_scale_or_default(outputs(), 0, &scale_fractional_next); if (UNLIKELY(scale_next == 0)) { return false; } - GWL_Window *win = this->w; - const uint32_t dpi_curr = win->dpi; - const int scale_curr = win->scale; + const wl_fixed_t scale_fractional_curr = window_->scale_fractional; + const int scale_curr = window_->scale; bool changed = false; if (scale_next != scale_curr) { - /* Unlikely but possible there is a pending size change is set. */ - win->size_pending[0] = (win->size_pending[0] / scale_curr) * scale_next; - win->size_pending[1] = (win->size_pending[1] / scale_curr) * scale_next; + window_->scale = scale_next; + wl_surface_set_buffer_scale(window_->wl_surface, scale_next); + + /* It's important to resize the window immediately, to avoid the window changing size + * and flickering in a constant feedback loop (in some bases). */ + if ((window_->size_pending[0] != 0) && (window_->size_pending[1] != 0)) { + /* Unlikely but possible there is a pending size change is set. */ + window_->size[0] = window_->size_pending[0]; + window_->size[1] = window_->size_pending[1]; + window_->size_pending[0] = 0; + window_->size_pending[1] = 0; + } + window_->size[0] = (window_->size[0] / scale_curr) * scale_next; + window_->size[1] = (window_->size[1] / scale_curr) * scale_next; + wl_egl_window_resize(window_->egl_window, UNPACK2(window_->size), 0, 0); + window_->ghost_window->notify_size(); - win->scale = scale_next; - wl_surface_set_buffer_scale(w->wl_surface, scale_next); changed = true; } - if (dpi_next != dpi_curr) { - /* Using the real DPI will cause wrong scaling of the UI - * use a multiplier for the default DPI as workaround. */ - win->dpi = dpi_next; + if (scale_fractional_next != scale_fractional_curr) { + window_->scale_fractional = scale_fractional_next; changed = true; /* As this is a low-level function, we might want adding this event to be optional, @@ -979,7 +1080,7 @@ bool GHOST_WindowWayland::outputs_changed_update_scale() bool GHOST_WindowWayland::outputs_enter(GWL_Output *output) { - std::vector<GWL_Output *> &outputs = w->outputs; + std::vector<GWL_Output *> &outputs = window_->outputs; auto it = std::find(outputs.begin(), outputs.end(), output); if (it != outputs.end()) { return false; @@ -990,7 +1091,7 @@ bool GHOST_WindowWayland::outputs_enter(GWL_Output *output) bool GHOST_WindowWayland::outputs_leave(GWL_Output *output) { - std::vector<GWL_Output *> &outputs = w->outputs; + std::vector<GWL_Output *> &outputs = window_->outputs; auto it = std::find(outputs.begin(), outputs.end(), output); if (it == outputs.end()) { return false; |