core: Add support for HDR and color management protocols (#8715)

This commit is contained in:
UjinT34 2025-01-07 21:32:50 +03:00 committed by GitHub
parent 95542e4488
commit 830350a1f7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 3000 additions and 0 deletions

View file

@ -316,6 +316,8 @@ protocolnew("protocols" "kde-server-decoration" true)
protocolnew("protocols" "wlr-data-control-unstable-v1" true) protocolnew("protocols" "wlr-data-control-unstable-v1" true)
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-focus-grab-v1" true) protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-focus-grab-v1" true)
protocolnew("protocols" "wlr-layer-shell-unstable-v1" true) protocolnew("protocols" "wlr-layer-shell-unstable-v1" true)
protocolnew("protocols" "xx-color-management-v4" true)
protocolnew("protocols" "frog-color-management-v1" true)
protocolnew("protocols" "wayland-drm" true) protocolnew("protocols" "wayland-drm" true)
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-ctm-control-v1" true) protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-ctm-control-v1" true)
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-surface-v1" true) protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-surface-v1" true)

View file

@ -0,0 +1,366 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="frog_color_management_v1">
<copyright>
Copyright © 2023 Joshua Ashton for Valve Software
Copyright © 2023 Xaver Hugl
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.
</copyright>
<description summary="experimental color management protocol">
The aim of this color management extension is to get HDR games working quickly,
and have an easy way to test implementations in the wild before the upstream
protocol is ready to be merged.
For that purpose it's intentionally limited and cut down and does not serve
all uses cases.
</description>
<interface name="frog_color_management_factory_v1" version="1">
<description summary="color management factory">
The color management factory singleton creates color managed surface objects.
</description>
<request name="destroy" type="destructor"></request>
<request name="get_color_managed_surface">
<description summary="create color management interface for surface">
</description>
<arg name="surface" type="object" interface="wl_surface"
summary="target surface" />
<arg name="callback" type="new_id" interface="frog_color_managed_surface"
summary="new color managed surface object" />
</request>
</interface>
<interface name="frog_color_managed_surface" version="1">
<description summary="color managed surface">
Interface for changing surface color management and HDR state.
An implementation must: support every part of the version
of the frog_color_managed_surface interface it exposes.
Including all known enums associated with a given version.
</description>
<request name="destroy" type="destructor">
<description summary="destroy color managed surface">
Destroying the color managed surface resets all known color
state for the surface back to 'undefined' implementation-specific
values.
</description>
</request>
<enum name="transfer_function">
<description summary="known transfer functions">
Extended information on the transfer functions described
here can be found in the Khronos Data Format specification:
https://registry.khronos.org/DataFormat/specs/1.3/dataformat.1.3.html
</description>
<entry name="undefined" value="0"
summary="specifies undefined, implementation-specific handling of the surface's transfer function." />
<entry name="srgb" value="1"
summary="specifies the sRGB non-linear EOTF. An implementation may: display this as Gamma 2.2 for the purposes of being consistent with content rendering across displays, rendering_intent and user expectations." />
<entry name="gamma_22" value="2" summary="specifies gamma 2.2 power curve as the EOTF" />
<entry name="st2084_pq" value="3"
summary="specifies the SMPTE ST2084 Perceptual Quantizer (PQ) EOTF" />
<entry name="scrgb_linear" value="4"
summary="specifies the scRGB (extended sRGB) linear EOTF. Note: Primaries outside the gamut triangle specified can be expressed with negative values for this transfer function." />
</enum>
<request name="set_known_transfer_function">
<description summary="sets a known transfer function for a surface" />
<arg name="transfer_function" type="uint" enum="transfer_function"
summary="transfer function for the surface" />
</request>
<enum name="primaries">
<description summary="known primaries" />
<entry name="undefined" value="0"
summary="specifies undefined, implementation-specific handling" />
<entry name="rec709" value="1" summary="specifies Rec.709/sRGB primaries with D65 white point" />
<entry name="rec2020" value="2"
summary="specifies Rec.2020/HDR10 primaries with D65 white point" />
</enum>
<request name="set_known_container_color_volume">
<description summary="sets the container color volume (primaries) for a surface" />
<arg name="primaries" type="uint" enum="primaries" summary="primaries for the surface" />
</request>
<enum name="render_intent">
<description summary="known render intents">
Extended information on render intents described
here can be found in ICC.1:2022:
https://www.color.org/specification/ICC.1-2022-05.pdf
</description>
<entry name="perceptual" value="0" summary="perceptual" />
</enum>
<request name="set_render_intent">
<description summary="sets the render intent for a surface">
NOTE: On a surface with "perceptual" (default) render intent, handling of the container's
color volume
is implementation-specific, and may differ between different transfer functions it is paired
with:
ie. sRGB + 709 rendering may have it's primaries widened to more of the available display's
gamut
to be be more pleasing for the viewer.
Compared to scRGB Linear + 709 being treated faithfully as 709
(including utilizing negatives out of the 709 gamut triangle)
</description>
<arg name="render_intent" type="uint" enum="render_intent"
summary="render intent for the surface" />
</request>
<request name="set_hdr_metadata">
<description summary="set HDR metadata for a surface">
Forwards HDR metadata from the client to the compositor.
HDR Metadata Infoframe as per CTA 861.G spec.
Usage of this HDR metadata is implementation specific and
outside of the scope of this protocol.
</description>
<arg name="mastering_display_primary_red_x" type="uint">
<description summary="red primary x coordinate">
Mastering Red Color Primary X Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="mastering_display_primary_red_y" type="uint">
<description summary="red primary y coordinate">
Mastering Red Color Primary Y Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="mastering_display_primary_green_x" type="uint">
<description summary="green primary x coordinate">
Mastering Green Color Primary X Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="mastering_display_primary_green_y" type="uint">
<description summary="green primary y coordinate">
Mastering Green Color Primary Y Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="mastering_display_primary_blue_x" type="uint">
<description summary="blue primary x coordinate">
Mastering Blue Color Primary X Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="mastering_display_primary_blue_y" type="uint">
<description summary="blue primary y coordinate">
Mastering Blue Color Primary Y Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="mastering_white_point_x" type="uint">
<description summary="white point x coordinate">
Mastering White Point X Coordinate of the Data.
These are coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="mastering_white_point_y" type="uint">
<description summary="white point y coordinate">
Mastering White Point Y Coordinate of the Data.
These are coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="max_display_mastering_luminance" type="uint">
<description summary="max display mastering luminance">
Max Mastering Display Luminance.
This value is coded as an unsigned 16-bit value in units of 1 cd/m2,
where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2.
</description>
</arg>
<arg name="min_display_mastering_luminance" type="uint">
<description summary="min display mastering luminance">
Min Mastering Display Luminance.
This value is coded as an unsigned 16-bit value in units of
0.0001 cd/m2, where 0x0001 represents 0.0001 cd/m2 and 0xFFFF
represents 6.5535 cd/m2.
</description>
</arg>
<arg name="max_cll" type="uint">
<description summary="max content light level">
Max Content Light Level.
This value is coded as an unsigned 16-bit value in units of 1 cd/m2,
where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2.
</description>
</arg>
<arg name="max_fall" type="uint">
<description summary="max frame average light level">
Max Frame Average Light Level.
This value is coded as an unsigned 16-bit value in units of 1 cd/m2,
where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2.
</description>
</arg>
</request>
<event name="preferred_metadata">
<description summary="preferred metadata for a surface">
Current preferred metadata for a surface.
The application should use this information to tone-map its buffers
to this target before committing.
This metadata does not necessarily correspond to any physical output, but
rather what the compositor thinks would be best for a given surface.
</description>
<arg name="transfer_function" type="uint" enum="transfer_function">
<description summary="output's current transfer function">
Specifies a known transfer function that corresponds to the
output the surface is targeting.
</description>
</arg>
<arg name="output_display_primary_red_x" type="uint">
<description summary="red primary x coordinate">
Output Red Color Primary X Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="output_display_primary_red_y" type="uint">
<description summary="red primary y coordinate">
Output Red Color Primary Y Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="output_display_primary_green_x" type="uint">
<description summary="green primary x coordinate">
Output Green Color Primary X Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="output_display_primary_green_y" type="uint">
<description summary="green primary y coordinate">
Output Green Color Primary Y Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="output_display_primary_blue_x" type="uint">
<description summary="blue primary x coordinate">
Output Blue Color Primary X Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="output_display_primary_blue_y" type="uint">
<description summary="blue primary y coordinate">
Output Blue Color Primary Y Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="output_white_point_x" type="uint">
<description summary="white point x coordinate">
Output White Point X Coordinate of the Data.
These are coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="output_white_point_y" type="uint">
<description summary="white point y coordinate">
Output White Point Y Coordinate of the Data.
These are coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="max_luminance" type="uint">
<description summary="maximum luminance">
Max Output Luminance
The max luminance in nits that the output is capable of rendering in small areas.
Content should: not exceed this value to avoid clipping.
This value is coded as an unsigned 16-bit value in units of 1 cd/m2,
where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2.
</description>
</arg>
<arg name="min_luminance" type="uint">
<description summary="minimum luminance">
Min Output Luminance
The min luminance that the output is capable of rendering.
Content should: not exceed this value to avoid clipping.
This value is coded as an unsigned 16-bit value in units of
0.0001 cd/m2, where 0x0001 represents 0.0001 cd/m2 and 0xFFFF
represents 6.5535 cd/m2.
</description>
</arg>
<arg name="max_full_frame_luminance" type="uint">
<description summary="maximum full frame luminance">
Max Full Frame Luminance
The max luminance in nits that the output is capable of rendering for the
full frame sustained.
This value is coded as an unsigned 16-bit value in units of 1 cd/m2,
where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2.
</description>
</arg>
</event>
</interface>
</protocol>

