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

384 lines
13 KiB
C++

/* SPDX-FileCopyrightText: 2020-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup GHOST
* Declaration of GHOST_SystemWayland class.
*/
#pragma once
#include "../GHOST_Types.hh"
#include "GHOST_System.hh"
#include "GHOST_WindowWayland.hh"
#ifdef WITH_GHOST_WAYLAND_DYNLOAD
# include <wayland_dynload_client.h>
#endif
#include <wayland-client.h>
#include <mutex>
#include <span>
#include <string>
#ifdef USE_EVENT_BACKGROUND_THREAD
# include <atomic>
# include <thread>
#endif
class GHOST_WindowWayland;
bool ghost_wl_display_report_error_if_set(wl_display *display);
bool ghost_wl_output_own(const struct wl_output *wl_output);
void ghost_wl_output_tag(struct wl_output *wl_output);
struct GWL_Output *ghost_wl_output_user_data(struct wl_output *wl_output);
/**
* Enter/exit handlers may be called with a null window surface (when the window just closed),
* so add a version of the function that checks this.
*
* All of the functions could in fact however paranoid null checks make the expected
* state difficult to reason about, so only use this in cases the surface may be null.
*/
bool ghost_wl_surface_own_with_null_check(const struct wl_surface *wl_surface);
bool ghost_wl_surface_own(const struct wl_surface *wl_surface);
void ghost_wl_surface_tag(struct wl_surface *wl_surface);
GHOST_WindowWayland *ghost_wl_surface_user_data(struct wl_surface *wl_surface);
bool ghost_wl_surface_own_cursor_pointer(const struct wl_surface *wl_surface);
void ghost_wl_surface_tag_cursor_pointer(struct wl_surface *wl_surface);
bool ghost_wl_surface_own_cursor_tablet(const struct wl_surface *wl_surface);
void ghost_wl_surface_tag_cursor_tablet(struct wl_surface *wl_surface);
/* Scaling to: translates from WAYLAND into GHOST (viewport local) coordinates.
* Scaling from: performs the reverse translation.
*
* Scaling "to" is used to map WAYLAND location cursor coordinates to GHOST coordinates.
* Scaling "from" is used to clamp cursor coordinates in WAYLAND local coordinates. */
struct GWL_WindowScaleParams;
wl_fixed_t gwl_window_scale_wl_fixed_to(const GWL_WindowScaleParams &scale_params,
wl_fixed_t value);
wl_fixed_t gwl_window_scale_wl_fixed_from(const GWL_WindowScaleParams &scale_params,
wl_fixed_t value);
/* Avoid this where possible as scaling integers often incurs rounding errors.
* Scale #wl_fixed_t where possible.
*
* In general scale by large values where this is less likely to be a problem. */
int gwl_window_scale_int_to(const GWL_WindowScaleParams &scale_params, int value);
int gwl_window_scale_int_from(const GWL_WindowScaleParams &scale_params, int value);
/**
* Scale a logical buffer size to physical pixels, returning an integer buffer scale.
* The buffer scale is rounded up so `result / *r_buffer_scale == logical_size`.
*/
int gwl_window_scale_buffer_size_to(const GWL_WindowScaleParams &scale_params,
int logical_size,
int *r_buffer_scale);
#define FRACTIONAL_DENOMINATOR 120
/**
* The current desktop (Gnome, KDE etc..).
*
* \note Use this as a last resort, ideally wayland integration would *not* depend on this.
*/
enum class GWL_CurrentDesktopType {
Other = 0,
Gnome,
KDE,
};
#ifdef WITH_GHOST_WAYLAND_DYNLOAD
/**
* Return true when all required WAYLAND libraries are present.
*/
bool ghost_wl_dynload_libraries_init();
void ghost_wl_dynload_libraries_exit();
#endif
struct GWL_Output {
/** Wayland core types. */
struct {
wl_output *output = nullptr;
} wl;
/** XDG native types. */
struct {
struct zxdg_output_v1 *output = nullptr;
} xdg;
GHOST_SystemWayland *system = nullptr;
/**
* Dimensions in pixels.
*
* \note Rotation (from the `transform` flag) has *not* been applied.
* So a vertical monitor will still have a larger width.
*/
int32_t size_native[2] = {0, 0};
/** Dimensions in millimeter. */
int32_t size_mm[2] = {0, 0};
/**
* Dimensions in logical points.
*
* \note A 2x Hi-DPI monitor with a `size_native` of 1600x1200
* would have a `size_logical` of 800x600.
*
* \note Rotation (from the `transform` flag) *has* been applied.
*/
int32_t size_logical[2] = {0, 0};
bool has_size_logical = false;
/** Monitor position in pixels. */
int32_t position_logical[2] = {0, 0};
bool has_position_logical = false;
int transform = 0;
int scale = 1;
/**
* The integer `scale` value should be used in almost all cases,
* as this is what is used for most API calls.
* Only use fractional scaling to calculate the DPI.
*
* \note Use the same scale as #wp_fractional_scale_manager_v1
* (avoid floating point arithmetic in general).
*/
int scale_fractional = (1 * FRACTIONAL_DENOMINATOR);
bool has_scale_fractional = false;
std::string make;
std::string model;
};
class GHOST_SystemWayland : public GHOST_System {
public:
GHOST_SystemWayland(bool background);
GHOST_SystemWayland() : GHOST_SystemWayland(true) {};
~GHOST_SystemWayland() override;
GHOST_TSuccess init() override;
bool processEvents(bool waitForEvent) override;
bool setConsoleWindowState(GHOST_TConsoleWindowState action) override;
GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys &keys) const override;
GHOST_TSuccess getButtons(GHOST_Buttons &buttons) const override;
char *getClipboard(bool selection) const override;
void putClipboard(const char *buffer, bool selection) const override;
/**
* Returns GHOST_kSuccess if the clipboard contains an image.
*/
GHOST_TSuccess hasClipboardImage() const override;
/**
* Get image data from the Clipboard
* \param r_width: the returned image width in pixels.
* \param r_height: the returned image height in pixels.
* \return pointer uint array in RGBA byte order. Caller must free.
*/
uint *getClipboardImage(int *r_width, int *r_height) const override;
/**
* Put image data to the Clipboard
* \param rgba: uint array in RGBA byte order.
* \param width: the image width in pixels.
* \param height: the image height in pixels.
*/
GHOST_TSuccess putClipboardImage(uint *rgba, int width, int height) const override;
uint8_t getNumDisplays() const override;
uint64_t getMilliSeconds() const override;
GHOST_TSuccess getCursorPositionClientRelative(const GHOST_IWindow *window,
int32_t &x,
int32_t &y) const override;
GHOST_TSuccess setCursorPositionClientRelative(GHOST_IWindow *window,
int32_t x,
int32_t y) override;
GHOST_TSuccess getCursorPosition(int32_t &x, int32_t &y) const override;
GHOST_TSuccess setCursorPosition(int32_t x, int32_t y) override;
uint32_t getCursorPreferredLogicalSize() const override;
void getMainDisplayDimensions(uint32_t &width, uint32_t &height) const override;
void getAllDisplayDimensions(uint32_t &width, uint32_t &height) const override;
GHOST_IContext *createOffscreenContext(GHOST_GPUSettings gpu_settings) override;
GHOST_TSuccess disposeContext(GHOST_IContext *context) override;
GHOST_IWindow *createWindow(const char *title,
int32_t left,
int32_t top,
uint32_t width,
uint32_t height,
GHOST_TWindowState state,
GHOST_GPUSettings gpu_settings,
const bool exclusive,
const bool is_dialog,
const GHOST_IWindow *parent_window) override;
GHOST_TCapabilityFlag getCapabilities() const override;
void setMultitouchGestures(const bool use) override;
/* WAYLAND utility functions (share window/system logic). */
GHOST_TSuccess cursor_shape_set(GHOST_TStandardCursor shape);
GHOST_TSuccess cursor_shape_check(GHOST_TStandardCursor cursor_shape);
GHOST_TSuccess cursor_shape_custom_set(const GHOST_CursorGenerator &cg);
GHOST_TSuccess cursor_bitmap_get(GHOST_CursorBitmapRef *bitmap);
GHOST_TSuccess cursor_visibility_set(bool visible);
bool cursor_grab_use_software_display_get(const GHOST_TGrabCursorMode mode);
/**
* Return a separate WAYLAND local timer manager to #GHOST_System::getTimerManager
* Manipulation & access must lock with #GHOST_WaylandSystem::server_mutex.
*
* See #GWL_Display::key_repeat_timer_manager docstring for details on why this is needed.
*/
GHOST_TimerManager *key_repeat_timer_manager();
void xdg_toplevel_icon_update(GHOST_WindowWayland *window, struct xdg_toplevel *toplevel);
/* WAYLAND direct-data access. */
struct wl_display *wl_display_get();
struct wl_compositor *wl_compositor_get();
struct zwp_primary_selection_device_manager_v1 *wp_primary_selection_manager_get();
struct xdg_activation_v1 *xdg_activation_manager_get();
struct zwp_pointer_gestures_v1 *wp_pointer_gestures_get();
struct wp_fractional_scale_manager_v1 *wp_fractional_scale_manager_get();
struct wp_viewporter *wp_viewporter_get();
struct wp_color_manager_v1 *wp_color_manager_get();
struct wl_event_queue *wp_color_manager_queue_get();
bool supports_color_manager_feature_windows_scrgb() const;
bool supports_color_manager_extended_srgb_linear() const;
struct xdg_wm_base *xdg_decor_shell_get();
struct zxdg_decoration_manager_v1 *xdg_decor_manager_get();
/* End `xdg_decor`. */
const std::span<GWL_Output *const> outputs_get() const;
struct wl_shm *wl_shm_get() const;
void ime_begin(const GHOST_WindowWayland *win,
int32_t x,
int32_t y,
int32_t w,
int32_t h,
bool completed) const;
void ime_end(const GHOST_WindowWayland *win) const;
bool use_window_frame_get() const;
bool use_window_frame_csd_get() const;
#ifdef WITH_GHOST_CSD
const GHOST_CSD_Layout &csd_layout_base_get() const;
#endif
static const char *xdg_app_id_get();
/* WAYLAND utility functions. */
/**
* Use this function instead of #GHOST_System::getMilliSeconds,
* passing in the time-stamp from WAYLAND input to get the event
* time-stamp with an offset applied to make it compatible with `getMilliSeconds`.
*/
uint64_t ms_from_input_time(const uint32_t timestamp_as_uint);
/**
* Push an event, with support for calling from a thread.
* NOTE: only needed for `USE_EVENT_BACKGROUND_THREAD`.
*/
GHOST_TSuccess pushEvent_maybe_pending(std::unique_ptr<const GHOST_IEvent> event);
/** Set this seat to be active. */
void seat_active_set(const struct GWL_Seat *seat);
struct wl_seat *wl_seat_active_get_with_input_serial(uint32_t &serial);
/**
* Clear all references to this output.
*
* \note The compositor should have already called the `wl_surface_listener.leave` callback,
* however some compositors may not (see #103586).
* So remove references to the output before it's destroyed to avoid crashing.
*
* \return true when any references were removed.
*/
bool output_unref(struct wl_output *wl_output);
void output_scale_update(GWL_Output *output);
/**
* Clear all references to this surface to prevent accessing nullptr pointers.
*
* \return true when any references were removed.
*/
bool window_surface_unref(const wl_surface *wl_surface);
bool window_cursor_grab_set(const GHOST_TGrabCursorMode mode,
const GHOST_TGrabCursorMode mode_current,
int32_t init_grab_xy[2],
const GHOST_Rect *wrap_bounds,
GHOST_TAxisFlag wrap_axis,
wl_surface *wl_surface,
const struct GWL_WindowScaleParams &scale_params);
#ifdef USE_EVENT_BACKGROUND_THREAD
/* NOTE: allocate mutex so `const` functions can lock the mutex. */
/** Lock to prevent #wl_display_dispatch / #wl_display_roundtrip / #wl_display_flush
* from running at the same time. */
std::mutex *server_mutex = nullptr;
/**
* Threads must lock this before manipulating #GWL_Display::ghost_timer_manager.
*
* \note Using a separate lock to `server_mutex` is necessary because the
* server lock is already held when calling `ghost_wl_display_event_pump`.
* If manipulating the timer used the `server_mutex`, event pump can indirectly
* handle key up/down events which would lock `server_mutex` causing a dead-lock.
*/
std::mutex *timer_mutex = nullptr;
std::thread::id main_thread_id;
std::atomic<bool> has_pending_actions_for_window = false;
#endif
private:
/**
* Support freeing the internal data separately from the destructor
* so it can be called when WAYLAND isn't running (immediately before raising an exception).
*/
void display_destroy_and_free_all();
struct GWL_Display *display_;
};