From 013320bcde675f7119e9c1d28e2b4791ea06bb12 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Fri, 5 Jul 2024 16:14:56 +0800 Subject: [PATCH] Display (Wayland): support fractional scale for KDE --- CMakeLists.txt | 7 + .../kde-output-device-v2-client-protocol.h | 845 ++++++++++++++++++ .../wayland/kde-output-device-v2-protocol.c | 81 ++ .../displayserver/linux/wayland/kde-output.c | 225 +++++ .../displayserver/linux/wayland/wayland.c | 9 +- .../displayserver/linux/wayland/wayland.h | 2 + src/logo/image/image.c | 47 +- src/util/base64.c | 115 +++ src/util/base64.h | 23 + 9 files changed, 1309 insertions(+), 45 deletions(-) create mode 100644 src/detection/displayserver/linux/wayland/kde-output-device-v2-client-protocol.h create mode 100644 src/detection/displayserver/linux/wayland/kde-output-device-v2-protocol.c create mode 100644 src/detection/displayserver/linux/wayland/kde-output.c create mode 100644 src/util/base64.c create mode 100644 src/util/base64.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e22b7edb3..52407a14b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -395,6 +395,7 @@ set(LIBFASTFETCH_SRC src/options/general.c src/options/library.c src/util/edidHelper.c + src/util/base64.c src/util/FFlist.c src/util/FFstrbuf.c src/util/platform/FFPlatform.c @@ -429,7 +430,9 @@ if(LINUX) src/detection/displayserver/linux/wayland/wayland.c src/detection/displayserver/linux/wayland/global-output.c src/detection/displayserver/linux/wayland/zwlr-output.c + src/detection/displayserver/linux/wayland/kde-output.c src/detection/displayserver/linux/wayland/wlr-output-management-unstable-v1-protocol.c + src/detection/displayserver/linux/wayland/kde-output-device-v2-protocol.c src/detection/displayserver/linux/wmde.c src/detection/displayserver/linux/xcb.c src/detection/displayserver/linux/xlib.c @@ -562,7 +565,9 @@ elseif(BSD) src/detection/displayserver/linux/wayland/wayland.c src/detection/displayserver/linux/wayland/global-output.c src/detection/displayserver/linux/wayland/zwlr-output.c + src/detection/displayserver/linux/wayland/kde-output.c src/detection/displayserver/linux/wayland/wlr-output-management-unstable-v1-protocol.c + src/detection/displayserver/linux/wayland/kde-output-device-v2-protocol.c src/detection/displayserver/linux/wmde.c src/detection/displayserver/linux/xcb.c src/detection/displayserver/linux/xlib.c @@ -763,7 +768,9 @@ elseif(SunOS) src/detection/displayserver/linux/wayland/wayland.c src/detection/displayserver/linux/wayland/global-output.c src/detection/displayserver/linux/wayland/zwlr-output.c + src/detection/displayserver/linux/wayland/kde-output.c src/detection/displayserver/linux/wayland/wlr-output-management-unstable-v1-protocol.c + src/detection/displayserver/linux/wayland/kde-output-device-v2-protocol.c src/detection/displayserver/linux/wmde.c src/detection/displayserver/linux/xcb.c src/detection/displayserver/linux/xlib.c diff --git a/src/detection/displayserver/linux/wayland/kde-output-device-v2-client-protocol.h b/src/detection/displayserver/linux/wayland/kde-output-device-v2-client-protocol.h new file mode 100644 index 000000000..605f41e39 --- /dev/null +++ b/src/detection/displayserver/linux/wayland/kde-output-device-v2-client-protocol.h @@ -0,0 +1,845 @@ +/* Generated by wayland-scanner 1.22.0 */ + +#ifndef KDE_OUTPUT_DEVICE_V2_CLIENT_PROTOCOL_H +#define KDE_OUTPUT_DEVICE_V2_CLIENT_PROTOCOL_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_kde_output_device_v2 The kde_output_device_v2 protocol + * @section page_ifaces_kde_output_device_v2 Interfaces + * - @subpage page_iface_kde_output_device_v2 - output configuration representation + * - @subpage page_iface_kde_output_device_mode_v2 - output mode + * @section page_copyright_kde_output_device_v2 Copyright + *
+ *
+ * SPDX-FileCopyrightText: 2008-2011 Kristian Høgsberg
+ * SPDX-FileCopyrightText: 2010-2011 Intel Corporation
+ * SPDX-FileCopyrightText: 2012-2013 Collabora, Ltd.
+ * SPDX-FileCopyrightText: 2015 Sebastian Kügler 
+ * SPDX-FileCopyrightText: 2021 Méven Car 
+ *
+ * SPDX-License-Identifier: MIT-CMU
+ * 
+ */ +struct kde_output_device_mode_v2; +struct kde_output_device_v2; + +#ifndef KDE_OUTPUT_DEVICE_V2_INTERFACE +#define KDE_OUTPUT_DEVICE_V2_INTERFACE +/** + * @page page_iface_kde_output_device_v2 kde_output_device_v2 + * @section page_iface_kde_output_device_v2_desc Description + * + * An output device describes a display device available to the compositor. + * output_device is similar to wl_output, but focuses on output + * configuration management. + * + * A client can query all global output_device objects to enlist all + * available display devices, even those that may currently not be + * represented by the compositor as a wl_output. + * + * The client sends configuration changes to the server through the + * outputconfiguration interface, and the server applies the configuration + * changes to the hardware and signals changes to the output devices + * accordingly. + * + * This object is published as global during start up for every available + * display devices, or when one later becomes available, for example by + * being hotplugged via a physical connector. + * @section page_iface_kde_output_device_v2_api API + * See @ref iface_kde_output_device_v2. + */ +/** + * @defgroup iface_kde_output_device_v2 The kde_output_device_v2 interface + * + * An output device describes a display device available to the compositor. + * output_device is similar to wl_output, but focuses on output + * configuration management. + * + * A client can query all global output_device objects to enlist all + * available display devices, even those that may currently not be + * represented by the compositor as a wl_output. + * + * The client sends configuration changes to the server through the + * outputconfiguration interface, and the server applies the configuration + * changes to the hardware and signals changes to the output devices + * accordingly. + * + * This object is published as global during start up for every available + * display devices, or when one later becomes available, for example by + * being hotplugged via a physical connector. + */ +extern const struct wl_interface kde_output_device_v2_interface; +#endif +#ifndef KDE_OUTPUT_DEVICE_MODE_V2_INTERFACE +#define KDE_OUTPUT_DEVICE_MODE_V2_INTERFACE +/** + * @page page_iface_kde_output_device_mode_v2 kde_output_device_mode_v2 + * @section page_iface_kde_output_device_mode_v2_desc Description + * + * This object describes an output mode. + * + * Some heads don't support output modes, in which case modes won't be + * advertised. + * + * Properties sent via this interface are applied atomically via the + * kde_output_device.done event. No guarantees are made regarding the order + * in which properties are sent. + * @section page_iface_kde_output_device_mode_v2_api API + * See @ref iface_kde_output_device_mode_v2. + */ +/** + * @defgroup iface_kde_output_device_mode_v2 The kde_output_device_mode_v2 interface + * + * This object describes an output mode. + * + * Some heads don't support output modes, in which case modes won't be + * advertised. + * + * Properties sent via this interface are applied atomically via the + * kde_output_device.done event. No guarantees are made regarding the order + * in which properties are sent. + */ +extern const struct wl_interface kde_output_device_mode_v2_interface; +#endif + +#ifndef KDE_OUTPUT_DEVICE_V2_SUBPIXEL_ENUM +#define KDE_OUTPUT_DEVICE_V2_SUBPIXEL_ENUM +/** + * @ingroup iface_kde_output_device_v2 + * subpixel geometry information + * + * This enumeration describes how the physical pixels on an output are + * laid out. + */ +enum kde_output_device_v2_subpixel { + KDE_OUTPUT_DEVICE_V2_SUBPIXEL_UNKNOWN = 0, + KDE_OUTPUT_DEVICE_V2_SUBPIXEL_NONE = 1, + KDE_OUTPUT_DEVICE_V2_SUBPIXEL_HORIZONTAL_RGB = 2, + KDE_OUTPUT_DEVICE_V2_SUBPIXEL_HORIZONTAL_BGR = 3, + KDE_OUTPUT_DEVICE_V2_SUBPIXEL_VERTICAL_RGB = 4, + KDE_OUTPUT_DEVICE_V2_SUBPIXEL_VERTICAL_BGR = 5, +}; +#endif /* KDE_OUTPUT_DEVICE_V2_SUBPIXEL_ENUM */ + +#ifndef KDE_OUTPUT_DEVICE_V2_TRANSFORM_ENUM +#define KDE_OUTPUT_DEVICE_V2_TRANSFORM_ENUM +/** + * @ingroup iface_kde_output_device_v2 + * transform from framebuffer to output + * + * This describes the transform, that a compositor will apply to a + * surface to compensate for the rotation or mirroring of an + * output device. + * + * The flipped values correspond to an initial flip around a + * vertical axis followed by rotation. + * + * The purpose is mainly to allow clients to render accordingly and + * tell the compositor, so that for fullscreen surfaces, the + * compositor is still able to scan out directly client surfaces. + */ +enum kde_output_device_v2_transform { + KDE_OUTPUT_DEVICE_V2_TRANSFORM_NORMAL = 0, + KDE_OUTPUT_DEVICE_V2_TRANSFORM_90 = 1, + KDE_OUTPUT_DEVICE_V2_TRANSFORM_180 = 2, + KDE_OUTPUT_DEVICE_V2_TRANSFORM_270 = 3, + KDE_OUTPUT_DEVICE_V2_TRANSFORM_FLIPPED = 4, + KDE_OUTPUT_DEVICE_V2_TRANSFORM_FLIPPED_90 = 5, + KDE_OUTPUT_DEVICE_V2_TRANSFORM_FLIPPED_180 = 6, + KDE_OUTPUT_DEVICE_V2_TRANSFORM_FLIPPED_270 = 7, +}; +#endif /* KDE_OUTPUT_DEVICE_V2_TRANSFORM_ENUM */ + +#ifndef KDE_OUTPUT_DEVICE_V2_CAPABILITY_ENUM +#define KDE_OUTPUT_DEVICE_V2_CAPABILITY_ENUM +/** + * @ingroup iface_kde_output_device_v2 + * describes capabilities of the outputdevice + * + * Describes what capabilities this device has. + */ +enum kde_output_device_v2_capability { + /** + * if this output_device can use overscan + */ + KDE_OUTPUT_DEVICE_V2_CAPABILITY_OVERSCAN = 0x1, + /** + * if this outputdevice supports variable refresh rate + */ + KDE_OUTPUT_DEVICE_V2_CAPABILITY_VRR = 0x2, + /** + * if setting the rgb range is possible + */ + KDE_OUTPUT_DEVICE_V2_CAPABILITY_RGB_RANGE = 0x4, + /** + * if this outputdevice supports high dynamic range + * @since 3 + */ + KDE_OUTPUT_DEVICE_V2_CAPABILITY_HIGH_DYNAMIC_RANGE = 0x8, + /** + * if this outputdevice supports a wide color gamut + * @since 3 + */ + KDE_OUTPUT_DEVICE_V2_CAPABILITY_WIDE_COLOR_GAMUT = 0x10, + /** + * if this outputdevice supports autorotation + * @since 4 + */ + KDE_OUTPUT_DEVICE_V2_CAPABILITY_AUTO_ROTATE = 0x20, + /** + * if this outputdevice supports icc profiles + * @since 5 + */ + KDE_OUTPUT_DEVICE_V2_CAPABILITY_ICC_PROFILE = 0x40, +}; +/** + * @ingroup iface_kde_output_device_v2 + */ +#define KDE_OUTPUT_DEVICE_V2_CAPABILITY_HIGH_DYNAMIC_RANGE_SINCE_VERSION 3 +/** + * @ingroup iface_kde_output_device_v2 + */ +#define KDE_OUTPUT_DEVICE_V2_CAPABILITY_WIDE_COLOR_GAMUT_SINCE_VERSION 3 +/** + * @ingroup iface_kde_output_device_v2 + */ +#define KDE_OUTPUT_DEVICE_V2_CAPABILITY_AUTO_ROTATE_SINCE_VERSION 4 +/** + * @ingroup iface_kde_output_device_v2 + */ +#define KDE_OUTPUT_DEVICE_V2_CAPABILITY_ICC_PROFILE_SINCE_VERSION 5 +#endif /* KDE_OUTPUT_DEVICE_V2_CAPABILITY_ENUM */ + +#ifndef KDE_OUTPUT_DEVICE_V2_VRR_POLICY_ENUM +#define KDE_OUTPUT_DEVICE_V2_VRR_POLICY_ENUM +/** + * @ingroup iface_kde_output_device_v2 + * describes vrr policy + * + * Describes when the compositor may employ variable refresh rate + */ +enum kde_output_device_v2_vrr_policy { + KDE_OUTPUT_DEVICE_V2_VRR_POLICY_NEVER = 0, + KDE_OUTPUT_DEVICE_V2_VRR_POLICY_ALWAYS = 1, + KDE_OUTPUT_DEVICE_V2_VRR_POLICY_AUTOMATIC = 2, +}; +#endif /* KDE_OUTPUT_DEVICE_V2_VRR_POLICY_ENUM */ + +#ifndef KDE_OUTPUT_DEVICE_V2_RGB_RANGE_ENUM +#define KDE_OUTPUT_DEVICE_V2_RGB_RANGE_ENUM +/** + * @ingroup iface_kde_output_device_v2 + * describes RGB range policy + * + * Whether full or limited color range should be used + */ +enum kde_output_device_v2_rgb_range { + KDE_OUTPUT_DEVICE_V2_RGB_RANGE_AUTOMATIC = 0, + KDE_OUTPUT_DEVICE_V2_RGB_RANGE_FULL = 1, + KDE_OUTPUT_DEVICE_V2_RGB_RANGE_LIMITED = 2, +}; +#endif /* KDE_OUTPUT_DEVICE_V2_RGB_RANGE_ENUM */ + +#ifndef KDE_OUTPUT_DEVICE_V2_AUTO_ROTATE_POLICY_ENUM +#define KDE_OUTPUT_DEVICE_V2_AUTO_ROTATE_POLICY_ENUM +/** + * @ingroup iface_kde_output_device_v2 + * describes when auto rotate should be used + */ +enum kde_output_device_v2_auto_rotate_policy { + KDE_OUTPUT_DEVICE_V2_AUTO_ROTATE_POLICY_NEVER = 0, + KDE_OUTPUT_DEVICE_V2_AUTO_ROTATE_POLICY_IN_TABLET_MODE = 1, + KDE_OUTPUT_DEVICE_V2_AUTO_ROTATE_POLICY_ALWAYS = 2, +}; +#endif /* KDE_OUTPUT_DEVICE_V2_AUTO_ROTATE_POLICY_ENUM */ + +#ifndef KDE_OUTPUT_DEVICE_V2_COLOR_PROFILE_SOURCE_ENUM +#define KDE_OUTPUT_DEVICE_V2_COLOR_PROFILE_SOURCE_ENUM +/** + * @ingroup iface_kde_output_device_v2 + * which source the compositor should use for the color profile on an output + */ +enum kde_output_device_v2_color_profile_source { + KDE_OUTPUT_DEVICE_V2_COLOR_PROFILE_SOURCE_SRGB = 0, + KDE_OUTPUT_DEVICE_V2_COLOR_PROFILE_SOURCE_ICC = 1, + KDE_OUTPUT_DEVICE_V2_COLOR_PROFILE_SOURCE_EDID = 2, +}; +#endif /* KDE_OUTPUT_DEVICE_V2_COLOR_PROFILE_SOURCE_ENUM */ + +/** + * @ingroup iface_kde_output_device_v2 + * @struct kde_output_device_v2_listener + */ +struct kde_output_device_v2_listener { + /** + * geometric properties of the output + * + * The geometry event describes geometric properties of the + * output. The event is sent when binding to the output object and + * whenever any of the properties change. + * @param x x position within the global compositor space + * @param y y position within the global compositor space + * @param physical_width width in millimeters of the output + * @param physical_height height in millimeters of the output + * @param subpixel subpixel orientation of the output + * @param make textual description of the manufacturer + * @param model textual description of the model + * @param transform transform that maps framebuffer to output + */ + void (*geometry)(void *data, + struct kde_output_device_v2 *kde_output_device_v2, + int32_t x, + int32_t y, + int32_t physical_width, + int32_t physical_height, + int32_t subpixel, + const char *make, + const char *model, + int32_t transform); + /** + * current mode + * + * This event describes the mode currently in use for this head. + * It is only sent if the output is enabled. + */ + void (*current_mode)(void *data, + struct kde_output_device_v2 *kde_output_device_v2, + struct kde_output_device_mode_v2 *mode); + /** + * advertise available output modes and current one + * + * The mode event describes an available mode for the output. + * + * When the client binds to the output_device object, the server + * sends this event once for every available mode the output_device + * can be operated by. + * + * There will always be at least one event sent out on initial + * binding, which represents the current mode. + * + * Later if an output changes, its mode event is sent again for the + * eventual added modes and lastly the current mode. In other + * words, the current mode is always represented by the latest + * event sent with the current flag set. + * + * The size of a mode is given in physical hardware units of the + * output device. This is not necessarily the same as the output + * size in the global compositor space. For instance, the output + * may be scaled, as described in kde_output_device_v2.scale, or + * transformed, as described in kde_output_device_v2.transform. + */ + void (*mode)(void *data, + struct kde_output_device_v2 *kde_output_device_v2, + struct kde_output_device_mode_v2 *mode); + /** + * sent all information about output + * + * This event is sent after all other properties have been sent + * on binding to the output object as well as after any other + * output property change have been applied later on. This allows + * to see changes to the output properties as atomic, even if + * multiple events successively announce them. + */ + void (*done)(void *data, + struct kde_output_device_v2 *kde_output_device_v2); + /** + * output scaling properties + * + * This event contains scaling geometry information that is not + * in the geometry event. It may be sent after binding the output + * object or if the output scale changes later. If it is not sent, + * the client should assume a scale of 1. + * + * A scale larger than 1 means that the compositor will + * automatically scale surface buffers by this amount when + * rendering. This is used for high resolution displays where + * applications rendering at the native resolution would be too + * small to be legible. + * + * It is intended that scaling aware clients track the current + * output of a surface, and if it is on a scaled output it should + * use wl_surface.set_buffer_scale with the scale of the output. + * That way the compositor can avoid scaling the surface, and the + * client can supply a higher detail image. + * @param factor scaling factor of output + */ + void (*scale)(void *data, + struct kde_output_device_v2 *kde_output_device_v2, + wl_fixed_t factor); + /** + * advertise EDID data for the output + * + * The edid event encapsulates the EDID data for the + * outputdevice. + * + * The event is sent when binding to the output object. The EDID + * data may be empty, in which case this event is sent anyway. If + * the EDID information is empty, you can fall back to the name et + * al. properties of the outputdevice. + * @param raw base64-encoded EDID string + */ + void (*edid)(void *data, + struct kde_output_device_v2 *kde_output_device_v2, + const char *raw); + /** + * output is enabled or disabled + * + * The enabled event notifies whether this output is currently + * enabled and used for displaying content by the server. The event + * is sent when binding to the output object and whenever later on + * an output changes its state by becoming enabled or disabled. + * @param enabled output enabled state + */ + void (*enabled)(void *data, + struct kde_output_device_v2 *kde_output_device_v2, + int32_t enabled); + /** + * A unique id for this outputdevice + * + * The uuid can be used to identify the output. It's controlled + * by the server entirely. The server should make sure the uuid is + * persistent across restarts. An empty uuid is considered invalid. + * @param uuid output devices ID + */ + void (*uuid)(void *data, + struct kde_output_device_v2 *kde_output_device_v2, + const char *uuid); + /** + * Serial Number + * + * Serial ID of the monitor, sent on startup before the first + * done event. + * @param serialNumber textual representation of serial number + */ + void (*serial_number)(void *data, + struct kde_output_device_v2 *kde_output_device_v2, + const char *serialNumber); + /** + * EISA ID + * + * EISA ID of the monitor, sent on startup before the first done + * event. + * @param eisaId textual representation of EISA identifier + */ + void (*eisa_id)(void *data, + struct kde_output_device_v2 *kde_output_device_v2, + const char *eisaId); + /** + * capability flags + * + * What capabilities this device has, sent on startup before the + * first done event. + */ + void (*capabilities)(void *data, + struct kde_output_device_v2 *kde_output_device_v2, + uint32_t flags); + /** + * overscan + * + * Overscan value of the monitor in percent, sent on startup + * before the first done event. + * @param overscan amount of overscan of the monitor + */ + void (*overscan)(void *data, + struct kde_output_device_v2 *kde_output_device_v2, + uint32_t overscan); + /** + * Variable Refresh Rate Policy + * + * What policy the compositor will employ regarding its use of + * variable refresh rate. + */ + void (*vrr_policy)(void *data, + struct kde_output_device_v2 *kde_output_device_v2, + uint32_t vrr_policy); + /** + * RGB range + * + * What rgb range the compositor is using for this output + */ + void (*rgb_range)(void *data, + struct kde_output_device_v2 *kde_output_device_v2, + uint32_t rgb_range); + /** + * Output's name + * + * Name of the output, it's useful to cross-reference to an + * zxdg_output_v1 and ultimately QScreen + * @since 2 + */ + void (*name)(void *data, + struct kde_output_device_v2 *kde_output_device_v2, + const char *name); + /** + * if HDR is enabled + * + * Whether or not high dynamic range is enabled for this output + * @param hdr_enabled 1 if enabled, 0 if disabled + * @since 3 + */ + void (*high_dynamic_range)(void *data, + struct kde_output_device_v2 *kde_output_device_v2, + uint32_t hdr_enabled); + /** + * the brightness of sdr if hdr is enabled + * + * If high dynamic range is used, this value defines the + * brightness in nits for content that's in standard dynamic range + * format. Note that while the value is in nits, that doesn't + * necessarily translate to the same brightness on the screen. + * @since 3 + */ + void (*sdr_brightness)(void *data, + struct kde_output_device_v2 *kde_output_device_v2, + uint32_t sdr_brightness); + /** + * if WCG is enabled + * + * Whether or not the use of a wide color gamut is enabled for + * this output + * @param wcg_enabled 1 if enabled, 0 if disabled + * @since 3 + */ + void (*wide_color_gamut)(void *data, + struct kde_output_device_v2 *kde_output_device_v2, + uint32_t wcg_enabled); + /** + * describes when auto rotate is used + * + * + * @since 4 + */ + void (*auto_rotate_policy)(void *data, + struct kde_output_device_v2 *kde_output_device_v2, + uint32_t policy); + /** + * describes when auto rotate is used + * + * + * @since 5 + */ + void (*icc_profile_path)(void *data, + struct kde_output_device_v2 *kde_output_device_v2, + const char *profile_path); + /** + * metadata about the screen's brightness limits + * + * + * @param max_peak_brightness in nits + * @param max_frame_average_brightness in nits + * @param min_brightness in 0.0001 nits + * @since 6 + */ + void (*brightness_metadata)(void *data, + struct kde_output_device_v2 *kde_output_device_v2, + uint32_t max_peak_brightness, + uint32_t max_frame_average_brightness, + uint32_t min_brightness); + /** + * overrides for the screen's brightness limits + * + * + * @param max_peak_brightness -1 for no override, positive values are the brightness in nits + * @param max_average_brightness -1 for no override, positive values are the brightness in nits + * @param min_brightness -1 for no override, positive values are the brightness in 0.0001 nits + * @since 6 + */ + void (*brightness_overrides)(void *data, + struct kde_output_device_v2 *kde_output_device_v2, + int32_t max_peak_brightness, + int32_t max_average_brightness, + int32_t min_brightness); + /** + * describes which gamut is assumed for sRGB applications + * + * This can be used to provide the colors users assume sRGB + * applications should have based on the default experience on many + * modern sRGB screens. + * @param gamut_wideness 0 means rec.709 primaries, 10000 means native primaries + * @since 6 + */ + void (*sdr_gamut_wideness)(void *data, + struct kde_output_device_v2 *kde_output_device_v2, + uint32_t gamut_wideness); + /** + * describes which source the compositor uses for the color profile on an output + * + * + * @since 7 + */ + void (*color_profile_source)(void *data, + struct kde_output_device_v2 *kde_output_device_v2, + uint32_t source); + /** + * brightness multiplier + * + * This is the brightness modifier of the output. It doesn't + * specify any absolute values, but is merely a multiplier on top + * of other brightness values, like sdr_brightness and + * brightness_metadata. 0 is the minimum brightness (not completely + * dark) and 10000 is the maximum brightness. This is currently + * only supported / meaningful while HDR is active. + * @param brightness brightness in 0-10000 + * @since 8 + */ + void (*brightness)(void *data, + struct kde_output_device_v2 *kde_output_device_v2, + uint32_t brightness); +}; + +/** + * @ingroup iface_kde_output_device_v2 + */ +static inline int +kde_output_device_v2_add_listener(struct kde_output_device_v2 *kde_output_device_v2, + const struct kde_output_device_v2_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) kde_output_device_v2, + (void (**)(void)) listener, data); +} + +/** + * @ingroup iface_kde_output_device_v2 + */ +#define KDE_OUTPUT_DEVICE_V2_GEOMETRY_SINCE_VERSION 1 +/** + * @ingroup iface_kde_output_device_v2 + */ +#define KDE_OUTPUT_DEVICE_V2_CURRENT_MODE_SINCE_VERSION 1 +/** + * @ingroup iface_kde_output_device_v2 + */ +#define KDE_OUTPUT_DEVICE_V2_MODE_SINCE_VERSION 1 +/** + * @ingroup iface_kde_output_device_v2 + */ +#define KDE_OUTPUT_DEVICE_V2_DONE_SINCE_VERSION 1 +/** + * @ingroup iface_kde_output_device_v2 + */ +#define KDE_OUTPUT_DEVICE_V2_SCALE_SINCE_VERSION 1 +/** + * @ingroup iface_kde_output_device_v2 + */ +#define KDE_OUTPUT_DEVICE_V2_EDID_SINCE_VERSION 1 +/** + * @ingroup iface_kde_output_device_v2 + */ +#define KDE_OUTPUT_DEVICE_V2_ENABLED_SINCE_VERSION 1 +/** + * @ingroup iface_kde_output_device_v2 + */ +#define KDE_OUTPUT_DEVICE_V2_UUID_SINCE_VERSION 1 +/** + * @ingroup iface_kde_output_device_v2 + */ +#define KDE_OUTPUT_DEVICE_V2_SERIAL_NUMBER_SINCE_VERSION 1 +/** + * @ingroup iface_kde_output_device_v2 + */ +#define KDE_OUTPUT_DEVICE_V2_EISA_ID_SINCE_VERSION 1 +/** + * @ingroup iface_kde_output_device_v2 + */ +#define KDE_OUTPUT_DEVICE_V2_CAPABILITIES_SINCE_VERSION 1 +/** + * @ingroup iface_kde_output_device_v2 + */ +#define KDE_OUTPUT_DEVICE_V2_OVERSCAN_SINCE_VERSION 1 +/** + * @ingroup iface_kde_output_device_v2 + */ +#define KDE_OUTPUT_DEVICE_V2_VRR_POLICY_SINCE_VERSION 1 +/** + * @ingroup iface_kde_output_device_v2 + */ +#define KDE_OUTPUT_DEVICE_V2_RGB_RANGE_SINCE_VERSION 1 +/** + * @ingroup iface_kde_output_device_v2 + */ +#define KDE_OUTPUT_DEVICE_V2_NAME_SINCE_VERSION 2 +/** + * @ingroup iface_kde_output_device_v2 + */ +#define KDE_OUTPUT_DEVICE_V2_HIGH_DYNAMIC_RANGE_SINCE_VERSION 3 +/** + * @ingroup iface_kde_output_device_v2 + */ +#define KDE_OUTPUT_DEVICE_V2_SDR_BRIGHTNESS_SINCE_VERSION 3 +/** + * @ingroup iface_kde_output_device_v2 + */ +#define KDE_OUTPUT_DEVICE_V2_WIDE_COLOR_GAMUT_SINCE_VERSION 3 +/** + * @ingroup iface_kde_output_device_v2 + */ +#define KDE_OUTPUT_DEVICE_V2_AUTO_ROTATE_POLICY_SINCE_VERSION 4 +/** + * @ingroup iface_kde_output_device_v2 + */ +#define KDE_OUTPUT_DEVICE_V2_ICC_PROFILE_PATH_SINCE_VERSION 5 +/** + * @ingroup iface_kde_output_device_v2 + */ +#define KDE_OUTPUT_DEVICE_V2_BRIGHTNESS_METADATA_SINCE_VERSION 6 +/** + * @ingroup iface_kde_output_device_v2 + */ +#define KDE_OUTPUT_DEVICE_V2_BRIGHTNESS_OVERRIDES_SINCE_VERSION 6 +/** + * @ingroup iface_kde_output_device_v2 + */ +#define KDE_OUTPUT_DEVICE_V2_SDR_GAMUT_WIDENESS_SINCE_VERSION 6 +/** + * @ingroup iface_kde_output_device_v2 + */ +#define KDE_OUTPUT_DEVICE_V2_COLOR_PROFILE_SOURCE_SINCE_VERSION 7 +/** + * @ingroup iface_kde_output_device_v2 + */ +#define KDE_OUTPUT_DEVICE_V2_BRIGHTNESS_SINCE_VERSION 8 + + +/** @ingroup iface_kde_output_device_v2 */ +static inline void +kde_output_device_v2_set_user_data(struct kde_output_device_v2 *kde_output_device_v2, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) kde_output_device_v2, user_data); +} + +/** @ingroup iface_kde_output_device_v2 */ +static inline void * +kde_output_device_v2_get_user_data(struct kde_output_device_v2 *kde_output_device_v2) +{ + return wl_proxy_get_user_data((struct wl_proxy *) kde_output_device_v2); +} + +static inline uint32_t +kde_output_device_v2_get_version(struct kde_output_device_v2 *kde_output_device_v2) +{ + return wl_proxy_get_version((struct wl_proxy *) kde_output_device_v2); +} + +/** @ingroup iface_kde_output_device_v2 */ +static inline void +kde_output_device_v2_destroy(struct kde_output_device_v2 *kde_output_device_v2) +{ + wl_proxy_destroy((struct wl_proxy *) kde_output_device_v2); +} + +/** + * @ingroup iface_kde_output_device_mode_v2 + * @struct kde_output_device_mode_v2_listener + */ +struct kde_output_device_mode_v2_listener { + /** + * mode size + * + * This event describes the mode size. The size is given in + * physical hardware units of the output device. This is not + * necessarily the same as the output size in the global compositor + * space. For instance, the output may be scaled or transformed. + * @param width width of the mode in hardware units + * @param height height of the mode in hardware units + */ + void (*size)(void *data, + struct kde_output_device_mode_v2 *kde_output_device_mode_v2, + int32_t width, + int32_t height); + /** + * mode refresh rate + * + * This event describes the mode's fixed vertical refresh rate. + * It is only sent if the mode has a fixed refresh rate. + * @param refresh vertical refresh rate in mHz + */ + void (*refresh)(void *data, + struct kde_output_device_mode_v2 *kde_output_device_mode_v2, + int32_t refresh); + /** + * mode is preferred + * + * This event advertises this mode as preferred. + */ + void (*preferred)(void *data, + struct kde_output_device_mode_v2 *kde_output_device_mode_v2); + /** + * the mode has been destroyed + * + * The compositor will destroy the object immediately after + * sending this event, so it will become invalid and the client + * should release any resources associated with it. + */ + void (*removed)(void *data, + struct kde_output_device_mode_v2 *kde_output_device_mode_v2); +}; + +/** + * @ingroup iface_kde_output_device_mode_v2 + */ +static inline int +kde_output_device_mode_v2_add_listener(struct kde_output_device_mode_v2 *kde_output_device_mode_v2, + const struct kde_output_device_mode_v2_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) kde_output_device_mode_v2, + (void (**)(void)) listener, data); +} + +/** + * @ingroup iface_kde_output_device_mode_v2 + */ +#define KDE_OUTPUT_DEVICE_MODE_V2_SIZE_SINCE_VERSION 1 +/** + * @ingroup iface_kde_output_device_mode_v2 + */ +#define KDE_OUTPUT_DEVICE_MODE_V2_REFRESH_SINCE_VERSION 1 +/** + * @ingroup iface_kde_output_device_mode_v2 + */ +#define KDE_OUTPUT_DEVICE_MODE_V2_PREFERRED_SINCE_VERSION 1 +/** + * @ingroup iface_kde_output_device_mode_v2 + */ +#define KDE_OUTPUT_DEVICE_MODE_V2_REMOVED_SINCE_VERSION 1 + + +/** @ingroup iface_kde_output_device_mode_v2 */ +static inline void +kde_output_device_mode_v2_set_user_data(struct kde_output_device_mode_v2 *kde_output_device_mode_v2, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) kde_output_device_mode_v2, user_data); +} + +/** @ingroup iface_kde_output_device_mode_v2 */ +static inline void * +kde_output_device_mode_v2_get_user_data(struct kde_output_device_mode_v2 *kde_output_device_mode_v2) +{ + return wl_proxy_get_user_data((struct wl_proxy *) kde_output_device_mode_v2); +} + +static inline uint32_t +kde_output_device_mode_v2_get_version(struct kde_output_device_mode_v2 *kde_output_device_mode_v2) +{ + return wl_proxy_get_version((struct wl_proxy *) kde_output_device_mode_v2); +} + +/** @ingroup iface_kde_output_device_mode_v2 */ +static inline void +kde_output_device_mode_v2_destroy(struct kde_output_device_mode_v2 *kde_output_device_mode_v2) +{ + wl_proxy_destroy((struct wl_proxy *) kde_output_device_mode_v2); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/detection/displayserver/linux/wayland/kde-output-device-v2-protocol.c b/src/detection/displayserver/linux/wayland/kde-output-device-v2-protocol.c new file mode 100644 index 000000000..885962e5b --- /dev/null +++ b/src/detection/displayserver/linux/wayland/kde-output-device-v2-protocol.c @@ -0,0 +1,81 @@ +#ifdef FF_HAVE_WAYLAND + +/* Generated by wayland-scanner 1.22.0 */ + +/* + * SPDX-FileCopyrightText: 2008-2011 Kristian Høgsberg + * SPDX-FileCopyrightText: 2010-2011 Intel Corporation + * SPDX-FileCopyrightText: 2012-2013 Collabora, Ltd. + * SPDX-FileCopyrightText: 2015 Sebastian Kügler + * SPDX-FileCopyrightText: 2021 Méven Car + * + * SPDX-License-Identifier: MIT-CMU + */ + +#include +#include +#include + +extern const struct wl_interface kde_output_device_mode_v2_interface; + +static const struct wl_interface *kde_output_device_v2_types[] = { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + &kde_output_device_mode_v2_interface, + &kde_output_device_mode_v2_interface, +}; + +static const struct wl_message kde_output_device_v2_events[] = { + { "geometry", "iiiiissi", kde_output_device_v2_types + 0 }, + { "current_mode", "o", kde_output_device_v2_types + 8 }, + { "mode", "n", kde_output_device_v2_types + 9 }, + { "done", "", kde_output_device_v2_types + 0 }, + { "scale", "f", kde_output_device_v2_types + 0 }, + { "edid", "s", kde_output_device_v2_types + 0 }, + { "enabled", "i", kde_output_device_v2_types + 0 }, + { "uuid", "s", kde_output_device_v2_types + 0 }, + { "serial_number", "s", kde_output_device_v2_types + 0 }, + { "eisa_id", "s", kde_output_device_v2_types + 0 }, + { "capabilities", "u", kde_output_device_v2_types + 0 }, + { "overscan", "u", kde_output_device_v2_types + 0 }, + { "vrr_policy", "u", kde_output_device_v2_types + 0 }, + { "rgb_range", "u", kde_output_device_v2_types + 0 }, + { "name", "2s", kde_output_device_v2_types + 0 }, + { "high_dynamic_range", "3u", kde_output_device_v2_types + 0 }, + { "sdr_brightness", "3u", kde_output_device_v2_types + 0 }, + { "wide_color_gamut", "3u", kde_output_device_v2_types + 0 }, + { "auto_rotate_policy", "4u", kde_output_device_v2_types + 0 }, + { "icc_profile_path", "5s", kde_output_device_v2_types + 0 }, + { "brightness_metadata", "6uuu", kde_output_device_v2_types + 0 }, + { "brightness_overrides", "6iii", kde_output_device_v2_types + 0 }, + { "sdr_gamut_wideness", "6u", kde_output_device_v2_types + 0 }, + { "color_profile_source", "7u", kde_output_device_v2_types + 0 }, + { "brightness", "8u", kde_output_device_v2_types + 0 }, +}; + +WL_EXPORT const struct wl_interface kde_output_device_v2_interface = { + "kde_output_device_v2", 8, + 0, NULL, + 25, kde_output_device_v2_events, +}; + +static const struct wl_message kde_output_device_mode_v2_events[] = { + { "size", "ii", kde_output_device_v2_types + 0 }, + { "refresh", "i", kde_output_device_v2_types + 0 }, + { "preferred", "", kde_output_device_v2_types + 0 }, + { "removed", "", kde_output_device_v2_types + 0 }, +}; + +WL_EXPORT const struct wl_interface kde_output_device_mode_v2_interface = { + "kde_output_device_mode_v2", 1, + 0, NULL, + 4, kde_output_device_mode_v2_events, +}; + +#endif diff --git a/src/detection/displayserver/linux/wayland/kde-output.c b/src/detection/displayserver/linux/wayland/kde-output.c new file mode 100644 index 000000000..07ad91a65 --- /dev/null +++ b/src/detection/displayserver/linux/wayland/kde-output.c @@ -0,0 +1,225 @@ +#ifdef FF_HAVE_WAYLAND + +#include "wayland.h" +#include "kde-output-device-v2-client-protocol.h" +#include "util/edidHelper.h" +#include "util/base64.h" + +typedef struct WaylandKdeMode +{ + int32_t width; + int32_t height; + int32_t refreshRate; + struct kde_output_device_mode_v2* pMode; +} WaylandKdeMode; + +static void waylandKdeSizeListener(void* data, FF_MAYBE_UNUSED struct kde_output_device_mode_v2 *_, int32_t width, int32_t height) +{ + WaylandKdeMode* mode = (WaylandKdeMode*) data; + mode->width = width; + mode->height = height; +} + +static void waylandKdeRefreshListener(void* data, FF_MAYBE_UNUSED struct kde_output_device_mode_v2 *_, int32_t rate) +{ + WaylandKdeMode* mode = (WaylandKdeMode*) data; + mode->refreshRate = rate; +} + +static const struct kde_output_device_mode_v2_listener modeListener = { + .size = waylandKdeSizeListener, + .refresh = waylandKdeRefreshListener, + .preferred = (void*) stubListener, + .removed = (void*) stubListener, +}; + +static void waylandKdeModeListener(void* data, FF_MAYBE_UNUSED struct kde_output_device_v2* _, struct kde_output_device_mode_v2 *mode) +{ + WaylandDisplay* wldata = (WaylandDisplay*) data; + if (!wldata->internal) return; + + WaylandKdeMode* newMode = ffListAdd((FFlist*) wldata->internal); + newMode->pMode = mode; + + // Strangely, the listener is called only in this function, but not in `waylandKdeCurrentModeListener` + wldata->parent->ffwl_proxy_add_listener((struct wl_proxy *) mode, (void (**)(void)) &modeListener, newMode); +} + +static void waylandKdeCurrentModeListener(void* data, FF_MAYBE_UNUSED struct kde_output_device_v2 *_, struct kde_output_device_mode_v2 *mode) +{ + // waylandKdeModeListener is always run before this + WaylandDisplay* wldata = (WaylandDisplay*) data; + if (!wldata->internal) return; + + WaylandKdeMode* current = NULL; + FF_LIST_FOR_EACH(WaylandKdeMode, m, *(FFlist*) wldata->internal) + { + if (m->pMode == mode) + { + current = m; + break; + } + } + wldata->width = current->width; + wldata->height = current->height; + wldata->refreshRate = current->refreshRate; +} + +static void waylandKdeScaleListener(void* data, FF_MAYBE_UNUSED struct kde_output_device_v2* _, wl_fixed_t scale) +{ + WaylandDisplay* wldata = (WaylandDisplay*) data; + wldata->scale = wl_fixed_to_double(scale); +} + +static void waylandKdeEdidListener(void* data, FF_MAYBE_UNUSED struct kde_output_device_v2* _, const char* raw) +{ + if (!*raw) return; + WaylandDisplay* wldata = (WaylandDisplay*) data; + FF_STRBUF_AUTO_DESTROY b64 = ffStrbufCreateStatic(raw); + FF_STRBUF_AUTO_DESTROY edid = ffBase64DecodeStrbuf(&b64); + if (edid.length < 128) return; + ffEdidGetName((const uint8_t*) edid.chars, &wldata->edidName); +} + +static void waylandKdeEnabledListener(void* data, FF_MAYBE_UNUSED struct kde_output_device_v2* _, int32_t enabled) +{ + WaylandDisplay* wldata = (WaylandDisplay*) data; + if (!enabled) wldata->internal = NULL; +} + +static void waylandKdeGeometryListener(void *data, + FF_MAYBE_UNUSED struct kde_output_device_v2 *kde_output_device_v2, + FF_MAYBE_UNUSED int32_t x, + FF_MAYBE_UNUSED int32_t y, + FF_MAYBE_UNUSED int32_t physical_width, + FF_MAYBE_UNUSED int32_t physical_height, + FF_MAYBE_UNUSED int32_t subpixel, + FF_MAYBE_UNUSED const char *make, + FF_MAYBE_UNUSED const char *model, + int32_t transform) +{ + WaylandDisplay* display = data; + display->transform = (enum wl_output_transform) transform; +} + +void waylandOutputNameListener(void* data, FF_MAYBE_UNUSED struct kde_output_device_v2* output, const char *name) +{ + WaylandDisplay* display = data; + if(ffStrStartsWith(name, "eDP-")) + display->type = FF_DISPLAY_TYPE_BUILTIN; + else if(ffStrStartsWith(name, "HDMI-")) + display->type = FF_DISPLAY_TYPE_EXTERNAL; + strncpy((char*) &display->id, name, sizeof(display->id)); + ffStrbufAppendS(&display->name, name); +} + +void ffWaylandHandleKdeOutput(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version) +{ + struct wl_proxy* output = wldata->ffwl_proxy_marshal_constructor_versioned((struct wl_proxy*) registry, WL_REGISTRY_BIND, &kde_output_device_v2_interface, version, name, kde_output_device_v2_interface.name, version, NULL); + if(output == NULL) + return; + + FF_LIST_AUTO_DESTROY modes = ffListCreate(sizeof(WaylandKdeMode)); + WaylandDisplay display = { + .parent = wldata, + .width = 0, + .height = 0, + .refreshRate = 0, + .scale = 1, + .transform = WL_OUTPUT_TRANSFORM_NORMAL, + .type = FF_DISPLAY_TYPE_UNKNOWN, + .name = ffStrbufCreate(), + .description = ffStrbufCreate(), + .edidName = ffStrbufCreate(), + .internal = &modes, + }; + + struct kde_output_device_v2_listener outputListener = { + .geometry = waylandKdeGeometryListener, + .current_mode = waylandKdeCurrentModeListener, + .mode = waylandKdeModeListener, + .done = (void*) stubListener, + .scale = waylandKdeScaleListener, + .edid = waylandKdeEdidListener, + .enabled = waylandKdeEnabledListener, + .uuid = (void*) stubListener, + .serial_number = (void*) stubListener, + .eisa_id = (void*) stubListener, + .capabilities = (void*) stubListener, + .overscan = (void*) stubListener, + .vrr_policy = (void*) stubListener, + .rgb_range = (void*) stubListener, + .name = waylandOutputNameListener, + .high_dynamic_range = (void*) stubListener, + .sdr_brightness = (void*) stubListener, + .wide_color_gamut = (void*) stubListener, + .auto_rotate_policy = (void*) stubListener, + .icc_profile_path = (void*) stubListener, + .brightness_metadata = (void*) stubListener, + .brightness_overrides = (void*) stubListener, + .sdr_gamut_wideness = (void*) stubListener, + .color_profile_source = (void*) stubListener, + .brightness = (void*) stubListener, + }; + + wldata->ffwl_proxy_add_listener(output, (void(**)(void)) &outputListener, &display); + wldata->ffwl_display_roundtrip(wldata->display); + wldata->ffwl_proxy_destroy(output); + + if(display.width <= 0 || display.height <= 0 || !display.internal) + return; + + uint32_t rotation; + switch(display.transform) + { + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + case WL_OUTPUT_TRANSFORM_90: + rotation = 90; + break; + case WL_OUTPUT_TRANSFORM_FLIPPED_180: + case WL_OUTPUT_TRANSFORM_180: + rotation = 180; + break; + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + case WL_OUTPUT_TRANSFORM_270: + rotation = 270; + break; + default: + rotation = 0; + break; + } + + switch(rotation) + { + case 90: + case 270: { + int32_t temp = display.width; + display.width = display.height; + display.height = temp; + break; + } + default: + break; + } + + ffdsAppendDisplay(wldata->result, + (uint32_t) display.width, + (uint32_t) display.height, + display.refreshRate / 1000.0, + (uint32_t) (display.width / display.scale), + (uint32_t) (display.height / display.scale), + rotation, + display.edidName.length + ? &display.edidName + : &display.name, + display.type, + false, + 0 + ); + + ffStrbufDestroy(&display.description); + ffStrbufDestroy(&display.name); + ffStrbufDestroy(&display.edidName); +} + +#endif diff --git a/src/detection/displayserver/linux/wayland/wayland.c b/src/detection/displayserver/linux/wayland/wayland.c index 9628dcdd7..9479e7ba8 100644 --- a/src/detection/displayserver/linux/wayland/wayland.c +++ b/src/detection/displayserver/linux/wayland/wayland.c @@ -11,6 +11,7 @@ #include "wayland.h" #include "wlr-output-management-unstable-v1-client-protocol.h" +#include "kde-output-device-v2-client-protocol.h" #ifndef __FreeBSD__ static void waylandDetectWM(int fd, FFDisplayServerResult* result) @@ -47,6 +48,11 @@ static void waylandGlobalAddListener(void* data, struct wl_registry* registry, u wldata->protocolType = FF_WAYLAND_PROTOCOL_TYPE_ZWLR; ffWaylandHandleZwlrOutput(wldata, registry, name, version); } + else if((wldata->protocolType == FF_WAYLAND_PROTOCOL_TYPE_NONE || wldata->protocolType == FF_WAYLAND_PROTOCOL_TYPE_KDE) && ffStrEquals(interface, kde_output_device_v2_interface.name)) + { + wldata->protocolType = FF_WAYLAND_PROTOCOL_TYPE_KDE; + ffWaylandHandleKdeOutput(wldata, registry, name, version); + } } bool detectWayland(FFDisplayServerResult* result) @@ -107,7 +113,8 @@ void ffWaylandOutputNameListener(void* data, FF_MAYBE_UNUSED void* output, const display->type = FF_DISPLAY_TYPE_BUILTIN; else if(ffStrStartsWith(name, "HDMI-")) display->type = FF_DISPLAY_TYPE_EXTERNAL; - ffdsMatchDrmConnector(name, &display->edidName); + if (!display->edidName.length) + ffdsMatchDrmConnector(name, &display->edidName); ffStrbufAppendS(&display->name, name); } diff --git a/src/detection/displayserver/linux/wayland/wayland.h b/src/detection/displayserver/linux/wayland/wayland.h index 4a65a98ad..ada81ab01 100644 --- a/src/detection/displayserver/linux/wayland/wayland.h +++ b/src/detection/displayserver/linux/wayland/wayland.h @@ -14,6 +14,7 @@ typedef enum WaylandProtocolType FF_WAYLAND_PROTOCOL_TYPE_NONE, FF_WAYLAND_PROTOCOL_TYPE_GLOBAL, FF_WAYLAND_PROTOCOL_TYPE_ZWLR, + FF_WAYLAND_PROTOCOL_TYPE_KDE, } WaylandProtocolType; typedef struct WaylandData @@ -53,5 +54,6 @@ void ffWaylandOutputDescriptionListener(void* data, FF_MAYBE_UNUSED void* output void ffWaylandHandleGlobalOutput(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version); void ffWaylandHandleZwlrOutput(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version); +void ffWaylandHandleKdeOutput(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version); #endif diff --git a/src/logo/image/image.c b/src/logo/image/image.c index 36bdea281..c95455f50 100644 --- a/src/logo/image/image.c +++ b/src/logo/image/image.c @@ -2,6 +2,7 @@ #include "common/io/io.h" #include "common/printing.h" #include "util/stringUtils.h" +#include "util/base64.h" #include #include @@ -16,48 +17,6 @@ #include #endif -// https://github.com/kostya/benchmarks/blob/master/base64/test-nolib.c#L145 -static void base64EncodeRaw(uint32_t size, const char *str, uint32_t *out_size, char *output) -{ - static const char chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - char *out = output; - const char *ends = str + (size - size % 3); - while (str != ends) { - uint32_t n = __builtin_bswap32(*(uint32_t*) str); - *out++ = chars[(n >> 26) & 63]; - *out++ = chars[(n >> 20) & 63]; - *out++ = chars[(n >> 14) & 63]; - *out++ = chars[(n >> 8) & 63]; - str += 3; - } - - if (size % 3 == 1) { - uint64_t n = (uint64_t)*str << 16; - *out++ = chars[(n >> 18) & 63]; - *out++ = chars[(n >> 12) & 63]; - *out++ = '='; - *out++ = '='; - } else if (size % 3 == 2) { - uint64_t n = (uint64_t)*str++ << 16; - n |= (uint64_t)*str << 8; - *out++ = chars[(n >> 18) & 63]; - *out++ = chars[(n >> 12) & 63]; - *out++ = chars[(n >> 6) & 63]; - *out++ = '='; - } - *out = '\0'; - *out_size = (uint32_t) (out - output); -} - -static FFstrbuf base64Encode(const FFstrbuf* in) -{ - FFstrbuf out = ffStrbufCreateA(10 + in->length * 4 / 3); - base64EncodeRaw(in->length, in->chars, &out.length, out.chars); - assert(out.length < out.allocated); - - return out; -} - static bool printImageIterm(bool printError) { const FFOptionsLogo* options = &instance.config.logo; @@ -71,7 +30,7 @@ static bool printImageIterm(bool printError) fflush(stdout); - FF_STRBUF_AUTO_DESTROY base64 = base64Encode(&buf); + FF_STRBUF_AUTO_DESTROY base64 = ffBase64EncodeStrbuf(&buf); ffStrbufClear(&buf); if (!options->width || !options->height) @@ -166,7 +125,7 @@ static bool printImageKittyDirect(bool printError) return false; } - FF_STRBUF_AUTO_DESTROY base64 = base64Encode(&options->source); + FF_STRBUF_AUTO_DESTROY base64 = ffBase64EncodeStrbuf(&options->source); if (!options->width || !options->height) { diff --git a/src/util/base64.c b/src/util/base64.c new file mode 100644 index 000000000..866d59428 --- /dev/null +++ b/src/util/base64.c @@ -0,0 +1,115 @@ +#include "base64.h" + +// https://github.com/kostya/benchmarks/blob/master/base64/test-nolib.c#L145 +void ffBase64EncodeRaw(uint32_t size, const char *str, uint32_t *out_size, char *output) +{ + static const char chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + char *out = output; + const char *ends = str + (size - size % 3); + while (str != ends) + { + uint32_t n = __builtin_bswap32(*(uint32_t *)str); + *out++ = chars[(n >> 26) & 63]; + *out++ = chars[(n >> 20) & 63]; + *out++ = chars[(n >> 14) & 63]; + *out++ = chars[(n >> 8) & 63]; + str += 3; + } + + if (size % 3 == 1) + { + uint64_t n = (uint64_t)*str << 16; + *out++ = chars[(n >> 18) & 63]; + *out++ = chars[(n >> 12) & 63]; + *out++ = '='; + *out++ = '='; + } + else if (size % 3 == 2) + { + uint64_t n = (uint64_t)*str++ << 16; + n |= (uint64_t)*str << 8; + *out++ = chars[(n >> 18) & 63]; + *out++ = chars[(n >> 12) & 63]; + *out++ = chars[(n >> 6) & 63]; + *out++ = '='; + } + *out = '\0'; + *out_size = (uint32_t)(out - output); +} + +static char decode_table[256]; + +void init_decode_table() +{ + uint8_t ch = 0; + do + { + int32_t code = -1; + if (ch >= 'A' && ch <= 'Z') + code = ch - 0x41; + if (ch >= 'a' && ch <= 'z') + code = ch - 0x47; + if (ch >= '0' && ch <= '9') + code = ch + 0x04; + if (ch == '+' || ch == '-') + code = 0x3E; + if (ch == '/' || ch == '_') + code = 0x3F; + decode_table[ch] = (char) code; + } while (ch++ < 0xFF); +} + +#define next_char(x) \ + char x = decode_table[(unsigned char)*str++]; \ + if (x < 0) \ + return false; + +bool ffBase64DecodeRaw(uint32_t size, const char *str, uint32_t *out_size, char *output) +{ + if (*(uint64_t*) decode_table == 0) + init_decode_table(); + + char *out = output; + while (size > 0 && (str[size - 1] == '\n' || str[size - 1] == '\r' || str[size - 1] == '=')) + size--; + + const char *ends = str + size - 4; + while (true) + { + if (str > ends) + break; + while (*str == '\n' || *str == '\r') + str++; + + if (str > ends) + break; + next_char(a); + next_char(b); + next_char(c); + next_char(d); + + *out++ = (char)(a << 2 | b >> 4); + *out++ = (char)(b << 4 | c >> 2); + *out++ = (char)(c << 6 | d >> 0); + } + + uint8_t mod = (uint8_t) (ends - str + 4) % 4; + if (mod == 2) + { + next_char(a); + next_char(b); + *out++ = (char)(a << 2 | b >> 4); + } + else if (mod == 3) + { + next_char(a); + next_char(b); + next_char(c); + *out++ = (char)(a << 2 | b >> 4); + *out++ = (char)(b << 4 | c >> 2); + } + + *out = '\0'; + *out_size = (uint32_t) (out - output); + return true; +} diff --git a/src/util/base64.h b/src/util/base64.h new file mode 100644 index 000000000..5dc8ee7c6 --- /dev/null +++ b/src/util/base64.h @@ -0,0 +1,23 @@ +#pragma once + +#include "fastfetch.h" + +void ffBase64EncodeRaw(uint32_t size, const char *str, uint32_t *out_size, char *output); +static inline FFstrbuf ffBase64EncodeStrbuf(const FFstrbuf* in) +{ + FFstrbuf out = ffStrbufCreateA(10 + in->length * 4 / 3); + ffBase64EncodeRaw(in->length, in->chars, &out.length, out.chars); + assert(out.length < out.allocated); + + return out; +} + +bool ffBase64DecodeRaw(uint32_t size, const char *str, uint32_t *out_size, char *output); +static inline FFstrbuf ffBase64DecodeStrbuf(const FFstrbuf* in) +{ + FFstrbuf out = ffStrbufCreateA(10 + in->length * 3 / 4); + ffBase64DecodeRaw(in->length, in->chars, &out.length, out.chars); + assert(out.length < out.allocated); + + return out; +}