View file

@ -33,6 +33,8 @@ protocols = [
'wayland-drm.xml', 'wayland-drm.xml',
'wlr-data-control-unstable-v1.xml', 'wlr-data-control-unstable-v1.xml',
'wlr-screencopy-unstable-v1.xml', 'wlr-screencopy-unstable-v1.xml',
'xx-color-management-v4.xml',
'frog-color-management-v1.xml',
hyprland_protocol_dir / 'protocols/hyprland-global-shortcuts-v1.xml', hyprland_protocol_dir / 'protocols/hyprland-global-shortcuts-v1.xml',
hyprland_protocol_dir / 'protocols/hyprland-toplevel-export-v1.xml', hyprland_protocol_dir / 'protocols/hyprland-toplevel-export-v1.xml',
hyprland_protocol_dir / 'protocols/hyprland-focus-grab-v1.xml', hyprland_protocol_dir / 'protocols/hyprland-focus-grab-v1.xml',

File diff suppressed because it is too large Load diff

View file

@ -31,6 +31,7 @@
#include "protocols/XDGShell.hpp" #include "protocols/XDGShell.hpp"
#include "protocols/XDGOutput.hpp" #include "protocols/XDGOutput.hpp"
#include "protocols/SecurityContext.hpp" #include "protocols/SecurityContext.hpp"
#include "protocols/ColorManagement.hpp"
#include "protocols/core/Compositor.hpp" #include "protocols/core/Compositor.hpp"
#include "protocols/core/Subcompositor.hpp" #include "protocols/core/Subcompositor.hpp"
#include "desktop/LayerSurface.hpp" #include "desktop/LayerSurface.hpp"
@ -2985,4 +2986,22 @@ void CCompositor::onNewMonitor(SP<Aquamarine::IOutput> output) {
g_pHyprRenderer->damageMonitor(PNEWMONITOR); g_pHyprRenderer->damageMonitor(PNEWMONITOR);
PNEWMONITOR->onMonitorFrame(); PNEWMONITOR->onMonitorFrame();
if (PROTO::colorManagement && shouldChangePreferredImageDescription())
PROTO::colorManagement->onImagePreferredChanged();
}
SImageDescription CCompositor::getPreferredImageDescription() {
if (!PROTO::colorManagement) {
Debug::log(ERR, "FIXME: color management protocol is not enabled, returning empty image description");
return SImageDescription{};
}
Debug::log(WARN, "FIXME: color management protocol is enabled, determine correct preferred image description");
// should determine some common settings to avoid unnecessary transformations while keeping maximum displayable precision
return SImageDescription{.primaries = NColorPrimaries::BT709};
}
bool CCompositor::shouldChangePreferredImageDescription() {
Debug::log(WARN, "FIXME: color management protocol is enabled and outputs changed, check preferred image description changes");
return false;
} }

View file

@ -23,6 +23,7 @@
#include "helpers/Monitor.hpp" #include "helpers/Monitor.hpp"
#include "desktop/Workspace.hpp" #include "desktop/Workspace.hpp"
#include "desktop/Window.hpp" #include "desktop/Window.hpp"
#include "protocols/types/ColorManagement.hpp"
#include "render/Renderer.hpp" #include "render/Renderer.hpp"
#include "render/OpenGL.hpp" #include "render/OpenGL.hpp"
#include "hyprerror/HyprError.hpp" #include "hyprerror/HyprError.hpp"
@ -169,6 +170,9 @@ class CCompositor {
void updateSuspendedStates(); void updateSuspendedStates();
void onNewMonitor(SP<Aquamarine::IOutput> output); void onNewMonitor(SP<Aquamarine::IOutput> output);
SImageDescription getPreferredImageDescription();
bool shouldChangePreferredImageDescription();
std::string explicitConfigPath; std::string explicitConfigPath;
private: private:

View file

@ -1658,4 +1658,22 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_BOOL, .type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true}, .data = SConfigOptionDescription::SBoolData{true},
}, },
SConfigOptionDescription{
.value = "experimental:wide_color_gamut",
.description = "force wide color gamut for all supported outputs",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "experimental:hdr",
.description = "force static hdr for all supported outputs",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "experimental:xx_color_management_v4",
.description = "enable color management protocol",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
}; };

View file

@ -622,6 +622,10 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("ecosystem:no_update_news", Hyprlang::INT{0}); m_pConfig->addConfigValue("ecosystem:no_update_news", Hyprlang::INT{0});
m_pConfig->addConfigValue("experimental:wide_color_gamut", Hyprlang::INT{0});
m_pConfig->addConfigValue("experimental:hdr", Hyprlang::INT{0});
m_pConfig->addConfigValue("experimental:xx_color_management_v4", Hyprlang::INT{0});
// devices // devices
m_pConfig->addSpecialCategory("device", {"name"}); m_pConfig->addSpecialCategory("device", {"name"});
m_pConfig->addSpecialConfigValue("device", "sensitivity", {0.F}); m_pConfig->addSpecialConfigValue("device", "sensitivity", {0.F});

View file

@ -119,4 +119,5 @@ class CWLSurface {
} listeners; } listeners;
friend class CPointerConstraint; friend class CPointerConstraint;
friend class CXxColorManagerV4;
}; };

View file

