diff --git a/CMakeLists.txt b/CMakeLists.txt index 80df37dd..cb5ae042 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -129,7 +129,7 @@ pkg_check_modules( xkbcommon uuid wayland-server>=1.22.90 - wayland-protocols + wayland-protocols>=1.41 cairo pango pangocairo @@ -378,6 +378,7 @@ protocolnew("staging/xdg-dialog" "xdg-dialog-v1" false) protocolnew("staging/single-pixel-buffer" "single-pixel-buffer-v1" false) protocolnew("staging/security-context" "security-context-v1" false) protocolnew("staging/content-type" "content-type-v1" false) +protocolnew("staging/color-management" "color-management-v1" false) protocolwayland() diff --git a/protocols/meson.build b/protocols/meson.build index f0a6a7e6..97594f06 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -1,6 +1,6 @@ wayland_protos = dependency( 'wayland-protocols', - version: '>=1.40', + version: '>=1.41', fallback: 'wayland-protocols', default_options: ['tests=false'], ) @@ -71,6 +71,7 @@ protocols = [ wayland_protocol_dir / 'staging/single-pixel-buffer/single-pixel-buffer-v1.xml', wayland_protocol_dir / 'staging/security-context/security-context-v1.xml', wayland_protocol_dir / 'staging/content-type/content-type-v1.xml', + wayland_protocol_dir / 'staging/color-management/color-management-v1.xml', ] wl_protocols = [] diff --git a/src/Compositor.cpp b/src/Compositor.cpp index a35e4216..78ee17b3 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -76,6 +76,7 @@ using namespace Hyprutils::String; using namespace Aquamarine; using enum NContentType::eContentType; +using namespace NColorManagement; static int handleCritSignal(int signo, void* data) { Debug::log(LOG, "Hyprland received signal {}", signo); @@ -3029,8 +3030,10 @@ void CCompositor::onNewMonitor(SP output) { g_pHyprRenderer->damageMonitor(PNEWMONITOR); PNEWMONITOR->onMonitorFrame(); - if (PROTO::colorManagement && shouldChangePreferredImageDescription()) - PROTO::colorManagement->onImagePreferredChanged(); + if (PROTO::colorManagement && shouldChangePreferredImageDescription()) { + Debug::log(ERR, "FIXME: color management protocol is enabled, need a preferred image description id"); + PROTO::colorManagement->onImagePreferredChanged(0); + } } SImageDescription CCompositor::getPreferredImageDescription() { diff --git a/src/Compositor.hpp b/src/Compositor.hpp index 1ae367a3..9f0d5711 100644 --- a/src/Compositor.hpp +++ b/src/Compositor.hpp @@ -149,10 +149,10 @@ class CCompositor { void onNewMonitor(SP output); void ensurePersistentWorkspacesPresent(const std::vector& rules, PHLWORKSPACE pWorkspace = nullptr); - SImageDescription getPreferredImageDescription(); - bool shouldChangePreferredImageDescription(); + NColorManagement::SImageDescription getPreferredImageDescription(); + bool shouldChangePreferredImageDescription(); - std::string explicitConfigPath; + std::string explicitConfigPath; private: void initAllSignals(); diff --git a/src/config/ConfigDescriptions.hpp b/src/config/ConfigDescriptions.hpp index 73579af0..842df3ef 100644 --- a/src/config/ConfigDescriptions.hpp +++ b/src/config/ConfigDescriptions.hpp @@ -1574,6 +1574,12 @@ inline static const std::vector CONFIG_OPTIONS = { .type = CONFIG_OPTION_BOOL, .data = SConfigOptionDescription::SBoolData{false}, }, + SConfigOptionDescription{ + .value = "debug:full_cm_proto", + .description = "claims support for all cm proto features (requires restart)", + .type = CONFIG_OPTION_BOOL, + .data = SConfigOptionDescription::SBoolData{false}, + }, /* * dwindle: diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 2f57a37f..2547992b 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -499,6 +499,7 @@ CConfigManager::CConfigManager() { registerConfigVar("debug:watchdog_timeout", Hyprlang::INT{5}); registerConfigVar("debug:disable_scale_checks", Hyprlang::INT{0}); registerConfigVar("debug:colored_stdout_logs", Hyprlang::INT{1}); + registerConfigVar("debug:full_cm_proto", Hyprlang::INT{0}); registerConfigVar("decoration:rounding", Hyprlang::INT{0}); registerConfigVar("decoration:rounding_power", {2.F}); diff --git a/src/helpers/Monitor.hpp b/src/helpers/Monitor.hpp index 690b8c4a..5a58fc06 100644 --- a/src/helpers/Monitor.hpp +++ b/src/helpers/Monitor.hpp @@ -12,6 +12,7 @@ #include "Timer.hpp" #include "math/Math.hpp" #include +#include "protocols/types/ColorManagement.hpp" #include "signal/Signal.hpp" #include "DamageRing.hpp" #include @@ -162,38 +163,39 @@ class CMonitor { std::array, 4> m_aLayerSurfaceLayers; // methods - void onConnect(bool noRule); - void onDisconnect(bool destroy = false); - bool applyMonitorRule(SMonitorRule* pMonitorRule, bool force = false); - void addDamage(const pixman_region32_t* rg); - void addDamage(const CRegion& rg); - void addDamage(const CBox& box); - bool shouldSkipScheduleFrameOnMouseEvent(); - void setMirror(const std::string&); - bool isMirror(); - bool matchesStaticSelector(const std::string& selector) const; - float getDefaultScale(); - void changeWorkspace(const PHLWORKSPACE& pWorkspace, bool internal = false, bool noMouseMove = false, bool noFocus = false); - void changeWorkspace(const WORKSPACEID& id, bool internal = false, bool noMouseMove = false, bool noFocus = false); - void setSpecialWorkspace(const PHLWORKSPACE& pWorkspace); - void setSpecialWorkspace(const WORKSPACEID& id); - void moveTo(const Vector2D& pos); - Vector2D middle(); - void updateMatrix(); - WORKSPACEID activeWorkspaceID(); - WORKSPACEID activeSpecialWorkspaceID(); - CBox logicalBox(); - void scheduleDone(); - bool attemptDirectScanout(); - void setCTM(const Mat3x3& ctm); - void onCursorMovedOnMonitor(); + void onConnect(bool noRule); + void onDisconnect(bool destroy = false); + bool applyMonitorRule(SMonitorRule* pMonitorRule, bool force = false); + void addDamage(const pixman_region32_t* rg); + void addDamage(const CRegion& rg); + void addDamage(const CBox& box); + bool shouldSkipScheduleFrameOnMouseEvent(); + void setMirror(const std::string&); + bool isMirror(); + bool matchesStaticSelector(const std::string& selector) const; + float getDefaultScale(); + void changeWorkspace(const PHLWORKSPACE& pWorkspace, bool internal = false, bool noMouseMove = false, bool noFocus = false); + void changeWorkspace(const WORKSPACEID& id, bool internal = false, bool noMouseMove = false, bool noFocus = false); + void setSpecialWorkspace(const PHLWORKSPACE& pWorkspace); + void setSpecialWorkspace(const WORKSPACEID& id); + void moveTo(const Vector2D& pos); + Vector2D middle(); + void updateMatrix(); + WORKSPACEID activeWorkspaceID(); + WORKSPACEID activeSpecialWorkspaceID(); + CBox logicalBox(); + void scheduleDone(); + bool attemptDirectScanout(); + void setCTM(const Mat3x3& ctm); + void onCursorMovedOnMonitor(); - void debugLastPresentation(const std::string& message); - void onMonitorFrame(); + void debugLastPresentation(const std::string& message); + void onMonitorFrame(); - bool m_bEnabled = false; - bool m_bRenderingInitPassed = false; - WP m_previousFSWindow; + bool m_bEnabled = false; + bool m_bRenderingInitPassed = false; + WP m_previousFSWindow; + NColorManagement::SImageDescription imageDescription; // For the list lookup diff --git a/src/managers/ProtocolManager.cpp b/src/managers/ProtocolManager.cpp index 6474d661..5e548c9f 100644 --- a/src/managers/ProtocolManager.cpp +++ b/src/managers/ProtocolManager.cpp @@ -57,6 +57,7 @@ #include "../protocols/core/Output.hpp" #include "../protocols/core/Shm.hpp" #include "../protocols/ColorManagement.hpp" +#include "../protocols/XXColorManagement.hpp" #include "../protocols/FrogColorManagement.hpp" #include "../protocols/ContentType.hpp" @@ -83,14 +84,17 @@ void CProtocolManager::onMonitorModeChange(PHLMONITOR pMonitor) { PROTO::outputs.emplace(pMonitor->szName, makeShared(&wl_output_interface, 4, std::format("WLOutput ({})", pMonitor->szName), pMonitor->self.lock())); } - if (PROTO::colorManagement && g_pCompositor->shouldChangePreferredImageDescription()) - PROTO::colorManagement->onImagePreferredChanged(); + if (PROTO::colorManagement && g_pCompositor->shouldChangePreferredImageDescription()) { + Debug::log(ERR, "FIXME: color management protocol is enabled, need a preferred image description id"); + PROTO::colorManagement->onImagePreferredChanged(0); + } } CProtocolManager::CProtocolManager() { static const auto PENABLEEXPLICIT = CConfigValue("render:explicit_sync"); static const auto PENABLEXXCM = CConfigValue("experimental:xx_color_management_v4"); + static const auto PDEBUGCM = CConfigValue("debug:full_cm_proto"); // Outputs are a bit dumb, we have to agree. static auto P = g_pHookSystem->hookDynamic("monitorAdded", [this](void* self, SCallbackInfo& info, std::any param) { @@ -172,9 +176,10 @@ CProtocolManager::CProtocolManager() { PROTO::ctm = makeUnique(&hyprland_ctm_control_manager_v1_interface, 2, "CTMControl"); PROTO::hyprlandSurface = makeUnique(&hyprland_surface_manager_v1_interface, 2, "HyprlandSurface"); PROTO::contentType = makeUnique(&wp_content_type_manager_v1_interface, 1, "ContentType"); + PROTO::colorManagement = makeUnique(&wp_color_manager_v1_interface, 1, "ColorManagement", *PDEBUGCM); if (*PENABLEXXCM) { - PROTO::colorManagement = makeUnique(&xx_color_manager_v4_interface, 1, "ColorManagement"); + PROTO::xxColorManagement = makeUnique(&xx_color_manager_v4_interface, 1, "XXColorManagement"); PROTO::frogColorManagement = makeUnique(&frog_color_management_factory_v1_interface, 1, "FrogColorManagement"); } diff --git a/src/protocols/ColorManagement.cpp b/src/protocols/ColorManagement.cpp index c2814361..4ddc7c47 100644 --- a/src/protocols/ColorManagement.cpp +++ b/src/protocols/ColorManagement.cpp @@ -1,42 +1,89 @@ #include "ColorManagement.hpp" #include "Compositor.hpp" +#include "color-management-v1.hpp" +#include "helpers/Monitor.hpp" +#include "protocols/core/Output.hpp" +#include "protocols/types/ColorManagement.hpp" +#include -CColorManager::CColorManager(SP resource_) : resource(resource_) { +using namespace NColorManagement; + +static uint64_t lastImageID = 0; // FIXME use for deduplication + +CColorManager::CColorManager(SP resource) : m_resource(resource) { if UNLIKELY (!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); + if (PROTO::colorManagement->m_debug) { + m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_ICC_V2_V4); + m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_PARAMETRIC); + m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_SET_PRIMARIES); + m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_SET_TF_POWER); + m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_SET_LUMINANCES); + m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_SET_MASTERING_DISPLAY_PRIMARIES); + m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_EXTENDED_TARGET_VOLUME); + m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_WINDOWS_SCRGB); + } - 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); + m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_SRGB); + if (PROTO::colorManagement->m_debug) { + m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_BT2020); // HDR for fullscreen only - // 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); + m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_PAL_M); + m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_PAL); + m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_NTSC); + m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_GENERIC_FILM); + m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_CIE1931_XYZ); + m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_DCI_P3); + m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_DISPLAY_P3); + m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_ADOBE_RGB); + } - 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); + m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB); + if (PROTO::colorManagement->m_debug) { + m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ); // HDR for fullscreen only - 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) { + m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_BT1886); + m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22); + m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA28); + m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST240); + m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR); + m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_LOG_100); + m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_LOG_316); + m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_XVYCC); + m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_SRGB); + m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST428); + m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_HLG); + } + + m_resource->sendSupportedIntent(WP_COLOR_MANAGER_V1_RENDER_INTENT_PERCEPTUAL); + if (PROTO::colorManagement->m_debug) { + m_resource->sendSupportedIntent(WP_COLOR_MANAGER_V1_RENDER_INTENT_RELATIVE); + m_resource->sendSupportedIntent(WP_COLOR_MANAGER_V1_RENDER_INTENT_SATURATION); + m_resource->sendSupportedIntent(WP_COLOR_MANAGER_V1_RENDER_INTENT_ABSOLUTE); + m_resource->sendSupportedIntent(WP_COLOR_MANAGER_V1_RENDER_INTENT_RELATIVE_BPC); + } + + m_resource->setDestroy([](CWpColorManagerV1* r) { LOGM(TRACE, "Destroy WP_color_manager at {:x} (generated default)", (uintptr_t)r); }); + m_resource->setGetOutput([](CWpColorManagerV1* r, uint32_t id, wl_resource* output) { LOGM(TRACE, "Get output for id={}, output={}", id, (uintptr_t)output); + + const auto OUTPUTRESOURCE = CWLOutputResource::fromResource(output); + + if UNLIKELY (!OUTPUTRESOURCE) { + r->error(-1, "Invalid output (2)"); + return; + } + + const auto PMONITOR = OUTPUTRESOURCE->monitor.lock(); + + if UNLIKELY (!PMONITOR) { + r->error(-1, "Invalid output (2)"); + return; + } + const auto RESOURCE = - PROTO::colorManagement->m_vOutputs.emplace_back(makeShared(makeShared(r->client(), r->version(), id))); + PROTO::colorManagement->m_vOutputs.emplace_back(makeShared(makeShared(r->client(), r->version(), id), PMONITOR)); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); @@ -46,7 +93,7 @@ CColorManager::CColorManager(SP resource_) : resource(resourc RESOURCE->self = RESOURCE; }); - resource->setGetSurface([](CXxColorManagerV4* r, uint32_t id, wl_resource* surface) { + m_resource->setGetSurface([](CWpColorManagerV1* r, uint32_t id, wl_resource* surface) { LOGM(TRACE, "Get surface for id={}, surface={}", id, (uintptr_t)surface); auto SURF = CWLSurfaceResource::fromResource(surface); @@ -57,12 +104,12 @@ CColorManager::CColorManager(SP resource_) : resource(resourc } if (SURF->colorManagement) { - r->error(XX_COLOR_MANAGER_V4_ERROR_SURFACE_EXISTS, "CM Surface already exists"); + r->error(WP_COLOR_MANAGER_V1_ERROR_SURFACE_EXISTS, "CM Surface already exists"); return; } const auto RESOURCE = - PROTO::colorManagement->m_vSurfaces.emplace_back(makeShared(makeShared(r->client(), r->version(), id), SURF)); + PROTO::colorManagement->m_vSurfaces.emplace_back(makeShared(makeShared(r->client(), r->version(), id), SURF)); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); PROTO::colorManagement->m_vSurfaces.pop_back(); @@ -73,7 +120,7 @@ CColorManager::CColorManager(SP resource_) : resource(resourc SURF->colorManagement = RESOURCE; }); - resource->setGetFeedbackSurface([](CXxColorManagerV4* r, uint32_t id, wl_resource* surface) { + m_resource->setGetSurfaceFeedback([](CWpColorManagerV1* r, uint32_t id, wl_resource* surface) { LOGM(TRACE, "Get feedback surface for id={}, surface={}", id, (uintptr_t)surface); auto SURF = CWLSurfaceResource::fromResource(surface); @@ -84,7 +131,7 @@ CColorManager::CColorManager(SP resource_) : resource(resourc } const auto RESOURCE = PROTO::colorManagement->m_vFeedbackSurfaces.emplace_back( - makeShared(makeShared(r->client(), r->version(), id), SURF)); + makeShared(makeShared(r->client(), r->version(), id), SURF)); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); @@ -94,15 +141,34 @@ CColorManager::CColorManager(SP resource_) : resource(resourc RESOURCE->self = RESOURCE; }); - resource->setNewIccCreator([](CXxColorManagerV4* r, uint32_t id) { + m_resource->setCreateIccCreator([](CWpColorManagerV1* 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"); + if (!PROTO::colorManagement->m_debug) { + r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "ICC profiles are not supported"); + return; + } + + const auto RESOURCE = + PROTO::colorManagement->m_vIccCreators.emplace_back(makeShared(makeShared(r->client(), r->version(), id))); + + if UNLIKELY (!RESOURCE->good()) { + r->noMemory(); + PROTO::colorManagement->m_vIccCreators.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; }); - resource->setNewParametricCreator([](CXxColorManagerV4* r, uint32_t id) { + m_resource->setCreateParametricCreator([](CWpColorManagerV1* r, uint32_t id) { LOGM(TRACE, "New parametric creator for id={}", id); + if (!PROTO::colorManagement->m_debug) { + r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "Parametric creator is not supported"); + return; + } + const auto RESOURCE = PROTO::colorManagement->m_vParametricCreators.emplace_back( - makeShared(makeShared(r->client(), r->version(), id))); + makeShared(makeShared(r->client(), r->version(), id))); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); @@ -112,30 +178,59 @@ CColorManager::CColorManager(SP resource_) : resource(resourc RESOURCE->self = RESOURCE; }); + m_resource->setCreateWindowsScrgb([](CWpColorManagerV1* r, uint32_t id) { + LOGM(WARN, "New Windows scRGB description id={} (unsupported)", id); + if (!PROTO::colorManagement->m_debug) { + r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "Windows scRGB profiles are not supported"); + return; + } - resource->setOnDestroy([this](CXxColorManagerV4* r) { PROTO::colorManagement->destroyResource(this); }); + const auto RESOURCE = PROTO::colorManagement->m_vImageDescriptions.emplace_back( + makeShared(makeShared(r->client(), r->version(), id))); + + if UNLIKELY (!RESOURCE->good()) { + r->noMemory(); + PROTO::colorManagement->m_vImageDescriptions.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + RESOURCE->settings.id = ++lastImageID; + RESOURCE->settings.windowsScRGB = true; + RESOURCE->settings.primariesNamed = NColorManagement::CM_PRIMARIES_SRGB; + RESOURCE->settings.primariesNameSet = true; + RESOURCE->settings.primaries = NColorPrimaries::BT709; + RESOURCE->settings.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_EXT_LINEAR; + RESOURCE->settings.luminances.reference = 203; + RESOURCE->resource()->sendReady(RESOURCE->settings.id); + }); + + m_resource->setOnDestroy([this](CWpColorManagerV1* r) { PROTO::colorManagement->destroyResource(this); }); + + m_resource->sendDone(); } bool CColorManager::good() { - return resource->resource(); + return m_resource->resource(); } -CColorManagementOutput::CColorManagementOutput(SP resource_) : resource(resource_) { +CColorManagementOutput::CColorManagementOutput(SP resource, WP monitor) : m_resource(resource), m_monitor(monitor) { if UNLIKELY (!good()) return; - pClient = resource->client(); + pClient = m_resource->client(); - resource->setDestroy([this](CXxColorManagementOutputV4* r) { PROTO::colorManagement->destroyResource(this); }); - resource->setOnDestroy([this](CXxColorManagementOutputV4* r) { PROTO::colorManagement->destroyResource(this); }); + m_resource->setDestroy([this](CWpColorManagementOutputV1* r) { PROTO::colorManagement->destroyResource(this); }); + m_resource->setOnDestroy([this](CWpColorManagementOutputV1* r) { PROTO::colorManagement->destroyResource(this); }); - resource->setGetImageDescription([this](CXxColorManagementOutputV4* r, uint32_t id) { + m_resource->setGetImageDescription([this](CWpColorManagementOutputV1* 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(makeShared(r->client(), r->version(), id))); + makeShared(makeShared(r->client(), r->version(), id))); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); @@ -144,11 +239,19 @@ CColorManagementOutput::CColorManagementOutput(SP re } RESOURCE->self = RESOURCE; + if (!m_monitor.valid()) + RESOURCE->m_resource->sendFailed(WP_IMAGE_DESCRIPTION_V1_CAUSE_NO_OUTPUT, "No output"); + else { + RESOURCE->settings = m_monitor->imageDescription; + if (RESOURCE->settings.id) + RESOURCE->settings.id = ++lastImageID; + RESOURCE->m_resource->sendReady(RESOURCE->settings.id); // FIXME: create correct id + } }); } bool CColorManagementOutput::good() { - return resource->resource(); + return m_resource->resource(); } wl_client* CColorManagementOutput::client() { @@ -159,45 +262,45 @@ CColorManagementSurface::CColorManagementSurface(SP surface_ // only for frog cm untill wayland cm is adopted } -CColorManagementSurface::CColorManagementSurface(SP resource_, SP surface_) : surface(surface_), resource(resource_) { +CColorManagementSurface::CColorManagementSurface(SP resource, SP surface_) : surface(surface_), m_resource(resource) { if UNLIKELY (!good()) return; - pClient = resource->client(); + pClient = m_resource->client(); - resource->setDestroy([this](CXxColorManagementSurfaceV4* r) { + m_resource->setDestroy([this](CWpColorManagementSurfaceV1* r) { LOGM(TRACE, "Destroy xx cm surface {}", (uintptr_t)surface); PROTO::colorManagement->destroyResource(this); }); - resource->setOnDestroy([this](CXxColorManagementSurfaceV4* r) { + m_resource->setOnDestroy([this](CWpColorManagementSurfaceV1* 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) { + m_resource->setSetImageDescription([this](CWpColorManagementSurfaceV1* 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); + const auto PO = (CWpImageDescriptionV1*)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"); + r->error(WP_COLOR_MANAGEMENT_SURFACE_V1_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"); + if (render_intent != WP_COLOR_MANAGER_V1_RENDER_INTENT_PERCEPTUAL) { + r->error(WP_COLOR_MANAGEMENT_SURFACE_V1_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"); + r->error(WP_COLOR_MANAGEMENT_SURFACE_V1_ERROR_IMAGE_DESCRIPTION, "Image description not found"); return; } setHasImageDescription(true); m_imageDescription = imageDescription->get()->settings; }); - resource->setUnsetImageDescription([this](CXxColorManagementSurfaceV4* r) { + m_resource->setUnsetImageDescription([this](CWpColorManagementSurfaceV1* r) { LOGM(TRACE, "Unset image description for surface={}", (uintptr_t)r); m_imageDescription = SImageDescription{}; setHasImageDescription(false); @@ -205,7 +308,7 @@ CColorManagementSurface::CColorManagementSurface(SP } bool CColorManagementSurface::good() { - return resource && resource->resource(); + return m_resource && m_resource->resource(); } wl_client* CColorManagementSurface::client() { @@ -240,34 +343,34 @@ bool CColorManagementSurface::needsHdrMetadataUpdate() { return m_needsNewMetadata; } -CColorManagementFeedbackSurface::CColorManagementFeedbackSurface(SP resource_, SP surface_) : - surface(surface_), resource(resource_) { +CColorManagementFeedbackSurface::CColorManagementFeedbackSurface(SP resource, SP surface_) : + surface(surface_), m_resource(resource) { if UNLIKELY (!good()) return; - pClient = resource->client(); + pClient = m_resource->client(); - resource->setDestroy([this](CXxColorManagementFeedbackSurfaceV4* r) { + m_resource->setDestroy([this](CWpColorManagementSurfaceFeedbackV1* 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) { + m_resource->setOnDestroy([this](CWpColorManagementSurfaceFeedbackV1* 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) { + m_resource->setGetPreferred([this](CWpColorManagementSurfaceFeedbackV1* 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(makeShared(r->client(), r->version(), id), true)); + makeShared(makeShared(r->client(), r->version(), id), true)); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); @@ -279,44 +382,130 @@ CColorManagementFeedbackSurface::CColorManagementFeedbackSurface(SPsettings = g_pCompositor->getPreferredImageDescription(); + if (!m_currentPreferred->settings.id) + m_currentPreferred->settings.id = ++lastImageID; - RESOURCE->resource()->sendReady(id); + RESOURCE->resource()->sendReady(++lastImageID); // FIXME: create correct id + }); + + m_resource->setGetPreferredParametric([this](CWpColorManagementSurfaceFeedbackV1* r, uint32_t id) { + LOGM(TRACE, "Get preferred for id {}", id); + + if (!PROTO::colorManagement->m_debug) { + r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "Parametric descriptions are not supported"); + return; + } + + if (m_currentPreferred.valid()) + PROTO::colorManagement->destroyResource(m_currentPreferred.get()); + + const auto RESOURCE = PROTO::colorManagement->m_vImageDescriptions.emplace_back( + makeShared(makeShared(r->client(), r->version(), id), true)); + + if UNLIKELY (!RESOURCE->good()) { + r->noMemory(); + PROTO::colorManagement->m_vImageDescriptions.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + m_currentPreferred = RESOURCE; + + m_currentPreferred->settings = g_pCompositor->getPreferredImageDescription(); + if (!PROTO::colorManagement->m_debug && m_currentPreferred->settings.icc.fd) { + LOGM(ERR, "FIXME: parse icc profile"); + r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "ICC profiles are not supported"); + return; + } + + RESOURCE->resource()->sendReady(++lastImageID); // FIXME: create correct id }); } bool CColorManagementFeedbackSurface::good() { - return resource->resource(); + return m_resource->resource(); } wl_client* CColorManagementFeedbackSurface::client() { return pClient; } -CColorManagementParametricCreator::CColorManagementParametricCreator(SP resource_) : resource(resource_) { +CColorManagementIccCreator::CColorManagementIccCreator(SP resource) : m_resource(resource) { if UNLIKELY (!good()) return; // - pClient = resource->client(); + pClient = m_resource->client(); - resource->setOnDestroy([this](CXxImageDescriptionCreatorParamsV4* r) { PROTO::colorManagement->destroyResource(this); }); + m_resource->setOnDestroy([this](CWpImageDescriptionCreatorIccV1* r) { PROTO::colorManagement->destroyResource(this); }); - resource->setCreate([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t id) { - LOGM(TRACE, "Create image description from params for id {}", id); + m_resource->setCreate([this](CWpImageDescriptionCreatorIccV1* r, uint32_t id) { + LOGM(TRACE, "Create image description from icc 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"); + if (settings.icc.fd < 0 || !settings.icc.length) { + r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INCOMPLETE_SET, "Missing required settings"); return; } const auto RESOURCE = PROTO::colorManagement->m_vImageDescriptions.emplace_back( - makeShared(makeShared(r->client(), r->version(), id))); + makeShared(makeShared(r->client(), r->version(), id))); + + if UNLIKELY (!RESOURCE->good()) { + r->noMemory(); + PROTO::colorManagement->m_vImageDescriptions.pop_back(); + return; + } + + LOGM(ERR, "FIXME: Parse icc file {}({},{}) for id {}", settings.icc.fd, settings.icc.offset, settings.icc.length, id); + + // FIXME actually check support + if (settings.icc.fd < 0 || !settings.icc.length) { + RESOURCE->resource()->sendFailed(WP_IMAGE_DESCRIPTION_V1_CAUSE_UNSUPPORTED, "unsupported"); + return; + } + + RESOURCE->self = RESOURCE; + RESOURCE->settings = settings; + settings.id = ++lastImageID; + RESOURCE->resource()->sendReady(settings.id); // FIXME: create correct id + + PROTO::colorManagement->destroyResource(this); + }); + + m_resource->setSetIccFile([this](CWpImageDescriptionCreatorIccV1* r, int fd, uint32_t offset, uint32_t length) { + settings.icc.fd = fd; + settings.icc.offset = offset; + settings.icc.length = length; + }); +} + +bool CColorManagementIccCreator::good() { + return m_resource->resource(); +} + +wl_client* CColorManagementIccCreator::client() { + return pClient; +} + +CColorManagementParametricCreator::CColorManagementParametricCreator(SP resource) : m_resource(resource) { + if UNLIKELY (!good()) + return; + // + pClient = m_resource->client(); + + m_resource->setOnDestroy([this](CWpImageDescriptionCreatorParamsV1* r) { PROTO::colorManagement->destroyResource(this); }); + + m_resource->setCreate([this](CWpImageDescriptionCreatorParamsV1* r, uint32_t id) { + LOGM(TRACE, "Create image description from params for id {}", id); + + // FIXME actually check completeness + if (!valuesSet) { + r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INCOMPLETE_SET, "Missing required settings"); + return; + } + + const auto RESOURCE = PROTO::colorManagement->m_vImageDescriptions.emplace_back( + makeShared(makeShared(r->client(), r->version(), id))); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); @@ -326,128 +515,169 @@ CColorManagementParametricCreator::CColorManagementParametricCreator(SPresource()->sendFailed(XX_IMAGE_DESCRIPTION_V4_CAUSE_UNSUPPORTED, "unsupported"); + RESOURCE->resource()->sendFailed(WP_IMAGE_DESCRIPTION_V1_CAUSE_UNSUPPORTED, "unsupported"); return; } RESOURCE->self = RESOURCE; RESOURCE->settings = settings; - RESOURCE->resource()->sendReady(id); + settings.id = ++lastImageID; + RESOURCE->resource()->sendReady(settings.id); // FIXME: create correct id PROTO::colorManagement->destroyResource(this); }); - resource->setSetTfNamed([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t tf) { + m_resource->setSetTfNamed([this](CWpImageDescriptionCreatorParamsV1* 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"); + r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_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; + case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB: break; + case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ: break; + default: r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_TF, "Unsupported transfer function"); return; } - settings.transferFunction = (xxColorManagerV4TransferFunction)tf; + settings.transferFunction = convertTransferFunction((wpColorManagerV1TransferFunction)tf); valuesSet |= PC_TF; }); - resource->setSetTfPower([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t eexp) { + m_resource->setSetTfPower([this](CWpImageDescriptionCreatorParamsV1* 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"); + r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET, "Transfer function power already set"); + return; + } + if (!PROTO::colorManagement->m_debug) { + r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "TF power is not supported"); return; } settings.transferFunctionPower = eexp / 10000.0f; + if (settings.transferFunctionPower < 1.0 || settings.transferFunctionPower > 10.0) { + r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_TF, "Power should be between 1.0 and 10.0"); + return; + } valuesSet |= PC_TF_POWER; }); - resource->setSetPrimariesNamed([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t primaries) { + m_resource->setSetPrimariesNamed([this](CWpImageDescriptionCreatorParamsV1* 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"); + r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET, "Primaries already set"); + return; + } + + if (!PROTO::colorManagement->m_debug && primaries != WP_COLOR_MANAGER_V1_PRIMARIES_SRGB) { + r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_PRIMARIES_NAMED, "Unsupported primaries"); return; } switch (primaries) { - case XX_COLOR_MANAGER_V4_PRIMARIES_SRGB: + case WP_COLOR_MANAGER_V1_PRIMARIES_SRGB: + case WP_COLOR_MANAGER_V1_PRIMARIES_BT2020: + case WP_COLOR_MANAGER_V1_PRIMARIES_PAL_M: + case WP_COLOR_MANAGER_V1_PRIMARIES_PAL: + case WP_COLOR_MANAGER_V1_PRIMARIES_NTSC: + case WP_COLOR_MANAGER_V1_PRIMARIES_GENERIC_FILM: + case WP_COLOR_MANAGER_V1_PRIMARIES_DCI_P3: + case WP_COLOR_MANAGER_V1_PRIMARIES_DISPLAY_P3: + case WP_COLOR_MANAGER_V1_PRIMARIES_ADOBE_RGB: settings.primariesNameSet = true; - settings.primariesNamed = XX_COLOR_MANAGER_V4_PRIMARIES_SRGB; - settings.primaries = NColorPrimaries::BT709; + settings.primariesNamed = convertPrimaries((wpColorManagerV1Primaries)primaries); + settings.primaries = getPrimaries(settings.primariesNamed); 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"); + default: r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_PRIMARIES_NAMED, "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) { + m_resource->setSetPrimaries( + [this](CWpImageDescriptionCreatorParamsV1* 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"); + r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET, "Primaries already set"); + return; + } + if (!PROTO::colorManagement->m_debug) { + r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "Custom primaries aren't supported"); 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}}; + settings.primaries = SPCPRimaries{.red = {.x = r_x / 1000000.0f, .y = r_y / 1000000.0f}, + .green = {.x = g_x / 1000000.0f, .y = g_y / 1000000.0f}, + .blue = {.x = b_x / 1000000.0f, .y = b_y / 1000000.0f}, + .white = {.x = w_x / 1000000.0f, .y = w_y / 1000000.0f}}; valuesSet |= PC_PRIMARIES; }); - resource->setSetLuminances([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t min_lum, uint32_t max_lum, uint32_t reference_lum) { + m_resource->setSetLuminances([this](CWpImageDescriptionCreatorParamsV1* 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"); + r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_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"); + r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_LUMINANCE, "Invalid luminances"); + return; + } + if (!PROTO::colorManagement->m_debug) { + r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "Luminances aren't supported"); 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) { + m_resource->setSetMasteringDisplayPrimaries( + [this](CWpImageDescriptionCreatorParamsV1* 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}}; + if (valuesSet & PC_MASTERING_PRIMARIES) { + r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET, "Mastering primaries already set"); + return; + } + if (!PROTO::colorManagement->m_debug) { + r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "Mastering primaries are not supported"); + return; + } + settings.masteringPrimaries = SPCPRimaries{.red = {.x = r_x / 1000000.0f, .y = r_y / 1000000.0f}, + .green = {.x = g_x / 1000000.0f, .y = g_y / 1000000.0f}, + .blue = {.x = b_x / 1000000.0f, .y = b_y / 1000000.0f}, + .white = {.x = w_x / 1000000.0f, .y = w_y / 1000000.0f}}; valuesSet |= PC_MASTERING_PRIMARIES; + + // FIXME: + // If a compositor additionally supports target color volume exceeding the primary color volume, it must advertise wp_color_manager_v1.feature.extended_target_volume. + // If a client uses target color volume exceeding the primary color volume and the compositor does not support it, the result is implementation defined. + // Compositors are recommended to detect this case and fail the image description gracefully, but it may as well result in color artifacts. }); - resource->setSetMasteringLuminance([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t min_lum, uint32_t max_lum) { + m_resource->setSetMasteringLuminance([this](CWpImageDescriptionCreatorParamsV1* 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"); + // r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_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"); + r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_LUMINANCE, "Invalid luminances"); + return; + } + if (!PROTO::colorManagement->m_debug) { + r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "Mastering luminances are not supported"); return; } settings.masteringLuminances = SImageDescription::SPCMasteringLuminances{.min = min, .max = max_lum}; valuesSet |= PC_MASTERING_LUMINANCES; }); - resource->setSetMaxCll([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t max_cll) { + m_resource->setSetMaxCll([this](CWpImageDescriptionCreatorParamsV1* 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"); + // r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET, "Max CLL already set"); // return; // } settings.maxCLL = max_cll; valuesSet |= PC_CLL; }); - resource->setSetMaxFall([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t max_fall) { + m_resource->setSetMaxFall([this](CWpImageDescriptionCreatorParamsV1* 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"); + // r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET, "Max FALL already set"); // return; // } settings.maxFALL = max_fall; @@ -456,31 +686,31 @@ CColorManagementParametricCreator::CColorManagementParametricCreator(SPresource(); + return m_resource->resource(); } wl_client* CColorManagementParametricCreator::client() { return pClient; } -CColorManagementImageDescription::CColorManagementImageDescription(SP resource_, bool allowGetInformation) : - m_resource(resource_), m_allowGetInformation(allowGetInformation) { +CColorManagementImageDescription::CColorManagementImageDescription(SP resource, bool allowGetInformation) : + m_resource(resource), m_allowGetInformation(allowGetInformation) { if UNLIKELY (!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->setDestroy([this](CWpImageDescriptionV1* r) { PROTO::colorManagement->destroyResource(this); }); + m_resource->setOnDestroy([this](CWpImageDescriptionV1* r) { PROTO::colorManagement->destroyResource(this); }); - m_resource->setGetInformation([this](CXxImageDescriptionV4* r, uint32_t id) { + m_resource->setGetInformation([this](CWpImageDescriptionV1* 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"); + r->error(WP_IMAGE_DESCRIPTION_V1_ERROR_NO_INFORMATION, "Image descriptions doesn't allow get_information request"); return; } - auto RESOURCE = makeShared(makeShared(r->client(), r->version(), id), settings); + auto RESOURCE = makeShared(makeShared(r->client(), r->version(), id), settings); if UNLIKELY (!RESOURCE->good()) r->noMemory(); @@ -498,12 +728,12 @@ wl_client* CColorManagementImageDescription::client() { return pClient; } -SP CColorManagementImageDescription::resource() { +SP CColorManagementImageDescription::resource() { return m_resource; } -CColorManagementImageDescriptionInfo::CColorManagementImageDescriptionInfo(SP resource_, const SImageDescription& settings_) : - m_resource(resource_), settings(settings_) { +CColorManagementImageDescriptionInfo::CColorManagementImageDescriptionInfo(SP resource, const SImageDescription& settings_) : + m_resource(resource), settings(settings_) { if UNLIKELY (!good()) return; @@ -511,8 +741,8 @@ CColorManagementImageDescriptionInfo::CColorManagementImageDescriptionInfo(SP= 0) - m_resource->sendIccFile(settings.iccFd, settings.iccSize); + if (settings.icc.fd >= 0) + m_resource->sendIccFile(settings.icc.fd, settings.icc.length); // 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), @@ -542,12 +772,13 @@ wl_client* CColorManagementImageDescriptionInfo::client() { return pClient; } -CColorManagementProtocol::CColorManagementProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { +CColorManagementProtocol::CColorManagementProtocol(const wl_interface* iface, const int& ver, const std::string& name, bool debug) : + IWaylandProtocol(iface, ver, name), m_debug(debug) { ; } void CColorManagementProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); if UNLIKELY (!RESOURCE->good()) { wl_client_post_no_memory(client); @@ -555,12 +786,19 @@ void CColorManagementProtocol::bindManager(wl_client* client, void* data, uint32 return; } - LOGM(TRACE, "New xx_color_manager at {:x}", (uintptr_t)RESOURCE.get()); + LOGM(TRACE, "New WP_color_manager at {:x}", (uintptr_t)RESOURCE.get()); } -void CColorManagementProtocol::onImagePreferredChanged() { +void CColorManagementProtocol::onImagePreferredChanged(uint32_t preferredId) { for (auto const& feedback : m_vFeedbackSurfaces) { - feedback->resource->sendPreferredChanged(); + feedback->m_resource->sendPreferredChanged(preferredId); + } +} + +void CColorManagementProtocol::onMonitorImageDescriptionChanged(WP monitor) { + for (auto const& output : m_vOutputs) { + if (output->m_monitor == monitor) + output->m_resource->sendImageDescriptionChanged(); } } @@ -580,6 +818,10 @@ void CColorManagementProtocol::destroyResource(CColorManagementFeedbackSurface* std::erase_if(m_vFeedbackSurfaces, [&](const auto& other) { return other.get() == resource; }); } +void CColorManagementProtocol::destroyResource(CColorManagementIccCreator* resource) { + std::erase_if(m_vIccCreators, [&](const auto& other) { return other.get() == resource; }); +} + void CColorManagementProtocol::destroyResource(CColorManagementParametricCreator* resource) { std::erase_if(m_vParametricCreators, [&](const auto& other) { return other.get() == resource; }); } diff --git a/src/protocols/ColorManagement.hpp b/src/protocols/ColorManagement.hpp index 39e3f486..644b0a4d 100644 --- a/src/protocols/ColorManagement.hpp +++ b/src/protocols/ColorManagement.hpp @@ -4,8 +4,9 @@ #include #include #include "WaylandProtocol.hpp" +#include "helpers/Monitor.hpp" #include "protocols/core/Compositor.hpp" -#include "xx-color-management-v4.hpp" +#include "color-management-v1.hpp" #include "types/ColorManagement.hpp" class CColorManager; @@ -15,17 +16,17 @@ class CColorManagementProtocol; class CColorManager { public: - CColorManager(SP resource_); + CColorManager(SP resource); bool good(); private: - SP resource; + SP m_resource; }; class CColorManagementOutput { public: - CColorManagementOutput(SP resource_); + CColorManagementOutput(SP resource, WP monitor); bool good(); wl_client* client(); @@ -34,8 +35,9 @@ class CColorManagementOutput { WP imageDescription; private: - SP resource; + SP m_resource; wl_client* pClient = nullptr; + WP m_monitor; friend class CColorManagementProtocol; friend class CColorManagementImageDescription; @@ -44,35 +46,36 @@ class CColorManagementOutput { class CColorManagementSurface { public: CColorManagementSurface(SP surface_); // temporary interface for frog CM - CColorManagementSurface(SP resource_, SP surface_); + CColorManagementSurface(SP resource, SP surface_); - bool good(); - wl_client* client(); + bool good(); + wl_client* client(); - WP self; - WP surface; + WP self; + WP surface; - const SImageDescription& imageDescription(); - bool hasImageDescription(); - void setHasImageDescription(bool has); - const hdr_output_metadata& hdrMetadata(); - void setHDRMetadata(const hdr_output_metadata& metadata); - bool needsHdrMetadataUpdate(); + const NColorManagement::SImageDescription& imageDescription(); + bool hasImageDescription(); + void setHasImageDescription(bool has); + const hdr_output_metadata& hdrMetadata(); + void setHDRMetadata(const hdr_output_metadata& metadata); + bool needsHdrMetadataUpdate(); private: - SP resource; - wl_client* pClient = nullptr; - SImageDescription m_imageDescription; - bool m_hasImageDescription = false; - bool m_needsNewMetadata = false; - hdr_output_metadata m_hdrMetadata; + SP m_resource; + wl_client* pClient = nullptr; + NColorManagement::SImageDescription m_imageDescription; + bool m_hasImageDescription = false; + bool m_needsNewMetadata = false; + hdr_output_metadata m_hdrMetadata; + friend class CXXColorManagementSurface; friend class CFrogColorManagementSurface; }; class CColorManagementFeedbackSurface { public: - CColorManagementFeedbackSurface(SP resource_, SP surface_); + CColorManagementFeedbackSurface(SP resource, SP surface_); bool good(); wl_client* client(); @@ -81,7 +84,7 @@ class CColorManagementFeedbackSurface { WP surface; private: - SP resource; + SP m_resource; wl_client* pClient = nullptr; WP m_currentPreferred; @@ -89,16 +92,32 @@ class CColorManagementFeedbackSurface { friend class CColorManagementProtocol; }; +class CColorManagementIccCreator { + public: + CColorManagementIccCreator(SP resource); + + bool good(); + wl_client* client(); + + WP self; + + NColorManagement::SImageDescription settings; + + private: + SP m_resource; + wl_client* pClient = nullptr; +}; + class CColorManagementParametricCreator { public: - CColorManagementParametricCreator(SP resource_); + CColorManagementParametricCreator(SP resource); bool good(); wl_client* client(); WP self; - SImageDescription settings; + NColorManagement::SImageDescription settings; private: enum eValuesSet : uint32_t { // NOLINT @@ -112,25 +131,25 @@ class CColorManagementParametricCreator { PC_FALL = (1 << 7), }; - SP resource; + SP m_resource; wl_client* pClient = nullptr; uint32_t valuesSet = 0; // enum eValuesSet }; class CColorManagementImageDescription { public: - CColorManagementImageDescription(SP resource_, bool allowGetInformation = false); + CColorManagementImageDescription(SP resource, bool allowGetInformation = false); bool good(); wl_client* client(); - SP resource(); + SP resource(); WP self; - SImageDescription settings; + NColorManagement::SImageDescription settings; private: - SP m_resource; + SP m_resource; wl_client* pClient = nullptr; bool m_allowGetInformation = false; @@ -139,30 +158,32 @@ class CColorManagementImageDescription { class CColorManagementImageDescriptionInfo { public: - CColorManagementImageDescriptionInfo(SP resource_, const SImageDescription& settings_); + CColorManagementImageDescriptionInfo(SP resource, const NColorManagement::SImageDescription& settings_); bool good(); wl_client* client(); private: - SP m_resource; - wl_client* pClient = nullptr; - SImageDescription settings; + SP m_resource; + wl_client* pClient = nullptr; + NColorManagement::SImageDescription settings; }; class CColorManagementProtocol : public IWaylandProtocol { public: - CColorManagementProtocol(const wl_interface* iface, const int& ver, const std::string& name); + CColorManagementProtocol(const wl_interface* iface, const int& ver, const std::string& name, bool debug = false); virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); - void onImagePreferredChanged(); + void onImagePreferredChanged(uint32_t preferredId); + void onMonitorImageDescriptionChanged(WP monitor); private: void destroyResource(CColorManager* resource); void destroyResource(CColorManagementOutput* resource); void destroyResource(CColorManagementSurface* resource); void destroyResource(CColorManagementFeedbackSurface* resource); + void destroyResource(CColorManagementIccCreator* resource); void destroyResource(CColorManagementParametricCreator* resource); void destroyResource(CColorManagementImageDescription* resource); @@ -170,16 +191,20 @@ class CColorManagementProtocol : public IWaylandProtocol { std::vector> m_vOutputs; std::vector> m_vSurfaces; std::vector> m_vFeedbackSurfaces; + std::vector> m_vIccCreators; std::vector> m_vParametricCreators; std::vector> m_vImageDescriptions; + bool m_debug = false; friend class CColorManager; friend class CColorManagementOutput; friend class CColorManagementSurface; friend class CColorManagementFeedbackSurface; + friend class CColorManagementIccCreator; friend class CColorManagementParametricCreator; friend class CColorManagementImageDescription; + friend class CXXColorManagementSurface; friend class CFrogColorManagementSurface; }; diff --git a/src/protocols/FrogColorManagement.cpp b/src/protocols/FrogColorManagement.cpp index cb2eed12..65afa75e 100644 --- a/src/protocols/FrogColorManagement.cpp +++ b/src/protocols/FrogColorManagement.cpp @@ -1,6 +1,24 @@ #include "FrogColorManagement.hpp" +#include "color-management-v1.hpp" #include "protocols/ColorManagement.hpp" #include "protocols/core/Subcompositor.hpp" +#include "protocols/types/ColorManagement.hpp" + +using namespace NColorManagement; + +static wpColorManagerV1TransferFunction getWPTransferFunction(frogColorManagedSurfaceTransferFunction tf) { + switch (tf) { + case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_UNDEFINED: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_BT1886; + case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_SRGB: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB; + case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_GAMMA_22: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22; + case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_ST2084_PQ: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ; + case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_SCRGB_LINEAR: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR; + } +} + +static wpColorManagerV1Primaries getWPPrimaries(frogColorManagedSurfacePrimaries primaries) { + return (wpColorManagerV1Primaries)(primaries + 1); +} CFrogColorManager::CFrogColorManager(SP resource_) : resource(resource_) { if UNLIKELY (!good()) @@ -19,9 +37,6 @@ CFrogColorManager::CFrogColorManager(SP resource_ 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(makeShared(r->client(), r->version(), id), SURF)); if UNLIKELY (!RESOURCE->good()) { @@ -77,20 +92,22 @@ CFrogColorManagementSurface::CFrogColorManagementSurface(SPid()); switch (tf) { case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_ST2084_PQ: - surface->colorManagement->m_imageDescription.transferFunction = XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ; + surface->colorManagement->m_imageDescription.transferFunction = + convertTransferFunction(getWPTransferFunction(FROG_COLOR_MANAGED_SURFACE_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; + surface->colorManagement->m_imageDescription.transferFunction = + convertTransferFunction(getWPTransferFunction(FROG_COLOR_MANAGED_SURFACE_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, "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_imageDescription.transferFunction = convertTransferFunction(getWPTransferFunction(tf)); surface->colorManagement->setHasImageDescription(true); } @@ -102,6 +119,7 @@ CFrogColorManagementSurface::CFrogColorManagementSurface(SPcolorManagement->m_imageDescription.primaries = NColorPrimaries::BT709; break; case FROG_COLOR_MANAGED_SURFACE_PRIMARIES_REC2020: surface->colorManagement->m_imageDescription.primaries = NColorPrimaries::BT2020; break; } + surface->colorManagement->m_imageDescription.primariesNamed = convertPrimaries(getWPPrimaries(primariesName)); surface->colorManagement->setHasImageDescription(true); }); @@ -113,10 +131,10 @@ CFrogColorManagementSurface::CFrogColorManagementSurface(SPsetSetHdrMetadata([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.masteringPrimaries = 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; diff --git a/src/protocols/XXColorManagement.cpp b/src/protocols/XXColorManagement.cpp new file mode 100644 index 00000000..edcb004d --- /dev/null +++ b/src/protocols/XXColorManagement.cpp @@ -0,0 +1,639 @@ +#include "XXColorManagement.hpp" +#include "Compositor.hpp" +#include "ColorManagement.hpp" +#include "color-management-v1.hpp" +#include "protocols/types/ColorManagement.hpp" + +using namespace NColorManagement; + +static wpColorManagerV1TransferFunction getWPTransferFunction(xxColorManagerV4TransferFunction tf) { + switch (tf) { + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_BT709: + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_BT1361: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_BT1886; + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_GAMMA22: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22; + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_GAMMA28: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA28; + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST240: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST240; + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LINEAR: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR; + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LOG_100: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_LOG_100; + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LOG_316: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_LOG_316; + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_XVYCC: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_XVYCC; + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_SRGB: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB; + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_EXT_SRGB: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_SRGB; + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ; + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST428: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST428; + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_HLG: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_HLG; + } +} + +static wpColorManagerV1Primaries getWPPrimaries(xxColorManagerV4Primaries primaries) { + return (wpColorManagerV1Primaries)(primaries + 1); +} + +CXXColorManager::CXXColorManager(SP resource_) : resource(resource_) { + if UNLIKELY (!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::xxColorManagement->m_vOutputs.emplace_back(makeShared(makeShared(r->client(), r->version(), id))); + + if UNLIKELY (!RESOURCE->good()) { + r->noMemory(); + PROTO::xxColorManagement->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::xxColorManagement->m_vSurfaces.emplace_back(makeShared(makeShared(r->client(), r->version(), id), SURF)); + if UNLIKELY (!RESOURCE->good()) { + r->noMemory(); + PROTO::xxColorManagement->m_vSurfaces.pop_back(); + return; + } + + RESOURCE->self = 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::xxColorManagement->m_vFeedbackSurfaces.emplace_back( + makeShared(makeShared(r->client(), r->version(), id), SURF)); + + if UNLIKELY (!RESOURCE->good()) { + r->noMemory(); + PROTO::xxColorManagement->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::xxColorManagement->m_vParametricCreators.emplace_back( + makeShared(makeShared(r->client(), r->version(), id))); + + if UNLIKELY (!RESOURCE->good()) { + r->noMemory(); + PROTO::xxColorManagement->m_vParametricCreators.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + }); + + resource->setOnDestroy([this](CXxColorManagerV4* r) { PROTO::xxColorManagement->destroyResource(this); }); +} + +bool CXXColorManager::good() { + return resource->resource(); +} + +CXXColorManagementOutput::CXXColorManagementOutput(SP resource_) : resource(resource_) { + if UNLIKELY (!good()) + return; + + pClient = resource->client(); + + resource->setDestroy([this](CXxColorManagementOutputV4* r) { PROTO::xxColorManagement->destroyResource(this); }); + resource->setOnDestroy([this](CXxColorManagementOutputV4* r) { PROTO::xxColorManagement->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::xxColorManagement->destroyResource(imageDescription.get()); + + const auto RESOURCE = PROTO::xxColorManagement->m_vImageDescriptions.emplace_back( + makeShared(makeShared(r->client(), r->version(), id))); + + if UNLIKELY (!RESOURCE->good()) { + r->noMemory(); + PROTO::xxColorManagement->m_vImageDescriptions.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + }); +} + +bool CXXColorManagementOutput::good() { + return resource->resource(); +} + +wl_client* CXXColorManagementOutput::client() { + return pClient; +} + +CXXColorManagementSurface::CXXColorManagementSurface(SP surface_) : surface(surface_) { + // only for frog cm untill wayland cm is adopted +} + +CXXColorManagementSurface::CXXColorManagementSurface(SP resource_, SP surface_) : surface(surface_), resource(resource_) { + if UNLIKELY (!good()) + return; + + pClient = resource->client(); + + if (!surface->colorManagement.valid()) { + const auto RESOURCE = PROTO::colorManagement->m_vSurfaces.emplace_back(makeShared(surface_)); + if UNLIKELY (!RESOURCE) { + resource->noMemory(); + PROTO::colorManagement->m_vSurfaces.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + + surface->colorManagement = RESOURCE; + + resource->setOnDestroy([this](CXxColorManagementSurfaceV4* r) { + LOGM(TRACE, "Destroy wp cm and xx cm for surface {}", (uintptr_t)surface); + if (surface.valid()) + PROTO::colorManagement->destroyResource(surface->colorManagement.get()); + PROTO::xxColorManagement->destroyResource(this); + }); + } else + resource->setOnDestroy([this](CXxColorManagementSurfaceV4* r) { + LOGM(TRACE, "Destroy xx cm surface {}", (uintptr_t)surface); + PROTO::xxColorManagement->destroyResource(this); + }); + + resource->setDestroy([this](CXxColorManagementSurfaceV4* r) { + LOGM(TRACE, "Destroy xx cm surface {}", (uintptr_t)surface); + PROTO::xxColorManagement->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::xxColorManagement->m_vImageDescriptions.begin(), PROTO::xxColorManagement->m_vImageDescriptions.end(), + [&](const auto& other) { return other->resource()->resource() == image_description; }); + if (imageDescription == PROTO::xxColorManagement->m_vImageDescriptions.end()) { + r->error(XX_COLOR_MANAGEMENT_SURFACE_V4_ERROR_IMAGE_DESCRIPTION, "Image description not found"); + return; + } + + if (surface.valid()) { + surface->colorManagement->setHasImageDescription(true); + surface->colorManagement->m_imageDescription = imageDescription->get()->settings; + } else + LOGM(ERR, "Set image description for invalid surface"); + }); + resource->setUnsetImageDescription([this](CXxColorManagementSurfaceV4* r) { + LOGM(TRACE, "Unset image description for surface={}", (uintptr_t)r); + if (surface.valid()) { + surface->colorManagement->m_imageDescription = SImageDescription{}; + surface->colorManagement->setHasImageDescription(false); + } else + LOGM(ERR, "Unset image description for invalid surface"); + }); +} + +bool CXXColorManagementSurface::good() { + return resource && resource->resource(); +} + +wl_client* CXXColorManagementSurface::client() { + return pClient; +} + +const SImageDescription& CXXColorManagementSurface::imageDescription() { + if (!hasImageDescription()) + LOGM(WARN, "Reading imageDescription while none set. Returns default or empty values"); + return m_imageDescription; +} + +bool CXXColorManagementSurface::hasImageDescription() { + return m_hasImageDescription; +} + +void CXXColorManagementSurface::setHasImageDescription(bool has) { + m_hasImageDescription = has; + m_needsNewMetadata = true; +} + +const hdr_output_metadata& CXXColorManagementSurface::hdrMetadata() { + return m_hdrMetadata; +} + +void CXXColorManagementSurface::setHDRMetadata(const hdr_output_metadata& metadata) { + m_hdrMetadata = metadata; + m_needsNewMetadata = false; +} + +bool CXXColorManagementSurface::needsHdrMetadataUpdate() { + return m_needsNewMetadata; +} + +CXXColorManagementFeedbackSurface::CXXColorManagementFeedbackSurface(SP resource_, SP surface_) : + surface(surface_), resource(resource_) { + if UNLIKELY (!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::xxColorManagement->destroyResource(m_currentPreferred.get()); + PROTO::xxColorManagement->destroyResource(this); + }); + resource->setOnDestroy([this](CXxColorManagementFeedbackSurfaceV4* r) { + LOGM(TRACE, "Destroy xx cm feedback surface {}", (uintptr_t)surface); + if (m_currentPreferred.valid()) + PROTO::xxColorManagement->destroyResource(m_currentPreferred.get()); + PROTO::xxColorManagement->destroyResource(this); + }); + + resource->setGetPreferred([this](CXxColorManagementFeedbackSurfaceV4* r, uint32_t id) { + LOGM(TRACE, "Get preferred for id {}", id); + + if (m_currentPreferred.valid()) + PROTO::xxColorManagement->destroyResource(m_currentPreferred.get()); + + const auto RESOURCE = PROTO::xxColorManagement->m_vImageDescriptions.emplace_back( + makeShared(makeShared(r->client(), r->version(), id), true)); + + if UNLIKELY (!RESOURCE->good()) { + r->noMemory(); + PROTO::xxColorManagement->m_vImageDescriptions.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + m_currentPreferred = RESOURCE; + + m_currentPreferred->settings = g_pCompositor->getPreferredImageDescription(); + + RESOURCE->resource()->sendReady(id); + }); +} + +bool CXXColorManagementFeedbackSurface::good() { + return resource->resource(); +} + +wl_client* CXXColorManagementFeedbackSurface::client() { + return pClient; +} + +CXXColorManagementParametricCreator::CXXColorManagementParametricCreator(SP resource_) : resource(resource_) { + if UNLIKELY (!good()) + return; + // + pClient = resource->client(); + + resource->setOnDestroy([this](CXxImageDescriptionCreatorParamsV4* r) { PROTO::xxColorManagement->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::xxColorManagement->m_vImageDescriptions.emplace_back( + makeShared(makeShared(r->client(), r->version(), id))); + + if UNLIKELY (!RESOURCE->good()) { + r->noMemory(); + PROTO::xxColorManagement->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::xxColorManagement->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 = convertTransferFunction(getWPTransferFunction((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 = convertPrimaries(getWPPrimaries(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 = convertPrimaries(getWPPrimaries(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 = 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 = 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 CXXColorManagementParametricCreator::good() { + return resource->resource(); +} + +wl_client* CXXColorManagementParametricCreator::client() { + return pClient; +} + +CXXColorManagementImageDescription::CXXColorManagementImageDescription(SP resource_, bool allowGetInformation) : + m_resource(resource_), m_allowGetInformation(allowGetInformation) { + if UNLIKELY (!good()) + return; + + pClient = m_resource->client(); + + m_resource->setDestroy([this](CXxImageDescriptionV4* r) { PROTO::xxColorManagement->destroyResource(this); }); + m_resource->setOnDestroy([this](CXxImageDescriptionV4* r) { PROTO::xxColorManagement->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(makeShared(r->client(), r->version(), id), settings); + + if UNLIKELY (!RESOURCE->good()) + r->noMemory(); + + // CXXColorManagementImageDescriptionInfo should send everything in the constructor and be ready for destroying at this point + RESOURCE.reset(); + }); +} + +bool CXXColorManagementImageDescription::good() { + return m_resource->resource(); +} + +wl_client* CXXColorManagementImageDescription::client() { + return pClient; +} + +SP CXXColorManagementImageDescription::resource() { + return m_resource; +} + +CXXColorManagementImageDescriptionInfo::CXXColorManagementImageDescriptionInfo(SP resource_, const SImageDescription& settings_) : + m_resource(resource_), settings(settings_) { + if UNLIKELY (!good()) + return; + + pClient = m_resource->client(); + + const auto toProto = [](float value) { return int32_t(std::round(value * 10000)); }; + + if (settings.icc.fd >= 0) + m_resource->sendIccFile(settings.icc.fd, settings.icc.length); + + // 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 CXXColorManagementImageDescriptionInfo::good() { + return m_resource->resource(); +} + +wl_client* CXXColorManagementImageDescriptionInfo::client() { + return pClient; +} + +CXXColorManagementProtocol::CXXColorManagementProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { + ; +} + +void CXXColorManagementProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); + + if UNLIKELY (!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 CXXColorManagementProtocol::onImagePreferredChanged() { + for (auto const& feedback : m_vFeedbackSurfaces) { + feedback->resource->sendPreferredChanged(); + } +} + +void CXXColorManagementProtocol::destroyResource(CXXColorManager* resource) { + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; }); +} + +void CXXColorManagementProtocol::destroyResource(CXXColorManagementOutput* resource) { + std::erase_if(m_vOutputs, [&](const auto& other) { return other.get() == resource; }); +} + +void CXXColorManagementProtocol::destroyResource(CXXColorManagementSurface* resource) { + std::erase_if(m_vSurfaces, [&](const auto& other) { return other.get() == resource; }); +} + +void CXXColorManagementProtocol::destroyResource(CXXColorManagementFeedbackSurface* resource) { + std::erase_if(m_vFeedbackSurfaces, [&](const auto& other) { return other.get() == resource; }); +} + +void CXXColorManagementProtocol::destroyResource(CXXColorManagementParametricCreator* resource) { + std::erase_if(m_vParametricCreators, [&](const auto& other) { return other.get() == resource; }); +} + +void CXXColorManagementProtocol::destroyResource(CXXColorManagementImageDescription* resource) { + std::erase_if(m_vImageDescriptions, [&](const auto& other) { return other.get() == resource; }); +} diff --git a/src/protocols/XXColorManagement.hpp b/src/protocols/XXColorManagement.hpp new file mode 100644 index 00000000..b653c204 --- /dev/null +++ b/src/protocols/XXColorManagement.hpp @@ -0,0 +1,188 @@ +#pragma once + +#include +#include +#include +#include "WaylandProtocol.hpp" +#include "protocols/core/Compositor.hpp" +#include "xx-color-management-v4.hpp" +#include "types/ColorManagement.hpp" + +class CXXColorManager; +class CXXColorManagementOutput; +class CXXColorManagementImageDescription; +class CXXColorManagementProtocol; + +class CXXColorManager { + public: + CXXColorManager(SP resource_); + + bool good(); + + private: + SP resource; +}; + +class CXXColorManagementOutput { + public: + CXXColorManagementOutput(SP resource_); + + bool good(); + wl_client* client(); + + WP self; + WP imageDescription; + + private: + SP resource; + wl_client* pClient = nullptr; + + friend class CXXColorManagementProtocol; + friend class CXXColorManagementImageDescription; +}; + +class CXXColorManagementSurface { + public: + CXXColorManagementSurface(SP surface_); // temporary interface for frog CM + CXXColorManagementSurface(SP resource_, SP surface_); + + bool good(); + wl_client* client(); + + WP self; + WP surface; + + const NColorManagement::SImageDescription& imageDescription(); + bool hasImageDescription(); + void setHasImageDescription(bool has); + const hdr_output_metadata& hdrMetadata(); + void setHDRMetadata(const hdr_output_metadata& metadata); + bool needsHdrMetadataUpdate(); + + private: + SP resource; + wl_client* pClient = nullptr; + NColorManagement::SImageDescription m_imageDescription; + bool m_hasImageDescription = false; + bool m_needsNewMetadata = false; + hdr_output_metadata m_hdrMetadata; + + friend class CFrogColorManagementSurface; +}; + +class CXXColorManagementFeedbackSurface { + public: + CXXColorManagementFeedbackSurface(SP resource_, SP surface_); + + bool good(); + wl_client* client(); + + WP self; + WP surface; + + private: + SP resource; + wl_client* pClient = nullptr; + + WP m_currentPreferred; + + friend class CXXColorManagementProtocol; +}; + +class CXXColorManagementParametricCreator { + public: + CXXColorManagementParametricCreator(SP resource_); + + bool good(); + wl_client* client(); + + WP self; + + NColorManagement::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 resource; + wl_client* pClient = nullptr; + uint32_t valuesSet = 0; // enum eValuesSet +}; + +class CXXColorManagementImageDescription { + public: + CXXColorManagementImageDescription(SP resource_, bool allowGetInformation = false); + + bool good(); + wl_client* client(); + SP resource(); + + WP self; + + NColorManagement::SImageDescription settings; + + private: + SP m_resource; + wl_client* pClient = nullptr; + bool m_allowGetInformation = false; + + friend class CXXColorManagementOutput; +}; + +class CXXColorManagementImageDescriptionInfo { + public: + CXXColorManagementImageDescriptionInfo(SP resource_, const NColorManagement::SImageDescription& settings_); + + bool good(); + wl_client* client(); + + private: + SP m_resource; + wl_client* pClient = nullptr; + NColorManagement::SImageDescription settings; +}; + +class CXXColorManagementProtocol : public IWaylandProtocol { + public: + CXXColorManagementProtocol(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(CXXColorManager* resource); + void destroyResource(CXXColorManagementOutput* resource); + void destroyResource(CXXColorManagementSurface* resource); + void destroyResource(CXXColorManagementFeedbackSurface* resource); + void destroyResource(CXXColorManagementParametricCreator* resource); + void destroyResource(CXXColorManagementImageDescription* resource); + + std::vector> m_vManagers; + std::vector> m_vOutputs; + std::vector> m_vSurfaces; + std::vector> m_vFeedbackSurfaces; + std::vector> m_vParametricCreators; + std::vector> m_vImageDescriptions; + + friend class CXXColorManager; + friend class CXXColorManagementOutput; + friend class CXXColorManagementSurface; + friend class CXXColorManagementFeedbackSurface; + friend class CXXColorManagementParametricCreator; + friend class CXXColorManagementImageDescription; + + friend class CFrogColorManagementSurface; +}; + +namespace PROTO { + inline UP xxColorManagement; +}; diff --git a/src/protocols/core/Compositor.cpp b/src/protocols/core/Compositor.cpp index 92c3c425..06977338 100644 --- a/src/protocols/core/Compositor.cpp +++ b/src/protocols/core/Compositor.cpp @@ -306,6 +306,23 @@ void CWLSurfaceResource::breadthfirst(std::function, bfHelper(surfs, fn, data); } +SP CWLSurfaceResource::findFirstPreorderHelper(SP root, std::function)> fn) { + if (fn(root)) + return root; + for (auto const& sub : root->subsurfaces) { + if (sub.expired() || sub->surface.expired()) + continue; + const auto found = findFirstPreorderHelper(sub->surface.lock(), fn); + if (found) + return found; + } + return nullptr; +} + +SP CWLSurfaceResource::findFirstPreorder(std::function)> fn) { + return findFirstPreorderHelper(self.lock(), fn); +} + std::pair, Vector2D> CWLSurfaceResource::at(const Vector2D& localCoords, bool allowsInput) { std::vector, Vector2D>> surfs; breadthfirst([&surfs](SP surf, const Vector2D& offset, void* data) { surfs.emplace_back(std::make_pair<>(surf, offset)); }, &surfs); diff --git a/src/protocols/core/Compositor.hpp b/src/protocols/core/Compositor.hpp index aaaf9b1a..39690017 100644 --- a/src/protocols/core/Compositor.hpp +++ b/src/protocols/core/Compositor.hpp @@ -127,6 +127,7 @@ class CWLSurfaceResource { WP contentType; void breadthfirst(std::function, const Vector2D&, void*)> fn, void* data); + SP findFirstPreorder(std::function)> fn); CRegion accumulateCurrentBufferDamage(); void presentFeedback(timespec* when, PHLMONITOR pMonitor, bool discarded = false); void lockPendingState(); @@ -142,17 +143,18 @@ class CWLSurfaceResource { // this is for cursor dumb copy. Due to our (and wayland's...) architecture, // this stupid-ass hack is used - WP lastBuffer; + WP lastBuffer; - int stateLocks = 0; + int stateLocks = 0; - void destroy(); - void releaseBuffers(bool onlyCurrent = true); - void dropPendingBuffer(); - void dropCurrentBuffer(); - void commitPendingState(); - void bfHelper(std::vector> const& nodes, std::function, const Vector2D&, void*)> fn, void* data); - void updateCursorShm(CRegion damage = CBox{0, 0, INT16_MAX, INT16_MAX}); + void destroy(); + void releaseBuffers(bool onlyCurrent = true); + void dropPendingBuffer(); + void dropCurrentBuffer(); + void commitPendingState(); + void bfHelper(std::vector> const& nodes, std::function, const Vector2D&, void*)> fn, void* data); + SP findFirstPreorderHelper(SP root, std::function)> fn); + void updateCursorShm(CRegion damage = CBox{0, 0, INT16_MAX, INT16_MAX}); friend class CWLPointerResource; }; diff --git a/src/protocols/types/ColorManagement.cpp b/src/protocols/types/ColorManagement.cpp new file mode 100644 index 00000000..9daff1e2 --- /dev/null +++ b/src/protocols/types/ColorManagement.cpp @@ -0,0 +1,20 @@ +#include "ColorManagement.hpp" + +namespace NColorManagement { + const SPCPRimaries& getPrimaries(ePrimaries name) { + switch (name) { + case CM_PRIMARIES_SRGB: return NColorPrimaries::BT709; + case CM_PRIMARIES_BT2020: return NColorPrimaries::BT2020; + case CM_PRIMARIES_PAL_M: return NColorPrimaries::PAL_M; + case CM_PRIMARIES_PAL: return NColorPrimaries::PAL; + case CM_PRIMARIES_NTSC: return NColorPrimaries::NTSC; + case CM_PRIMARIES_GENERIC_FILM: return NColorPrimaries::GENERIC_FILM; + case CM_PRIMARIES_CIE1931_XYZ: return NColorPrimaries::DEFAULT_PRIMARIES; // FIXME + case CM_PRIMARIES_DCI_P3: return NColorPrimaries::DCI_P3; + case CM_PRIMARIES_DISPLAY_P3: return NColorPrimaries::DISPLAY_P3; + case CM_PRIMARIES_ADOBE_RGB: return NColorPrimaries::ADOBE_RGB; + default: return NColorPrimaries::DEFAULT_PRIMARIES; + } + } + +} \ No newline at end of file diff --git a/src/protocols/types/ColorManagement.hpp b/src/protocols/types/ColorManagement.hpp index e18f0b84..8c0643c1 100644 --- a/src/protocols/types/ColorManagement.hpp +++ b/src/protocols/types/ColorManagement.hpp @@ -1,47 +1,158 @@ #pragma once -#include "xx-color-management-v4.hpp" +#include "color-management-v1.hpp" -struct SImageDescription { - int iccFd = -1; - uint32_t iccSize = 0; +namespace NColorManagement { + enum ePrimaries : uint8_t { + CM_PRIMARIES_SRGB = 1, + CM_PRIMARIES_PAL_M = 2, + CM_PRIMARIES_PAL = 3, + CM_PRIMARIES_NTSC = 4, + CM_PRIMARIES_GENERIC_FILM = 5, + CM_PRIMARIES_BT2020 = 6, + CM_PRIMARIES_CIE1931_XYZ = 7, + CM_PRIMARIES_DCI_P3 = 8, + CM_PRIMARIES_DISPLAY_P3 = 9, + CM_PRIMARIES_ADOBE_RGB = 10, + }; - xxColorManagerV4TransferFunction transferFunction = XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_SRGB; - float transferFunctionPower = 1.0f; + enum eTransferFunction : uint8_t { + CM_TRANSFER_FUNCTION_BT1886 = 1, + CM_TRANSFER_FUNCTION_GAMMA22 = 2, + CM_TRANSFER_FUNCTION_GAMMA28 = 3, + CM_TRANSFER_FUNCTION_ST240 = 4, + CM_TRANSFER_FUNCTION_EXT_LINEAR = 5, + CM_TRANSFER_FUNCTION_LOG_100 = 6, + CM_TRANSFER_FUNCTION_LOG_316 = 7, + CM_TRANSFER_FUNCTION_XVYCC = 8, + CM_TRANSFER_FUNCTION_SRGB = 9, + CM_TRANSFER_FUNCTION_EXT_SRGB = 10, + CM_TRANSFER_FUNCTION_ST2084_PQ = 11, + CM_TRANSFER_FUNCTION_ST428 = 12, + CM_TRANSFER_FUNCTION_HLG = 13, + }; + + // NOTE should be ok this way. unsupported primaries/tfs must be rejected earlier. supported enum values should be in sync with proto. + // might need a proper switch-case and additional INVALID enum value. + inline wpColorManagerV1Primaries convertPrimaries(ePrimaries primaries) { + return (wpColorManagerV1Primaries)primaries; + } + inline ePrimaries convertPrimaries(wpColorManagerV1Primaries primaries) { + return (ePrimaries)primaries; + } + inline wpColorManagerV1TransferFunction convertTransferFunction(eTransferFunction tf) { + return (wpColorManagerV1TransferFunction)tf; + } + inline eTransferFunction convertTransferFunction(wpColorManagerV1TransferFunction tf) { + return (eTransferFunction)tf; + } - 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; + namespace NColorPrimaries { + static const auto DEFAULT_PRIMARIES = SPCPRimaries{}; - uint32_t maxCLL = 0; - uint32_t maxFALL = 0; -}; + static const auto BT709 = 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 PAL_M = SPCPRimaries{ + .red = {.x = 0.67, .y = 0.33}, + .green = {.x = 0.21, .y = 0.71}, + .blue = {.x = 0.14, .y = 0.08}, + .white = {.x = 0.310, .y = 0.316}, + }; + static const auto PAL = SPCPRimaries{ + .red = {.x = 0.640, .y = 0.330}, + .green = {.x = 0.290, .y = 0.600}, + .blue = {.x = 0.150, .y = 0.060}, + .white = {.x = 0.3127, .y = 0.3290}, + }; + static const auto NTSC = SPCPRimaries{ + .red = {.x = 0.630, .y = 0.340}, + .green = {.x = 0.310, .y = 0.595}, + .blue = {.x = 0.155, .y = 0.070}, + .white = {.x = 0.3127, .y = 0.3290}, + }; + static const auto GENERIC_FILM = SPCPRimaries{ + .red = {.x = 0.243, .y = 0.692}, + .green = {.x = 0.145, .y = 0.049}, + .blue = {.x = 0.681, .y = 0.319}, // NOLINT(modernize-use-std-numbers) + .white = {.x = 0.310, .y = 0.316}, + }; + static const auto BT2020 = 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}, + }; -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}}; + // FIXME CIE1931_XYZ - 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}}; + static const auto DCI_P3 = SPCPRimaries{ + .red = {.x = 0.680, .y = 0.320}, + .green = {.x = 0.265, .y = 0.690}, + .blue = {.x = 0.150, .y = 0.060}, + .white = {.x = 0.314, .y = 0.351}, + }; + static const auto DISPLAY_P3 = SPCPRimaries{ + .red = {.x = 0.680, .y = 0.320}, + .green = {.x = 0.265, .y = 0.690}, + .blue = {.x = 0.150, .y = 0.060}, + .white = {.x = 0.3127, .y = 0.3290}, + }; + static const auto ADOBE_RGB = SPCPRimaries{ + .red = {.x = 0.6400, .y = 0.3300}, + .green = {.x = 0.2100, .y = 0.7100}, + .blue = {.x = 0.1500, .y = 0.0600}, + .white = {.x = 0.3127, .y = 0.3290}, + }; + } + + const SPCPRimaries& getPrimaries(ePrimaries name); + + struct SImageDescription { + uint32_t id = 0; // FIXME needs id setting + + struct SIccFile { + int fd = -1; + uint32_t length = 0; + uint32_t offset = 0; + } icc; + + bool windowsScRGB = false; + + eTransferFunction transferFunction = CM_TRANSFER_FUNCTION_SRGB; + float transferFunctionPower = 1.0f; + + bool primariesNameSet = false; + ePrimaries primariesNamed = CM_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 + SPCPRimaries 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; + }; } \ No newline at end of file diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index f01a0d12..2ebf5629 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -41,6 +41,7 @@ using namespace Hyprutils::Utils; using namespace Hyprutils::OS; using enum NContentType::eContentType; +using namespace NColorManagement; extern "C" { #include @@ -1449,7 +1450,7 @@ static hdr_output_metadata createHDRMetadata(uint8_t eotf, Aquamarine::IOutput:: } static hdr_output_metadata createHDRMetadata(SImageDescription settings, Aquamarine::IOutput::SParsedEDID edid) { - if (settings.transferFunction != XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ) + if (settings.transferFunction != CM_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)); }; @@ -1498,12 +1499,15 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) { Debug::log(TRACE, "ColorManagement supportsBT2020 {}, supportsPQ {}", pMonitor->output->parsedEDID.supportsBT2020, SUPPORTSPQ); if (pMonitor->output->parsedEDID.supportsBT2020 && 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()) { + const auto WINDOW = pMonitor->activeWorkspace->getFullscreenWindow(); + const auto ROOT_SURF = WINDOW->m_pWLSurface->resource(); + const auto SURF = + ROOT_SURF->findFirstPreorder([ROOT_SURF](SP surf) { return surf->colorManagement.valid() && surf->extends() == ROOT_SURF->extends(); }); + + if (SURF && SURF->colorManagement.valid() && SURF->colorManagement->hasImageDescription()) { bool needsHdrMetadataUpdate = SURF->colorManagement->needsHdrMetadataUpdate() || pMonitor->m_previousFSWindow != WINDOW; if (SURF->colorManagement->needsHdrMetadataUpdate()) - SURF->colorManagement->setHDRMetadata(createHDRMetadata(SURF->colorManagement.get()->imageDescription(), pMonitor->output->parsedEDID)); + SURF->colorManagement->setHDRMetadata(createHDRMetadata(SURF->colorManagement->imageDescription(), pMonitor->output->parsedEDID)); if (needsHdrMetadataUpdate) pMonitor->output->state->setHDRMetadata(SURF->colorManagement->hdrMetadata()); } else if ((pMonitor->output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2) != *PHDR)