Files
workinf_Blender_Wasm/blender-5.2.0/intern/ghost/intern/GHOST_WindowWayland.hh
2026-08-12 04:47:48 -04:00

265 lines
8.5 KiB
C++

/* SPDX-FileCopyrightText: 2020-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup GHOST
*
* Declaration of GHOST_WindowWayland class.
*/
#pragma once
#include "GHOST_Window.hh"
#include <span>
#include <vector>
#include <wayland-util.h> /* For #wl_fixed_t */
/**
* Define to workaround for a bug/limitation in WAYLAND, see: #100855 & upstream report:
* https://gitlab.freedesktop.org/wayland/wayland/-/issues/159
*
* Consume events from WAYLAND in a thread, this is needed because overflowing the event queue
* causes a fatal error (more than `sizeof(wl_buffer.data)` at the time of writing).
*
* Solve this using a thread that handles events, locking must be performed as follows:
*
* - Lock #GWL_Display.server_mutex to prevent #wl_display_dispatch / #wl_display_roundtrip
* running from multiple threads at once.
*
* Even though WAYLAND functions that communicate with `wl_display_*` have their own locks,
* GHOST functions that communicate with WAYLAND must use this lock to be thread safe.
*
* Without this reading/writing values such as changing the cursor or setting the window size
* could conflict with WAYLAND callbacks running in a separate thread.
* So the `server_mutex` ensures either GHOST or WAYLAND is manipulating this data,
* having two WAYLAND callbacks accessing the data at once isn't a problem.
*
* \warning Some operations such as #ContextEGL creation/deletion & swap-buffers may call
* #wl_display_dispatch indirectly, so it's important to take care to lock the `server_mutex`,
* before accessing these functions too.
*
* \warning An unfortunate side-effect of this is care needs to be taken not to call public
* GHOST functions from each other in situations that would otherwise be supported.
* As both using a `server_mutex` results in a dead-lock. In some cases this means the
* implementation and the public function may need to be split.
*
* - Lock #GWL_Display.timer_mutex when WAYLAND callbacks manipulate timers.
*
* - Lock #GWL_Display.events_pending_mutex before manipulating #GWL_Display.events_pending.
*
* - Lock #GWL_Window.frame_pending_mutex before changing window size & frame settings,
* this is flushed in #GHOST_WindowWayland::pending_actions_handle.
*
* \note Keep this define as it can be useful to disable threading when troubleshooting
* issues with events.
*/
#define USE_EVENT_BACKGROUND_THREAD
class GHOST_SystemWayland;
struct GWL_Output;
struct GWL_Window;
struct GHOST_CSD_EventState_ButtonAction {
GHOST_TCSD_Type type = GHOST_kCSDTypeBody;
int32_t xy[2] = {0, 0};
uint32_t serial = 0;
uint64_t ms = 0;
bool is_press = false;
};
struct GHOST_CSD_EventState_Button {
GHOST_CSD_EventState_ButtonAction action_history[3];
int action_history_num = 0;
};
struct GHOST_CSD_EventState {
int32_t event_xy[2] = {0, 0};
/**
* Pointing device button, indexed with #GHOST_TButton.
*
* Only track the left-button for now since there is no need
* to track double-click or press-drag for other buttons.
* Extend as needed.
*/
GHOST_CSD_EventState_Button buttons[/*GHOST_kButtonMaskLeft + 1*/ 1];
};
/**
* Icon buffer kept alive per the `xdg_toplevel_icon_v1` spec.
* Freed on window destroy.
*/
struct GWL_XDG_WindowIcon {
struct wl_buffer *buffer = nullptr;
void *buffer_data = nullptr;
size_t buffer_size = 0;
/** The icon dimensions in physical pixels (width == height, always square). */
int icon_pixel_size = 0;
};
void gwl_xdg_window_icon_free(GWL_XDG_WindowIcon &icon);
class GHOST_WindowWayland : public GHOST_Window {
public:
GHOST_TSuccess hasCursorShape(GHOST_TStandardCursor cursor_shape) override;
GHOST_WindowWayland(GHOST_SystemWayland *system,
const char *title,
int32_t left,
int32_t top,
uint32_t width,
uint32_t height,
GHOST_TWindowState state,
const GHOST_IWindow *parent_window,
GHOST_TDrawingContextType type,
const bool is_dialog,
const GHOST_ContextParams &context_params,
const bool exclusive,
const GHOST_GPUDevice &preferred_device);
~GHOST_WindowWayland() override;
/* Ghost API */
#ifdef USE_EVENT_BACKGROUND_THREAD
GHOST_TSuccess swapBufferRelease() override; /* Only for assertion. */
#endif
uint16_t getDPIHint() override;
GHOST_TSuccess setWindowCursorGrab(GHOST_TGrabCursorMode mode) override;
GHOST_TSuccess setWindowCursorShape(GHOST_TStandardCursor shape) override;
GHOST_TSuccess setWindowCustomCursorGenerator(GHOST_CursorGenerator *cursor_generator) override;
GHOST_TSuccess setWindowCustomCursorShape(const uint8_t *bitmap,
const uint8_t *mask,
const int size[2],
const int hot_spot[2],
bool can_invert_color) override;
bool getCursorGrabUseSoftwareDisplay() override;
GHOST_TSuccess getCursorBitmap(GHOST_CursorBitmapRef *bitmap) override;
bool getValid() const override;
void setTitle(const char *title) override;
std::string getTitle() const override;
void getWindowBounds(GHOST_Rect &bounds) const override;
void getClientBounds(GHOST_Rect &bounds) const override;
GHOST_TSuccess setClientWidth(uint32_t width) override;
GHOST_TSuccess setClientHeight(uint32_t height) override;
GHOST_TSuccess setClientSize(uint32_t width, uint32_t height) override;
void screenToClient(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const override;
void clientToScreen(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const override;
GHOST_TSuccess setWindowCursorVisibility(bool visible) override;
GHOST_TSuccess setState(GHOST_TWindowState state) override;
GHOST_TWindowState getState() const override;
GHOST_TSuccess invalidate() override;
GHOST_TSuccess setOrder(GHOST_TWindowOrder order) override;
bool isDialog() const override;
#ifdef WITH_INPUT_IME
void beginIME(int32_t x, int32_t y, int32_t w, int32_t h, bool completed) override;
void endIME() override;
#endif /* WITH_INPUT_IME */
/* WAYLAND direct-data access. */
int scale_get() const;
const struct GWL_WindowScaleParams &scale_params_get() const;
struct wl_surface *wl_surface_get() const;
const std::span<GWL_Output *const> outputs_get() const;
wl_fixed_t wl_fixed_from_window(wl_fixed_t value) const;
wl_fixed_t wl_fixed_to_window(wl_fixed_t value) const;
GWL_XDG_WindowIcon &gwl_xdg_icon_get();
/* WAYLAND window-level functions. */
/**
* Set the window as active and send an #GHOST_kEventWindowActivate event.
*
* \note The current active state is *not* checked, the caller is responsible for
* not activating windows which are already active.
*/
GHOST_TSuccess activate();
/**
* De-activate the window and send a #GHOST_kEventWindowDeactivate event.
*
* \note The current active state is *not* checked, the caller is responsible for
* not de-activating windows that aren't active.
*/
GHOST_TSuccess deactivate();
GHOST_TSuccess close();
GHOST_TSuccess notify_size();
GHOST_TSuccess notify_decor_redraw();
/* WAYLAND utility functions. */
/**
* Refresh the cursor using the cursor assigned to this window.
*
* \note This is needed because in GHOST the cursor is per window,
* where as in WAYLAND the cursor is set per-seat (and per input device).
* When an input device enters a window, this function must run.
*/
GHOST_TSuccess cursor_shape_refresh();
bool outputs_enter(GWL_Output *output);
bool outputs_leave(GWL_Output *output);
/**
* Return true when the windows scale or DPI changes.
*/
bool outputs_changed_update_scale();
void outputs_changed_update_scale_tag();
#ifdef USE_EVENT_BACKGROUND_THREAD
void pending_actions_handle();
#endif
#ifdef WITH_GHOST_CSD
struct xdg_toplevel *xdg_toplevel_get();
GHOST_TWindowState xdg_toplevel_state_get();
const GHOST_CSD_Elem *csd_layout(int *r_num);
GHOST_CSD_EventState &csd_eventstate_get();
GHOST_TCSD_Type csd_elem_active_type_get() const;
void csd_elem_active_type_set(GHOST_TCSD_Type type);
#endif
private:
GHOST_SystemWayland *system_;
struct GWL_Window *window_;
GHOST_GPUDevice preferred_device_;
/**
* \param type: The type of rendering context create.
* \return Indication of success.
*/
GHOST_Context *newDrawingContext(GHOST_TDrawingContextType type) override;
};