@ -55,6 +55,8 @@
#include "../protocols/core/Subcompositor.hpp" #include "../protocols/core/Subcompositor.hpp"
#include "../protocols/core/Output.hpp" #include "../protocols/core/Output.hpp"
#include "../protocols/core/Shm.hpp" #include "../protocols/core/Shm.hpp"
#include "../protocols/ColorManagement.hpp"
#include "../protocols/FrogColorManagement.hpp"
#include "../helpers/Monitor.hpp" #include "../helpers/Monitor.hpp"
#include "../render/Renderer.hpp" #include "../render/Renderer.hpp"
@ -77,11 +79,15 @@ void CProtocolManager::onMonitorModeChange(PHLMONITOR pMonitor) {
PROTO::outputs.erase(pMonitor->szName); PROTO::outputs.erase(pMonitor->szName);
PROTO::outputs.emplace(pMonitor->szName, makeShared<CWLOutputProtocol>(&wl_output_interface, 4, std::format("WLOutput ({})", pMonitor->szName), pMonitor->self.lock())); PROTO::outputs.emplace(pMonitor->szName, makeShared<CWLOutputProtocol>(&wl_output_interface, 4, std::format("WLOutput ({})", pMonitor->szName), pMonitor->self.lock()));
} }
if (PROTO::colorManagement && g_pCompositor->shouldChangePreferredImageDescription())
PROTO::colorManagement->onImagePreferredChanged();
} }
CProtocolManager::CProtocolManager() { CProtocolManager::CProtocolManager() {
static const auto PENABLEEXPLICIT = CConfigValue<Hyprlang::INT>("render:explicit_sync"); static const auto PENABLEEXPLICIT = CConfigValue<Hyprlang::INT>("render:explicit_sync");
static const auto PENABLEXXCM = CConfigValue<Hyprlang::INT>("experimental:xx_color_management_v4");
// Outputs are a bit dumb, we have to agree. // Outputs are a bit dumb, we have to agree.
static auto P = g_pHookSystem->hookDynamic("monitorAdded", [this](void* self, SCallbackInfo& info, std::any param) { static auto P = g_pHookSystem->hookDynamic("monitorAdded", [this](void* self, SCallbackInfo& info, std::any param) {
@ -162,6 +168,11 @@ CProtocolManager::CProtocolManager() {
PROTO::ctm = std::make_unique<CHyprlandCTMControlProtocol>(&hyprland_ctm_control_manager_v1_interface, 1, "CTMControl"); PROTO::ctm = std::make_unique<CHyprlandCTMControlProtocol>(&hyprland_ctm_control_manager_v1_interface, 1, "CTMControl");
PROTO::hyprlandSurface = std::make_unique<CHyprlandSurfaceProtocol>(&hyprland_surface_manager_v1_interface, 1, "HyprlandSurface"); PROTO::hyprlandSurface = std::make_unique<CHyprlandSurfaceProtocol>(&hyprland_surface_manager_v1_interface, 1, "HyprlandSurface");
if (*PENABLEXXCM) {
PROTO::colorManagement = std::make_unique<CColorManagementProtocol>(&xx_color_manager_v4_interface, 1, "ColorManagement");
PROTO::frogColorManagement = std::make_unique<CFrogColorManagementProtocol>(&frog_color_management_factory_v1_interface, 1, "FrogColorManagement");
}
for (auto const& b : g_pCompositor->m_pAqBackend->getImplementations()) { for (auto const& b : g_pCompositor->m_pAqBackend->getImplementations()) {
if (b->type() != Aquamarine::AQ_BACKEND_DRM) if (b->type() != Aquamarine::AQ_BACKEND_DRM)
continue; continue;

View file

@ -0,0 +1,571 @@
#include "ColorManagement.hpp"
#include "Compositor.hpp"
CColorManager::CColorManager(SP<CXxColorManagerV4> resource_) : resource(resource_) {
if (!good())
return;
resource->sendSupportedFeature(XX_COLOR_MANAGER_V4_FEATURE_PARAMETRIC);
resource->sendSupportedFeature(XX_COLOR_MANAGER_V4_FEATURE_EXTENDED_TARGET_VOLUME);
resource->sendSupportedFeature(XX_COLOR_MANAGER_V4_FEATURE_SET_MASTERING_DISPLAY_PRIMARIES);
resource->sendSupportedFeature(XX_COLOR_MANAGER_V4_FEATURE_SET_PRIMARIES);
resource->sendSupportedFeature(XX_COLOR_MANAGER_V4_FEATURE_SET_LUMINANCES);
resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_SRGB);
// resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_PAL_M);
// resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_PAL);
// resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_NTSC);
// resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_GENERIC_FILM);
resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_BT2020);
// resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_CIE1931_XYZ);
// resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_DCI_P3);
// resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_DISPLAY_P3);
// resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_ADOBE_RGB);
// resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_GAMMA22);
resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_SRGB);
resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ);
// resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LINEAR);
resource->sendSupportedIntent(XX_COLOR_MANAGER_V4_RENDER_INTENT_PERCEPTUAL);
// resource->sendSupportedIntent(XX_COLOR_MANAGER_V4_RENDER_INTENT_RELATIVE);
// resource->sendSupportedIntent(XX_COLOR_MANAGER_V4_RENDER_INTENT_ABSOLUTE);
// resource->sendSupportedIntent(XX_COLOR_MANAGER_V4_RENDER_INTENT_RELATIVE_BPC);
resource->setDestroy([](CXxColorManagerV4* r) { LOGM(TRACE, "Destroy xx_color_manager at {:x} (generated default)", (uintptr_t)r); });
resource->setGetOutput([](CXxColorManagerV4* r, uint32_t id, wl_resource* output) {
LOGM(TRACE, "Get output for id={}, output={}", id, (uintptr_t)output);
const auto RESOURCE =
PROTO::colorManagement->m_vOutputs.emplace_back(makeShared<CColorManagementOutput>(makeShared<CXxColorManagementOutputV4>(r->client(), r->version(), id)));
if (!RESOURCE->good()) {
r->noMemory();
PROTO::colorManagement->m_vOutputs.pop_back();
return;
}
RESOURCE->self = RESOURCE;
});
resource->setGetSurface([](CXxColorManagerV4* r, uint32_t id, wl_resource* surface) {
LOGM(TRACE, "Get surface for id={}, surface={}", id, (uintptr_t)surface);
auto SURF = CWLSurfaceResource::fromResource(surface);
if (!SURF) {
LOGM(ERR, "No surface for resource {}", (uintptr_t)surface);
r->error(-1, "Invalid surface (2)");
return;
}
if (SURF->colorManagement) {
r->error(XX_COLOR_MANAGER_V4_ERROR_SURFACE_EXISTS, "CM Surface already exists");
return;
}
const auto RESOURCE =
PROTO::colorManagement->m_vSurfaces.emplace_back(makeShared<CColorManagementSurface>(makeShared<CXxColorManagementSurfaceV4>(r->client(), r->version(), id), SURF));
if (!RESOURCE->good()) {
r->noMemory();
PROTO::colorManagement->m_vSurfaces.pop_back();
return;
}
RESOURCE->self = RESOURCE;
SURF->colorManagement = RESOURCE;
});
resource->setGetFeedbackSurface([](CXxColorManagerV4* r, uint32_t id, wl_resource* surface) {
LOGM(TRACE, "Get feedback surface for id={}, surface={}", id, (uintptr_t)surface);
auto SURF = CWLSurfaceResource::fromResource(surface);
if (!SURF) {
LOGM(ERR, "No surface for resource {}", (uintptr_t)surface);
r->error(-1, "Invalid surface (2)");
return;
}
const auto RESOURCE = PROTO::colorManagement->m_vFeedbackSurfaces.emplace_back(
makeShared<CColorManagementFeedbackSurface>(makeShared<CXxColorManagementFeedbackSurfaceV4>(r->client(), r->version(), id), SURF));
if (!RESOURCE->good()) {
r->noMemory();
PROTO::colorManagement->m_vFeedbackSurfaces.pop_back();
return;
}
RESOURCE->self = RESOURCE;
});
resource->setNewIccCreator([](CXxColorManagerV4* r, uint32_t id) {
LOGM(WARN, "New ICC creator for id={} (unsupported)", id);
r->error(XX_COLOR_MANAGER_V4_ERROR_UNSUPPORTED_FEATURE, "ICC profiles are not supported");
});
resource->setNewParametricCreator([](CXxColorManagerV4* r, uint32_t id) {
LOGM(TRACE, "New parametric creator for id={}", id);
const auto RESOURCE = PROTO::colorManagement->m_vParametricCreators.emplace_back(
makeShared<CColorManagementParametricCreator>(makeShared<CXxImageDescriptionCreatorParamsV4>(r->client(), r->version(), id)));
if (!RESOURCE->good()) {
r->noMemory();
PROTO::colorManagement->m_vParametricCreators.pop_back();
return;
}
RESOURCE->self = RESOURCE;
});
resource->setOnDestroy([this](CXxColorManagerV4* r) { PROTO::colorManagement->destroyResource(this); });
}
bool CColorManager::good() {
return resource->resource();
}
CColorManagementOutput::CColorManagementOutput(SP<CXxColorManagementOutputV4> resource_) : resource(resource_) {
if (!good())
return;
pClient = resource->client();
resource->setDestroy([this](CXxColorManagementOutputV4* r) { PROTO::colorManagement->destroyResource(this); });
resource->setOnDestroy([this](CXxColorManagementOutputV4* r) { PROTO::colorManagement->destroyResource(this); });
resource->setGetImageDescription([this](CXxColorManagementOutputV4* r, uint32_t id) {
LOGM(TRACE, "Get image description for output={}, id={}", (uintptr_t)r, id);
if (imageDescription.valid())
PROTO::colorManagement->destroyResource(imageDescription.get());
const auto RESOURCE = PROTO::colorManagement->m_vImageDescriptions.emplace_back(
makeShared<CColorManagementImageDescription>(makeShared<CXxImageDescriptionV4>(r->client(), r->version(), id)));
if (!RESOURCE->good()) {
r->noMemory();
PROTO::colorManagement->m_vImageDescriptions.pop_back();
return;
}
RESOURCE->self = RESOURCE;
});
}
bool CColorManagementOutput::good() {
return resource->resource();
}
wl_client* CColorManagementOutput::client() {
return pClient;
}
CColorManagementSurface::CColorManagementSurface(SP<CWLSurfaceResource> surface_) : surface(surface_) {
// only for frog cm untill wayland cm is adopted
}
CColorManagementSurface::CColorManagementSurface(SP<CXxColorManagementSurfaceV4> resource_, SP<CWLSurfaceResource> surface_) : surface(surface_), resource(resource_) {
if (!good())
return;
pClient = resource->client();
resource->setDestroy([this](CXxColorManagementSurfaceV4* r) {
LOGM(TRACE, "Destroy xx cm surface {}", (uintptr_t)surface);
PROTO::colorManagement->destroyResource(this);
});
resource->setOnDestroy([this](CXxColorManagementSurfaceV4* r) {
LOGM(TRACE, "Destroy xx cm surface {}", (uintptr_t)surface);
PROTO::colorManagement->destroyResource(this);
});
resource->setSetImageDescription([this](CXxColorManagementSurfaceV4* r, wl_resource* image_description, uint32_t render_intent) {
LOGM(TRACE, "Set image description for surface={}, desc={}, intent={}", (uintptr_t)r, (uintptr_t)image_description, render_intent);
const auto PO = (CXxImageDescriptionV4*)wl_resource_get_user_data(image_description);
if (!PO) { // FIXME check validity
r->error(XX_COLOR_MANAGEMENT_SURFACE_V4_ERROR_IMAGE_DESCRIPTION, "Image description creation failed");
return;
}
if (render_intent != XX_COLOR_MANAGER_V4_RENDER_INTENT_PERCEPTUAL) {
r->error(XX_COLOR_MANAGEMENT_SURFACE_V4_ERROR_RENDER_INTENT, "Unsupported render intent");
return;
}
const auto imageDescription = std::find_if(PROTO::colorManagement->m_vImageDescriptions.begin(), PROTO::colorManagement->m_vImageDescriptions.end(),
[&](const auto& other) { return other->resource()->resource() == image_description; });
if (imageDescription == PROTO::colorManagement->m_vImageDescriptions.end()) {
r->error(XX_COLOR_MANAGEMENT_SURFACE_V4_ERROR_IMAGE_DESCRIPTION, "Image description not found");
return;
}
m_hasImageDescription = true;
m_imageDescription = imageDescription->get()->settings;
});
resource->setUnsetImageDescription([this](CXxColorManagementSurfaceV4* r) {
LOGM(TRACE, "Unset image description for surface={}", (uintptr_t)r);
m_imageDescription = SImageDescription{};
m_hasImageDescription = false;
});
}
bool CColorManagementSurface::good() {
return resource && resource->resource();
}
wl_client* CColorManagementSurface::client() {
return pClient;
}
const SImageDescription& CColorManagementSurface::imageDescription() {
if (!hasImageDescription())
LOGM(WARN, "Reading imageDescription while none set. Returns default or empty values");
return m_imageDescription;
}
bool CColorManagementSurface::hasImageDescription() {
return m_hasImageDescription;
}
CColorManagementFeedbackSurface::CColorManagementFeedbackSurface(SP<CXxColorManagementFeedbackSurfaceV4> resource_, SP<CWLSurfaceResource> surface_) :
surface(surface_), resource(resource_) {
if (!good())
return;
pClient = resource->client();
resource->setDestroy([this](CXxColorManagementFeedbackSurfaceV4* r) {
LOGM(TRACE, "Destroy xx cm feedback surface {}", (uintptr_t)surface);
if (m_currentPreferred.valid())
PROTO::colorManagement->destroyResource(m_currentPreferred.get());
PROTO::colorManagement->destroyResource(this);
});
resource->setOnDestroy([this](CXxColorManagementFeedbackSurfaceV4* r) {
LOGM(TRACE, "Destroy xx cm feedback surface {}", (uintptr_t)surface);
if (m_currentPreferred.valid())
PROTO::colorManagement->destroyResource(m_currentPreferred.get());
PROTO::colorManagement->destroyResource(this);
});
resource->setGetPreferred([this](CXxColorManagementFeedbackSurfaceV4* r, uint32_t id) {
LOGM(TRACE, "Get preferred for id {}", id);
if (m_currentPreferred.valid())
PROTO::colorManagement->destroyResource(m_currentPreferred.get());
const auto RESOURCE = PROTO::colorManagement->m_vImageDescriptions.emplace_back(
makeShared<CColorManagementImageDescription>(makeShared<CXxImageDescriptionV4>(r->client(), r->version(), id), true));
if (!RESOURCE->good()) {
r->noMemory();
PROTO::colorManagement->m_vImageDescriptions.pop_back();
return;
}
RESOURCE->self = RESOURCE;
m_currentPreferred = RESOURCE;
m_currentPreferred->settings = g_pCompositor->getPreferredImageDescription();
RESOURCE->resource()->sendReady(id);
});
}
bool CColorManagementFeedbackSurface::good() {
return resource->resource();
}
wl_client* CColorManagementFeedbackSurface::client() {
return pClient;
}
CColorManagementParametricCreator::CColorManagementParametricCreator(SP<CXxImageDescriptionCreatorParamsV4> resource_) : resource(resource_) {
if (!good())
return;
//
pClient = resource->client();
resource->setOnDestroy([this](CXxImageDescriptionCreatorParamsV4* r) { PROTO::colorManagement->destroyResource(this); });
resource->setCreate([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t id) {
LOGM(TRACE, "Create image description from params for id {}", id);
// FIXME actually check completeness
if (!valuesSet) {
r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INCOMPLETE_SET, "Missing required settings");
return;
}
// FIXME actually check consistency
if (!valuesSet) {
r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INCONSISTENT_SET, "Set is not consistent");
return;
}
const auto RESOURCE = PROTO::colorManagement->m_vImageDescriptions.emplace_back(
makeShared<CColorManagementImageDescription>(makeShared<CXxImageDescriptionV4>(r->client(), r->version(), id)));
if (!RESOURCE->good()) {
r->noMemory();
PROTO::colorManagement->m_vImageDescriptions.pop_back();
return;
}
// FIXME actually check support
if (!valuesSet) {
RESOURCE->resource()->sendFailed(XX_IMAGE_DESCRIPTION_V4_CAUSE_UNSUPPORTED, "unsupported");
return;
}
RESOURCE->self = RESOURCE;
RESOURCE->settings = settings;
RESOURCE->resource()->sendReady(id);
PROTO::colorManagement->destroyResource(this);
});
resource->setSetTfNamed([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t tf) {
LOGM(TRACE, "Set image description transfer function to {}", tf);
if (valuesSet & PC_TF) {
r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Transfer function already set");
return;
}
switch (tf) {
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_SRGB: break;
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ: break;
default: r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_TF, "Unsupported transfer function"); return;
}
settings.transferFunction = (xxColorManagerV4TransferFunction)tf;
valuesSet |= PC_TF;
});
resource->setSetTfPower([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t eexp) {
LOGM(TRACE, "Set image description tf power to {}", eexp);
if (valuesSet & PC_TF_POWER) {
r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Transfer function power already set");
return;
}
settings.transferFunctionPower = eexp / 10000.0f;
valuesSet |= PC_TF_POWER;
});
resource->setSetPrimariesNamed([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t primaries) {
LOGM(TRACE, "Set image description primaries by name {}", primaries);
if (valuesSet & PC_PRIMARIES) {
r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Primaries already set");
return;
}
switch (primaries) {
case XX_COLOR_MANAGER_V4_PRIMARIES_SRGB:
settings.primariesNameSet = true;
settings.primariesNamed = XX_COLOR_MANAGER_V4_PRIMARIES_SRGB;
settings.primaries = NColorPrimaries::BT709;
valuesSet |= PC_PRIMARIES;
break;
case XX_COLOR_MANAGER_V4_PRIMARIES_BT2020:
settings.primariesNameSet = true;
settings.primariesNamed = XX_COLOR_MANAGER_V4_PRIMARIES_BT2020;
settings.primaries = NColorPrimaries::BT2020;
valuesSet |= PC_PRIMARIES;
break;
default: r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_PRIMARIES, "Unsupported primaries");
}
});
resource->setSetPrimaries(
[this](CXxImageDescriptionCreatorParamsV4* r, 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) {
LOGM(TRACE, "Set image description primaries by values r:{},{} g:{},{} b:{},{} w:{},{}", r_x, r_y, g_x, g_y, b_x, b_y, w_x, w_y);
if (valuesSet & PC_PRIMARIES) {
r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Primaries already set");
return;
}
settings.primariesNameSet = false;
settings.primaries =
SImageDescription::SPCPRimaries{.red = {.x = r_x, .y = r_y}, .green = {.x = g_x, .y = g_y}, .blue = {.x = b_x, .y = b_y}, .white = {.x = w_x, .y = w_y}};
valuesSet |= PC_PRIMARIES;
});
resource->setSetLuminances([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t min_lum, uint32_t max_lum, uint32_t reference_lum) {
auto min = min_lum / 10000.0f;
LOGM(TRACE, "Set image description luminances to {} - {} ({})", min, max_lum, reference_lum);
if (valuesSet & PC_LUMINANCES) {
r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Luminances already set");
return;
}
if (max_lum < reference_lum || reference_lum <= min) {
r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_LUMINANCE, "Invalid luminances");
return;
}
settings.luminances = SImageDescription::SPCLuminances{.min = min, .max = max_lum, .reference = reference_lum};
valuesSet |= PC_LUMINANCES;
});
resource->setSetMasteringDisplayPrimaries(
[this](CXxImageDescriptionCreatorParamsV4* r, 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) {
LOGM(TRACE, "Set image description mastering primaries by values r:{},{} g:{},{} b:{},{} w:{},{}", r_x, r_y, g_x, g_y, b_x, b_y, w_x, w_y);
// if (valuesSet & PC_MASTERING_PRIMARIES) {
// r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Mastering primaries already set");
// return;
// }
settings.masteringPrimaries =
SImageDescription::SPCPRimaries{.red = {.x = r_x, .y = r_y}, .green = {.x = g_x, .y = g_y}, .blue = {.x = b_x, .y = b_y}, .white = {.x = w_x, .y = w_y}};
valuesSet |= PC_MASTERING_PRIMARIES;
});
resource->setSetMasteringLuminance([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t min_lum, uint32_t max_lum) {
auto min = min_lum / 10000.0f;
LOGM(TRACE, "Set image description mastering luminances to {} - {}", min, max_lum);
// if (valuesSet & PC_MASTERING_LUMINANCES) {
// r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Mastering luminances already set");
// return;
// }
if (min > 0 && max_lum > 0 && max_lum <= min) {
r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_LUMINANCE, "Invalid luminances");
return;
}
settings.masteringLuminances = SImageDescription::SPCMasteringLuminances{.min = min, .max = max_lum};
valuesSet |= PC_MASTERING_LUMINANCES;
});
resource->setSetMaxCll([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t max_cll) {
LOGM(TRACE, "Set image description max content light level to {}", max_cll);
// if (valuesSet & PC_CLL) {
// r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Max CLL already set");
// return;
// }
settings.maxCLL = max_cll;
valuesSet |= PC_CLL;
});
resource->setSetMaxFall([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t max_fall) {
LOGM(TRACE, "Set image description max frame-average light level to {}", max_fall);
// if (valuesSet & PC_FALL) {
// r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Max FALL already set");
// return;
// }
settings.maxFALL = max_fall;
valuesSet |= PC_FALL;
});
}
bool CColorManagementParametricCreator::good() {
return resource->resource();
}
wl_client* CColorManagementParametricCreator::client() {
return pClient;
}
CColorManagementImageDescription::CColorManagementImageDescription(SP<CXxImageDescriptionV4> resource_, bool allowGetInformation) :
m_resource(resource_), m_allowGetInformation(allowGetInformation) {
if (!good())
return;
pClient = m_resource->client();
m_resource->setDestroy([this](CXxImageDescriptionV4* r) { PROTO::colorManagement->destroyResource(this); });
m_resource->setOnDestroy([this](CXxImageDescriptionV4* r) { PROTO::colorManagement->destroyResource(this); });
m_resource->setGetInformation([this](CXxImageDescriptionV4* r, uint32_t id) {
LOGM(TRACE, "Get image information for image={}, id={}", (uintptr_t)r, id);
if (!m_allowGetInformation) {
r->error(XX_IMAGE_DESCRIPTION_V4_ERROR_NO_INFORMATION, "Image descriptions doesn't allow get_information request");
return;
}
auto RESOURCE = makeShared<CColorManagementImageDescriptionInfo>(makeShared<CXxImageDescriptionInfoV4>(r->client(), r->version(), id), settings);
if (!RESOURCE->good())
r->noMemory();
// CColorManagementImageDescriptionInfo should send everything in the constructor and be ready for destroying at this point
RESOURCE.reset();
});
}
bool CColorManagementImageDescription::good() {
return m_resource->resource();
}
wl_client* CColorManagementImageDescription::client() {
return pClient;
}
SP<CXxImageDescriptionV4> CColorManagementImageDescription::resource() {
return m_resource;
}
CColorManagementImageDescriptionInfo::CColorManagementImageDescriptionInfo(SP<CXxImageDescriptionInfoV4> resource_, const SImageDescription& settings_) :
m_resource(resource_), settings(settings_) {
if (!good())
return;
pClient = m_resource->client();
const auto toProto = [](float value) { return int32_t(std::round(value * 10000)); };
if (settings.iccFd >= 0)
m_resource->sendIccFile(settings.iccFd, settings.iccSize);
// send preferred client paramateres
m_resource->sendPrimaries(toProto(settings.primaries.red.x), toProto(settings.primaries.red.y), toProto(settings.primaries.green.x), toProto(settings.primaries.green.y),
toProto(settings.primaries.blue.x), toProto(settings.primaries.blue.y), toProto(settings.primaries.white.x), toProto(settings.primaries.white.y));
if (settings.primariesNameSet)
m_resource->sendPrimariesNamed(settings.primariesNamed);
m_resource->sendTfPower(std::round(settings.transferFunctionPower * 10000));
m_resource->sendTfNamed(settings.transferFunction);
m_resource->sendLuminances(std::round(settings.luminances.min * 10000), settings.luminances.max, settings.luminances.reference);
// send expexted display paramateres
m_resource->sendTargetPrimaries(toProto(settings.masteringPrimaries.red.x), toProto(settings.masteringPrimaries.red.y), toProto(settings.masteringPrimaries.green.x),
toProto(settings.masteringPrimaries.green.y), toProto(settings.masteringPrimaries.blue.x), toProto(settings.masteringPrimaries.blue.y),
toProto(settings.masteringPrimaries.white.x), toProto(settings.masteringPrimaries.white.y));
m_resource->sendTargetLuminance(std::round(settings.masteringLuminances.min * 10000), settings.masteringLuminances.max);
m_resource->sendTargetMaxCll(settings.maxCLL);
m_resource->sendTargetMaxFall(settings.maxFALL);
m_resource->sendDone();
}
bool CColorManagementImageDescriptionInfo::good() {
return m_resource->resource();
}
wl_client* CColorManagementImageDescriptionInfo::client() {
return pClient;
}
CColorManagementProtocol::CColorManagementProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
;
}
void CColorManagementProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
const auto RESOURCE = m_vManagers.emplace_back(makeShared<CColorManager>(makeShared<CXxColorManagerV4>(client, ver, id)));
if (!RESOURCE->good()) {
wl_client_post_no_memory(client);
m_vManagers.pop_back();
return;
}
LOGM(TRACE, "New xx_color_manager at {:x}", (uintptr_t)RESOURCE.get());
}
void CColorManagementProtocol::onImagePreferredChanged() {
for (auto const& feedback : m_vFeedbackSurfaces) {
feedback->resource->sendPreferredChanged();
}
}
void CColorManagementProtocol::destroyResource(CColorManager* resource) {
std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; });
}
void CColorManagementProtocol::destroyResource(CColorManagementOutput* resource) {
std::erase_if(m_vOutputs, [&](const auto& other) { return other.get() == resource; });
}
void CColorManagementProtocol::destroyResource(CColorManagementSurface* resource) {
std::erase_if(m_vSurfaces, [&](const auto& other) { return other.get() == resource; });
}
void CColorManagementProtocol::destroyResource(CColorManagementFeedbackSurface* resource) {
std::erase_if(m_vFeedbackSurfaces, [&](const auto& other) { return other.get() == resource; });
}
void CColorManagementProtocol::destroyResource(CColorManagementParametricCreator* resource) {
std::erase_if(m_vParametricCreators, [&](const auto& other) { return other.get() == resource; });
}
void CColorManagementProtocol::destroyResource(CColorManagementImageDescription* resource) {
std::erase_if(m_vImageDescriptions, [&](const auto& other) { return other.get() == resource; });
}

