GHOST/Wayland: add fractional-scale interface support
Previously, fractional scaling was detected but set an integer buffer scale which the compositor would down-scale causing blurry output. Now the fractional scaling interface is used when available to set the DPI and set the internal buffers size & viewport transformation to ensure 1:1 pixels from Blender to the Wayland output. Tested to work with multiple monitors with mixed fractional/non-fractional scale. Note that this change causes a regression for when fractional scaling is set on a compositor without support for fractional-scale-v1. Supporting fractional scaling in both cases is possible but overly complicated. This case already wasn't working so well - with blurry output due to image scaling, now the DPI wont be accurate in this case although Blender is still usable.
This commit is contained in:
parent
75127f9b09
commit
cde99075e8
|
@ -378,6 +378,14 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
|
|||
unset(_name)
|
||||
endmacro()
|
||||
|
||||
macro(generate_protocol_bindings_when_found PROT_DEF FOUND_VAR)
|
||||
if(EXISTS ${PROT_DEF})
|
||||
generate_protocol_bindings(${PROT_DEF})
|
||||
set(${FOUND_VAR} TRUE)
|
||||
else()
|
||||
set(${FOUND_VAR} FALSE)
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
list(APPEND INC_SYS
|
||||
${INC_DST}
|
||||
|
@ -402,6 +410,17 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
|
|||
generate_protocol_bindings(
|
||||
"${WAYLAND_PROTOCOLS_DIR}/staging/xdg-activation/xdg-activation-v1.xml"
|
||||
)
|
||||
# Fractional scale.
|
||||
generate_protocol_bindings_when_found(
|
||||
"${WAYLAND_PROTOCOLS_DIR}/staging/fractional-scale/fractional-scale-v1.xml"
|
||||
_has_fractional_scale
|
||||
)
|
||||
if(_has_fractional_scale)
|
||||
# Viewport (only required when fractional scale is in use).
|
||||
generate_protocol_bindings(
|
||||
"${WAYLAND_PROTOCOLS_DIR}/stable/viewporter/viewporter.xml"
|
||||
)
|
||||
endif()
|
||||
# Pointer-constraints.
|
||||
generate_protocol_bindings(
|
||||
"${WAYLAND_PROTOCOLS_DIR}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml"
|
||||
|
@ -427,6 +446,11 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
|
|||
|
||||
add_definitions(-DWITH_GHOST_WAYLAND)
|
||||
|
||||
if(_has_fractional_scale)
|
||||
add_definitions(-DWITH_GHOST_WAYLAND_FRACTIONAL_SCALE)
|
||||
endif()
|
||||
unset(_has_fractional_scale)
|
||||
|
||||
if(NOT WITH_GHOST_WAYLAND_APP_ID STREQUAL "")
|
||||
add_definitions(-DWITH_GHOST_WAYLAND_APP_ID=${WITH_GHOST_WAYLAND_APP_ID})
|
||||
endif()
|
||||
|
|
|
@ -61,6 +61,10 @@
|
|||
#include <tablet-unstable-v2-client-protocol.h>
|
||||
#include <xdg-activation-v1-client-protocol.h>
|
||||
#include <xdg-output-unstable-v1-client-protocol.h>
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
# include <fractional-scale-v1-client-protocol.h>
|
||||
# include <viewporter-client-protocol.h>
|
||||
#endif
|
||||
|
||||
/* Decorations `xdg_decor`. */
|
||||
#include <xdg-decoration-unstable-v1-client-protocol.h>
|
||||
|
@ -944,6 +948,10 @@ struct GWL_Display {
|
|||
struct zwp_relative_pointer_manager_v1 *wp_relative_pointer_manager = nullptr;
|
||||
struct zwp_primary_selection_device_manager_v1 *wp_primary_selection_device_manager = nullptr;
|
||||
struct xdg_activation_v1 *xdg_activation_manager = nullptr;
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
struct wp_fractional_scale_manager_v1 *wp_fractional_scale_manager = nullptr;
|
||||
struct wp_viewporter *wp_viewporter = nullptr;
|
||||
#endif
|
||||
|
||||
struct zwp_pointer_constraints_v1 *wp_pointer_constraints = nullptr;
|
||||
struct zwp_pointer_gestures_v1 *wp_pointer_gestures = nullptr;
|
||||
|
@ -3006,19 +3014,6 @@ static void gesture_pinch_handle_begin(void *data,
|
|||
if (wl_surface *wl_surface_focus = seat->pointer.wl_surface_window) {
|
||||
win = ghost_wl_surface_user_data(wl_surface_focus);
|
||||
}
|
||||
/* NOTE(@ideasman42): Blender's use of track-pad coordinates is inconsistent and needs work.
|
||||
* This isn't specific to WAYLAND, in practice they tend to work well enough in most cases.
|
||||
* Some operators scale by the UI scale, some don't.
|
||||
* Even this window scale is not correct because it doesn't account for:
|
||||
* 1) Fractional window scale.
|
||||
* 2) Blender's UI scale preference (which GHOST doesn't know about).
|
||||
*
|
||||
* If support for this were all that was needed it could be handled in GHOST,
|
||||
* however as the operators are not even using coordinates compatible with each other,
|
||||
* it would be better to resolve this by passing rotation & zoom levels directly,
|
||||
* instead of attempting to handle them as cursor coordinates.
|
||||
*/
|
||||
const wl_fixed_t win_scale = win ? win->scale() : 1;
|
||||
|
||||
/* NOTE(@ideasman42): Scale factors match Blender's operators & default preferences.
|
||||
* For these values to work correctly, operator logic will need to be changed not to scale input
|
||||
|
@ -3033,10 +3028,30 @@ static void gesture_pinch_handle_begin(void *data,
|
|||
seat->pointer_gesture_pinch.scale.value = wl_fixed_from_int(1);
|
||||
/* The value 300 matches a value used in clip & image zoom operators.
|
||||
* It seems OK for the 3D view too. */
|
||||
seat->pointer_gesture_pinch.scale.factor = 300 * win_scale;
|
||||
seat->pointer_gesture_pinch.scale.factor = 300;
|
||||
/* The value 5 is used on macOS and roughly maps 1:1 with turntable rotation,
|
||||
* although preferences can scale the sensitivity (which would be skipped ideally). */
|
||||
seat->pointer_gesture_pinch.rotation.factor = 5 * win_scale;
|
||||
seat->pointer_gesture_pinch.rotation.factor = 5;
|
||||
|
||||
if (win) {
|
||||
/* NOTE(@ideasman42): Blender's use of track-pad coordinates is inconsistent and needs work.
|
||||
* This isn't specific to WAYLAND, in practice they tend to work well enough in most cases.
|
||||
* Some operators scale by the UI scale, some don't.
|
||||
* Even this window scale is not correct because it doesn't account for:
|
||||
* 1) Fractional window scale.
|
||||
* 2) Blender's UI scale preference (which GHOST doesn't know about).
|
||||
*
|
||||
* If support for this were all that was needed it could be handled in GHOST,
|
||||
* however as the operators are not even using coordinates compatible with each other,
|
||||
* it would be better to resolve this by passing rotation & zoom levels directly,
|
||||
* instead of attempting to handle them as cursor coordinates.
|
||||
*/
|
||||
const struct GWL_WindowScaleParams &scale_params = win->scale_params();
|
||||
seat->pointer_gesture_pinch.scale.factor = gwl_window_scale_int_to(
|
||||
scale_params, seat->pointer_gesture_pinch.scale.factor);
|
||||
seat->pointer_gesture_pinch.rotation.factor = gwl_window_scale_int_to(
|
||||
scale_params, seat->pointer_gesture_pinch.rotation.factor);
|
||||
}
|
||||
}
|
||||
|
||||
static void gesture_pinch_handle_update(void *data,
|
||||
|
@ -5167,6 +5182,47 @@ static void gwl_registry_xdg_activation_remove(GWL_Display *display,
|
|||
*value_p = nullptr;
|
||||
}
|
||||
|
||||
/* #GWL_Display.wp_fractional_scale_manger */
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
|
||||
static void gwl_registry_wp_fractional_scale_manager_add(GWL_Display *display,
|
||||
const GWL_RegisteryAdd_Params *params)
|
||||
{
|
||||
display->wp_fractional_scale_manager = static_cast<wp_fractional_scale_manager_v1 *>(
|
||||
wl_registry_bind(
|
||||
display->wl_registry, params->name, &wp_fractional_scale_manager_v1_interface, 1));
|
||||
gwl_registry_entry_add(display, params, nullptr);
|
||||
}
|
||||
static void gwl_registry_wp_fractional_scale_manager_remove(GWL_Display *display,
|
||||
void * /*user_data*/,
|
||||
const bool /*on_exit*/)
|
||||
{
|
||||
struct wp_fractional_scale_manager_v1 **value_p = &display->wp_fractional_scale_manager;
|
||||
wp_fractional_scale_manager_v1_destroy(*value_p);
|
||||
*value_p = nullptr;
|
||||
}
|
||||
|
||||
/* #GWL_Display.wl_viewport */
|
||||
|
||||
static void gwl_registry_wp_viewporter_add(GWL_Display *display,
|
||||
const GWL_RegisteryAdd_Params *params)
|
||||
{
|
||||
display->wp_viewporter = static_cast<wp_viewporter *>(
|
||||
wl_registry_bind(display->wl_registry, params->name, &wp_viewporter_interface, 1));
|
||||
gwl_registry_entry_add(display, params, nullptr);
|
||||
}
|
||||
static void gwl_registry_wp_viewporter_remove(GWL_Display *display,
|
||||
void * /*user_data*/,
|
||||
const bool /*on_exit*/)
|
||||
{
|
||||
struct wp_viewporter **value_p = &display->wp_viewporter;
|
||||
wp_viewporter_destroy(*value_p);
|
||||
*value_p = nullptr;
|
||||
}
|
||||
|
||||
#endif /* WITH_GHOST_WAYLAND_FRACTIONAL_SCALE */
|
||||
|
||||
/* #GWL_Display.wp_primary_selection_device_manager */
|
||||
|
||||
static void gwl_registry_wp_primary_selection_device_manager_add(
|
||||
|
@ -5276,6 +5332,20 @@ static const GWL_RegistryHandler gwl_registry_handlers[] = {
|
|||
/*update_fn*/ nullptr,
|
||||
/*remove_fn*/ gwl_registry_xdg_activation_remove,
|
||||
},
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
{
|
||||
/*interface_p*/ &wp_fractional_scale_manager_v1_interface.name,
|
||||
/*add_fn*/ gwl_registry_wp_fractional_scale_manager_add,
|
||||
/*update_fn*/ nullptr,
|
||||
/*remove_fn*/ gwl_registry_wp_fractional_scale_manager_remove,
|
||||
},
|
||||
{
|
||||
/*interface_p*/ &wp_viewporter_interface.name,
|
||||
/*add_fn*/ gwl_registry_wp_viewporter_add,
|
||||
/*update_fn*/ nullptr,
|
||||
/*remove_fn*/ gwl_registry_wp_viewporter_remove,
|
||||
},
|
||||
#endif /* WITH_GHOST_WAYLAND_FRACTIONAL_SCALE */
|
||||
/* Display outputs. */
|
||||
{
|
||||
/*interface_p*/ &wl_output_interface.name,
|
||||
|
@ -6840,6 +6910,17 @@ struct xdg_activation_v1 *GHOST_SystemWayland::xdg_activation_manager()
|
|||
return display_->xdg_activation_manager;
|
||||
}
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
struct wp_fractional_scale_manager_v1 *GHOST_SystemWayland::wp_fractional_scale_manager()
|
||||
{
|
||||
return display_->wp_fractional_scale_manager;
|
||||
}
|
||||
struct wp_viewporter *GHOST_SystemWayland::wp_viewporter()
|
||||
{
|
||||
return display_->wp_viewporter;
|
||||
}
|
||||
#endif /* WITH_GHOST_WAYLAND_FRACTIONAL_SCALE */
|
||||
|
||||
struct zwp_pointer_gestures_v1 *GHOST_SystemWayland::wp_pointer_gestures()
|
||||
{
|
||||
return display_->wp_pointer_gestures;
|
||||
|
@ -7055,7 +7136,7 @@ bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mod
|
|||
const GHOST_Rect *wrap_bounds,
|
||||
const GHOST_TAxisFlag wrap_axis,
|
||||
wl_surface *wl_surface,
|
||||
const int scale)
|
||||
const struct GWL_WindowScaleParams &scale_params)
|
||||
{
|
||||
/* Caller must lock `server_mutex`. */
|
||||
|
||||
|
@ -7115,10 +7196,14 @@ bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mod
|
|||
|
||||
GHOST_Rect bounds_scale;
|
||||
|
||||
bounds_scale.m_l = wl_fixed_from_int(wrap_bounds->m_l) / scale;
|
||||
bounds_scale.m_t = wl_fixed_from_int(wrap_bounds->m_t) / scale;
|
||||
bounds_scale.m_r = wl_fixed_from_int(wrap_bounds->m_r) / scale;
|
||||
bounds_scale.m_b = wl_fixed_from_int(wrap_bounds->m_b) / scale;
|
||||
bounds_scale.m_l = gwl_window_scale_wl_fixed_from(scale_params,
|
||||
wl_fixed_from_int(wrap_bounds->m_l));
|
||||
bounds_scale.m_t = gwl_window_scale_wl_fixed_from(scale_params,
|
||||
wl_fixed_from_int(wrap_bounds->m_t));
|
||||
bounds_scale.m_r = gwl_window_scale_wl_fixed_from(scale_params,
|
||||
wl_fixed_from_int(wrap_bounds->m_r));
|
||||
bounds_scale.m_b = gwl_window_scale_wl_fixed_from(scale_params,
|
||||
wl_fixed_from_int(wrap_bounds->m_b));
|
||||
|
||||
bounds_scale.wrapPoint(UNPACK2(xy_next), 0, wrap_axis);
|
||||
|
||||
|
@ -7138,8 +7223,8 @@ bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mod
|
|||
if ((init_grab_xy[0] != seat->grab_lock_xy[0]) ||
|
||||
(init_grab_xy[1] != seat->grab_lock_xy[1])) {
|
||||
const wl_fixed_t xy_next[2] = {
|
||||
wl_fixed_from_int(init_grab_xy[0]) / scale,
|
||||
wl_fixed_from_int(init_grab_xy[1]) / scale,
|
||||
gwl_window_scale_wl_fixed_from(scale_params, wl_fixed_from_int(init_grab_xy[0])),
|
||||
gwl_window_scale_wl_fixed_from(scale_params, wl_fixed_from_int(init_grab_xy[1])),
|
||||
};
|
||||
zwp_locked_pointer_v1_set_cursor_position_hint(seat->wp_locked_pointer,
|
||||
UNPACK2(xy_next));
|
||||
|
@ -7164,13 +7249,13 @@ bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mod
|
|||
#endif
|
||||
|
||||
if (xy_motion_create_event) {
|
||||
seat->system->pushEvent_maybe_pending(
|
||||
new GHOST_EventCursor(seat->system->getMilliSeconds(),
|
||||
GHOST_kEventCursorMove,
|
||||
ghost_wl_surface_user_data(wl_surface),
|
||||
wl_fixed_to_int(scale * xy_motion[0]),
|
||||
wl_fixed_to_int(scale * xy_motion[1]),
|
||||
GHOST_TABLET_DATA_NONE));
|
||||
seat->system->pushEvent_maybe_pending(new GHOST_EventCursor(
|
||||
seat->system->getMilliSeconds(),
|
||||
GHOST_kEventCursorMove,
|
||||
ghost_wl_surface_user_data(wl_surface),
|
||||
wl_fixed_to_int(gwl_window_scale_wl_fixed_to(scale_params, xy_motion[0])),
|
||||
wl_fixed_to_int(gwl_window_scale_wl_fixed_to(scale_params, xy_motion[1])),
|
||||
GHOST_TABLET_DATA_NONE));
|
||||
}
|
||||
|
||||
zwp_locked_pointer_v1_destroy(seat->wp_locked_pointer);
|
||||
|
@ -7206,8 +7291,10 @@ bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mod
|
|||
if (mode == GHOST_kGrabHide) {
|
||||
/* Set the initial position to detect any changes when un-grabbing,
|
||||
* otherwise the unlocked cursor defaults to un-locking in-place. */
|
||||
init_grab_xy[0] = wl_fixed_to_int(scale * seat->pointer.xy[0]);
|
||||
init_grab_xy[1] = wl_fixed_to_int(scale * seat->pointer.xy[1]);
|
||||
init_grab_xy[0] = wl_fixed_to_int(
|
||||
gwl_window_scale_wl_fixed_to(scale_params, seat->pointer.xy[0]));
|
||||
init_grab_xy[1] = wl_fixed_to_int(
|
||||
gwl_window_scale_wl_fixed_to(scale_params, seat->pointer.xy[1]));
|
||||
seat->grab_lock_xy[0] = init_grab_xy[0];
|
||||
seat->grab_lock_xy[1] = init_grab_xy[1];
|
||||
}
|
||||
|
|
|
@ -47,6 +47,26 @@ void ghost_wl_surface_tag_cursor_pointer(struct wl_surface *surface);
|
|||
bool ghost_wl_surface_own_cursor_tablet(const struct wl_surface *surface);
|
||||
void ghost_wl_surface_tag_cursor_tablet(struct wl_surface *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);
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_DYNLOAD
|
||||
/**
|
||||
* Return true when all required WAYLAND libraries are present,
|
||||
|
@ -181,6 +201,10 @@ class GHOST_SystemWayland : public GHOST_System {
|
|||
struct zwp_primary_selection_device_manager_v1 *wp_primary_selection_manager();
|
||||
struct xdg_activation_v1 *xdg_activation_manager();
|
||||
struct zwp_pointer_gestures_v1 *wp_pointer_gestures();
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
struct wp_fractional_scale_manager_v1 *wp_fractional_scale_manager();
|
||||
struct wp_viewporter *wp_viewporter();
|
||||
#endif
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
|
||||
libdecor *libdecor_context();
|
||||
|
@ -234,7 +258,7 @@ class GHOST_SystemWayland : public GHOST_System {
|
|||
const GHOST_Rect *wrap_bounds,
|
||||
GHOST_TAxisFlag wrap_axis,
|
||||
wl_surface *wl_surface,
|
||||
int scale);
|
||||
const struct GWL_WindowScaleParams &scale_params);
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
|
||||
static bool use_libdecor_runtime();
|
||||
|
|
|
@ -38,6 +38,11 @@
|
|||
#include <xdg-activation-v1-client-protocol.h>
|
||||
#include <xdg-decoration-unstable-v1-client-protocol.h>
|
||||
#include <xdg-shell-client-protocol.h>
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
# include <fractional-scale-v1-client-protocol.h>
|
||||
# include <viewporter-client-protocol.h>
|
||||
# define FRACTIONAL_DENOMINATOR 120
|
||||
#endif
|
||||
|
||||
#include <atomic>
|
||||
|
||||
|
@ -83,6 +88,52 @@ static void gwl_xdg_decor_window_destroy(WGL_XDG_Decor_Window *decor)
|
|||
delete decor;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Window-Viewport/Wayland to/from Scale Conversion
|
||||
* \{ */
|
||||
|
||||
struct GWL_WindowScaleParams {
|
||||
bool is_fractional = false;
|
||||
/**
|
||||
* When fractional:
|
||||
* Scale is multiplied by #FRACTIONAL_DENOMINATOR.
|
||||
* Otherwise scale is an integer.
|
||||
*/
|
||||
wl_fixed_t scale = 0;
|
||||
};
|
||||
|
||||
wl_fixed_t gwl_window_scale_wl_fixed_to(const GWL_WindowScaleParams &scale_params,
|
||||
wl_fixed_t value)
|
||||
{
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
if (scale_params.is_fractional) {
|
||||
return (value * scale_params.scale) / FRACTIONAL_DENOMINATOR;
|
||||
}
|
||||
#endif
|
||||
return value * scale_params.scale;
|
||||
}
|
||||
wl_fixed_t gwl_window_scale_wl_fixed_from(const GWL_WindowScaleParams &scale_params,
|
||||
wl_fixed_t value)
|
||||
{
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
if (scale_params.is_fractional) {
|
||||
return (value * FRACTIONAL_DENOMINATOR) / scale_params.scale;
|
||||
}
|
||||
#endif
|
||||
return value / scale_params.scale;
|
||||
}
|
||||
|
||||
int gwl_window_scale_int_to(const GWL_WindowScaleParams &scale_params, int value)
|
||||
{
|
||||
return wl_fixed_to_int(gwl_window_scale_wl_fixed_to(scale_params, wl_fixed_from_int(value)));
|
||||
}
|
||||
int gwl_window_scale_int_from(const GWL_WindowScaleParams &scale_params, int value)
|
||||
{
|
||||
return wl_fixed_to_int(gwl_window_scale_wl_fixed_from(scale_params, wl_fixed_from_int(value)));
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Internal #GWL_Window
|
||||
* \{ */
|
||||
|
@ -111,10 +162,18 @@ enum eGWL_PendingWindowActions {
|
|||
#endif /* USE_EVENT_BACKGROUND_THREAD */
|
||||
|
||||
struct GWL_WindowFrame {
|
||||
/**
|
||||
* The frame size (in GHOST window coordinates).
|
||||
*
|
||||
* These must be converted to WAYLADN relative coordinates when the window is scaled
|
||||
* by Hi-DPI/fractional scaling.
|
||||
*/
|
||||
int32_t size[2] = {0, 0};
|
||||
bool is_maximised = false;
|
||||
bool is_fullscreen = false;
|
||||
bool is_active = false;
|
||||
/** Disable when the fractional scale is a whole number. */
|
||||
int fractional_scale = 0;
|
||||
};
|
||||
|
||||
struct GWL_Window {
|
||||
|
@ -132,14 +191,20 @@ struct GWL_Window {
|
|||
/** The scale value written to #wl_surface_set_buffer_scale. */
|
||||
int scale = 0;
|
||||
/**
|
||||
* The fractional scale used to calculate the DPI.
|
||||
* (always set, even when scaling is rounded to whole units).
|
||||
* The scale value to be used in the case fractional scale is disable.
|
||||
* In general this should only be used when changing states
|
||||
* (when disabling fractional scale).
|
||||
*/
|
||||
wl_fixed_t scale_fractional = 0;
|
||||
int scale_on_output = 0;
|
||||
|
||||
/** A temporary token used for the window to be notified of it's activation. */
|
||||
struct xdg_activation_token_v1 *xdg_activation_token = nullptr;
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
struct wp_viewport *viewport = nullptr;
|
||||
struct wp_fractional_scale_v1 *fractional_scale_handle = nullptr;
|
||||
#endif
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
|
||||
WGL_LibDecor_Window *libdecor = nullptr;
|
||||
#endif
|
||||
|
@ -166,6 +231,9 @@ struct GWL_Window {
|
|||
|
||||
bool is_dialog = false;
|
||||
|
||||
/** Currently only initialized on access (avoids allocations & allows to keep private). */
|
||||
GWL_WindowScaleParams scale_params;
|
||||
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
/**
|
||||
* These pending actions can't be performed when WAYLAND handlers are running from a thread.
|
||||
|
@ -310,6 +378,124 @@ static bool gwl_window_state_set(GWL_Window *win, const GHOST_TWindowState state
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Internal #GWL_Window Viewport Setup/Teardown
|
||||
*
|
||||
* A viewport is needed to implement fractional scale,
|
||||
* as the outputs scale may change at runtime, support creating & clearing the viewport as needed.
|
||||
* \{ */
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
|
||||
/**
|
||||
* Scale a value from a viewport value to Wayland windowing.
|
||||
* Scale down or not at all.
|
||||
*/
|
||||
static int gwl_window_fractional_to_viewport(const GWL_WindowFrame &frame, int value)
|
||||
{
|
||||
GHOST_ASSERT(frame.fractional_scale != 0, "Not fractional or called before initiazlized!");
|
||||
return (value * frame.fractional_scale) / FRACTIONAL_DENOMINATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scale a value from a Wayland windowing value to the viewport.
|
||||
* Scales up or not at all.
|
||||
*/
|
||||
static int gwl_window_fractional_from_viewport(const GWL_WindowFrame &frame, int value)
|
||||
{
|
||||
GHOST_ASSERT(frame.fractional_scale != 0, "Not fractional or called before initiazlized!");
|
||||
return (value * FRACTIONAL_DENOMINATOR) / frame.fractional_scale;
|
||||
}
|
||||
|
||||
/* NOTE: rounded versions are needed for window-frame dimensions conversions.
|
||||
* (rounding is part of the WAYLAND spec). All other conversions such as cursor coordinates
|
||||
* can used simple integer division as rounding is not defined in this case. */
|
||||
|
||||
static int gwl_window_fractional_to_viewport_round(const GWL_WindowFrame &frame, int value)
|
||||
{
|
||||
GHOST_ASSERT(frame.fractional_scale != 0, "Not fractional or called before initiazlized!");
|
||||
return lroundf(double(value * frame.fractional_scale) / double(FRACTIONAL_DENOMINATOR));
|
||||
}
|
||||
|
||||
static int gwl_window_fractional_from_viewport_round(const GWL_WindowFrame &frame, int value)
|
||||
{
|
||||
GHOST_ASSERT(frame.fractional_scale != 0, "Not fractional or called before initiazlized!");
|
||||
return lroundf(double(value * FRACTIONAL_DENOMINATOR) / double(frame.fractional_scale));
|
||||
}
|
||||
|
||||
static bool gwl_window_viewport_set(GWL_Window *win, bool *r_surface_needs_commit)
|
||||
{
|
||||
if (win->viewport != nullptr) {
|
||||
return false;
|
||||
}
|
||||
struct wp_viewporter *viewporter = win->ghost_system->wp_viewporter();
|
||||
if (viewporter == nullptr) {
|
||||
return false;
|
||||
}
|
||||
win->viewport = wp_viewporter_get_viewport(viewporter, win->wl_surface);
|
||||
if (win->viewport == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Set the buffer scale to 1 since a viewport will be used. */
|
||||
if (win->scale != 1) {
|
||||
win->scale = 1;
|
||||
wl_surface_set_buffer_scale(win->wl_surface, win->scale);
|
||||
if (r_surface_needs_commit) {
|
||||
*r_surface_needs_commit = true;
|
||||
}
|
||||
else {
|
||||
wl_surface_commit(win->wl_surface);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gwl_window_viewport_unset(GWL_Window *win, bool *r_surface_needs_commit)
|
||||
{
|
||||
if (win->viewport == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
wp_viewport_destroy(win->viewport);
|
||||
win->viewport = nullptr;
|
||||
|
||||
GHOST_ASSERT(win->scale == 1, "Unexpected scale!");
|
||||
if (win->scale != win->scale_on_output) {
|
||||
win->scale = win->scale_on_output;
|
||||
wl_surface_set_buffer_scale(win->wl_surface, win->scale);
|
||||
if (r_surface_needs_commit) {
|
||||
*r_surface_needs_commit = true;
|
||||
}
|
||||
else {
|
||||
wl_surface_commit(win->wl_surface);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gwl_window_viewport_size_update(GWL_Window *win)
|
||||
{
|
||||
if (win->viewport == nullptr) {
|
||||
return false;
|
||||
}
|
||||
wp_viewport_set_source(win->viewport,
|
||||
wl_fixed_from_int(0),
|
||||
wl_fixed_from_int(0),
|
||||
wl_fixed_from_int(win->frame.size[0]),
|
||||
wl_fixed_from_int(win->frame.size[1]));
|
||||
wp_viewport_set_destination(
|
||||
win->viewport,
|
||||
gwl_window_fractional_from_viewport_round(win->frame, win->frame.size[0]),
|
||||
gwl_window_fractional_from_viewport_round(win->frame, win->frame.size[1]));
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* WITH_GHOST_WAYLAND_FRACTIONAL_SCALE */
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Internal #GWL_Window Activation
|
||||
* \{ */
|
||||
|
@ -369,7 +555,26 @@ static void gwl_window_activate(GWL_Window *win)
|
|||
/** \name Internal #GWL_Window Pending Actions
|
||||
* \{ */
|
||||
|
||||
static void gwl_window_frame_pending_size_set(GWL_Window *win)
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
static void gwl_window_frame_pending_fractional_scale_set(GWL_Window *win,
|
||||
bool *r_surface_needs_commit)
|
||||
{
|
||||
if (win->frame_pending.fractional_scale == win->frame.fractional_scale) {
|
||||
return;
|
||||
}
|
||||
|
||||
win->frame.fractional_scale = win->frame_pending.fractional_scale;
|
||||
if (win->frame_pending.fractional_scale) {
|
||||
gwl_window_viewport_set(win, r_surface_needs_commit);
|
||||
gwl_window_viewport_size_update(win);
|
||||
}
|
||||
else {
|
||||
gwl_window_viewport_unset(win, r_surface_needs_commit);
|
||||
}
|
||||
}
|
||||
#endif /* WITH_GHOST_WAYLAND_FRACTIONAL_SCALE */
|
||||
|
||||
static void gwl_window_frame_pending_size_set(GWL_Window *win, bool *r_surface_needs_commit)
|
||||
{
|
||||
if (win->frame_pending.size[0] == 0 || win->frame_pending.size[1] == 0) {
|
||||
return;
|
||||
|
@ -378,7 +583,19 @@ static void gwl_window_frame_pending_size_set(GWL_Window *win)
|
|||
win->frame.size[0] = win->frame_pending.size[0];
|
||||
win->frame.size[1] = win->frame_pending.size[1];
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
if (win->frame_pending.fractional_scale != win->frame.fractional_scale) {
|
||||
gwl_window_frame_pending_fractional_scale_set(win, r_surface_needs_commit);
|
||||
}
|
||||
else {
|
||||
gwl_window_viewport_size_update(win);
|
||||
}
|
||||
#else
|
||||
(void)r_surface_needs_commit;
|
||||
#endif
|
||||
|
||||
wl_egl_window_resize(win->egl_window, UNPACK2(win->frame.size), 0, 0);
|
||||
|
||||
win->ghost_window->notify_size();
|
||||
|
||||
win->frame_pending.size[0] = 0;
|
||||
|
@ -401,6 +618,9 @@ static void gwl_window_pending_actions_handle(GWL_Window *win)
|
|||
gwl_window_frame_update_from_pending(win);
|
||||
}
|
||||
if (win->pending_actions[PENDING_EGL_WINDOW_RESIZE].exchange(false)) {
|
||||
# ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
gwl_window_viewport_size_update(win);
|
||||
# endif
|
||||
wl_egl_window_resize(win->egl_window, UNPACK2(win->frame.size), 0, 0);
|
||||
}
|
||||
# ifdef GHOST_OPENGL_ALPHA
|
||||
|
@ -427,13 +647,32 @@ static void gwl_window_frame_update_from_pending_no_lock(GWL_Window *win)
|
|||
|
||||
#endif
|
||||
|
||||
const bool dpi_changed = win->frame_pending.fractional_scale != win->frame.fractional_scale;
|
||||
bool surface_needs_commit = false;
|
||||
|
||||
if (win->frame_pending.size[0] != 0 && win->frame_pending.size[1] != 0) {
|
||||
if ((win->frame.size[0] != win->frame_pending.size[0]) ||
|
||||
(win->frame.size[1] != win->frame_pending.size[1])) {
|
||||
gwl_window_frame_pending_size_set(win);
|
||||
gwl_window_frame_pending_size_set(win, &surface_needs_commit);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
if (win->frame_pending.fractional_scale != win->frame.fractional_scale) {
|
||||
gwl_window_frame_pending_fractional_scale_set(win, &surface_needs_commit);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (surface_needs_commit) {
|
||||
wl_surface_commit(win->wl_surface);
|
||||
}
|
||||
|
||||
if (dpi_changed) {
|
||||
GHOST_SystemWayland *system = win->ghost_system;
|
||||
system->pushEvent(new GHOST_Event(
|
||||
system->getMilliSeconds(), GHOST_kEventWindowDPIHintChanged, win->ghost_window));
|
||||
}
|
||||
|
||||
if (win->frame_pending.is_active) {
|
||||
win->ghost_window->activate();
|
||||
}
|
||||
|
@ -476,26 +715,11 @@ static int output_scale_cmp(const GWL_Output *output_a, const GWL_Output *output
|
|||
if (output_a->scale > output_b->scale) {
|
||||
return 1;
|
||||
}
|
||||
if (output_a->has_scale_fractional || output_b->has_scale_fractional) {
|
||||
const wl_fixed_t scale_fractional_a = output_a->has_scale_fractional ?
|
||||
output_a->scale_fractional :
|
||||
wl_fixed_from_int(output_a->scale);
|
||||
const wl_fixed_t scale_fractional_b = output_b->has_scale_fractional ?
|
||||
output_b->scale_fractional :
|
||||
wl_fixed_from_int(output_b->scale);
|
||||
if (scale_fractional_a < scale_fractional_b) {
|
||||
return -1;
|
||||
}
|
||||
if (scale_fractional_a > scale_fractional_b) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int outputs_max_scale_or_default(const std::vector<GWL_Output *> &outputs,
|
||||
const int32_t scale_default,
|
||||
wl_fixed_t *r_scale_fractional)
|
||||
const int32_t scale_default)
|
||||
{
|
||||
const GWL_Output *output_max = nullptr;
|
||||
for (const GWL_Output *reg_output : outputs) {
|
||||
|
@ -505,17 +729,9 @@ static int outputs_max_scale_or_default(const std::vector<GWL_Output *> &outputs
|
|||
}
|
||||
|
||||
if (output_max) {
|
||||
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_scale_fractional) {
|
||||
*r_scale_fractional = wl_fixed_from_int(scale_default);
|
||||
}
|
||||
return scale_default;
|
||||
}
|
||||
|
||||
|
@ -543,8 +759,17 @@ static void xdg_toplevel_handle_configure(void *data,
|
|||
std::lock_guard lock_frame_guard{win->frame_pending_mutex};
|
||||
#endif
|
||||
|
||||
win->frame_pending.size[0] = win->scale * width;
|
||||
win->frame_pending.size[1] = win->scale * height;
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
if (win->frame.fractional_scale) {
|
||||
win->frame_pending.size[0] = gwl_window_fractional_to_viewport_round(win->frame, width);
|
||||
win->frame_pending.size[1] = gwl_window_fractional_to_viewport_round(win->frame, height);
|
||||
}
|
||||
else
|
||||
#endif /* WITH_GHOST_WAYLAND_FRACTIONAL_SCALE */
|
||||
{
|
||||
win->frame_pending.size[0] = width * win->scale;
|
||||
win->frame_pending.size[1] = height * win->scale;
|
||||
}
|
||||
|
||||
win->frame_pending.is_maximised = false;
|
||||
win->frame_pending.is_fullscreen = false;
|
||||
|
@ -619,6 +844,63 @@ static const struct xdg_activation_token_v1_listener *xdg_activation_listener_ge
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Listener (Fractional Scale), #wp_fractional_scale_manager_v1_interface
|
||||
*
|
||||
* Used by #gwl_window_activate.
|
||||
* \{ */
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
|
||||
static CLG_LogRef LOG_WL_FRACTIONAL_SCALE = {"ghost.wl.handle.fractional_scale"};
|
||||
# define LOG (&LOG_WL_FRACTIONAL_SCALE)
|
||||
|
||||
static void wp_fractional_scale_handle_preferred_scale(
|
||||
void *data, struct wp_fractional_scale_v1 * /*wp_fractional_scale_v1*/, uint preferred_scale)
|
||||
{
|
||||
CLOG_INFO(LOG,
|
||||
2,
|
||||
"preferred_scale (preferred_scale=%.6f)",
|
||||
double(preferred_scale) / FRACTIONAL_DENOMINATOR);
|
||||
GWL_Window *win = static_cast<GWL_Window *>(data);
|
||||
const bool is_fractional = (preferred_scale % FRACTIONAL_DENOMINATOR) != 0;
|
||||
/* When non-fractional, never use fractional scaling! */
|
||||
win->frame_pending.fractional_scale = is_fractional ? preferred_scale : 0;
|
||||
|
||||
if (win->frame.fractional_scale != win->frame_pending.fractional_scale) {
|
||||
|
||||
/* Resize the window failing to do so results in severe flickering with a
|
||||
* multi-monitor setup when multiple monitors have different scales.
|
||||
*
|
||||
* NOTE: some flickering is still possible even when resizing this
|
||||
* happens when dragging the right hand side of the title-bar in KDE
|
||||
* as expanding changed the size on the RHS, this may be up to the compositor to fix. */
|
||||
const int scale_prev = win->frame.fractional_scale ?
|
||||
win->frame.fractional_scale :
|
||||
win->scale_on_output * FRACTIONAL_DENOMINATOR;
|
||||
const int scale_next = win->frame_pending.fractional_scale ?
|
||||
win->frame_pending.fractional_scale :
|
||||
win->scale_on_output * FRACTIONAL_DENOMINATOR;
|
||||
for (size_t i = 0; i < ARRAY_SIZE(win->frame_pending.size); i++) {
|
||||
const int value = win->frame_pending.size[i] ? win->frame_pending.size[i] :
|
||||
win->frame.size[i];
|
||||
win->frame_pending.size[i] = lroundf(value * (double(scale_next) / double(scale_prev)));
|
||||
}
|
||||
|
||||
gwl_window_pending_actions_tag(win, PENDING_WINDOW_FRAME_CONFIGURE);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct wp_fractional_scale_v1_listener wp_fractional_scale_listener = {
|
||||
/*preferred_scale*/ wp_fractional_scale_handle_preferred_scale,
|
||||
};
|
||||
|
||||
# undef LOG
|
||||
|
||||
#endif /* WITH_GHOST_WAYLAND_FRACTIONAL_SCALE */
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Listener (LibDecor Frame), #libdecor_frame_interface
|
||||
* \{ */
|
||||
|
@ -643,16 +925,27 @@ static void frame_handle_configure(struct libdecor_frame *frame,
|
|||
/* Set the size. */
|
||||
int size_next[2];
|
||||
{
|
||||
const int scale = static_cast<GWL_Window *>(data)->scale;
|
||||
GWL_Window *win = static_cast<GWL_Window *>(data);
|
||||
const int scale = win->scale;
|
||||
if (!libdecor_configuration_get_content_size(
|
||||
configuration, frame, &size_next[0], &size_next[1])) {
|
||||
GWL_Window *win = static_cast<GWL_Window *>(data);
|
||||
size_next[0] = win->frame.size[0] / scale;
|
||||
size_next[1] = win->frame.size[1] / scale;
|
||||
}
|
||||
|
||||
frame_pending->size[0] = scale * size_next[0];
|
||||
frame_pending->size[1] = scale * size_next[1];
|
||||
# ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
if (win->frame.fractional_scale) {
|
||||
win->frame_pending.size[0] = gwl_window_fractional_to_viewport_round(win->frame,
|
||||
size_next[0]);
|
||||
win->frame_pending.size[1] = gwl_window_fractional_to_viewport_round(win->frame,
|
||||
size_next[1]);
|
||||
}
|
||||
else
|
||||
# endif /* WITH_GHOST_WAYLAND_FRACTIONAL_SCALE */
|
||||
{
|
||||
frame_pending->size[0] = size_next[0] * scale;
|
||||
frame_pending->size[1] = size_next[1] * scale;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the state. */
|
||||
|
@ -892,7 +1185,8 @@ 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. */
|
||||
window_->scale = outputs_max_scale_or_default(system_->outputs(), 1, &window_->scale_fractional);
|
||||
window_->scale = outputs_max_scale_or_default(system_->outputs(), 1);
|
||||
window_->scale_on_output = window_->scale;
|
||||
|
||||
window_->frame.size[0] = int32_t(width);
|
||||
window_->frame.size[1] = int32_t(height);
|
||||
|
@ -915,6 +1209,17 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
|
|||
window_->egl_window = wl_egl_window_create(
|
||||
window_->wl_surface, int(window_->frame.size[0]), int(window_->frame.size[1]));
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
struct wp_fractional_scale_manager_v1 *fractional_scale_manager =
|
||||
system->wp_fractional_scale_manager();
|
||||
if (fractional_scale_manager) {
|
||||
window_->fractional_scale_handle = wp_fractional_scale_manager_v1_get_fractional_scale(
|
||||
fractional_scale_manager, window_->wl_surface);
|
||||
wp_fractional_scale_v1_add_listener(
|
||||
window_->fractional_scale_handle, &wp_fractional_scale_listener, window_);
|
||||
}
|
||||
#endif /* WITH_GHOST_WAYLAND_FRACTIONAL_SCALE */
|
||||
|
||||
/* 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
|
||||
|
@ -1033,13 +1338,14 @@ GHOST_TSuccess GHOST_WindowWayland::setWindowCursorGrab(GHOST_TGrabCursorMode mo
|
|||
}
|
||||
bounds = &bounds_buf;
|
||||
}
|
||||
|
||||
if (system_->window_cursor_grab_set(mode,
|
||||
m_cursorGrab,
|
||||
m_cursorGrabInitPos,
|
||||
bounds,
|
||||
m_cursorGrabAxis,
|
||||
window_->wl_surface,
|
||||
window_->scale)) {
|
||||
this->scale_params())) {
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
return GHOST_kFailure;
|
||||
|
@ -1125,7 +1431,7 @@ GHOST_TSuccess GHOST_WindowWayland::setClientSize(const uint32_t width, const ui
|
|||
window_->frame_pending.size[0] = width;
|
||||
window_->frame_pending.size[1] = height;
|
||||
|
||||
gwl_window_frame_pending_size_set(window_);
|
||||
gwl_window_frame_pending_size_set(window_, nullptr);
|
||||
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
@ -1163,6 +1469,18 @@ GHOST_WindowWayland::~GHOST_WindowWayland()
|
|||
window_->xdg_activation_token = nullptr;
|
||||
}
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
if (window_->fractional_scale_handle) {
|
||||
wp_fractional_scale_v1_destroy(window_->fractional_scale_handle);
|
||||
window_->fractional_scale_handle = nullptr;
|
||||
}
|
||||
|
||||
if (window_->viewport) {
|
||||
wp_viewport_destroy(window_->viewport);
|
||||
window_->viewport = nullptr;
|
||||
}
|
||||
#endif /* WITH_GHOST_WAYLAND_FRACTIONAL_SCALE */
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
|
||||
if (use_libdecor) {
|
||||
gwl_libdecor_window_destroy(window_->libdecor);
|
||||
|
@ -1192,25 +1510,13 @@ uint16_t GHOST_WindowWayland::getDPIHint()
|
|||
/* No need to lock `server_mutex`
|
||||
* (`outputs_changed_update_scale` never changes values in a non-main thread). */
|
||||
|
||||
const wl_fixed_t scale_fractional = window_->scale_fractional;
|
||||
GHOST_ASSERT(wl_fixed_from_int(window_->scale) >= scale_fractional,
|
||||
"Fractional scale should always be less than the fixed scale.");
|
||||
|
||||
/* Using the physical DPI will cause wrong scaling of the UI
|
||||
* use a multiplier for the default DPI as a workaround. */
|
||||
if (wl_fixed_from_int(window_->scale) == scale_fractional || window_->scale <= 1) {
|
||||
/* No fractional scaling. */
|
||||
return window_->scale * base_dpi;
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
if (window_->frame.fractional_scale) {
|
||||
return gwl_window_fractional_to_viewport(window_->frame, base_dpi);
|
||||
}
|
||||
const int scale_ceil = window_->scale;
|
||||
const int scale_floor = scale_ceil - 1;
|
||||
const wl_fixed_t scale_fractional_final = wl_fixed_to_int(
|
||||
scale_fractional *
|
||||
/* Compensate for the buffer being rendered at `window_->scale`,
|
||||
* then scaled down fractionally. */
|
||||
(wl_fixed_from_int(1) + ((wl_fixed_from_int(scale_ceil) - scale_fractional) / scale_floor)));
|
||||
#endif /* WITH_GHOST_WAYLAND_FRACTIONAL_SCALE */
|
||||
|
||||
return wl_fixed_to_int(scale_fractional_final * base_dpi);
|
||||
return window_->scale * base_dpi;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_WindowWayland::setWindowCursorVisibility(bool visible)
|
||||
|
@ -1383,18 +1689,34 @@ int GHOST_WindowWayland::scale() const
|
|||
return window_->scale;
|
||||
}
|
||||
|
||||
wl_fixed_t GHOST_WindowWayland::scale_fractional() const
|
||||
const struct GWL_WindowScaleParams &GHOST_WindowWayland::scale_params() const
|
||||
{
|
||||
return window_->scale_fractional;
|
||||
/* NOTE(@ideasman42): This could be kept initialized,
|
||||
* since it's such a small struct it's not so important. */
|
||||
GWL_WindowScaleParams *scale_params = &window_->scale_params;
|
||||
scale_params->is_fractional = (window_->frame.fractional_scale != 0);
|
||||
scale_params->scale = scale_params->is_fractional ? window_->frame.fractional_scale :
|
||||
window_->scale;
|
||||
return *scale_params;
|
||||
}
|
||||
|
||||
wl_fixed_t GHOST_WindowWayland::wl_fixed_from_window(wl_fixed_t value) const
|
||||
{
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
if (window_->frame.fractional_scale) {
|
||||
return gwl_window_fractional_from_viewport(window_->frame, value);
|
||||
}
|
||||
#endif
|
||||
return value / window_->scale;
|
||||
}
|
||||
|
||||
wl_fixed_t GHOST_WindowWayland::wl_fixed_to_window(wl_fixed_t value) const
|
||||
{
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
if (window_->frame.fractional_scale) {
|
||||
return gwl_window_fractional_to_viewport(window_->frame, value);
|
||||
}
|
||||
#endif
|
||||
return value * window_->scale;
|
||||
}
|
||||
|
||||
|
@ -1513,13 +1835,19 @@ bool GHOST_WindowWayland::outputs_changed_update_scale()
|
|||
}
|
||||
#endif
|
||||
|
||||
wl_fixed_t scale_fractional_next = 0;
|
||||
const int scale_next = outputs_max_scale_or_default(outputs(), 0, &scale_fractional_next);
|
||||
const int scale_next = outputs_max_scale_or_default(outputs(), 0);
|
||||
if (UNLIKELY(scale_next == 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const wl_fixed_t scale_fractional_curr = window_->scale_fractional;
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
if (window_->frame.fractional_scale) {
|
||||
GHOST_ASSERT(window_->scale == 1, "Scale is expected to be 1 for fractional scaling!");
|
||||
window_->scale_on_output = scale_next;
|
||||
return false;
|
||||
}
|
||||
#endif /* WITH_GHOST_WAYLAND_FRACTIONAL_SCALE */
|
||||
|
||||
const int scale_curr = window_->scale;
|
||||
bool changed = false;
|
||||
|
||||
|
@ -1544,22 +1872,18 @@ bool GHOST_WindowWayland::outputs_changed_update_scale()
|
|||
window_->frame_pending.size[0] = (window_->frame.size[0] / scale_curr) * scale_next;
|
||||
window_->frame_pending.size[1] = (window_->frame.size[1] / scale_curr) * scale_next;
|
||||
|
||||
gwl_window_frame_pending_size_set(window_);
|
||||
gwl_window_frame_pending_size_set(window_, nullptr);
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
||||
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,
|
||||
* always add the event unless it causes issues. */
|
||||
GHOST_SystemWayland *system = window_->ghost_system;
|
||||
system->pushEvent(
|
||||
new GHOST_Event(system->getMilliSeconds(), GHOST_kEventWindowDPIHintChanged, this));
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
||||
/* Ensure both are always set. */
|
||||
window_->scale_on_output = window_->scale;
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
|
|
@ -140,7 +140,8 @@ class GHOST_WindowWayland : public GHOST_Window {
|
|||
/* WAYLAND direct-data access. */
|
||||
|
||||
int scale() const;
|
||||
wl_fixed_t scale_fractional() const;
|
||||
const struct GWL_WindowScaleParams &scale_params() const;
|
||||
|
||||
struct wl_surface *wl_surface() const;
|
||||
const std::vector<GWL_Output *> &outputs();
|
||||
|
||||
|
|
Loading…
Reference in New Issue