HDR output for linuxbsd (wayland)

This commit is contained in:
ArchercatNEO
2026-03-13 16:48:58 +00:00
parent 220b0b2f74
commit eee3384e20
14 changed files with 2310 additions and 24 deletions
+1 -1
View File
@@ -2751,7 +2751,7 @@
Display server supports interaction with screen reader or Braille display. [b]Linux (X11/Wayland), macOS, Windows[/b]
</constant>
<constant name="FEATURE_HDR_OUTPUT" value="35" enum="Feature">
Display server supports HDR output. [b]macOS, iOS, visionOS, Windows[/b]
Display server supports HDR output. [b]Linux (Wayland), macOS, iOS, visionOS, Windows[/b]
</constant>
<constant name="ROLE_UNKNOWN" value="0" enum="AccessibilityRole" deprecated="Use [AccessibilityServer] instead.">
Unknown or custom role.
@@ -155,6 +155,7 @@ public:
virtual bool surface_get_needs_resize(SurfaceID p_surface) const override;
virtual void surface_destroy(SurfaceID p_surface) override;
virtual bool is_debug_utils_enabled() const override;
virtual bool is_colorspace_externally_managed() const { return false; }
bool is_colorspace_supported() const;
// Vulkan-only methods.
@@ -3427,9 +3427,10 @@ void RenderingDeviceDriverVulkan::command_buffer_execute_secondary(CommandBuffer
struct FormatCandidate {
VkFormat format;
VkColorSpaceKHR colorspace;
RDD::ColorSpace rdd_colorspace;
};
bool RenderingDeviceDriverVulkan::_determine_swap_chain_format(RenderingContextDriver::SurfaceID p_surface, VkFormat &r_format, VkColorSpaceKHR &r_color_space) {
bool RenderingDeviceDriverVulkan::_determine_swap_chain_format(RenderingContextDriver::SurfaceID p_surface, VkFormat &r_format, VkColorSpaceKHR &r_color_space, RDD::ColorSpace &r_rdd_color_space) {
DEV_ASSERT(p_surface != 0);
RenderingContextDriverVulkan::Surface *surface = (RenderingContextDriverVulkan::Surface *)(p_surface);
@@ -3456,17 +3457,31 @@ bool RenderingDeviceDriverVulkan::_determine_swap_chain_format(RenderingContextD
bool hdr_output_requested = context_driver->surface_get_hdr_output_enabled(p_surface);
// Determine which formats to prefer based on the requested capabilities.
FixedVector<FormatCandidate, 3> preferred_formats;
FixedVector<FormatCandidate, 6> preferred_formats;
if (hdr_output_requested) {
// Our preferred HDR format is 16-bit float + extended linear.
if (context_driver->is_colorspace_externally_managed()) {
// When the colorspace is managed externally to the driver we need to disable its color management.
// The colorspace which disables color management is VK_COLOR_SPACE_PASS_THROUGH_EXT.
preferred_formats.push_back({ VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_PASS_THROUGH_EXT, COLOR_SPACE_REC709_LINEAR });
// If the surface requests HDR output, try to get an HDR format.
if (hdr_output_requested && colorspace_supported) {
// This format is preferred for HDR output.
preferred_formats.push_back({ VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT });
// SRGB_NONLINEAR_KHR is required for some NVIDIA drivers that support HDR output but do not support PASS_THROUGH_EXT.
preferred_formats.push_back({ VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, COLOR_SPACE_REC709_LINEAR });
} else if (colorspace_supported) {
preferred_formats.push_back({ VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT, COLOR_SPACE_REC709_LINEAR });
}
}
// Some drivers may use wp-color-management even when performing SDR.
// https://github.com/godotengine/godot/pull/102987#discussion_r2913373482
if (context_driver->is_colorspace_externally_managed()) {
preferred_formats.push_back({ VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_PASS_THROUGH_EXT, COLOR_SPACE_REC709_NONLINEAR_SRGB });
preferred_formats.push_back({ VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_PASS_THROUGH_EXT, COLOR_SPACE_REC709_NONLINEAR_SRGB });
}
// These formats are always considered for SDR.
preferred_formats.push_back({ VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR });
preferred_formats.push_back({ VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR });
preferred_formats.push_back({ VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, COLOR_SPACE_REC709_NONLINEAR_SRGB });
preferred_formats.push_back({ VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, COLOR_SPACE_REC709_NONLINEAR_SRGB });
bool found = false;
for (const FormatCandidate &candidate : preferred_formats) {
@@ -3474,6 +3489,7 @@ bool RenderingDeviceDriverVulkan::_determine_swap_chain_format(RenderingContextD
if (formats[i].format == candidate.format && formats[i].colorSpace == candidate.colorspace) {
r_format = formats[i].format;
r_color_space = formats[i].colorSpace;
r_rdd_color_space = candidate.rdd_colorspace;
found = true;
break;
}
@@ -3486,11 +3502,11 @@ bool RenderingDeviceDriverVulkan::_determine_swap_chain_format(RenderingContextD
// Warnings for when HDR capabilities are requested but not found.
if (hdr_output_requested) {
if (!colorspace_supported) {
if (!colorspace_supported && !context_driver->is_colorspace_externally_managed()) {
WARN_PRINT("HDR output requested but the vulkan driver does not support VK_EXT_swapchain_colorspace, falling back to SDR.");
}
if (r_color_space == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
if (r_rdd_color_space == COLOR_SPACE_REC709_NONLINEAR_SRGB) {
WARN_PRINT("HDR output requested but no HDR compatible format was found, falling back to SDR.");
}
}
@@ -3687,11 +3703,13 @@ Error RenderingDeviceDriverVulkan::swap_chain_resize(CommandQueueID p_cmd_queue,
// Determine the format and color space for the swap chain.
VkFormat format = VK_FORMAT_UNDEFINED;
VkColorSpaceKHR color_space = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
if (!_determine_swap_chain_format(swap_chain->surface, format, color_space)) {
RDD::ColorSpace rdd_color_space = COLOR_SPACE_REC709_NONLINEAR_SRGB;
if (!_determine_swap_chain_format(swap_chain->surface, format, color_space, rdd_color_space)) {
ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Surface did not return any valid formats.");
} else {
swap_chain->format = format;
swap_chain->color_space = color_space;
swap_chain->rdd_color_space = rdd_color_space;
}
VkSwapchainCreateInfoKHR swap_create_info = {};
@@ -3973,15 +3991,7 @@ RDD::ColorSpace RenderingDeviceDriverVulkan::swap_chain_get_color_space(SwapChai
DEV_ASSERT(p_swap_chain.id != 0);
SwapChain *swap_chain = (SwapChain *)(p_swap_chain.id);
switch (swap_chain->color_space) {
case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR:
return COLOR_SPACE_REC709_NONLINEAR_SRGB;
case VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT:
return COLOR_SPACE_REC709_LINEAR;
default:
DEV_ASSERT(false && "Unknown swap chain color space.");
return COLOR_SPACE_MAX;
}
return swap_chain->rdd_color_space;
}
void RenderingDeviceDriverVulkan::swap_chain_set_max_fps(SwapChainID p_swap_chain, int p_max_fps) {
@@ -418,6 +418,7 @@ private:
RenderingContextDriver::SurfaceID surface = RenderingContextDriver::SurfaceID();
VkFormat format = VK_FORMAT_UNDEFINED;
VkColorSpaceKHR color_space = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
RDD::ColorSpace rdd_color_space = RDD::COLOR_SPACE_REC709_NONLINEAR_SRGB;
TightLocalVector<VkImage> images;
TightLocalVector<VkImageView> image_views;
TightLocalVector<VkSemaphore> present_semaphores;
@@ -432,7 +433,7 @@ private:
#endif
};
bool _determine_swap_chain_format(RenderingContextDriver::SurfaceID p_surface, VkFormat &r_format, VkColorSpaceKHR &r_color_space);
bool _determine_swap_chain_format(RenderingContextDriver::SurfaceID p_surface, VkFormat &r_format, VkColorSpaceKHR &r_color_space, RDD::ColorSpace &r_rdd_color_space);
void _swap_chain_release(SwapChain *p_swap_chain);
public:
+3
View File
@@ -58,6 +58,9 @@ generated_sources = [
generate_from_xml("viewporter", "#thirdparty/wayland-protocols/stable/viewporter/viewporter.xml"),
generate_from_xml("xdg_shell", "#thirdparty/wayland-protocols/stable/xdg-shell/xdg-shell.xml"),
# Staging protocols
generate_from_xml(
"color_management", "#thirdparty/wayland-protocols/staging/color-management/color-management-v1.xml"
),
generate_from_xml("cursor_shape", "#thirdparty/wayland-protocols/staging/cursor-shape/cursor-shape-v1.xml"),
generate_from_xml(
"fractional_scale", "#thirdparty/wayland-protocols/staging/fractional-scale/fractional-scale-v1.xml"
@@ -213,7 +213,8 @@ bool DisplayServerWayland::has_feature(DisplayServerEnums::Feature p_feature) co
case DisplayServerEnums::FEATURE_CLIPBOARD_PRIMARY:
case DisplayServerEnums::FEATURE_SUBWINDOWS:
case DisplayServerEnums::FEATURE_WINDOW_EMBEDDING:
case DisplayServerEnums::FEATURE_SELF_FITTING_WINDOWS: {
case DisplayServerEnums::FEATURE_SELF_FITTING_WINDOWS:
case DisplayServerEnums::FEATURE_HDR_OUTPUT: {
return true;
} break;
@@ -1461,6 +1462,125 @@ void DisplayServerWayland::window_start_resize(DisplayServerEnums::WindowResizeE
wayland_thread.window_start_resize(p_edge, p_window);
}
void DisplayServerWayland::_window_update_hdr_state(WindowData &p_window) {
DisplayServerEnums::WindowID window_id = p_window.id;
#if defined(RD_ENABLED)
if (rendering_context) {
// The `display/window/hdr/request_hdr_output` project setting makes the main window "request" HDR.
// On Windows, this means enable HDR for the main window if it is on an HDR screen.
// Since all screens support HDR on Wayland, we use whether the window "prefers" HDR or not instead.
bool hdr_preferred = p_window.color_profile.target_max_luminance > p_window.color_profile.reference_luminance;
bool hdr_desired = wayland_thread.supports_hdr() && hdr_preferred && p_window.hdr_requested;
if (rendering_context->window_get_hdr_output_enabled(window_id) != hdr_desired) {
rendering_context->window_set_hdr_output_enabled(window_id, hdr_desired);
}
if (hdr_desired) {
rendering_context->window_set_hdr_output_max_luminance(window_id, p_window.color_profile.target_max_luminance);
rendering_context->window_set_hdr_output_reference_luminance(window_id, p_window.color_profile.reference_luminance);
rendering_context->window_set_hdr_output_linear_luminance_scale(window_id, p_window.color_profile.target_max_luminance);
p_window.color_profile.named_primary = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB;
p_window.color_profile.named_transfer_function = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR;
} else {
p_window.color_profile.named_primary = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB;
p_window.color_profile.named_transfer_function = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22;
}
if (p_window.visible) {
MutexLock mutex_lock(wayland_thread.mutex);
wayland_thread.window_set_color_profile(window_id, p_window.color_profile);
}
}
#endif
}
bool DisplayServerWayland::window_is_hdr_output_supported(DisplayServerEnums::WindowID p_window_id) const {
ERR_FAIL_COND_V(!windows.has(p_window_id), false);
const WindowData &wd = windows[p_window_id];
return wd.color_profile.target_max_luminance > wd.color_profile.reference_luminance;
}
void DisplayServerWayland::window_request_hdr_output(const bool p_enabled, DisplayServerEnums::WindowID p_window_id) {
#if defined(RD_ENABLED)
ERR_FAIL_COND_MSG(!(rendering_device && rendering_device->has_feature(RenderingDevice::Features::SUPPORTS_HDR_OUTPUT)), "HDR output is not supported by the rendering device.");
#endif
ERR_FAIL_COND(!windows.has(p_window_id));
WindowData &wd = windows[p_window_id];
wd.hdr_requested = p_enabled;
_window_update_hdr_state(wd);
}
bool DisplayServerWayland::window_is_hdr_output_requested(DisplayServerEnums::WindowID p_window_id) const {
ERR_FAIL_COND_V(!windows.has(p_window_id), false);
const WindowData &wd = windows[p_window_id];
return wd.hdr_requested;
}
bool DisplayServerWayland::window_is_hdr_output_enabled(DisplayServerEnums::WindowID p_window_id) const {
#if defined(RD_ENABLED)
if (rendering_context) {
return rendering_context->window_get_hdr_output_enabled(p_window_id);
}
#endif
return false;
}
void DisplayServerWayland::window_set_hdr_output_reference_luminance(const float p_reference_luminance, DisplayServerEnums::WindowID p_window_id) {
ERR_FAIL_COND(!windows.has(p_window_id));
if (p_reference_luminance >= 0.0f) {
ERR_PRINT_ONCE("Manually setting reference white luminance is not supported on Linux devices, as they provide a user-facing brightness setting that directly controls reference white luminance.");
}
}
float DisplayServerWayland::window_get_hdr_output_reference_luminance(DisplayServerEnums::WindowID p_window_id) const {
return -1.0;
}
float DisplayServerWayland::window_get_hdr_output_current_reference_luminance(DisplayServerEnums::WindowID p_window_id) const {
#if defined(RD_ENABLED)
if (rendering_context) {
return rendering_context->window_get_hdr_output_reference_luminance(p_window_id);
}
#endif
return 0.0f;
}
void DisplayServerWayland::window_set_hdr_output_max_luminance(const float p_max_luminance, DisplayServerEnums::WindowID p_window_id) {
ERR_FAIL_COND(!windows.has(p_window_id));
if (p_max_luminance >= 0.0f) {
ERR_PRINT_ONCE("Manually setting max luminance is not supported on Linux devices as they provide a built-in method of calibrating max luminance without the need for additional apps or tools.");
}
}
float DisplayServerWayland::window_get_hdr_output_max_luminance(DisplayServerEnums::WindowID p_window_id) const {
return -1.0;
}
float DisplayServerWayland::window_get_hdr_output_current_max_luminance(DisplayServerEnums::WindowID p_window_id) const {
#if defined(RD_ENABLED)
if (rendering_context) {
return rendering_context->window_get_hdr_output_max_luminance(p_window_id);
}
#endif
return 0.0f;
}
float DisplayServerWayland::window_get_output_max_linear_value(DisplayServerEnums::WindowID p_window_id) const {
#if defined(RD_ENABLED)
if (rendering_context) {
return rendering_context->window_get_output_max_linear_value(p_window_id);
}
#endif
return 1.0f;
}
void DisplayServerWayland::cursor_set_shape(DisplayServerEnums::CursorShape p_shape) {
ERR_FAIL_INDEX(p_shape, DisplayServerEnums::CURSOR_MAX);
@@ -1715,6 +1835,35 @@ void DisplayServerWayland::process_events() {
wayland_thread.keyboard_echo_keys();
#if defined(RD_ENABLED)
// Enabling HDR may have failed, in which case we need to clear the color profile.
// NOTE: this happens _before_ reading events because the rendering driver is only updated the frame _after_ we try to enable HDR.
if (rendering_device && (!OS::get_singleton()->is_in_low_processor_usage_mode() || RS::get_singleton()->has_changed())) {
for (KeyValue<DisplayServerEnums::WindowID, WindowData> &pair : windows) {
const RD::ColorSpace color_space = rendering_device->screen_get_color_space(pair.key);
bool dirty_srgb = color_space == RDD::COLOR_SPACE_REC709_NONLINEAR_SRGB && pair.value.color_profile.named_transfer_function != WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22;
bool dirty_linear = color_space == RDD::COLOR_SPACE_REC709_LINEAR && pair.value.color_profile.named_transfer_function != WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR;
if (dirty_srgb) {
pair.value.color_profile.named_primary = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB;
pair.value.color_profile.named_transfer_function = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22;
if (pair.value.visible) {
wayland_thread.window_set_color_profile(pair.key, pair.value.color_profile);
}
} else if (dirty_linear) {
pair.value.color_profile.named_primary = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB;
pair.value.color_profile.named_transfer_function = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR;
if (pair.value.visible) {
wayland_thread.window_set_color_profile(pair.key, pair.value.color_profile);
}
}
}
}
#endif
while (wayland_thread.has_message()) {
Ref<WaylandThread::Message> msg = wayland_thread.pop_message();
@@ -1846,6 +1995,15 @@ void DisplayServerWayland::process_events() {
}
continue;
}
Ref<WaylandThread::ColorProfileMessage> color_profile_msg = msg;
if (color_profile_msg.is_valid()) {
WindowData &wd = windows[color_profile_msg->id];
wd.color_profile = color_profile_msg->color_profile;
_window_update_hdr_state(wd);
continue;
}
}
switch (suspend_state) {
@@ -94,6 +94,9 @@ class DisplayServerWayland : public DisplayServer {
DisplayServerEnums::WindowMode mode = DisplayServerEnums::WINDOW_MODE_WINDOWED;
bool hdr_requested = false;
WaylandThread::ColorProfile color_profile;
Callable rect_changed_callback;
Callable window_event_callback;
Callable input_event_callback;
@@ -176,6 +179,8 @@ class DisplayServerWayland : public DisplayServer {
void _update_window_rect(const Rect2i &p_rect, DisplayServerEnums::WindowID p_window_id = DisplayServerEnums::MAIN_WINDOW_ID);
void _window_update_hdr_state(WindowData &p_window);
void try_suspend();
void initialize_tts() const;
@@ -317,6 +322,22 @@ public:
virtual void window_start_drag(DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) override;
virtual void window_start_resize(DisplayServerEnums::WindowResizeEdge p_edge, DisplayServerEnums::WindowID p_window) override;
virtual bool window_is_hdr_output_supported(DisplayServerEnums::WindowID p_window_id = DisplayServerEnums::MAIN_WINDOW_ID) const override;
virtual void window_request_hdr_output(const bool p_enabled, DisplayServerEnums::WindowID p_window_id = DisplayServerEnums::MAIN_WINDOW_ID) override;
virtual bool window_is_hdr_output_requested(DisplayServerEnums::WindowID p_window_id = DisplayServerEnums::MAIN_WINDOW_ID) const override;
virtual bool window_is_hdr_output_enabled(DisplayServerEnums::WindowID p_window_id = DisplayServerEnums::MAIN_WINDOW_ID) const override;
virtual void window_set_hdr_output_reference_luminance(const float p_reference_luminance, DisplayServerEnums::WindowID p_window_id = DisplayServerEnums::MAIN_WINDOW_ID) override;
virtual float window_get_hdr_output_reference_luminance(DisplayServerEnums::WindowID p_window_id = DisplayServerEnums::MAIN_WINDOW_ID) const override;
virtual float window_get_hdr_output_current_reference_luminance(DisplayServerEnums::WindowID p_window_id = DisplayServerEnums::MAIN_WINDOW_ID) const override;
virtual void window_set_hdr_output_max_luminance(const float p_max_luminance, DisplayServerEnums::WindowID p_window_id = DisplayServerEnums::MAIN_WINDOW_ID) override;
virtual float window_get_hdr_output_max_luminance(DisplayServerEnums::WindowID p_window_id = DisplayServerEnums::MAIN_WINDOW_ID) const override;
virtual float window_get_hdr_output_current_max_luminance(DisplayServerEnums::WindowID p_window_id = DisplayServerEnums::MAIN_WINDOW_ID) const override;
virtual float window_get_output_max_linear_value(DisplayServerEnums::WindowID p_window_id = DisplayServerEnums::MAIN_WINDOW_ID) const override;
virtual void cursor_set_shape(DisplayServerEnums::CursorShape p_shape) override;
virtual DisplayServerEnums::CursorShape cursor_get_shape() const override;
virtual void cursor_set_custom_image(const Ref<Resource> &p_cursor, DisplayServerEnums::CursorShape p_shape, const Vector2 &p_hotspot) override;
@@ -37,6 +37,9 @@
class RenderingContextDriverVulkanWayland : public RenderingContextDriverVulkan {
private:
virtual const char *_get_platform_surface_extension() const override final;
// If wp-color-management is supported, we will perform color management externally to the driver.
// If wp-color-management is not supported, the driver would not be able to perform color management anyway.
virtual bool is_colorspace_externally_managed() const override final { return true; }
protected:
SurfaceID surface_create(const void *p_platform_data) override final;
@@ -48,6 +48,7 @@
#include "protocol/linux_dmabuf_v1.gen.h"
#include "protocol/xdg_shell.gen.h"
#include "protocol/color_management.gen.h"
#include "protocol/commit_timing_v1.gen.h"
#include "protocol/cursor_shape.gen.h"
#include "protocol/fifo_v1.gen.h"
@@ -347,6 +348,16 @@ class WaylandEmbedder {
&zwp_linux_surface_synchronization_v1_interface,
&zwp_linux_buffer_release_v1_interface,
// color-management
&wp_color_manager_v1_interface,
&wp_color_management_output_v1_interface,
&wp_color_management_surface_v1_interface,
&wp_color_management_surface_feedback_v1_interface,
&wp_image_description_creator_icc_v1_interface,
&wp_image_description_creator_params_v1_interface,
&wp_image_description_v1_interface,
&wp_image_description_info_v1_interface,
// fractional-scale
&wp_fractional_scale_manager_v1_interface,
&wp_fractional_scale_v1_interface,
@@ -618,6 +618,16 @@ void WaylandThread::_wl_registry_on_global(void *data, struct wl_registry *wl_re
registry->wp_viewporter_name = name;
}
if (strcmp(interface, wp_color_manager_v1_interface.name) == 0) {
registry->wp_color_manager = (struct wp_color_manager_v1 *)wl_registry_bind(wl_registry, name, &wp_color_manager_v1_interface, 1);
registry->wp_color_manager_name = name;
wl_proxy_tag_godot((struct wl_proxy *)registry->wp_color_manager);
WaylandThread::ColorManagementState *color_state = memnew(WaylandThread::ColorManagementState);
wp_color_manager_v1_add_listener(registry->wp_color_manager, &wp_color_manager_listener, color_state);
return;
}
if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) {
registry->wp_cursor_shape_manager = (struct wp_cursor_shape_manager_v1 *)wl_registry_bind(wl_registry, name, &wp_cursor_shape_manager_v1_interface, 1);
registry->wp_cursor_shape_manager_name = name;
@@ -849,6 +859,32 @@ void WaylandThread::_wl_registry_on_global_remove(void *data, struct wl_registry
return;
}
if (name == registry->wp_color_manager_name) {
for (KeyValue<DisplayServerEnums::WindowID, WindowState> &pair : registry->wayland_thread->windows) {
WindowState ws = pair.value;
if (ws.wp_color_management_surface) {
wp_color_management_surface_v1_destroy(ws.wp_color_management_surface);
ws.wp_color_management_surface = nullptr;
}
if (ws.wp_color_management_surface_feedback) {
wp_color_management_surface_feedback_v1_destroy(ws.wp_color_management_surface_feedback);
ws.wp_color_management_surface_feedback = nullptr;
}
}
if (registry->wp_color_manager) {
ColorManagementState *color_state = wp_color_manager_get_state(registry->wp_color_manager);
memdelete(color_state);
wp_color_manager_v1_destroy(registry->wp_color_manager);
registry->wp_color_manager = nullptr;
}
registry->wp_color_manager_name = 0;
}
if (name == registry->wp_cursor_shape_manager_name) {
if (registry->wp_cursor_shape_manager) {
wp_cursor_shape_manager_v1_destroy(registry->wp_cursor_shape_manager);
@@ -2567,6 +2603,139 @@ void WaylandThread::_wl_data_source_on_dnd_finished(void *data, struct wl_data_s
void WaylandThread::_wl_data_source_on_action(void *data, struct wl_data_source *wl_data_source, uint32_t dnd_action) {
}
void WaylandThread::_wp_color_manager_on_supported_intent(void *data, struct wp_color_manager_v1 *wp_color_manager_v1, uint32_t render_intent) {
ColorManagementState *cms = (ColorManagementState *)data;
ERR_FAIL_NULL(cms);
cms->supported_render_intents |= 1 << render_intent;
}
void WaylandThread::_wp_color_manager_on_supported_feature(void *data, struct wp_color_manager_v1 *wp_color_manager_v1, uint32_t feature) {
ColorManagementState *cms = (ColorManagementState *)data;
ERR_FAIL_NULL(cms);
cms->supported_render_feature |= 1 << feature;
}
void WaylandThread::_wp_color_manager_on_supported_tf_named(void *data, struct wp_color_manager_v1 *wp_color_manager_v1, uint32_t tf) {
ColorManagementState *cms = (ColorManagementState *)data;
ERR_FAIL_NULL(cms);
cms->supported_transfer_function |= 1 << tf;
}
void WaylandThread::_wp_color_manager_on_supported_primaries_named(void *data, struct wp_color_manager_v1 *wp_color_manager_v1, uint32_t primaries) {
ColorManagementState *cms = (ColorManagementState *)data;
ERR_FAIL_NULL(cms);
cms->supported_primaries |= 1 << primaries;
}
void WaylandThread::_wp_color_manager_on_done(void *data, struct wp_color_manager_v1 *wp_color_manager_v1) {
ColorManagementState *cms = (ColorManagementState *)data;
ERR_FAIL_NULL(cms);
// We require parametric profiles until we can support reading ICC files.
if ((cms->supported_render_feature & (1 << WP_COLOR_MANAGER_V1_FEATURE_PARAMETRIC)) == 0) {
return;
}
// We require the compositor to support extended linear sRGB.
// sRGB primaries support is assumed.
cms->supports_hdr = cms->supported_transfer_function & (1 << WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR);
}
void WaylandThread::_wp_color_management_surface_feedback_on_preferred_changed(void *data, struct wp_color_management_surface_feedback_v1 *wp_color_management_surface_feedback_v1, uint32_t identity) {
_wp_color_management_surface_feedback_on_preferred_changed2(data, wp_color_management_surface_feedback_v1, 0, identity);
}
void WaylandThread::_wp_color_management_surface_feedback_on_preferred_changed2(void *data, struct wp_color_management_surface_feedback_v1 *wp_color_management_surface_feedback_v1, uint32_t identity_high, uint32_t identity_low) {
struct wp_image_description_v1 *image_description = wp_color_management_surface_feedback_v1_get_preferred_parametric(wp_color_management_surface_feedback_v1);
wp_image_description_v1_add_listener(image_description, &wp_image_description_listener, data);
}
void WaylandThread::_wp_image_description_on_failed(void *data, struct wp_image_description_v1 *image_descrptor, uint32_t cause, const char *msg) {
WARN_PRINT(msg);
}
void WaylandThread::_wp_image_description_on_ready(void *data, struct wp_image_description_v1 *image_descriptor, uint32_t identity) {
_wp_image_description_on_ready2(data, image_descriptor, 0, identity);
}
void WaylandThread::_wp_image_description_on_ready2(void *data, struct wp_image_description_v1 *image_descriptor, uint32_t identity_high, uint32_t identity_low) {
WindowState *ws = (WindowState *)data;
ERR_FAIL_NULL(ws);
struct wp_image_description_info_v1 *image_info = wp_image_description_v1_get_information(image_descriptor);
if (image_info != nullptr) {
ColorProfileMessage *msg = memnew(ColorProfileMessage);
msg->id = ws->id;
msg->wayland_thread = ws->wayland_thread;
wp_image_description_info_v1_add_listener(image_info, &wp_image_description_info_listener, msg);
wp_image_description_v1_destroy(image_descriptor);
}
}
void WaylandThread::_wp_image_description_info_on_done(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1) {
wp_image_description_info_v1_destroy(wp_image_description_info_v1);
ColorProfileMessage *msg = (ColorProfileMessage *)data;
ERR_FAIL_NULL(msg);
msg->wayland_thread->push_message(msg);
}
void WaylandThread::_wp_image_description_info_on_icc_file(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1, int32_t icc, uint32_t icc_size) {
::close(icc);
}
void WaylandThread::_wp_image_description_info_on_primaries(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1, int32_t r_x, int32_t r_y, int32_t g_x, int32_t g_y, int32_t b_x, int32_t b_y, int32_t w_x, int32_t w_y) {
}
void WaylandThread::_wp_image_description_info_on_primaries_named(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1, uint32_t primaries) {
ColorProfileMessage *msg = (ColorProfileMessage *)data;
ERR_FAIL_NULL(msg);
msg->color_profile.named_primary = primaries;
}
void WaylandThread::_wp_image_description_info_on_tf_power(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1, uint32_t eexp) {
}
void WaylandThread::_wp_image_description_info_on_tf_named(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1, uint32_t tf) {
ColorProfileMessage *msg = (ColorProfileMessage *)data;
ERR_FAIL_NULL(msg);
msg->color_profile.named_transfer_function = tf;
}
void WaylandThread::_wp_image_description_info_on_luminances(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1, uint32_t min_lum, uint32_t max_lum, uint32_t reference_lum) {
ColorProfileMessage *msg = (ColorProfileMessage *)data;
ERR_FAIL_NULL(msg);
msg->color_profile.reference_luminance = reference_lum;
}
void WaylandThread::_wp_image_description_info_on_target_primaries(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1, int32_t r_x, int32_t r_y, int32_t g_x, int32_t g_y, int32_t b_x, int32_t b_y, int32_t w_x, int32_t w_y) {
}
void WaylandThread::_wp_image_description_info_on_target_luminance(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1, uint32_t min_lum, uint32_t max_lum) {
ColorProfileMessage *msg = (ColorProfileMessage *)data;
ERR_FAIL_NULL(msg);
// The uint32 is multiplied by 10000 for precision.
msg->color_profile.target_min_luminance = static_cast<float>(min_lum) / 10000;
msg->color_profile.target_max_luminance = max_lum;
}
void WaylandThread::_wp_image_description_info_on_target_max_cll(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1, uint32_t max_cll) {
}
void WaylandThread::_wp_image_description_info_on_target_max_fall(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1, uint32_t max_fall) {
}
void WaylandThread::_wp_fractional_scale_on_preferred_scale(void *data, struct wp_fractional_scale_v1 *wp_fractional_scale_v1, uint32_t scale) {
WindowState *ws = (WindowState *)data;
ERR_FAIL_NULL(ws);
@@ -3479,6 +3648,14 @@ WaylandThread::OfferState *WaylandThread::wp_primary_selection_offer_get_offer_s
return nullptr;
}
WaylandThread::ColorManagementState *WaylandThread::wp_color_manager_get_state(wp_color_manager_v1 *p_color_manager) {
if (p_color_manager && wl_proxy_is_godot((wl_proxy *)p_color_manager)) {
return (ColorManagementState *)wp_color_manager_v1_get_user_data(p_color_manager);
}
return nullptr;
}
WaylandThread::EmbeddingCompositorState *WaylandThread::godot_embedding_compositor_get_state(struct godot_embedding_compositor *p_compositor) {
// NOTE: No need for tag check as it's a "fake" interface - nothing else exposes it.
if (p_compositor) {
@@ -3873,6 +4050,17 @@ void WaylandThread::window_create(DisplayServerEnums::WindowID p_window_id, cons
}
}
if (supports_hdr()) {
ws.wp_color_management_surface_feedback = wp_color_manager_v1_get_surface_feedback(registry.wp_color_manager, ws.wl_surface);
wp_color_management_surface_feedback_v1_add_listener(ws.wp_color_management_surface_feedback, &wp_color_management_surface_feedback_listener, &ws);
struct wp_image_description_v1 *image_description = wp_color_management_surface_feedback_v1_get_preferred_parametric(ws.wp_color_management_surface_feedback);
wp_image_description_v1_add_listener(image_description, &wp_image_description_listener, &ws);
//NOTE: requires vulkan to use the VK_COLOR_SPACE_PASSTHROUGH_EXT colorspace to not raise protocol errors
ws.wp_color_management_surface = wp_color_manager_v1_get_surface(registry.wp_color_manager, ws.wl_surface);
}
bool decorated = false;
#ifdef LIBDECOR_ENABLED
@@ -4052,6 +4240,14 @@ void WaylandThread::window_destroy(DisplayServerEnums::WindowID p_window_id) {
wp_fractional_scale_v1_destroy(ws.wp_fractional_scale);
}
if (ws.wp_color_management_surface) {
wp_color_management_surface_v1_destroy(ws.wp_color_management_surface);
}
if (ws.wp_color_management_surface_feedback) {
wp_color_management_surface_feedback_v1_destroy(ws.wp_color_management_surface_feedback);
}
if (ws.wp_viewport) {
wp_viewport_destroy(ws.wp_viewport);
}
@@ -4656,6 +4852,40 @@ bool WaylandThread::window_get_idle_inhibition(DisplayServerEnums::WindowID p_wi
return ws.wp_idle_inhibitor != nullptr;
}
void WaylandThread::window_set_color_profile(DisplayServerEnums::WindowID p_window_id, ColorProfile p_profile) {
ERR_FAIL_COND(!windows.has(p_window_id));
const WindowState &ws = windows[p_window_id];
if (!ws.wp_color_management_surface) {
return;
}
if (p_profile.named_transfer_function == WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22) {
// This is largely a problem with some HDR monitors applying "glare compensation" or not.
// For monitors which do not apply glare compensation, it is correct to report gamma22 like we would on SDR.
// But there also exist monitors which apply glare compensation, on these monitors it is correct to request compound_2_4.
// Since we have no way to know on which type of monitor we are in, we unset the image description in the hopes
// that future compositor features may include choosing a default transfer function.
wp_color_management_surface_v1_unset_image_description(ws.wp_color_management_surface);
return;
}
ColorManagementState *cms = wp_color_manager_get_state(registry.wp_color_manager);
struct wp_image_description_creator_params_v1 *builder = wp_color_manager_v1_create_parametric_creator(registry.wp_color_manager);
wp_image_description_creator_params_v1_set_primaries_named(builder, p_profile.named_primary);
wp_image_description_creator_params_v1_set_tf_named(builder, p_profile.named_transfer_function);
if ((cms->supported_render_feature & WP_COLOR_MANAGER_V1_FEATURE_SET_LUMINANCES) > 0) {
uint32_t min_luminance = static_cast<uint32_t>(p_profile.target_min_luminance * 10000);
wp_image_description_creator_params_v1_set_luminances(builder, min_luminance, p_profile.target_max_luminance, p_profile.reference_luminance);
}
struct wp_image_description_v1 *image_desc = wp_image_description_creator_params_v1_create(builder);
wp_color_management_surface_v1_set_image_description(ws.wp_color_management_surface, image_desc, WP_COLOR_MANAGER_V1_RENDER_INTENT_PERCEPTUAL);
wp_image_description_v1_destroy(image_desc);
}
WaylandThread::ScreenData WaylandThread::screen_get_data(int p_screen) const {
ERR_FAIL_INDEX_V(p_screen, registry.wl_outputs.size(), ScreenData());
@@ -5351,6 +5581,15 @@ void WaylandThread::primary_set_text(const String &p_text) {
wl_display_roundtrip(wl_display);
}
bool WaylandThread::supports_hdr() const {
ColorManagementState *color_state = wp_color_manager_get_state(registry.wp_color_manager);
if (!color_state) {
return false;
}
return color_state->supports_hdr;
}
void WaylandThread::commit_surfaces() {
for (KeyValue<DisplayServerEnums::WindowID, WindowState> &pair : windows) {
wl_surface_commit(pair.value.wl_surface);
@@ -5709,6 +5948,10 @@ void WaylandThread::destroy() {
zxdg_decoration_manager_v1_destroy(registry.xdg_decoration_manager);
}
if (registry.wp_color_manager) {
wp_color_manager_v1_destroy(registry.wp_color_manager);
}
if (registry.wp_cursor_shape_manager) {
wp_cursor_shape_manager_v1_destroy(registry.wp_cursor_shape_manager);
}
@@ -62,6 +62,7 @@
#include "wayland/protocol/pointer_warp.gen.h"
#include "wayland/protocol/relative_pointer.gen.h"
#undef pointer
#include "wayland/protocol/color_management.gen.h"
#include "wayland/protocol/fractional_scale.gen.h"
#include "wayland/protocol/tablet.gen.h"
#include "wayland/protocol/text_input.gen.h"
@@ -98,6 +99,16 @@ class Image;
class WaylandThread {
public:
struct ColorProfile {
uint32_t named_primary = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB;
uint32_t named_transfer_function = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22;
// The luminances the compositor recommends.
float target_min_luminance = 0;
float target_max_luminance = 0;
float reference_luminance = 0;
};
// Messages used for exchanging information between Godot's and Wayland's thread.
class Message : public RefCounted {
GDSOFTCLASS(Message, RefCounted);
@@ -160,6 +171,14 @@ public:
String text;
};
class ColorProfileMessage : public WindowMessage {
GDSOFTCLASS(ColorProfileMessage, WindowMessage);
public:
WaylandThread *wayland_thread;
ColorProfile color_profile;
};
struct RegistryState {
WaylandThread *wayland_thread;
@@ -196,6 +215,9 @@ public:
struct wp_viewporter *wp_viewporter = nullptr;
uint32_t wp_viewporter_name = 0;
struct wp_color_manager_v1 *wp_color_manager = nullptr;
uint32_t wp_color_manager_name = 0;
struct wp_fractional_scale_manager_v1 *wp_fractional_scale_manager = nullptr;
uint32_t wp_fractional_scale_manager_name = 0;
@@ -323,6 +345,9 @@ public:
// What the compositor is recommending us.
double preferred_fractional_scale = 0;
struct wp_color_management_surface_feedback_v1 *wp_color_management_surface_feedback = nullptr;
struct wp_color_management_surface_v1 *wp_color_management_surface = nullptr;
struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration = nullptr;
struct zwp_idle_inhibitor_v1 *wp_idle_inhibitor = nullptr;
@@ -567,6 +592,15 @@ public:
Point2i hotspot;
};
struct ColorManagementState {
// Compositor features.
uint32_t supported_render_intents = 0;
uint32_t supported_render_feature = 0;
uint32_t supported_transfer_function = 0;
uint32_t supported_primaries = 0;
bool supports_hdr = false;
};
struct EmbeddingCompositorState {
LocalVector<struct godot_embedded_client *> clients;
@@ -744,6 +778,31 @@ private:
static void _xdg_popup_on_repositioned(void *data, struct xdg_popup *xdg_popup, uint32_t token);
// wayland-protocols event handlers.
static void _wp_color_manager_on_supported_intent(void *data, struct wp_color_manager_v1 *wp_color_manager_v1, uint32_t render_intent);
static void _wp_color_manager_on_supported_feature(void *data, struct wp_color_manager_v1 *wp_color_manager_v1, uint32_t feature);
static void _wp_color_manager_on_supported_tf_named(void *data, struct wp_color_manager_v1 *wp_color_manager_v1, uint32_t tf);
static void _wp_color_manager_on_supported_primaries_named(void *data, struct wp_color_manager_v1 *wp_color_manager_v1, uint32_t primaries);
static void _wp_color_manager_on_done(void *data, struct wp_color_manager_v1 *wp_color_manager_v1);
static void _wp_color_management_surface_feedback_on_preferred_changed(void *data, struct wp_color_management_surface_feedback_v1 *wp_color_management_surface_feedback_v1, uint32_t identity);
static void _wp_color_management_surface_feedback_on_preferred_changed2(void *data, struct wp_color_management_surface_feedback_v1 *wp_color_management_surface_feedback_v1, uint32_t identity_high, uint32_t identity_low);
static void _wp_image_description_on_failed(void *data, struct wp_image_description_v1 *wp_image_description_v1, uint32_t cause, const char *msg);
static void _wp_image_description_on_ready(void *data, struct wp_image_description_v1 *wp_image_description_v1, uint32_t identity);
static void _wp_image_description_on_ready2(void *data, struct wp_image_description_v1 *wp_image_description_v1, uint32_t identity_high, uint32_t indentity_low);
static void _wp_image_description_info_on_done(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1);
static void _wp_image_description_info_on_icc_file(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1, int32_t icc, uint32_t icc_size);
static void _wp_image_description_info_on_primaries(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1, int32_t r_x, int32_t r_y, int32_t g_x, int32_t g_y, int32_t b_x, int32_t b_y, int32_t w_x, int32_t w_y);
static void _wp_image_description_info_on_primaries_named(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1, uint32_t primaries);
static void _wp_image_description_info_on_tf_power(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1, uint32_t eexp);
static void _wp_image_description_info_on_tf_named(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1, uint32_t tf);
static void _wp_image_description_info_on_luminances(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1, uint32_t min_lum, uint32_t max_lum, uint32_t reference_lum);
static void _wp_image_description_info_on_target_primaries(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1, int32_t r_x, int32_t r_y, int32_t g_x, int32_t g_y, int32_t b_x, int32_t b_y, int32_t w_x, int32_t w_y);
static void _wp_image_description_info_on_target_luminance(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1, uint32_t min_lum, uint32_t max_lum);
static void _wp_image_description_info_on_target_max_cll(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1, uint32_t max_cll);
static void _wp_image_description_info_on_target_max_fall(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1, uint32_t max_fall);
static void _wp_fractional_scale_on_preferred_scale(void *data, struct wp_fractional_scale_v1 *wp_fractional_scale_v1, uint32_t scale);
static void _wp_relative_pointer_on_relative_motion(void *data, struct zwp_relative_pointer_v1 *wp_relative_pointer_v1, uint32_t uptime_hi, uint32_t uptime_lo, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t dx_unaccel, wl_fixed_t dy_unaccel);
@@ -912,6 +971,39 @@ private:
};
// wayland-protocols event listeners.
static constexpr struct wp_color_manager_v1_listener wp_color_manager_listener = {
.supported_intent = _wp_color_manager_on_supported_intent,
.supported_feature = _wp_color_manager_on_supported_feature,
.supported_tf_named = _wp_color_manager_on_supported_tf_named,
.supported_primaries_named = _wp_color_manager_on_supported_primaries_named,
.done = _wp_color_manager_on_done,
};
static constexpr struct wp_color_management_surface_feedback_v1_listener wp_color_management_surface_feedback_listener = {
.preferred_changed = _wp_color_management_surface_feedback_on_preferred_changed,
.preferred_changed2 = _wp_color_management_surface_feedback_on_preferred_changed2,
};
static constexpr struct wp_image_description_v1_listener wp_image_description_listener = {
.failed = _wp_image_description_on_failed,
.ready = _wp_image_description_on_ready,
.ready2 = _wp_image_description_on_ready2,
};
static constexpr struct wp_image_description_info_v1_listener wp_image_description_info_listener = {
.done = _wp_image_description_info_on_done,
.icc_file = _wp_image_description_info_on_icc_file,
.primaries = _wp_image_description_info_on_primaries,
.primaries_named = _wp_image_description_info_on_primaries_named,
.tf_power = _wp_image_description_info_on_tf_power,
.tf_named = _wp_image_description_info_on_tf_named,
.luminances = _wp_image_description_info_on_luminances,
.target_primaries = _wp_image_description_info_on_target_primaries,
.target_luminance = _wp_image_description_info_on_target_luminance,
.target_max_cll = _wp_image_description_info_on_target_max_cll,
.target_max_fall = _wp_image_description_info_on_target_max_fall,
};
static constexpr struct wp_fractional_scale_v1_listener wp_fractional_scale_listener = {
.preferred_scale = _wp_fractional_scale_on_preferred_scale,
};
@@ -1087,6 +1179,7 @@ public:
static OfferState *wl_data_offer_get_offer_state(struct wl_data_offer *p_offer);
static OfferState *wp_primary_selection_offer_get_offer_state(struct zwp_primary_selection_offer_v1 *p_offer);
static ColorManagementState *wp_color_manager_get_state(wp_color_manager_v1 *p_color_manager);
static EmbeddingCompositorState *godot_embedding_compositor_get_state(struct godot_embedding_compositor *p_compositor);
@@ -1149,6 +1242,9 @@ public:
void window_set_idle_inhibition(DisplayServerEnums::WindowID p_window_id, bool p_enable);
bool window_get_idle_inhibition(DisplayServerEnums::WindowID p_window_id) const;
// Optional - require wp_color_management_v1
void window_set_color_profile(DisplayServerEnums::WindowID p_window_id, ColorProfile p_profile);
ScreenData screen_get_data(int p_screen) const;
int get_screen_count() const;
@@ -1191,6 +1287,8 @@ public:
void primary_set_text(const String &p_text);
bool supports_hdr() const;
void commit_surfaces();
void set_frame();
+3 -1
View File
@@ -1209,7 +1209,7 @@ Files extracted from upstream source:
# wayland-protocols
- Upstream: https://gitlab.freedesktop.org/wayland/wayland-protocols
- Version: 1.46 (6141e1154303dadd5c3e480bc4a16e26f1dcb2af, 2025)
- Version: 1.47 (88223018d1b578d0d8869866da66d9608e05f928, 2025)
- License: MIT
Files extracted from upstream source:
@@ -1220,6 +1220,8 @@ Files extracted from upstream source:
- `stable/viewporter/viewporter.xml`
- `stable/xdg-shell/README`
- `stable/xdg-shell/xdg-shell.xml`
- `staging/color-management/README.md`
- `staging/color-management/color-management-v1.xml`
- `staging/fractional-scale/README`
- `staging/fractional-scale/fractional-scale-v1.xml`
- `staging/xdg-activation/README`
@@ -0,0 +1,11 @@
Color management and HDR protocol
Maintainers:
Sebastian Wick <sebastian at sebastianwick.net>
Pekka Paalanen <pekka.paalanen@collabora.com>
Historical credits not mentioned in the first commit:
- Niels Ole Salscheider, for an early protocol version
https://lists.freedesktop.org/archives/wayland-devel/2014-October/017755.html
File diff suppressed because it is too large Load Diff