View file

@ -0,0 +1,182 @@
#pragma once
#include <memory>
#include <vector>
#include <cstdint>
#include "WaylandProtocol.hpp"
#include "protocols/core/Compositor.hpp"
#include "xx-color-management-v4.hpp"
#include "types/ColorManagement.hpp"
class CColorManager;
class CColorManagementOutput;
class CColorManagementImageDescription;
class CColorManagementProtocol;
class CColorManager {
public:
CColorManager(SP<CXxColorManagerV4> resource_);
bool good();
private:
SP<CXxColorManagerV4> resource;
};
class CColorManagementOutput {
public:
CColorManagementOutput(SP<CXxColorManagementOutputV4> resource_);
bool good();
wl_client* client();
WP<CColorManagementOutput> self;
WP<CColorManagementImageDescription> imageDescription;
private:
SP<CXxColorManagementOutputV4> resource;
wl_client* pClient = nullptr;
friend class CColorManagementProtocol;
friend class CColorManagementImageDescription;
};
class CColorManagementSurface {
public:
CColorManagementSurface(SP<CWLSurfaceResource> surface_); // temporary interface for frog CM
CColorManagementSurface(SP<CXxColorManagementSurfaceV4> resource_, SP<CWLSurfaceResource> surface_);
bool good();
wl_client* client();
WP<CColorManagementSurface> self;
WP<CWLSurfaceResource> surface;
const SImageDescription& imageDescription();
bool hasImageDescription();
private:
SP<CXxColorManagementSurfaceV4> resource;
wl_client* pClient = nullptr;
SImageDescription m_imageDescription;
bool m_hasImageDescription = false;
friend class CFrogColorManagementSurface;
};
class CColorManagementFeedbackSurface {
public:
CColorManagementFeedbackSurface(SP<CXxColorManagementFeedbackSurfaceV4> resource_, SP<CWLSurfaceResource> surface_);
bool good();
wl_client* client();
WP<CColorManagementFeedbackSurface> self;
WP<CWLSurfaceResource> surface;
private:
SP<CXxColorManagementFeedbackSurfaceV4> resource;
wl_client* pClient = nullptr;
WP<CColorManagementImageDescription> m_currentPreferred;
friend class CColorManagementProtocol;
};
class CColorManagementParametricCreator {
public:
CColorManagementParametricCreator(SP<CXxImageDescriptionCreatorParamsV4> resource_);
bool good();
wl_client* client();
WP<CColorManagementParametricCreator> self;
SImageDescription settings;
private:
enum eValuesSet : uint32_t { // NOLINT
PC_TF = (1 << 0),
PC_TF_POWER = (1 << 1),
PC_PRIMARIES = (1 << 2),
PC_LUMINANCES = (1 << 3),
PC_MASTERING_PRIMARIES = (1 << 4),
PC_MASTERING_LUMINANCES = (1 << 5),
PC_CLL = (1 << 6),
PC_FALL = (1 << 7),
};
SP<CXxImageDescriptionCreatorParamsV4> resource;
wl_client* pClient = nullptr;
uint32_t valuesSet = 0; // enum eValuesSet
};
class CColorManagementImageDescription {
public:
CColorManagementImageDescription(SP<CXxImageDescriptionV4> resource_, bool allowGetInformation = false);
bool good();
wl_client* client();
SP<CXxImageDescriptionV4> resource();
WP<CColorManagementImageDescription> self;
SImageDescription settings;
private:
SP<CXxImageDescriptionV4> m_resource;
wl_client* pClient = nullptr;
bool m_allowGetInformation = false;
friend class CColorManagementOutput;
};
class CColorManagementImageDescriptionInfo {
public:
CColorManagementImageDescriptionInfo(SP<CXxImageDescriptionInfoV4> resource_, const SImageDescription& settings_);
bool good();
wl_client* client();
private:
SP<CXxImageDescriptionInfoV4> m_resource;
wl_client* pClient = nullptr;
SImageDescription settings;
};
class CColorManagementProtocol : public IWaylandProtocol {
public:
CColorManagementProtocol(const wl_interface* iface, const int& ver, const std::string& name);
virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id);
void onImagePreferredChanged();
private:
void destroyResource(CColorManager* resource);
void destroyResource(CColorManagementOutput* resource);
void destroyResource(CColorManagementSurface* resource);
void destroyResource(CColorManagementFeedbackSurface* resource);
void destroyResource(CColorManagementParametricCreator* resource);
void destroyResource(CColorManagementImageDescription* resource);
std::vector<SP<CColorManager>> m_vManagers;
std::vector<SP<CColorManagementOutput>> m_vOutputs;
std::vector<SP<CColorManagementSurface>> m_vSurfaces;
std::vector<SP<CColorManagementFeedbackSurface>> m_vFeedbackSurfaces;
std::vector<SP<CColorManagementParametricCreator>> m_vParametricCreators;
std::vector<SP<CColorManagementImageDescription>> m_vImageDescriptions;
friend class CColorManager;
friend class CColorManagementOutput;
friend class CColorManagementSurface;
friend class CColorManagementFeedbackSurface;
friend class CColorManagementParametricCreator;
friend class CColorManagementImageDescription;
friend class CFrogColorManagementSurface;
};
namespace PROTO {
inline UP<CColorManagementProtocol> colorManagement;
};

