DisplayServer (Linux): adds HDR detection support

Fixes #2356
This commit is contained in:
Carter Li
2026-06-17 16:23:03 +08:00
committed by Carter Li
parent fe7962d5d2
commit c42ea6eeea
7 changed files with 3178 additions and 7 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
[codespell]
check-filenames =
builtin = clear,rare,usage,informal
skip = */.git,*/cmake-build-*,*/.idea,*/completions,*/presets,*/screenshots,*/tests,*/3rdparty,*/logo/ascii,./src/detection/gpu/asahi_drm.h,./src/detection/cpu/cpu_x86.c,./src/detection/gpu/gpu_bsddrm.c
skip = */.git,*/cmake-build-*,*/.idea,*/completions,*/presets,*/screenshots,*/tests,*/3rdparty,*/logo/ascii,./src/detection/gpu/asahi_drm.h,./src/detection/cpu/cpu_x86.c,./src/detection/gpu/gpu_bsddrm.c,./src/detection/displayserver/linux/wayland/*-protocol.*
ignore-words-list = iterm,compiletime,unknwn,pengwin,siduction,master,slave,sur,doas,conexant,ags,bu,renderD
+11 -3
View File
@@ -77,7 +77,7 @@ endif()
include(CMakeDependentOption)
cmake_dependent_option(ENABLE_VULKAN "Enable vulkan" ON "LINUX OR APPLE OR FreeBSD OR OpenBSD OR NetBSD OR WIN32 OR ANDROID OR SunOS OR Haiku OR GNU" OFF)
cmake_dependent_option(ENABLE_WAYLAND "Enable wayland-client" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR GNU" OFF)
cmake_dependent_option(ENABLE_WAYLAND "Enable wayland-client" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD" OFF)
cmake_dependent_option(ENABLE_XCB_RANDR "Enable xcb-randr" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR ANDROID OR SunOS OR GNU" OFF)
cmake_dependent_option(ENABLE_XRANDR "Enable xrandr" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR ANDROID OR SunOS OR GNU" OFF)
cmake_dependent_option(ENABLE_DRM "Enable libdrm" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS OR GNU" OFF)
@@ -599,6 +599,7 @@ if(LINUX)
src/detection/displayserver/linux/wayland/wayland.c
src/detection/displayserver/linux/wayland/global-output.c
src/detection/displayserver/linux/wayland/wl-output-protocol.c
src/detection/displayserver/linux/wayland/wp-color-management-v1-protocol.c
src/detection/displayserver/linux/wayland/kde-output.c
src/detection/displayserver/linux/wayland/kde-output-device-v2-protocol.c
src/detection/displayserver/linux/wayland/kde-output-order-v1-protocol.c
@@ -759,6 +760,8 @@ elseif(FreeBSD)
src/detection/displayserver/linux/wayland/wayland.c
src/detection/displayserver/linux/wayland/global-output.c
src/detection/displayserver/linux/wayland/kde-output.c
src/detection/displayserver/linux/wayland/wl-output-protocol.c
src/detection/displayserver/linux/wayland/wp-color-management-v1-protocol.c
src/detection/displayserver/linux/wayland/kde-output-device-v2-protocol.c
src/detection/displayserver/linux/wayland/kde-output-order-v1-protocol.c
src/detection/displayserver/linux/wayland/xdg-output-unstable-v1-protocol.c
@@ -853,6 +856,8 @@ elseif(NetBSD)
src/detection/displayserver/linux/wayland/wayland.c
src/detection/displayserver/linux/wayland/global-output.c
src/detection/displayserver/linux/wayland/kde-output.c
src/detection/displayserver/linux/wayland/wl-output-protocol.c
src/detection/displayserver/linux/wayland/wp-color-management-v1-protocol.c
src/detection/displayserver/linux/wayland/kde-output-device-v2-protocol.c
src/detection/displayserver/linux/wayland/kde-output-order-v1-protocol.c
src/detection/displayserver/linux/wayland/xdg-output-unstable-v1-protocol.c
@@ -936,6 +941,8 @@ elseif(OpenBSD)
src/detection/displayserver/linux/wayland/wayland.c
src/detection/displayserver/linux/wayland/global-output.c
src/detection/displayserver/linux/wayland/kde-output.c
src/detection/displayserver/linux/wayland/wl-output-protocol.c
src/detection/displayserver/linux/wayland/wp-color-management-v1-protocol.c
src/detection/displayserver/linux/wayland/kde-output-device-v2-protocol.c
src/detection/displayserver/linux/wayland/kde-output-order-v1-protocol.c
src/detection/displayserver/linux/wayland/xdg-output-unstable-v1-protocol.c
@@ -1179,6 +1186,8 @@ elseif(SunOS)
src/detection/displayserver/linux/wayland/wayland.c
src/detection/displayserver/linux/wayland/global-output.c
src/detection/displayserver/linux/wayland/kde-output.c
src/detection/displayserver/linux/wayland/wl-output-protocol.c
src/detection/displayserver/linux/wayland/wp-color-management-v1-protocol.c
src/detection/displayserver/linux/wayland/kde-output-device-v2-protocol.c
src/detection/displayserver/linux/wayland/kde-output-order-v1-protocol.c
src/detection/displayserver/linux/wayland/xdg-output-unstable-v1-protocol.c
@@ -1326,10 +1335,9 @@ elseif(GNU)
src/detection/physicalmemory/physicalmemory_nosupport.c
src/detection/diskio/diskio_nosupport.c
src/detection/displayserver/linux/displayserver_linux.c
src/detection/displayserver/linux/wayland/wayland.c
src/detection/displayserver/linux/common.c
src/detection/displayserver/linux/drm.c
src/detection/displayserver/linux/wayland/wayland.c
src/detection/displayserver/linux/wayland/global-output.c
src/detection/displayserver/linux/wmde.c
src/detection/displayserver/linux/xcb.c
src/detection/displayserver/linux/xlib.c
@@ -3,6 +3,7 @@
#include "wayland.h"
#include "common/strutil.h"
#include "xdg-output-unstable-v1-client-protocol.h"
#include "wp-color-management-v1-client-protocol.h"
static void waylandOutputModeListener(void* data, FF_A_UNUSED struct wl_output* output, uint32_t flags, int32_t width, int32_t height, int32_t refreshRate) {
WaylandDisplay* display = data;
@@ -70,6 +71,32 @@ static struct zxdg_output_v1_listener zxdgOutputListener = {
.description = (void*) ffWaylandOutputDescriptionListener,
};
static void handleWpTfNamed(void *data, FF_A_UNUSED struct wp_image_description_info_v1* wp_image_description_info_v1, uint32_t tf) {
// KDE reports `gamma 2.2` even in HDR mode, but it should be handled in KDE specific path
WaylandDisplay* display = data;
switch (tf) {
case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ:
case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_HLG:
display->hdrInfoAvailable = true;
display->hdrEnabled = true;
break;
}
}
static const struct wp_image_description_info_v1_listener wpImageDescInfoListener = {
.done = (void*) stubListener,
.icc_file = (void*) stubListener,
.primaries = (void*) stubListener,
.primaries_named = (void*) stubListener,
.tf_power = (void*) stubListener,
.tf_named = (void*) handleWpTfNamed,
.luminances = (void*) stubListener,
.target_primaries = (void*) stubListener,
.target_luminance = (void*) stubListener,
.target_max_cll = (void*) stubListener,
.target_max_fall = (void*) stubListener,
};
const char* ffWaylandHandleGlobalOutput(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version) {
const char* api = "wayland-global";
uint32_t bindVersion = min(version, WL_OUTPUT_DESCRIPTION_SINCE_VERSION);
@@ -108,6 +135,27 @@ const char* ffWaylandHandleGlobalOutput(WaylandData* wldata, struct wl_registry*
}
}
if (wldata->wpColorManager) {
uint32_t bindVersion = min(version, WP_COLOR_MANAGER_V1_GET_OUTPUT_SINCE_VERSION);
struct wl_proxy* wpColorOutput = wldata->ffwl_proxy_marshal_constructor_versioned(wldata->wpColorManager, WP_COLOR_MANAGER_V1_GET_OUTPUT, &wp_color_management_output_v1_interface, bindVersion, NULL, output);
if (wpColorOutput) {
struct wl_proxy* wpImageDesc = wldata->ffwl_proxy_marshal_constructor_versioned(wpColorOutput, WP_COLOR_MANAGEMENT_OUTPUT_V1_GET_IMAGE_DESCRIPTION, &wp_image_description_v1_interface, bindVersion, NULL);
if (wpImageDesc) {
struct wl_proxy* wpImageDescInfo = wldata->ffwl_proxy_marshal_constructor_versioned(wpImageDesc, WP_IMAGE_DESCRIPTION_V1_GET_INFORMATION, &wp_image_description_info_v1_interface, bindVersion, NULL);
if (wpImageDescInfo) {
wldata->ffwl_proxy_add_listener(wpImageDescInfo, (void (**)(void)) &wpImageDescInfoListener, &display);
wldata->ffwl_display_roundtrip(wldata->display);
wldata->ffwl_proxy_destroy(wpImageDescInfo);
}
wldata->ffwl_proxy_destroy(wpImageDesc);
}
wldata->ffwl_proxy_destroy(wpColorOutput);
wldata->ffwl_display_roundtrip(wldata->display);
api = "wayland-global-wpcolor";
}
}
wldata->ffwl_proxy_destroy(output);
if (display.width <= 0 || display.height <= 0) {
@@ -138,7 +186,9 @@ const char* ffWaylandHandleGlobalOutput(WaylandData* wldata, struct wl_registry*
(uint32_t) display.physicalHeight,
api);
if (item) {
if (display.hdrSupported) {
if (display.hdrEnabled) {
item->hdrStatus = FF_DISPLAY_HDR_STATUS_ENABLED;
} else if (display.hdrSupported) {
item->hdrStatus = FF_DISPLAY_HDR_STATUS_SUPPORTED;
} else if (display.hdrInfoAvailable) {
item->hdrStatus = FF_DISPLAY_HDR_STATUS_UNSUPPORTED;
@@ -170,4 +220,16 @@ const char* ffWaylandHandleZxdgOutput(WaylandData* wldata, struct wl_registry* r
return NULL;
}
const char* ffWaylandHandleWpColor(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version) {
uint32_t bindVersion = min(version, WP_COLOR_MANAGER_V1_GET_OUTPUT_SINCE_VERSION);
struct wl_proxy* manager = wldata->ffwl_proxy_marshal_constructor_versioned((struct wl_proxy*) registry, WL_REGISTRY_BIND, &wp_color_manager_v1_interface, bindVersion, name, wp_color_manager_v1_interface.name, bindVersion, NULL);
if (manager == NULL) {
return "Failed to create wp_color_manager_v1";
}
wldata->wpColorManager = manager;
return NULL;
}
#endif
@@ -16,6 +16,7 @@
#include "kde-output-device-v2-client-protocol.h"
#include "kde-output-order-v1-client-protocol.h"
#include "xdg-output-unstable-v1-client-protocol.h"
#include "wp-color-management-v1-client-protocol.h"
#if __FreeBSD__
#include <sys/un.h>
@@ -94,6 +95,8 @@ static void waylandGlobalAddListener(void* data, struct wl_registry* registry, u
ffWaylandHandleKdeOutputOrder(wldata, registry, name, version);
} else if ((wldata->protocolType == FF_WAYLAND_PROTOCOL_TYPE_GLOBAL || wldata->protocolType == FF_WAYLAND_PROTOCOL_TYPE_NONE) && ffStrEquals(interface, zxdg_output_manager_v1_interface.name)) {
ffWaylandHandleZxdgOutput(wldata, registry, name, version);
} else if ((wldata->protocolType == FF_WAYLAND_PROTOCOL_TYPE_GLOBAL || wldata->protocolType == FF_WAYLAND_PROTOCOL_TYPE_NONE) && ffStrEquals(interface, wp_color_manager_v1_interface.name)) {
ffWaylandHandleWpColor(wldata, registry, name, version);
}
}
@@ -122,9 +125,9 @@ static FF_A_UNUSED bool matchDrmConnector(const char* connName, WaylandDisplay*
uint8_t edidData[512];
ssize_t edidLength = ffReadFileData(path.chars, ARRAY_SIZE(edidData), edidData);
if (edidLength > 0 && edidLength % 128 == 0) {
if (edidLength > 0 && ffEdidIsValid(edidData, (uint32_t) edidLength)) {
ffEdidGetName(edidData, &wldata->edidName);
ffEdidGetHdrCompatible(edidData, (uint32_t) edidLength);
wldata->hdrSupported = ffEdidGetHdrCompatible(edidData, (uint32_t) edidLength);
ffEdidGetSerialAndManufactureDate(edidData, &wldata->serial, &wldata->myear, &wldata->mweek);
wldata->hdrInfoAvailable = true;
return true;
@@ -29,6 +29,7 @@ typedef struct WaylandData {
WaylandProtocolType protocolType;
uint64_t primaryDisplayId;
struct wl_proxy* zxdgOutputManager;
struct wl_proxy* wpColorManager;
} WaylandData;
typedef struct WaylandDisplay {
@@ -82,5 +83,6 @@ const char* ffWaylandHandleGlobalOutput(WaylandData* wldata, struct wl_registry*
const char* ffWaylandHandleKdeOutput(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version);
const char* ffWaylandHandleKdeOutputOrder(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version);
const char* ffWaylandHandleZxdgOutput(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version);
const char* ffWaylandHandleWpColor(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version);
#endif
@@ -0,0 +1,225 @@
#ifdef FF_HAVE_WAYLAND
/* Generated by wayland-scanner 1.24.0 */
/*
* Copyright 2019 Sebastian Wick
* Copyright 2019 Erwin Burema
* Copyright 2020 AMD
* Copyright 2020-2024 Collabora, Ltd.
* Copyright 2024 Xaver Hugl
* Copyright 2022-2025 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include <wayland-util.h>
extern const struct wl_interface wl_output_interface;
extern const struct wl_interface wl_surface_interface;
extern const struct wl_interface wp_color_management_output_v1_interface;
extern const struct wl_interface wp_color_management_surface_feedback_v1_interface;
extern const struct wl_interface wp_color_management_surface_v1_interface;
extern const struct wl_interface wp_image_description_creator_icc_v1_interface;
extern const struct wl_interface wp_image_description_creator_params_v1_interface;
extern const struct wl_interface wp_image_description_info_v1_interface;
extern const struct wl_interface wp_image_description_reference_v1_interface;
extern const struct wl_interface wp_image_description_v1_interface;
static const struct wl_interface *color_management_v1_types[] = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
&wp_color_management_output_v1_interface,
&wl_output_interface,
&wp_color_management_surface_v1_interface,
&wl_surface_interface,
&wp_color_management_surface_feedback_v1_interface,
&wl_surface_interface,
&wp_image_description_creator_icc_v1_interface,
&wp_image_description_creator_params_v1_interface,
&wp_image_description_v1_interface,
&wp_image_description_v1_interface,
&wp_image_description_reference_v1_interface,
&wp_image_description_v1_interface,
&wp_image_description_v1_interface,
NULL,
&wp_image_description_v1_interface,
&wp_image_description_v1_interface,
&wp_image_description_v1_interface,
&wp_image_description_v1_interface,
&wp_image_description_info_v1_interface,
};
static const struct wl_message wp_color_manager_v1_requests[] = {
{ "destroy", "", color_management_v1_types + 0 },
{ "get_output", "no", color_management_v1_types + 8 },
{ "get_surface", "no", color_management_v1_types + 10 },
{ "get_surface_feedback", "no", color_management_v1_types + 12 },
{ "create_icc_creator", "n", color_management_v1_types + 14 },
{ "create_parametric_creator", "n", color_management_v1_types + 15 },
{ "create_windows_scrgb", "n", color_management_v1_types + 16 },
{ "get_image_description", "2no", color_management_v1_types + 17 },
};
static const struct wl_message wp_color_manager_v1_events[] = {
{ "supported_intent", "u", color_management_v1_types + 0 },
{ "supported_feature", "u", color_management_v1_types + 0 },
{ "supported_tf_named", "u", color_management_v1_types + 0 },
{ "supported_primaries_named", "u", color_management_v1_types + 0 },
{ "done", "", color_management_v1_types + 0 },
};
WL_EXPORT const struct wl_interface wp_color_manager_v1_interface = {
"wp_color_manager_v1", 2,
8, wp_color_manager_v1_requests,
5, wp_color_manager_v1_events,
};
static const struct wl_message wp_color_management_output_v1_requests[] = {
{ "destroy", "", color_management_v1_types + 0 },
{ "get_image_description", "n", color_management_v1_types + 19 },
};
static const struct wl_message wp_color_management_output_v1_events[] = {
{ "image_description_changed", "", color_management_v1_types + 0 },
};
WL_EXPORT const struct wl_interface wp_color_management_output_v1_interface = {
"wp_color_management_output_v1", 2,
2, wp_color_management_output_v1_requests,
1, wp_color_management_output_v1_events,
};
static const struct wl_message wp_color_management_surface_v1_requests[] = {
{ "destroy", "", color_management_v1_types + 0 },
{ "set_image_description", "ou", color_management_v1_types + 20 },
{ "unset_image_description", "", color_management_v1_types + 0 },
};
WL_EXPORT const struct wl_interface wp_color_management_surface_v1_interface = {
"wp_color_management_surface_v1", 2,
3, wp_color_management_surface_v1_requests,
0, NULL,
};
static const struct wl_message wp_color_management_surface_feedback_v1_requests[] = {
{ "destroy", "", color_management_v1_types + 0 },
{ "get_preferred", "n", color_management_v1_types + 22 },
{ "get_preferred_parametric", "n", color_management_v1_types + 23 },
};
static const struct wl_message wp_color_management_surface_feedback_v1_events[] = {
{ "preferred_changed", "u", color_management_v1_types + 0 },
{ "preferred_changed2", "2uu", color_management_v1_types + 0 },
};
WL_EXPORT const struct wl_interface wp_color_management_surface_feedback_v1_interface = {
"wp_color_management_surface_feedback_v1", 2,
3, wp_color_management_surface_feedback_v1_requests,
2, wp_color_management_surface_feedback_v1_events,
};
static const struct wl_message wp_image_description_creator_icc_v1_requests[] = {
{ "create", "n", color_management_v1_types + 24 },
{ "set_icc_file", "huu", color_management_v1_types + 0 },
};
WL_EXPORT const struct wl_interface wp_image_description_creator_icc_v1_interface = {
"wp_image_description_creator_icc_v1", 2,
2, wp_image_description_creator_icc_v1_requests,
0, NULL,
};
static const struct wl_message wp_image_description_creator_params_v1_requests[] = {
{ "create", "n", color_management_v1_types + 25 },
{ "set_tf_named", "u", color_management_v1_types + 0 },
{ "set_tf_power", "u", color_management_v1_types + 0 },
{ "set_primaries_named", "u", color_management_v1_types + 0 },
{ "set_primaries", "iiiiiiii", color_management_v1_types + 0 },
{ "set_luminances", "uuu", color_management_v1_types + 0 },
{ "set_mastering_display_primaries", "iiiiiiii", color_management_v1_types + 0 },
{ "set_mastering_luminance", "uu", color_management_v1_types + 0 },
{ "set_max_cll", "u", color_management_v1_types + 0 },
{ "set_max_fall", "u", color_management_v1_types + 0 },
};
WL_EXPORT const struct wl_interface wp_image_description_creator_params_v1_interface = {
"wp_image_description_creator_params_v1", 2,
10, wp_image_description_creator_params_v1_requests,
0, NULL,
};
static const struct wl_message wp_image_description_v1_requests[] = {
{ "destroy", "", color_management_v1_types + 0 },
{ "get_information", "n", color_management_v1_types + 26 },
};
static const struct wl_message wp_image_description_v1_events[] = {
{ "failed", "us", color_management_v1_types + 0 },
{ "ready", "u", color_management_v1_types + 0 },
{ "ready2", "2uu", color_management_v1_types + 0 },
};
WL_EXPORT const struct wl_interface wp_image_description_v1_interface = {
"wp_image_description_v1", 2,
2, wp_image_description_v1_requests,
3, wp_image_description_v1_events,
};
static const struct wl_message wp_image_description_info_v1_events[] = {
{ "done", "", color_management_v1_types + 0 },
{ "icc_file", "hu", color_management_v1_types + 0 },
{ "primaries", "iiiiiiii", color_management_v1_types + 0 },
{ "primaries_named", "u", color_management_v1_types + 0 },
{ "tf_power", "u", color_management_v1_types + 0 },
{ "tf_named", "u", color_management_v1_types + 0 },
{ "luminances", "uuu", color_management_v1_types + 0 },
{ "target_primaries", "iiiiiiii", color_management_v1_types + 0 },
{ "target_luminance", "uu", color_management_v1_types + 0 },
{ "target_max_cll", "u", color_management_v1_types + 0 },
{ "target_max_fall", "u", color_management_v1_types + 0 },
};
WL_EXPORT const struct wl_interface wp_image_description_info_v1_interface = {
"wp_image_description_info_v1", 2,
0, NULL,
11, wp_image_description_info_v1_events,
};
static const struct wl_message wp_image_description_reference_v1_requests[] = {
{ "destroy", "", color_management_v1_types + 0 },
};
WL_EXPORT const struct wl_interface wp_image_description_reference_v1_interface = {
"wp_image_description_reference_v1", 1,
1, wp_image_description_reference_v1_requests,
0, NULL,
};
#endif