View file

@ -0,0 +1,159 @@
#include "FrogColorManagement.hpp"
#include "protocols/ColorManagement.hpp"
#include "protocols/core/Subcompositor.hpp"
CFrogColorManager::CFrogColorManager(SP<CFrogColorManagementFactoryV1> resource_) : resource(resource_) {
if (!good())
return;
resource->setDestroy([](CFrogColorManagementFactoryV1* r) { LOGM(TRACE, "Destroy frog_color_management at {:x} (generated default)", (uintptr_t)r); });
resource->setOnDestroy([this](CFrogColorManagementFactoryV1* r) { PROTO::frogColorManagement->destroyResource(this); });
resource->setGetColorManagedSurface([](CFrogColorManagementFactoryV1* r, wl_resource* surface, uint32_t id) {
LOGM(TRACE, "Get surface for id={}, surface={}", id, (uintptr_t)surface);
auto SURF = CWLSurfaceResource::fromResource(surface);
if (!SURF) {
LOGM(ERR, "No surface for resource {}", (uintptr_t)surface);
r->error(-1, "Invalid surface (2)");
return;
}
if (SURF->role->role() == SURFACE_ROLE_SUBSURFACE)
SURF = ((CSubsurfaceRole*)SURF->role.get())->subsurface->t1Parent();
const auto RESOURCE = PROTO::frogColorManagement->m_vSurfaces.emplace_back(
makeShared<CFrogColorManagementSurface>(makeShared<CFrogColorManagedSurface>(r->client(), r->version(), id), SURF));
if (!RESOURCE->good()) {
r->noMemory();
PROTO::frogColorManagement->m_vSurfaces.pop_back();
return;
}
RESOURCE->self = RESOURCE;
});
}
bool CFrogColorManager::good() {
return resource->resource();
}
CFrogColorManagementSurface::CFrogColorManagementSurface(SP<CFrogColorManagedSurface> resource_, SP<CWLSurfaceResource> surface_) : surface(surface_), resource(resource_) {
if (!good())
return;
pClient = resource->client();
if (!surface->colorManagement.valid()) {
const auto RESOURCE = PROTO::colorManagement->m_vSurfaces.emplace_back(makeShared<CColorManagementSurface>(surface_));
if (!RESOURCE) {
resource->noMemory();
PROTO::colorManagement->m_vSurfaces.pop_back();
return;
}
RESOURCE->self = RESOURCE;
surface->colorManagement = RESOURCE;
resource->setOnDestroy([this](CFrogColorManagedSurface* r) {
LOGM(TRACE, "Destroy frog cm and xx cm for surface {}", (uintptr_t)surface);
if (surface.valid())
PROTO::colorManagement->destroyResource(surface->colorManagement.get());
PROTO::frogColorManagement->destroyResource(this);
});
} else
resource->setOnDestroy([this](CFrogColorManagedSurface* r) {
LOGM(TRACE, "Destroy frog cm surface {}", (uintptr_t)surface);
PROTO::frogColorManagement->destroyResource(this);
});
resource->setDestroy([this](CFrogColorManagedSurface* r) {
LOGM(TRACE, "Destroy frog cm surface {}", (uintptr_t)surface);
PROTO::frogColorManagement->destroyResource(this);
});
resource->setSetKnownTransferFunction([this](CFrogColorManagedSurface* r, frogColorManagedSurfaceTransferFunction tf) {
LOGM(TRACE, "Set frog cm transfer function {} for {}", (uint32_t)tf, surface->id());
switch (tf) {
case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_ST2084_PQ:
surface->colorManagement->m_imageDescription.transferFunction = XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ;
break;
;
case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_GAMMA_22:
if (pqIntentSent) {
LOGM(TRACE,
"FIXME: assuming broken enum value 2 (FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_GAMMA_22) referring to eotf value 2 (TRANSFER_FUNCTION_ST2084_PQ)");
surface->colorManagement->m_imageDescription.transferFunction = XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ;
break;
}; // intended fall through
case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_UNDEFINED:
case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_SCRGB_LINEAR: LOGM(TRACE, std::format("FIXME: add tf support for {}", (uint32_t)tf)); // intended fall through
case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_SRGB:
surface->colorManagement->m_imageDescription.transferFunction = XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_SRGB;
surface->colorManagement->m_hasImageDescription = true;
}
});
resource->setSetKnownContainerColorVolume([this](CFrogColorManagedSurface* r, frogColorManagedSurfacePrimaries primariesName) {
LOGM(TRACE, "Set frog cm primaries {}", (uint32_t)primariesName);
switch (primariesName) {
case FROG_COLOR_MANAGED_SURFACE_PRIMARIES_UNDEFINED:
case FROG_COLOR_MANAGED_SURFACE_PRIMARIES_REC709: surface->colorManagement->m_imageDescription.primaries = NColorPrimaries::BT709; break;
case FROG_COLOR_MANAGED_SURFACE_PRIMARIES_REC2020: surface->colorManagement->m_imageDescription.primaries = NColorPrimaries::BT2020; break;
}
surface->colorManagement->m_hasImageDescription = true;
});
resource->setSetRenderIntent([this](CFrogColorManagedSurface* r, frogColorManagedSurfaceRenderIntent intent) {
LOGM(TRACE, "Set frog cm intent {}", (uint32_t)intent);
pqIntentSent = intent == FROG_COLOR_MANAGED_SURFACE_RENDER_INTENT_PERCEPTUAL;
surface->colorManagement->m_hasImageDescription = true;
});
resource->setSetHdrMetadata([this](CFrogColorManagedSurface* r, uint32_t r_x, uint32_t r_y, uint32_t g_x, uint32_t g_y, uint32_t b_x, uint32_t b_y, uint32_t w_x, uint32_t w_y,
uint32_t max_lum, uint32_t min_lum, uint32_t cll, uint32_t fall) {
LOGM(TRACE, "Set frog primaries r:{},{} g:{},{} b:{},{} w:{},{} luminances {} - {} cll {} fall {}", r_x, r_y, g_x, g_y, b_x, b_y, w_x, w_y, min_lum, max_lum, cll, fall);
surface->colorManagement->m_imageDescription.masteringPrimaries = SImageDescription::SPCPRimaries{.red = {.x = r_x / 50000.0f, .y = r_y / 50000.0f},
.green = {.x = g_x / 50000.0f, .y = g_y / 50000.0f},
.blue = {.x = b_x / 50000.0f, .y = b_y / 50000.0f},
.white = {.x = w_x / 50000.0f, .y = w_y / 50000.0f}};
surface->colorManagement->m_imageDescription.masteringLuminances.min = min_lum / 10000.0f;
surface->colorManagement->m_imageDescription.masteringLuminances.max = max_lum;
surface->colorManagement->m_imageDescription.maxCLL = cll;
surface->colorManagement->m_imageDescription.maxFALL = fall;
surface->colorManagement->m_hasImageDescription = true;
});
}
bool CFrogColorManagementSurface::good() {
return resource->resource();
}
wl_client* CFrogColorManagementSurface::client() {
return pClient;
}
CFrogColorManagementProtocol::CFrogColorManagementProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
;
}
void CFrogColorManagementProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
const auto RESOURCE = m_vManagers.emplace_back(makeShared<CFrogColorManager>(makeShared<CFrogColorManagementFactoryV1>(client, ver, id)));
if (!RESOURCE->good()) {
wl_client_post_no_memory(client);
m_vManagers.pop_back();
return;
}
LOGM(TRACE, "New frog_color_management at {:x}", (uintptr_t)RESOURCE.get());
}
void CFrogColorManagementProtocol::destroyResource(CFrogColorManager* resource) {
std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; });
}
void CFrogColorManagementProtocol::destroyResource(CFrogColorManagementSurface* resource) {
std::erase_if(m_vSurfaces, [&](const auto& other) { return other.get() == resource; });
}

View file

@ -0,0 +1,55 @@
#pragma once
#include <memory>
#include <cstdint>
#include "WaylandProtocol.hpp"
#include "protocols/core/Compositor.hpp"
#include "frog-color-management-v1.hpp"
class CFrogColorManager {
public:
CFrogColorManager(SP<CFrogColorManagementFactoryV1> resource_);
bool good();
private:
SP<CFrogColorManagementFactoryV1> resource;
};
class CFrogColorManagementSurface {
public:
CFrogColorManagementSurface(SP<CFrogColorManagedSurface> resource_, SP<CWLSurfaceResource> surface_);
bool good();
wl_client* client();
WP<CFrogColorManagementSurface> self;
WP<CWLSurfaceResource> surface;
bool pqIntentSent = false;
private:
SP<CFrogColorManagedSurface> resource;
wl_client* pClient = nullptr;
};
class CFrogColorManagementProtocol : public IWaylandProtocol {
public:
CFrogColorManagementProtocol(const wl_interface* iface, const int& ver, const std::string& name);
virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id);
private:
void destroyResource(CFrogColorManager* resource);
void destroyResource(CFrogColorManagementSurface* resource);
std::vector<SP<CFrogColorManager>> m_vManagers;
std::vector<SP<CFrogColorManagementSurface>> m_vSurfaces;
friend class CFrogColorManager;
friend class CFrogColorManagementSurface;
};
namespace PROTO {
inline UP<CFrogColorManagementProtocol> frogColorManagement;
};

View file

@ -25,6 +25,8 @@ class CWLSurfaceResource;
class CWLSubsurfaceResource; class CWLSubsurfaceResource;
class CViewportResource; class CViewportResource;
class CDRMSyncobjSurfaceResource; class CDRMSyncobjSurfaceResource;
class CColorManagementSurface;
class CFrogColorManagementSurface;
class CWLCallbackResource { class CWLCallbackResource {
public: public:
@ -121,6 +123,7 @@ class CWLSurfaceResource {
SP<ISurfaceRole> role; SP<ISurfaceRole> role;
WP<CViewportResource> viewportResource; WP<CViewportResource> viewportResource;
WP<CDRMSyncobjSurfaceResource> syncobj; // may not be present WP<CDRMSyncobjSurfaceResource> syncobj; // may not be present
WP<CColorManagementSurface> colorManagement;
void breadthfirst(std::function<void(SP<CWLSurfaceResource>, const Vector2D&, void*)> fn, void* data); void breadthfirst(std::function<void(SP<CWLSurfaceResource>, const Vector2D&, void*)> fn, void* data);
CRegion accumulateCurrentBufferDamage(); CRegion accumulateCurrentBufferDamage();

View file

@ -0,0 +1,47 @@
#pragma once
#include "xx-color-management-v4.hpp"
struct SImageDescription {
int iccFd = -1;
uint32_t iccSize = 0;
xxColorManagerV4TransferFunction transferFunction = XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_SRGB;
float transferFunctionPower = 1.0f;
bool primariesNameSet = false;
xxColorManagerV4Primaries primariesNamed = XX_COLOR_MANAGER_V4_PRIMARIES_SRGB;
// primaries are stored as FP values with the same scale as standard defines (0.0 - 1.0)
// wayland protocol expects int32_t values multiplied by 10000
// drm expects uint16_t values multiplied by 50000
// frog protocol expects drm values
struct SPCPRimaries {
struct {
float x = 0;
float y = 0;
} red, green, blue, white;
} primaries, masteringPrimaries;
// luminances in cd/m²
// protos and drm expect min * 10000
struct SPCLuminances {
float min = 0.2; // 0.2 cd/m²
uint32_t max = 80; // 80 cd/m²
uint32_t reference = 80; // 80 cd/m²
} luminances;
struct SPCMasteringLuminances {
float min = 0;
uint32_t max = 0;
} masteringLuminances;
uint32_t maxCLL = 0;
uint32_t maxFALL = 0;
};
namespace NColorPrimaries {
static const auto BT709 =
SImageDescription::SPCPRimaries{.red = {.x = 0.64, .y = 0.33}, .green = {.x = 0.30, .y = 0.60}, .blue = {.x = 0.15, .y = 0.06}, .white = {.x = 0.3127, .y = 0.3290}};
static const auto BT2020 =
SImageDescription::SPCPRimaries{.red = {.x = 0.708, .y = 0.292}, .green = {.x = 0.170, .y = 0.797}, .blue = {.x = 0.131, .y = 0.046}, .white = {.x = 0.3127, .y = 0.3290}};
}

View file

@ -25,6 +25,7 @@
#include "pass/RendererHintsPassElement.hpp" #include "pass/RendererHintsPassElement.hpp"
#include "pass/SurfacePassElement.hpp" #include "pass/SurfacePassElement.hpp"
#include "debug/Log.hpp" #include "debug/Log.hpp"
#include "protocols/ColorManagement.hpp"
#include <hyprutils/utils/ScopeGuard.hpp> #include <hyprutils/utils/ScopeGuard.hpp>
using namespace Hyprutils::Utils; using namespace Hyprutils::Utils;
@ -1374,6 +1375,82 @@ void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor) {
} }
} }
static const auto BT709 = Aquamarine::IOutput::SChromaticityCoords{
.red = Aquamarine::IOutput::xy{.x = 0.64, .y = 0.33},
.green = Aquamarine::IOutput::xy{.x = 0.30, .y = 0.60},
.blue = Aquamarine::IOutput::xy{.x = 0.15, .y = 0.06},
.white = Aquamarine::IOutput::xy{.x = 0.3127, .y = 0.3290},
};
static hdr_output_metadata createHDRMetadata(uint8_t eotf, Aquamarine::IOutput::SParsedEDID edid) {
if (eotf == 0)
return hdr_output_metadata{.hdmi_metadata_type1 = hdr_metadata_infoframe{.eotf = 0}}; // empty metadata for SDR
const auto toNits = [](float value) { return uint16_t(std::round(value)); };
const auto to16Bit = [](float value) { return uint16_t(std::round(value * 50000)); };
const auto colorimetry = edid.chromaticityCoords.value_or(BT709);
Debug::log(TRACE, "ColorManagement primaries {},{} {},{} {},{} {},{}", colorimetry.red.x, colorimetry.red.y, colorimetry.green.x, colorimetry.green.y, colorimetry.blue.x,
colorimetry.blue.y, colorimetry.white.x, colorimetry.white.y);
Debug::log(TRACE, "ColorManagement max avg {}, min {}, max {}", edid.hdrMetadata->desiredMaxFrameAverageLuminance, edid.hdrMetadata->desiredContentMinLuminance,
edid.hdrMetadata->desiredContentMaxLuminance);
return hdr_output_metadata{
.metadata_type = 0,
.hdmi_metadata_type1 =
hdr_metadata_infoframe{
.eotf = eotf,
.metadata_type = 0,
.display_primaries =
{
{.x = to16Bit(colorimetry.red.x), .y = to16Bit(colorimetry.red.y)},
{.x = to16Bit(colorimetry.green.x), .y = to16Bit(colorimetry.green.y)},
{.x = to16Bit(colorimetry.blue.x), .y = to16Bit(colorimetry.blue.y)},
},
.white_point = {.x = to16Bit(colorimetry.white.x), .y = to16Bit(colorimetry.white.y)},
.max_display_mastering_luminance = toNits(edid.hdrMetadata->desiredMaxFrameAverageLuminance),
.min_display_mastering_luminance = toNits(edid.hdrMetadata->desiredContentMinLuminance * 10000),
.max_cll = toNits(edid.hdrMetadata->desiredMaxFrameAverageLuminance),
.max_fall = toNits(edid.hdrMetadata->desiredMaxFrameAverageLuminance),
},
};
}
static hdr_output_metadata createHDRMetadata(SImageDescription settings, Aquamarine::IOutput::SParsedEDID edid) {
if (settings.transferFunction != XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ)
return hdr_output_metadata{.hdmi_metadata_type1 = hdr_metadata_infoframe{.eotf = 0}}; // empty metadata for SDR
const auto toNits = [](uint32_t value) { return uint16_t(std::round(value)); };
const auto to16Bit = [](uint32_t value) { return uint16_t(std::round(value * 50000)); };
auto colorimetry = settings.primaries;
auto luminances = settings.masteringLuminances.max > 0 ?
settings.masteringLuminances :
SImageDescription::SPCMasteringLuminances{.min = edid.hdrMetadata->desiredContentMinLuminance, .max = edid.hdrMetadata->desiredContentMaxLuminance};
Debug::log(TRACE, "ColorManagement primaries {},{} {},{} {},{} {},{}", colorimetry.red.x, colorimetry.red.y, colorimetry.green.x, colorimetry.green.y, colorimetry.blue.x,
colorimetry.blue.y, colorimetry.white.x, colorimetry.white.y);
Debug::log(TRACE, "ColorManagement min {}, max {}, cll {}, fall {}", luminances.min, luminances.max, settings.maxCLL, settings.maxFALL);
return hdr_output_metadata{
.metadata_type = 0,
.hdmi_metadata_type1 =
hdr_metadata_infoframe{
.eotf = 2,
.metadata_type = 0,
.display_primaries =
{
{.x = to16Bit(colorimetry.red.x), .y = to16Bit(colorimetry.red.y)},
{.x = to16Bit(colorimetry.green.x), .y = to16Bit(colorimetry.green.y)},
{.x = to16Bit(colorimetry.blue.x), .y = to16Bit(colorimetry.blue.y)},
},
.white_point = {.x = to16Bit(colorimetry.white.x), .y = to16Bit(colorimetry.white.y)},
.max_display_mastering_luminance = toNits(luminances.max),
.min_display_mastering_luminance = toNits(luminances.min * 10000),
.max_cll = toNits(settings.maxCLL),
.max_fall = toNits(settings.maxFALL),
},
};
}
bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) { bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) {
// apply timelines for explicit sync // apply timelines for explicit sync
// save inFD otherwise reset will reset it // save inFD otherwise reset will reset it
@ -1382,6 +1459,26 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) {
if (inFD >= 0) if (inFD >= 0)
pMonitor->output->state->setExplicitInFence(inFD); pMonitor->output->state->setExplicitInFence(inFD);
static auto PWIDE = CConfigValue<Hyprlang::INT>("experimental:wide_color_gamut");
if (pMonitor->output->state->state().wideColorGamut != *PWIDE)
Debug::log(TRACE, "Setting wide color gamut {}", *PWIDE ? "on" : "off");
pMonitor->output->state->setWideColorGamut(*PWIDE);
static auto PHDR = CConfigValue<Hyprlang::INT>("experimental:hdr");
Debug::log(TRACE, "ColorManagement supportsBT2020 {}, supportsPQ {}", pMonitor->output->parsedEDID.supportsBT2020, pMonitor->output->parsedEDID.hdrMetadata->supportsPQ);
if (pMonitor->output->parsedEDID.supportsBT2020 && pMonitor->output->parsedEDID.hdrMetadata->supportsPQ) {
if (pMonitor->activeWorkspace && pMonitor->activeWorkspace->m_bHasFullscreenWindow && pMonitor->activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN) {
const auto WINDOW = pMonitor->activeWorkspace->getFullscreenWindow();
const auto SURF = WINDOW->m_pWLSurface->resource();
if (SURF->colorManagement.valid() && SURF->colorManagement->hasImageDescription())
pMonitor->output->state->setHDRMetadata(createHDRMetadata(SURF->colorManagement.get()->imageDescription(), pMonitor->output->parsedEDID));
else
pMonitor->output->state->setHDRMetadata(*PHDR ? createHDRMetadata(2, pMonitor->output->parsedEDID) : createHDRMetadata(0, pMonitor->output->parsedEDID));
} else
pMonitor->output->state->setHDRMetadata(*PHDR ? createHDRMetadata(2, pMonitor->output->parsedEDID) : createHDRMetadata(0, pMonitor->output->parsedEDID));
}
if (pMonitor->ctmUpdated) { if (pMonitor->ctmUpdated) {
pMonitor->ctmUpdated = false; pMonitor->ctmUpdated = false;
pMonitor->output->state->setCTM(pMonitor->ctm); pMonitor->output->state->setCTM(pMonitor->ctm);

View file

@ -79,6 +79,8 @@ void CSurfacePassElement::draw(const CRegion& damage) {
DELTALESSTHAN(windowBox.height, data.surface->current.bufferSize.y, 3) /* off by one-or-two */ && DELTALESSTHAN(windowBox.height, data.surface->current.bufferSize.y, 3) /* off by one-or-two */ &&
(!data.pWindow || (!data.pWindow->m_vRealSize->isBeingAnimated() && !INTERACTIVERESIZEINPROGRESS)) /* not window or not animated/resizing */; (!data.pWindow || (!data.pWindow->m_vRealSize->isBeingAnimated() && !INTERACTIVERESIZEINPROGRESS)) /* not window or not animated/resizing */;
if (data.surface->colorManagement.valid())
Debug::log(TRACE, "FIXME: rendering surface with color management enabled, should apply necessary transformations");
g_pHyprRenderer->calculateUVForSurface(data.pWindow, data.surface, data.pMonitor->self.lock(), data.mainSurface, windowBox.size(), PROJSIZEUNSCALED, MISALIGNEDFSV1); g_pHyprRenderer->calculateUVForSurface(data.pWindow, data.surface, data.pMonitor->self.lock(), data.mainSurface, windowBox.size(), PROJSIZEUNSCALED, MISALIGNEDFSV1);
// check for fractional scale surfaces misaligning the buffer size // check for fractional scale surfaces misaligning the buffer size