diff --git a/CMakeLists.txt b/CMakeLists.txt index aa746d2e..c1ecb14c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -316,6 +316,8 @@ protocolnew("protocols" "kde-server-decoration" true) protocolnew("protocols" "wlr-data-control-unstable-v1" true) protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-focus-grab-v1" true) protocolnew("protocols" "wlr-layer-shell-unstable-v1" true) +protocolnew("protocols" "xx-color-management-v4" true) +protocolnew("protocols" "frog-color-management-v1" true) protocolnew("protocols" "wayland-drm" true) protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-ctm-control-v1" true) protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-surface-v1" true) diff --git a/protocols/frog-color-management-v1.xml b/protocols/frog-color-management-v1.xml new file mode 100644 index 00000000..aab235a7 --- /dev/null +++ b/protocols/frog-color-management-v1.xml @@ -0,0 +1,366 @@ + + + + + Copyright © 2023 Joshua Ashton for Valve Software + Copyright © 2023 Xaver Hugl + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + The aim of this color management extension is to get HDR games working quickly, + and have an easy way to test implementations in the wild before the upstream + protocol is ready to be merged. + For that purpose it's intentionally limited and cut down and does not serve + all uses cases. + + + + + The color management factory singleton creates color managed surface objects. + + + + + + + + + + + + + + + + Interface for changing surface color management and HDR state. + + An implementation must: support every part of the version + of the frog_color_managed_surface interface it exposes. + Including all known enums associated with a given version. + + + + + Destroying the color managed surface resets all known color + state for the surface back to 'undefined' implementation-specific + values. + + + + + + Extended information on the transfer functions described + here can be found in the Khronos Data Format specification: + https://registry.khronos.org/DataFormat/specs/1.3/dataformat.1.3.html + + + + + + + + + + + + + + + + + + + + + + + + + + + + Extended information on render intents described + here can be found in ICC.1:2022: + + https://www.color.org/specification/ICC.1-2022-05.pdf + + + + + + + NOTE: On a surface with "perceptual" (default) render intent, handling of the container's + color volume + is implementation-specific, and may differ between different transfer functions it is paired + with: + ie. sRGB + 709 rendering may have it's primaries widened to more of the available display's + gamut + to be be more pleasing for the viewer. + Compared to scRGB Linear + 709 being treated faithfully as 709 + (including utilizing negatives out of the 709 gamut triangle) + + + + + + + Forwards HDR metadata from the client to the compositor. + + HDR Metadata Infoframe as per CTA 861.G spec. + + Usage of this HDR metadata is implementation specific and + outside of the scope of this protocol. + + + + Mastering Red Color Primary X Coordinate of the Data. + + Coded as unsigned 16-bit values in units of + 0.00002, where 0x0000 represents zero and 0xC350 + represents 1.0000. + + + + + Mastering Red Color Primary Y Coordinate of the Data. + + Coded as unsigned 16-bit values in units of + 0.00002, where 0x0000 represents zero and 0xC350 + represents 1.0000. + + + + + Mastering Green Color Primary X Coordinate of the Data. + + Coded as unsigned 16-bit values in units of + 0.00002, where 0x0000 represents zero and 0xC350 + represents 1.0000. + + + + + Mastering Green Color Primary Y Coordinate of the Data. + + Coded as unsigned 16-bit values in units of + 0.00002, where 0x0000 represents zero and 0xC350 + represents 1.0000. + + + + + Mastering Blue Color Primary X Coordinate of the Data. + + Coded as unsigned 16-bit values in units of + 0.00002, where 0x0000 represents zero and 0xC350 + represents 1.0000. + + + + + Mastering Blue Color Primary Y Coordinate of the Data. + + Coded as unsigned 16-bit values in units of + 0.00002, where 0x0000 represents zero and 0xC350 + represents 1.0000. + + + + + Mastering White Point X Coordinate of the Data. + + These are coded as unsigned 16-bit values in units of + 0.00002, where 0x0000 represents zero and 0xC350 + represents 1.0000. + + + + + Mastering White Point Y Coordinate of the Data. + + These are coded as unsigned 16-bit values in units of + 0.00002, where 0x0000 represents zero and 0xC350 + represents 1.0000. + + + + + Max Mastering Display Luminance. + This value is coded as an unsigned 16-bit value in units of 1 cd/m2, + where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2. + + + + + Min Mastering Display Luminance. + This value is coded as an unsigned 16-bit value in units of + 0.0001 cd/m2, where 0x0001 represents 0.0001 cd/m2 and 0xFFFF + represents 6.5535 cd/m2. + + + + + Max Content Light Level. + This value is coded as an unsigned 16-bit value in units of 1 cd/m2, + where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2. + + + + + Max Frame Average Light Level. + This value is coded as an unsigned 16-bit value in units of 1 cd/m2, + where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2. + + + + + + + Current preferred metadata for a surface. + The application should use this information to tone-map its buffers + to this target before committing. + + This metadata does not necessarily correspond to any physical output, but + rather what the compositor thinks would be best for a given surface. + + + + Specifies a known transfer function that corresponds to the + output the surface is targeting. + + + + + Output Red Color Primary X Coordinate of the Data. + + Coded as unsigned 16-bit values in units of + 0.00002, where 0x0000 represents zero and 0xC350 + represents 1.0000. + + + + + Output Red Color Primary Y Coordinate of the Data. + + Coded as unsigned 16-bit values in units of + 0.00002, where 0x0000 represents zero and 0xC350 + represents 1.0000. + + + + + Output Green Color Primary X Coordinate of the Data. + + Coded as unsigned 16-bit values in units of + 0.00002, where 0x0000 represents zero and 0xC350 + represents 1.0000. + + + + + Output Green Color Primary Y Coordinate of the Data. + + Coded as unsigned 16-bit values in units of + 0.00002, where 0x0000 represents zero and 0xC350 + represents 1.0000. + + + + + Output Blue Color Primary X Coordinate of the Data. + + Coded as unsigned 16-bit values in units of + 0.00002, where 0x0000 represents zero and 0xC350 + represents 1.0000. + + + + + Output Blue Color Primary Y Coordinate of the Data. + + Coded as unsigned 16-bit values in units of + 0.00002, where 0x0000 represents zero and 0xC350 + represents 1.0000. + + + + + Output White Point X Coordinate of the Data. + + These are coded as unsigned 16-bit values in units of + 0.00002, where 0x0000 represents zero and 0xC350 + represents 1.0000. + + + + + Output White Point Y Coordinate of the Data. + + These are coded as unsigned 16-bit values in units of + 0.00002, where 0x0000 represents zero and 0xC350 + represents 1.0000. + + + + + Max Output Luminance + The max luminance in nits that the output is capable of rendering in small areas. + Content should: not exceed this value to avoid clipping. + + This value is coded as an unsigned 16-bit value in units of 1 cd/m2, + where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2. + + + + + Min Output Luminance + The min luminance that the output is capable of rendering. + Content should: not exceed this value to avoid clipping. + + This value is coded as an unsigned 16-bit value in units of + 0.0001 cd/m2, where 0x0001 represents 0.0001 cd/m2 and 0xFFFF + represents 6.5535 cd/m2. + + + + + Max Full Frame Luminance + The max luminance in nits that the output is capable of rendering for the + full frame sustained. + + This value is coded as an unsigned 16-bit value in units of 1 cd/m2, + where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2. + + + + + \ No newline at end of file diff --git a/protocols/meson.build b/protocols/meson.build index a39602b7..fdbbf181 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -33,6 +33,8 @@ protocols = [ 'wayland-drm.xml', 'wlr-data-control-unstable-v1.xml', 'wlr-screencopy-unstable-v1.xml', + 'xx-color-management-v4.xml', + 'frog-color-management-v1.xml', hyprland_protocol_dir / 'protocols/hyprland-global-shortcuts-v1.xml', hyprland_protocol_dir / 'protocols/hyprland-toplevel-export-v1.xml', hyprland_protocol_dir / 'protocols/hyprland-focus-grab-v1.xml', diff --git a/protocols/xx-color-management-v4.xml b/protocols/xx-color-management-v4.xml new file mode 100644 index 00000000..23ff716e --- /dev/null +++ b/protocols/xx-color-management-v4.xml @@ -0,0 +1,1457 @@ + + + + Copyright 2019 Sebastian Wick + Copyright 2019 Erwin Burema + Copyright 2020 AMD + Copyright 2020-2024 Collabora, Ltd. + Copyright 2024 Xaver Hugl + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + The aim of the color management extension is to allow clients to know + the color properties of outputs, and to tell the compositor about the color + properties of their content on surfaces. Doing this enables a compositor + to perform automatic color management of content for different outputs + according to how content is intended to look like. + + The color properties are represented as an image description object which + is immutable after it has been created. A wl_output always has an + associated image description that clients can observe. A wl_surface + always has an associated preferred image description as a hint chosen by + the compositor that clients can also observe. Clients can set an image + description on a wl_surface to denote the color characteristics of the + surface contents. + + An image description includes SDR and HDR colorimetry and encoding, HDR + metadata, and viewing environment parameters. An image description does + not include the properties set through color-representation extension. + It is expected that the color-representation extension is used in + conjunction with the color management extension when necessary, + particularly with the YUV family of pixel formats. + + Recommendation ITU-T H.273 + "Coding-independent code points for video signal type identification" + shall be referred to as simply H.273 here. + + The color-and-hdr repository + (https://gitlab.freedesktop.org/pq/color-and-hdr) contains + background information on the protocol design and legacy color management. + It also contains a glossary, learning resources for digital color, tools, + samples and more. + + The terminology used in this protocol is based on common color science and + color encoding terminology where possible. The glossary in the color-and-hdr + repository shall be the authority on the definition of terms in this + protocol. + + + + + A global interface used for getting color management extensions for + wl_surface and wl_output objects, and for creating client defined image + description objects. The extension interfaces allow + getting the image description of outputs and setting the image + description of surfaces. + + + + + Destroy the xx_color_manager_v4 object. This does not affect any other + objects in any way. + + + + + + + + + + + See the ICC.1:2022 specification from the International Color Consortium + for more details about rendering intents. + + The principles of ICC defined rendering intents apply with all types of + image descriptions, not only those with ICC file profiles. + + Compositors must support the perceptual rendering intent. Other + rendering intents are optional. + + + + + + + + + + + + + + + + + + + + The compositor supports set_mastering_display_primaries request with a + target color volume fully contained inside the primary color volume. + + + + + The compositor additionally supports target color volumes that + extend outside of the primary color volume. + + This can only be advertised if feature set_mastering_display_primaries + is supported as well. + + + + + + + Named color primaries used to encode well-known sets of primaries. H.273 + is the authority, when it comes to the exact values of primaries and + authoritative specifications, where an equivalent code point exists. + + Descriptions do list the specifications for convenience. + + + + + Color primaries as defined by + - Rec. ITU-R BT.709-6 + - Rec. ITU-R BT.1361-0 conventional colour gamut system and extended + colour gamut system (historical) + - IEC 61966-2-1 sRGB or sYCC + - IEC 61966-2-4 + - Society of Motion Picture and Television Engineers (SMPTE) RP 177 + (1993) Annex B + Equivalent to H.273 ColourPrimaries code point 1. + + + + + Color primaries as defined by + - Rec. ITU-R BT.470-6 System M (historical) + - United States National Television System Committee 1953 + Recommendation for transmission standards for color television + - United States Federal Communications Commission (2003) Title 47 Code + of Federal Regulations 73.682 (a)(20) + Equivalent to H.273 ColourPrimaries code point 4. + + + + + Color primaries as defined by + - Rec. ITU-R BT.470-6 System B, G (historical) + - Rec. ITU-R BT.601-7 625 + - Rec. ITU-R BT.1358-0 625 (historical) + - Rec. ITU-R BT.1700-0 625 PAL and 625 SECAM + Equivalent to H.273 ColourPrimaries code point 5. + + + + + Color primaries as defined by + - Rec. ITU-R BT.601-7 525 + - Rec. ITU-R BT.1358-1 525 or 625 (historical) + - Rec. ITU-R BT.1700-0 NTSC + - SMPTE 170M (2004) + - SMPTE 240M (1999) (historical) + Equivalent to H.273 ColourPrimaries code point 6 and 7. + + + + + Color primaries as defined by H.273 for generic film. + Equivalent to H.273 ColourPrimaries code point 8. + + + + + Color primaries as defined by + - Rec. ITU-R BT.2020-2 + - Rec. ITU-R BT.2100-0 + Equivalent to H.273 ColourPrimaries code point 9. + + + + + Color primaries as defined as the maximum of the CIE 1931 XYZ color + space by + - SMPTE ST 428-1 + - (CIE 1931 XYZ as in ISO 11664-1) + Equivalent to H.273 ColourPrimaries code point 10. + + + + + Color primaries as defined by Digital Cinema System and published in + SMPTE RP 431-2 (2011). Equivalent to H.273 ColourPrimaries code point + 11. + + + + + Color primaries as defined by Digital Cinema System and published in + SMPTE EG 432-1 (2010). + Equivalent to H.273 ColourPrimaries code point 12. + + + + + Color primaries as defined by Adobe as "Adobe RGB" and later published + by ISO 12640-4 (2011). + + + + + + + Named transfer functions used to encode well-known transfer + characteristics. H.273 is the authority, when it comes to the exact + formulas and authoritative specifications, where an equivalent code + point exists. + + Descriptions do list the specifications for convenience. + + + + + Transfer characteristics as defined by + - Rec. ITU-R BT.709-6 + - Rec. ITU-R BT.1361-0 conventional colour gamut system (historical) + Equivalent to H.273 TransferCharacteristics code point 1, 6, 14, 15. + + + + + Transfer characteristics as defined by + - Rec. ITU-R BT.470-6 System M (historical) + - United States National Television System Committee 1953 + Recommendation for transmission standards for color television + - United States Federal Communications Commission (2003) Title 47 Code + of Federal Regulations 73.682 (a) (20) + - Rec. ITU-R BT.1700-0 625 PAL and 625 SECAM + Equivalent to H.273 TransferCharacteristics code point 4. + + + + + Transfer characteristics as defined by + - Rec. ITU-R BT.470-6 System B, G (historical) + Equivalent to H.273 TransferCharacteristics code point 5. + + + + + Transfer characteristics as defined by + - SMPTE ST 240 (1999) + Equivalent to H.273 TransferCharacteristics code point 7. + + + + + Linear transfer characteristics. + Equivalent to H.273 TransferCharacteristics code point 8. + + + + + Logarithmic transfer characteristic (100:1 range). + Equivalent to H.273 TransferCharacteristics code point 9. + + + + + Logarithmic transfer characteristic (100 * Sqrt(10) : 1 range). + Equivalent to H.273 TransferCharacteristics code point 10. + + + + + Transfer characteristics as defined by + - IEC 61966-2-4 + Equivalent to H.273 TransferCharacteristics code point 11. + + + + + Transfer characteristics as defined by + - Rec. ITU-R BT.1361-0 extended colour gamut system (historical) + Equivalent to H.273 TransferCharacteristics code point 12. + + + + + Transfer characteristics as defined by + - IEC 61966-2-1 sRGB + Equivalent to H.273 TransferCharacteristics code point 13 with + MatrixCoefficients set to 0. + + + + + Transfer characteristics as defined by + - IEC 61966-2-1 sYCC + Equivalent to H.273 TransferCharacteristics code point 13 with + MatrixCoefficients set to anything but 0. + + + + + Transfer characteristics as defined by + - SMPTE ST 2084 (2014) for 10-, 12-, 14- and 16-bit systems + - Rec. ITU-R BT.2100-2 perceptual quantization (PQ) system + Equivalent to H.273 TransferCharacteristics code point 16. + + This TF implies these default luminances + - primary color volume minimum: 0.005 cd/m² + - primary color volume maximum: 10000 cd/m² + - reference white: 203 cd/m² + + + + + Transfer characteristics as defined by + - SMPTE ST 428-1 (2019) + Equivalent to H.273 TransferCharacteristics code point 17. + + + + + Transfer characteristics as defined by + - ARIB STD-B67 (2015) + - Rec. ITU-R BT.2100-2 hybrid log-gamma (HLG) system + Equivalent to H.273 TransferCharacteristics code point 18. + + This TF implies these default luminances + - primary color volume minimum: 0.005 cd/m² + - primary color volume maximum: 1000 cd/m² + - reference white: 203 cd/m² + Note: HLG is a scene referred signal. All absolute luminance values + used here for HLG assume a 1000 cd/m² display. + + + + + + + This creates a new xx_color_management_output_v4 object for the + given wl_output. + + See the xx_color_management_output_v4 interface for more details. + + + + + + + + + If a xx_color_management_surface_v4 object already exists for the given + wl_surface, the protocol error surface_exists is raised. + + This creates a new color xx_color_management_surface_v4 object for the + given wl_surface. + + See the xx_color_management_surface_v4 interface for more details. + + + + + + + + + This creates a new color xx_color_management_feedback_surface_v4 object + for the given wl_surface. + + See the xx_color_management_feedback_surface_v4 interface for more + details. + + + + + + + + + Makes a new ICC-based image description creator object with all + properties initially unset. The client can then use the object's + interface to define all the required properties for an image description + and finally create a xx_image_description_v4 object. + + This request can be used when the compositor advertises + xx_color_manager_v4.feature.icc_v2_v4. + Otherwise this request raises the protocol error unsupported_feature. + + + + + + + + Makes a new parametric image description creator object with all + properties initially unset. The client can then use the object's + interface to define all the required properties for an image description + and finally create a xx_image_description_v4 object. + + This request can be used when the compositor advertises + xx_color_manager_v4.feature.parametric. + Otherwise this request raises the protocol error unsupported_feature. + + + + + + + + When this object is created, it shall immediately send this event once + for each rendering intent the compositor supports. + + + + + + + + When this object is created, it shall immediately send this event once + for each compositor supported feature listed in the enumeration. + + + + + + + + When this object is created, it shall immediately send this event once + for each named transfer function the compositor supports with the + parametric image description creator. + + + + + + + + When this object is created, it shall immediately send this event once + for each named set of primaries the compositor supports with the + parametric image description creator. + + + + + + + + + A xx_color_management_output_v4 describes the color properties of an + output. + + The xx_color_management_output_v4 is associated with the wl_output global + underlying the wl_output object. Therefore the client destroying the + wl_output object has no impact, but the compositor removing the output + global makes the xx_color_management_output_v4 object inert. + + + + + Destroy the color xx_color_management_output_v4 object. This does not + affect any remaining protocol objects. + + + + + + This event is sent whenever the image description of the output changed, + followed by one wl_output.done event common to output events across all + extensions. + + If the client wants to use the updated image description, it needs to do + get_image_description again, because image description objects are + immutable. + + + + + + This creates a new xx_image_description_v4 object for the current image + description of the output. There always is exactly one image description + active for an output so the client should destroy the image description + created by earlier invocations of this request. This request is usually + sent as a reaction to the image_description_changed event or when + creating a xx_color_management_output_v4 object. + + The image description of an output represents the color encoding the + output expects. There might be performance and power advantages, as well + as improved color reproduction, if a content update matches the image + description of the output it is being shown on. If a content update is + shown on any other output than the one it matches the image description + of, then the color reproduction on those outputs might be considerably + worse. + + The created xx_image_description_v4 object preserves the image + description of the output from the time the object was created. + + The resulting image description object allows get_information request. + + If this protocol object is inert, the resulting image description object + shall immediately deliver the xx_image_description_v4.failed event with + the no_output cause. + + If the interface version is inadequate for the output's image + description, meaning that the client does not support all the events + needed to deliver the crucial information, the resulting image + description object shall immediately deliver the + xx_image_description_v4.failed event with the low_version cause. + + Otherwise the object shall immediately deliver the ready event. + + + + + + + + + A xx_color_management_surface_v4 allows the client to set the color + space and HDR properties of a surface. + + If the wl_surface associated with the xx_color_management_surface_v4 is + destroyed, the xx_color_management_surface_v4 object becomes inert. + + + + + Destroy the xx_color_management_surface_v4 object and do the same as + unset_image_description. + + + + + + + + + + + + Set the image description of the underlying surface. The image + description and rendering intent are double-buffered state, see + wl_surface.commit. + + It is the client's responsibility to understand the image description + it sets on a surface, and to provide content that matches that image + description. Compositors might convert images to match their own or any + other image descriptions. + + Image description whose creation gracefully failed (received + xx_image_description_v4.failed) are forbidden in this request, and in + such case the protocol error image_description is raised. + + All image descriptions whose creation succeeded (received + xx_image_description_v4.ready) are allowed and must always be accepted + by the compositor. + + A rendering intent provides the client's preference on how content + colors should be mapped to each output. The render_intent value must + be one advertised by the compositor with + xx_color_manager_v4.render_intent event, otherwise the protocol error + render_intent is raised. + + By default, a surface does not have an associated image description + nor a rendering intent. The handling of color on such surfaces is + compositor implementation defined. Compositors should handle such + surfaces as sRGB but may handle them differently if they have specific + requirements. + + + + + + + + + This request removes any image description from the surface. See + set_image_description for how a compositor handles a surface without + an image description. This is double-buffered state, see + wl_surface.commit. + + + + + + + A xx_color_management_feedback_surface_v4 allows the client to get the + preferred color description of a surface. + + If the wl_surface associated with this object is destroyed, the + xx_color_management_feedback_surface_v4 object becomes inert. + + + + + Destroy the xx_color_management_feedback_surface_v4 object. + + + + + + + + + + + The preferred image description is the one which likely has the most + performance and/or quality benefits for the compositor if used by the + client for its wl_surface contents. This event is sent whenever the + compositor changes the wl_surface's preferred image description. + + This event is merely a notification. When the client wants to know + what the preferred image description is, it shall use the get_preferred + request. + + The preferred image description is not automatically used for anything. + It is only a hint, and clients may set any valid image description with + set_image_description but there might be performance and color accuracy + improvements by providing the wl_surface contents in the preferred + image description. Therefore clients that can, should render according + to the preferred image description + + + + + + If this protocol object is inert, the protocol error inert is raised. + + The preferred image description represents the compositor's preferred + color encoding for this wl_surface at the current time. There might be + performance and power advantages, as well as improved color + reproduction, if the image description of a content update matches the + preferred image description. + + This creates a new xx_image_description_v4 object for the currently + preferred image description for the wl_surface. The client should + stop using and destroy the image descriptions created by earlier + invocations of this request for the associated wl_surface. + This request is usually sent as a reaction to the preferred_changed + event or when creating a xx_color_management_feedback_surface_v4 object + if the client is capable of adapting to image descriptions. + + The created xx_image_description_v4 object preserves the preferred image + description of the wl_surface from the time the object was created. + + The resulting image description object allows get_information request. + + If the interface version is inadequate for the preferred image + description, meaning that the client does not support all the + events needed to deliver the crucial information, the resulting image + description object shall immediately deliver the + xx_image_description_v4.failed event with the low_version cause, + otherwise the object shall immediately deliver the ready event. + + + + + + + + + This type of object is used for collecting all the information required + to create a xx_image_description_v4 object from an ICC file. A complete + set of required parameters consists of these properties: + - ICC file + + Each required property must be set exactly once if the client is to create + an image description. The set requests verify that a property was not + already set. The create request verifies that all required properties are + set. There may be several alternative requests for setting each property, + and in that case the client must choose one of them. + + Once all properties have been set, the create request must be used to + create the image description object, destroying the creator in the + process. + + + + + + + + + + + + + + + Create an image description object based on the ICC information + previously set on this object. A compositor must parse the ICC data in + some undefined but finite amount of time. + + The completeness of the parameter set is verified. If the set is not + complete, the protocol error incomplete_set is raised. For the + definition of a complete set, see the description of this interface. + + If the particular combination of the information is not supported + by the compositor, the resulting image description object shall + immediately deliver the xx_image_description_v4.failed event with the + 'unsupported' cause. If a valid image description was created from the + information, the xx_image_description_v4.ready event will eventually + be sent instead. + + This request destroys the xx_image_description_creator_icc_v4 object. + + The resulting image description object does not allow get_information + request. + + + + + + + + Sets the ICC profile file to be used as the basis of the image + description. + + The data shall be found through the given fd at the given offset, having + the given length. The fd must seekable and readable. Violating these + requirements raises the bad_fd protocol error. + + If reading the data fails due to an error independent of the client, the + compositor shall send the xx_image_description_v4.failed event on the + created xx_image_description_v4 with the 'operating_system' cause. + + The maximum size of the ICC profile is 4 MB. If length is greater than + that or zero, the protocol error bad_size is raised. If offset + length + exceeds the file size, the protocol error out_of_file is raised. + + A compositor may read the file at any time starting from this request + and only until whichever happens first: + - If create request was issued, the xx_image_description_v4 object + delivers either failed or ready event; or + - if create request was not issued, this + xx_image_description_creator_icc_v4 object is destroyed. + + A compositor shall not modify the contents of the file, and the fd may + be sealed for writes and size changes. The client must ensure to its + best ability that the data does not change while the compositor is + reading it. + + The data must represent a valid ICC profile. The ICC profile version + must be 2 or 4, it must be a 3 channel profile and the class must be + Display or ColorSpace. Violating these requirements will not result in a + protocol error but will eventually send the + xx_image_description_v4.failed event on the created + xx_image_description_v4 with the 'unsupported' cause. + + See the International Color Consortium specification ICC.1:2022 for more + details about ICC profiles. + + If ICC file has already been set on this object, the protocol error + already_set is raised. + + + + + + + + + + + This type of object is used for collecting all the parameters required + to create a xx_image_description_v4 object. A complete set of required + parameters consists of these properties: + - transfer characteristic function (tf) + - chromaticities of primaries and white point (primary color volume) + + The following properties are optional and have a well-defined default + if not explicitly set: + - primary color volume luminance range + - reference white luminance level + - mastering display primaries and white point (target color volume) + - mastering luminance range + - maximum content light level + - maximum frame-average light level + + Each required property must be set exactly once if the client is to create + an image description. The set requests verify that a property was not + already set. The create request verifies that all required properties are + set. There may be several alternative requests for setting each property, + and in that case the client must choose one of them. + + Once all properties have been set, the create request must be used to + create the image description object, destroying the creator in the + process. + + + + + + + + + + + + + + + + + + Create an image description object based on the parameters previously + set on this object. + + The completeness of the parameter set is verified. If the set is not + complete, the protocol error incomplete_set is raised. For the + definition of a complete set, see the description of this interface. + + Also, the combination of the parameter set is verified. If the set is + not consistent, the protocol error inconsistent_set is raised. + + If the particular combination of the parameter set is not supported + by the compositor, the resulting image description object shall + immediately deliver the xx_image_description_v4.failed event with the + 'unsupported' cause. If a valid image description was created from the + parameter set, the xx_image_description_v4.ready event will eventually + be sent instead. + + This request destroys the xx_image_description_creator_params_v4 + object. + + The resulting image description object does not allow get_information + request. + + + + + + + + Sets the transfer characteristic using explicitly enumerated named + functions. + + When the resulting image description is attached to an image, the + content should be encoded and decoded according to the industry standard + practices for the transfer characteristic. + + Only names advertised with xx_color_manager_v4 event supported_tf_named + are allowed. Other values shall raise the protocol error invalid_tf. + + If transfer characteristic has already been set on this object, the + protocol error already_set is raised. + + + + + + + + Sets the color component transfer characteristic to a power curve with + the given exponent. This curve represents the conversion from electrical + to optical pixel or color values. + + When the resulting image description is attached to an image, the + content should be encoded with the inverse of the power curve. + + The curve exponent shall be multiplied by 10000 to get the argument eexp + value to carry the precision of 4 decimals. + + The curve exponent must be at least 1.0 and at most 10.0. Otherwise the + protocol error invalid_tf is raised. + + If transfer characteristic has already been set on this object, the + protocol error already_set is raised. + + This request can be used when the compositor advertises + xx_color_manager_v4.feature.set_tf_power. Otherwise this request raises + the protocol error unsupported_feature. + + + + + + + + Sets the color primaries and white point using explicitly named sets. + This describes the primary color volume which is the basis for color + value encoding. + + Only names advertised with xx_color_manager_v4 event + supported_primaries_named are allowed. Other values shall raise the + protocol error invalid_primaries. + + If primaries have already been set on this object, the protocol error + already_set is raised. + + + + + + + + Sets the color primaries and white point using CIE 1931 xy chromaticity + coordinates. This describes the primary color volume which is the basis + for color value encoding. + + Each coordinate value is multiplied by 10000 to get the argument value + to carry precision of 4 decimals. + + If primaries have already been set on this object, the protocol error + already_set is raised. + + This request can be used if the compositor advertises + xx_color_manager_v4.feature.set_primaries. Otherwise this request raises + the protocol error unsupported_feature. + + + + + + + + + + + + + + + Sets the primary color volume luminance range and the reference white + luminance level. + + The default luminances are + - primary color volume minimum: 0.2 cd/m² + - primary color volume maximum: 80 cd/m² + - reference white: 80 cd/m² + + Setting a named transfer characteristic can imply other default + luminances. + + The default luminances get overwritten when this request is used. + + 'min_lum' and 'max_lum' specify the minimum and maximum luminances of + the primary color volume as reproduced by the targeted display. + + 'reference_lum' specifies the luminance of the reference white as + reproduced by the targeted display, and reflects the targeted viewing + environment. + + Compositors should make sure that all content is anchored, meaning that + an input signal level of 'reference_lum' on one image description and + another input signal level of 'reference_lum' on another image + description should produce the same output level, even though the + 'reference_lum' on both image representations can be different. + + If 'max_lum' is less than the 'reference_lum', or 'reference_lum' is + less than or equal to 'min_lum', the protocol error invalid_luminance is + raised. + + The minimum luminance is multiplied by 10000 to get the argument + 'min_lum' value and carries precision of 4 decimals. The maximum + luminance and reference white luminance values are unscaled. + + If the primary color volume luminance range and the reference white + luminance level have already been set on this object, the protocol error + already_set is raised. + + This request can be used if the compositor advertises + xx_color_manager_v4.feature.set_luminances. Otherwise this request + raises the protocol error unsupported_feature. + + + + + + + + + + Provides the color primaries and white point of the mastering display + using CIE 1931 xy chromaticity coordinates. This is compatible with the + SMPTE ST 2086 definition of HDR static metadata. + + The mastering display primaries define the target color volume. + + If mastering display primaries are not explicitly set, the target color + volume is assumed to be equal to the primary color volume. + + The target color volume is defined by all tristimulus values between 0.0 + and 1.0 (inclusive) of the color space defined by the given mastering + display primaries and white point. The colorimetry is identical between + the container color space and the mastering display color space, + including that no chromatic adaptation is applied even if the white + points differ. + + The target color volume can exceed the primary color volume to allow for + a greater color volume with an existing color space definition (for + example scRGB). It can be smaller than the primary color volume to + minimize gamut and tone mapping distances for big color spaces (HDR + metadata). + + To make use of the entire target color volume a suitable pixel format + has to be chosen (e.g. floating point to exceed the primary color + volume, or abusing limited quantization range as with xvYCC). + + Each coordinate value is multiplied by 10000 to get the argument value + to carry precision of 4 decimals. + + If mastering display primaries have already been set on this object, the + protocol error already_set is raised. + + This request can be used if the compositor advertises + xx_color_manager_v4.feature.set_mastering_display_primaries. Otherwise + this request raises the protocol error unsupported_feature. The + advertisement implies support only for target color volumes fully + contained within the primary color volume. + + If a compositor additionally supports target color volume exceeding the + primary color volume, it must advertise + xx_color_manager_v4.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. + + + + + + + + + + + + + + + Sets the luminance range that was used during the content mastering + process as the minimum and maximum absolute luminance L. This is + compatible with the SMPTE ST 2086 definition of HDR static metadata. + + The mastering luminance range is undefined by default. + + If max L is less than or equal to min L, the protocol error + invalid_luminance is raised. + + Min L value is multiplied by 10000 to get the argument min_lum value + and carry precision of 4 decimals. Max L value is unscaled for max_lum. + + + + + + + + + Sets the maximum content light level (max_cll) as defined by CTA-861-H. + + This can only be set when set_tf_cicp is used to set the transfer + characteristic to Rec. ITU-R BT.2100-2 perceptual quantization system. + Otherwise, 'create' request shall raise inconsistent_set protocol + error. + + max_cll is undefined by default. + + + + + + + + Sets the maximum frame-average light level (max_fall) as defined by + CTA-861-H. + + This can only be set when set_tf_cicp is used to set the transfer + characteristic to Rec. ITU-R BT.2100-2 perceptual quantization system. + Otherwise, 'create' request shall raise inconsistent_set protocol error. + + max_fall is undefined by default. + + + + + + + + + An image description carries information about the color encoding used on + a surface when attached to a wl_surface via + xx_color_management_surface_v4.set_image_description. A compositor can use + this information to decode pixel values into colorimetrically meaningful + quantities. + + Note, that the xx_image_description_v4 object is not ready to be used + immediately after creation. The object eventually delivers either the + 'ready' or the 'failed' event, specified in all requests creating it. The + object is deemed "ready" after receiving the 'ready' event. + + An object which is not ready is illegal to use, it can only be destroyed. + Any other request in this interface shall result in the 'not_ready' + protocol error. Attempts to use an object which is not ready through other + interfaces shall raise protocol errors defined there. + + Once created and regardless of how it was created, a + xx_image_description_v4 object always refers to one fixed image + description. It cannot change after creation. + + + + + Destroy this object. It is safe to destroy an object which is not ready. + + Destroying a xx_image_description_v4 object has no side-effects, not + even if a xx_color_management_surface_v4.set_image_description has not + yet been followed by a wl_surface.commit. + + + + + + + + + + + + + + + + + + + + + + If creating a xx_image_description_v4 object fails for a reason that is + not defined as a protocol error, this event is sent. + + The requests that create image description objects define whether and + when this can occur. Only such creation requests can trigger this event. + This event cannot be triggered after the image description was + successfully formed. + + Once this event has been sent, the xx_image_description_v4 object will + never become ready and it can only be destroyed. + + + + + + + + + Once this event has been sent, the xx_image_description_v4 object is + deemed "ready". Ready objects can be used to send requests and can be + used through other interfaces. + + Every ready xx_image_description_v4 protocol object refers to an + underlying image description record in the compositor. Multiple protocol + objects may end up referring to the same record. Clients may identify + these "copies" by comparing their id numbers: if the numbers from two + protocol objects are identical, the protocol objects refer to the same + image description record. Two different image description records + cannot have the same id number simultaneously. The id number does not + change during the lifetime of the image description record. + + The id number is valid only as long as the protocol object is alive. If + all protocol objects referring to the same image description record are + destroyed, the id number may be recycled for a different image + description record. + + Image description id number is not a protocol object id. Zero is + reserved as an invalid id number. It shall not be possible for a client + to refer to an image description by its id number in protocol. The id + numbers might not be portable between Wayland connections. + + This identity allows clients to de-duplicate image description records + and avoid get_information request if they already have the image + description information. + + + + + + + + Creates a xx_image_description_info_v4 object which delivers the + information that makes up the image description. + + Not all image description protocol objects allow get_information + request. Whether it is allowed or not is defined by the request that + created the object. If get_information is not allowed, the protocol + error no_information is raised. + + + + + + + + + Sends all matching events describing an image description object exactly + once and finally sends the 'done' event. + + Once a xx_image_description_info_v4 object has delivered a 'done' event it + is automatically destroyed. + + Every xx_image_description_info_v4 created from the same + xx_image_description_v4 shall always return the exact same data. + + + + + Signals the end of information events and destroys the object. + + + + + + The icc argument provides a file descriptor to the client which may be + memory-mapped to provide the ICC profile matching the image description. + The fd is read-only, and if mapped then it must be mapped with + MAP_PRIVATE by the client. + + The ICC profile version and other details are determined by the + compositor. There is no provision for a client to ask for a specific + kind of a profile. + + + + + + + + + + Delivers the primary color volume primaries and white point using CIE + 1931 xy chromaticity coordinates. + + Each coordinate value is multiplied by 10000 to get the argument value + to carry precision of 4 decimals. + + + + + + + + + + + + + + + Delivers the primary color volume primaries and white point using an + explicitly enumerated named set. + + + + + + + + The color component transfer characteristic of this image description is + a pure power curve. This event provides the exponent of the power + function. This curve represents the conversion from electrical to + optical pixel or color values. + + The curve exponent has been multiplied by 10000 to get the argument eexp + value to carry the precision of 4 decimals. + + + + + + + + Delivers the transfer characteristic using an explicitly enumerated + named function. + + + + + + + + Delivers the primary color volume luminance range and the reference + white luminance level. + + The minimum luminance is multiplied by 10000 to get the argument + 'min_lum' value and carries precision of 4 decimals. The maximum + luminance and reference white luminance values are unscaled. + + + + + + + + + + Provides the color primaries and white point of the target color volume + using CIE 1931 xy chromaticity coordinates. This is compatible with the + SMPTE ST 2086 definition of HDR static metadata for mastering displays. + + While primary color volume is about how color is encoded, the target + color volume is the actually displayable color volume. If target color + volume is equal to the primary color volume, then this event is not + sent. + + Each coordinate value is multiplied by 10000 to get the argument value + to carry precision of 4 decimals. + + + + + + + + + + + + + + + Provides the luminance range that the image description is targeting as + the minimum and maximum absolute luminance L. This is compatible with + the SMPTE ST 2086 definition of HDR static metadata. + + This luminance range is only theoretical and may not correspond to the + luminance of light emitted on an actual display. + + Min L value is multiplied by 10000 to get the argument min_lum value and + carry precision of 4 decimals. Max L value is unscaled for max_lum. + + + + + + + + + Provides the targeted max_cll of the image description. max_cll is + defined by CTA-861-H. + + This luminance is only theoretical and may not correspond to the + luminance of light emitted on an actual display. + + + + + + + + Provides the targeted max_fall of the image description. max_fall is + defined by CTA-861-H. + + This luminance is only theoretical and may not correspond to the + luminance of light emitted on an actual display. + + + + + + \ No newline at end of file diff --git a/src/Compositor.cpp b/src/Compositor.cpp index 5805eff9..3d159042 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -31,6 +31,7 @@ #include "protocols/XDGShell.hpp" #include "protocols/XDGOutput.hpp" #include "protocols/SecurityContext.hpp" +#include "protocols/ColorManagement.hpp" #include "protocols/core/Compositor.hpp" #include "protocols/core/Subcompositor.hpp" #include "desktop/LayerSurface.hpp" @@ -2985,4 +2986,22 @@ void CCompositor::onNewMonitor(SP output) { g_pHyprRenderer->damageMonitor(PNEWMONITOR); PNEWMONITOR->onMonitorFrame(); + + if (PROTO::colorManagement && shouldChangePreferredImageDescription()) + PROTO::colorManagement->onImagePreferredChanged(); +} + +SImageDescription CCompositor::getPreferredImageDescription() { + if (!PROTO::colorManagement) { + Debug::log(ERR, "FIXME: color management protocol is not enabled, returning empty image description"); + return SImageDescription{}; + } + Debug::log(WARN, "FIXME: color management protocol is enabled, determine correct preferred image description"); + // should determine some common settings to avoid unnecessary transformations while keeping maximum displayable precision + return SImageDescription{.primaries = NColorPrimaries::BT709}; +} + +bool CCompositor::shouldChangePreferredImageDescription() { + Debug::log(WARN, "FIXME: color management protocol is enabled and outputs changed, check preferred image description changes"); + return false; } diff --git a/src/Compositor.hpp b/src/Compositor.hpp index 4c428fca..629b71bc 100644 --- a/src/Compositor.hpp +++ b/src/Compositor.hpp @@ -23,6 +23,7 @@ #include "helpers/Monitor.hpp" #include "desktop/Workspace.hpp" #include "desktop/Window.hpp" +#include "protocols/types/ColorManagement.hpp" #include "render/Renderer.hpp" #include "render/OpenGL.hpp" #include "hyprerror/HyprError.hpp" @@ -169,6 +170,9 @@ class CCompositor { void updateSuspendedStates(); void onNewMonitor(SP output); + SImageDescription getPreferredImageDescription(); + bool shouldChangePreferredImageDescription(); + std::string explicitConfigPath; private: diff --git a/src/config/ConfigDescriptions.hpp b/src/config/ConfigDescriptions.hpp index 8c35a3b1..686b996a 100644 --- a/src/config/ConfigDescriptions.hpp +++ b/src/config/ConfigDescriptions.hpp @@ -1658,4 +1658,22 @@ inline static const std::vector CONFIG_OPTIONS = { .type = CONFIG_OPTION_BOOL, .data = SConfigOptionDescription::SBoolData{true}, }, + SConfigOptionDescription{ + .value = "experimental:wide_color_gamut", + .description = "force wide color gamut for all supported outputs", + .type = CONFIG_OPTION_BOOL, + .data = SConfigOptionDescription::SBoolData{false}, + }, + SConfigOptionDescription{ + .value = "experimental:hdr", + .description = "force static hdr for all supported outputs", + .type = CONFIG_OPTION_BOOL, + .data = SConfigOptionDescription::SBoolData{false}, + }, + SConfigOptionDescription{ + .value = "experimental:xx_color_management_v4", + .description = "enable color management protocol", + .type = CONFIG_OPTION_BOOL, + .data = SConfigOptionDescription::SBoolData{false}, + }, }; diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 1ab29b1f..bbb3e68f 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -622,6 +622,10 @@ CConfigManager::CConfigManager() { m_pConfig->addConfigValue("ecosystem:no_update_news", Hyprlang::INT{0}); + m_pConfig->addConfigValue("experimental:wide_color_gamut", Hyprlang::INT{0}); + m_pConfig->addConfigValue("experimental:hdr", Hyprlang::INT{0}); + m_pConfig->addConfigValue("experimental:xx_color_management_v4", Hyprlang::INT{0}); + // devices m_pConfig->addSpecialCategory("device", {"name"}); m_pConfig->addSpecialConfigValue("device", "sensitivity", {0.F}); diff --git a/src/desktop/WLSurface.hpp b/src/desktop/WLSurface.hpp index 07af13df..057a6f90 100644 --- a/src/desktop/WLSurface.hpp +++ b/src/desktop/WLSurface.hpp @@ -119,4 +119,5 @@ class CWLSurface { } listeners; friend class CPointerConstraint; + friend class CXxColorManagerV4; }; diff --git a/src/managers/ProtocolManager.cpp b/src/managers/ProtocolManager.cpp index 0e1c21f5..b7d61f67 100644 --- a/src/managers/ProtocolManager.cpp +++ b/src/managers/ProtocolManager.cpp @@ -55,6 +55,8 @@ #include "../protocols/core/Subcompositor.hpp" #include "../protocols/core/Output.hpp" #include "../protocols/core/Shm.hpp" +#include "../protocols/ColorManagement.hpp" +#include "../protocols/FrogColorManagement.hpp" #include "../helpers/Monitor.hpp" #include "../render/Renderer.hpp" @@ -77,11 +79,15 @@ void CProtocolManager::onMonitorModeChange(PHLMONITOR pMonitor) { PROTO::outputs.erase(pMonitor->szName); 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(); } CProtocolManager::CProtocolManager() { static const auto PENABLEEXPLICIT = CConfigValue("render:explicit_sync"); + static const auto PENABLEXXCM = CConfigValue("experimental:xx_color_management_v4"); // Outputs are a bit dumb, we have to agree. static auto P = g_pHookSystem->hookDynamic("monitorAdded", [this](void* self, SCallbackInfo& info, std::any param) { @@ -162,6 +168,11 @@ CProtocolManager::CProtocolManager() { PROTO::ctm = std::make_unique(&hyprland_ctm_control_manager_v1_interface, 1, "CTMControl"); PROTO::hyprlandSurface = std::make_unique(&hyprland_surface_manager_v1_interface, 1, "HyprlandSurface"); + if (*PENABLEXXCM) { + PROTO::colorManagement = std::make_unique(&xx_color_manager_v4_interface, 1, "ColorManagement"); + PROTO::frogColorManagement = std::make_unique(&frog_color_management_factory_v1_interface, 1, "FrogColorManagement"); + } + for (auto const& b : g_pCompositor->m_pAqBackend->getImplementations()) { if (b->type() != Aquamarine::AQ_BACKEND_DRM) continue; diff --git a/src/protocols/ColorManagement.cpp b/src/protocols/ColorManagement.cpp new file mode 100644 index 00000000..7ff4db4f --- /dev/null +++ b/src/protocols/ColorManagement.cpp @@ -0,0 +1,571 @@ +#include "ColorManagement.hpp" +#include "Compositor.hpp" + +CColorManager::CColorManager(SP resource_) : resource(resource_) { + if (!good()) + return; + + resource->sendSupportedFeature(XX_COLOR_MANAGER_V4_FEATURE_PARAMETRIC); + resource->sendSupportedFeature(XX_COLOR_MANAGER_V4_FEATURE_EXTENDED_TARGET_VOLUME); + resource->sendSupportedFeature(XX_COLOR_MANAGER_V4_FEATURE_SET_MASTERING_DISPLAY_PRIMARIES); + resource->sendSupportedFeature(XX_COLOR_MANAGER_V4_FEATURE_SET_PRIMARIES); + resource->sendSupportedFeature(XX_COLOR_MANAGER_V4_FEATURE_SET_LUMINANCES); + + resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_SRGB); + // resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_PAL_M); + // resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_PAL); + // resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_NTSC); + // resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_GENERIC_FILM); + resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_BT2020); + // resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_CIE1931_XYZ); + // resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_DCI_P3); + // resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_DISPLAY_P3); + // resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_ADOBE_RGB); + + // resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_GAMMA22); + resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_SRGB); + resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ); + // resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LINEAR); + + resource->sendSupportedIntent(XX_COLOR_MANAGER_V4_RENDER_INTENT_PERCEPTUAL); + // resource->sendSupportedIntent(XX_COLOR_MANAGER_V4_RENDER_INTENT_RELATIVE); + // resource->sendSupportedIntent(XX_COLOR_MANAGER_V4_RENDER_INTENT_ABSOLUTE); + // resource->sendSupportedIntent(XX_COLOR_MANAGER_V4_RENDER_INTENT_RELATIVE_BPC); + + resource->setDestroy([](CXxColorManagerV4* r) { LOGM(TRACE, "Destroy xx_color_manager at {:x} (generated default)", (uintptr_t)r); }); + resource->setGetOutput([](CXxColorManagerV4* r, uint32_t id, wl_resource* output) { + LOGM(TRACE, "Get output for id={}, output={}", id, (uintptr_t)output); + const auto RESOURCE = + PROTO::colorManagement->m_vOutputs.emplace_back(makeShared(makeShared(r->client(), r->version(), id))); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::colorManagement->m_vOutputs.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + }); + resource->setGetSurface([](CXxColorManagerV4* r, uint32_t id, wl_resource* surface) { + LOGM(TRACE, "Get surface for id={}, surface={}", id, (uintptr_t)surface); + auto SURF = CWLSurfaceResource::fromResource(surface); + + if (!SURF) { + LOGM(ERR, "No surface for resource {}", (uintptr_t)surface); + r->error(-1, "Invalid surface (2)"); + return; + } + + if (SURF->colorManagement) { + r->error(XX_COLOR_MANAGER_V4_ERROR_SURFACE_EXISTS, "CM Surface already exists"); + return; + } + + const auto RESOURCE = + PROTO::colorManagement->m_vSurfaces.emplace_back(makeShared(makeShared(r->client(), r->version(), id), SURF)); + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::colorManagement->m_vSurfaces.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + + SURF->colorManagement = RESOURCE; + }); + resource->setGetFeedbackSurface([](CXxColorManagerV4* r, uint32_t id, wl_resource* surface) { + LOGM(TRACE, "Get feedback surface for id={}, surface={}", id, (uintptr_t)surface); + auto SURF = CWLSurfaceResource::fromResource(surface); + + if (!SURF) { + LOGM(ERR, "No surface for resource {}", (uintptr_t)surface); + r->error(-1, "Invalid surface (2)"); + return; + } + + const auto RESOURCE = PROTO::colorManagement->m_vFeedbackSurfaces.emplace_back( + makeShared(makeShared(r->client(), r->version(), id), SURF)); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::colorManagement->m_vFeedbackSurfaces.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + }); + resource->setNewIccCreator([](CXxColorManagerV4* r, uint32_t id) { + LOGM(WARN, "New ICC creator for id={} (unsupported)", id); + r->error(XX_COLOR_MANAGER_V4_ERROR_UNSUPPORTED_FEATURE, "ICC profiles are not supported"); + }); + resource->setNewParametricCreator([](CXxColorManagerV4* r, uint32_t id) { + LOGM(TRACE, "New parametric creator for id={}", id); + + const auto RESOURCE = PROTO::colorManagement->m_vParametricCreators.emplace_back( + makeShared(makeShared(r->client(), r->version(), id))); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::colorManagement->m_vParametricCreators.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + }); + + resource->setOnDestroy([this](CXxColorManagerV4* r) { PROTO::colorManagement->destroyResource(this); }); +} + +bool CColorManager::good() { + return resource->resource(); +} + +CColorManagementOutput::CColorManagementOutput(SP resource_) : resource(resource_) { + if (!good()) + return; + + pClient = resource->client(); + + resource->setDestroy([this](CXxColorManagementOutputV4* r) { PROTO::colorManagement->destroyResource(this); }); + resource->setOnDestroy([this](CXxColorManagementOutputV4* r) { PROTO::colorManagement->destroyResource(this); }); + + resource->setGetImageDescription([this](CXxColorManagementOutputV4* r, uint32_t id) { + LOGM(TRACE, "Get image description for output={}, id={}", (uintptr_t)r, id); + if (imageDescription.valid()) + PROTO::colorManagement->destroyResource(imageDescription.get()); + + const auto RESOURCE = PROTO::colorManagement->m_vImageDescriptions.emplace_back( + makeShared(makeShared(r->client(), r->version(), id))); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::colorManagement->m_vImageDescriptions.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + }); +} + +bool CColorManagementOutput::good() { + return resource->resource(); +} + +wl_client* CColorManagementOutput::client() { + return pClient; +} + +CColorManagementSurface::CColorManagementSurface(SP surface_) : surface(surface_) { + // only for frog cm untill wayland cm is adopted +} + +CColorManagementSurface::CColorManagementSurface(SP resource_, SP surface_) : surface(surface_), resource(resource_) { + if (!good()) + return; + + pClient = resource->client(); + + resource->setDestroy([this](CXxColorManagementSurfaceV4* r) { + LOGM(TRACE, "Destroy xx cm surface {}", (uintptr_t)surface); + PROTO::colorManagement->destroyResource(this); + }); + resource->setOnDestroy([this](CXxColorManagementSurfaceV4* r) { + LOGM(TRACE, "Destroy xx cm surface {}", (uintptr_t)surface); + PROTO::colorManagement->destroyResource(this); + }); + + resource->setSetImageDescription([this](CXxColorManagementSurfaceV4* r, wl_resource* image_description, uint32_t render_intent) { + LOGM(TRACE, "Set image description for surface={}, desc={}, intent={}", (uintptr_t)r, (uintptr_t)image_description, render_intent); + + const auto PO = (CXxImageDescriptionV4*)wl_resource_get_user_data(image_description); + if (!PO) { // FIXME check validity + r->error(XX_COLOR_MANAGEMENT_SURFACE_V4_ERROR_IMAGE_DESCRIPTION, "Image description creation failed"); + return; + } + if (render_intent != XX_COLOR_MANAGER_V4_RENDER_INTENT_PERCEPTUAL) { + r->error(XX_COLOR_MANAGEMENT_SURFACE_V4_ERROR_RENDER_INTENT, "Unsupported render intent"); + return; + } + + const auto imageDescription = std::find_if(PROTO::colorManagement->m_vImageDescriptions.begin(), PROTO::colorManagement->m_vImageDescriptions.end(), + [&](const auto& other) { return other->resource()->resource() == image_description; }); + if (imageDescription == PROTO::colorManagement->m_vImageDescriptions.end()) { + r->error(XX_COLOR_MANAGEMENT_SURFACE_V4_ERROR_IMAGE_DESCRIPTION, "Image description not found"); + return; + } + + m_hasImageDescription = true; + m_imageDescription = imageDescription->get()->settings; + }); + resource->setUnsetImageDescription([this](CXxColorManagementSurfaceV4* r) { + LOGM(TRACE, "Unset image description for surface={}", (uintptr_t)r); + m_imageDescription = SImageDescription{}; + m_hasImageDescription = false; + }); +} + +bool CColorManagementSurface::good() { + return resource && resource->resource(); +} + +wl_client* CColorManagementSurface::client() { + return pClient; +} + +const SImageDescription& CColorManagementSurface::imageDescription() { + if (!hasImageDescription()) + LOGM(WARN, "Reading imageDescription while none set. Returns default or empty values"); + return m_imageDescription; +} + +bool CColorManagementSurface::hasImageDescription() { + return m_hasImageDescription; +} + +CColorManagementFeedbackSurface::CColorManagementFeedbackSurface(SP resource_, SP surface_) : + surface(surface_), resource(resource_) { + if (!good()) + return; + + pClient = resource->client(); + + resource->setDestroy([this](CXxColorManagementFeedbackSurfaceV4* r) { + LOGM(TRACE, "Destroy xx cm feedback surface {}", (uintptr_t)surface); + if (m_currentPreferred.valid()) + PROTO::colorManagement->destroyResource(m_currentPreferred.get()); + PROTO::colorManagement->destroyResource(this); + }); + resource->setOnDestroy([this](CXxColorManagementFeedbackSurfaceV4* r) { + LOGM(TRACE, "Destroy xx cm feedback surface {}", (uintptr_t)surface); + if (m_currentPreferred.valid()) + PROTO::colorManagement->destroyResource(m_currentPreferred.get()); + PROTO::colorManagement->destroyResource(this); + }); + + resource->setGetPreferred([this](CXxColorManagementFeedbackSurfaceV4* r, uint32_t id) { + LOGM(TRACE, "Get preferred for id {}", id); + + if (m_currentPreferred.valid()) + PROTO::colorManagement->destroyResource(m_currentPreferred.get()); + + const auto RESOURCE = PROTO::colorManagement->m_vImageDescriptions.emplace_back( + makeShared(makeShared(r->client(), r->version(), id), true)); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::colorManagement->m_vImageDescriptions.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + m_currentPreferred = RESOURCE; + + m_currentPreferred->settings = g_pCompositor->getPreferredImageDescription(); + + RESOURCE->resource()->sendReady(id); + }); +} + +bool CColorManagementFeedbackSurface::good() { + return resource->resource(); +} + +wl_client* CColorManagementFeedbackSurface::client() { + return pClient; +} + +CColorManagementParametricCreator::CColorManagementParametricCreator(SP resource_) : resource(resource_) { + if (!good()) + return; + // + pClient = resource->client(); + + resource->setOnDestroy([this](CXxImageDescriptionCreatorParamsV4* r) { PROTO::colorManagement->destroyResource(this); }); + + resource->setCreate([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t id) { + LOGM(TRACE, "Create image description from params for id {}", id); + + // FIXME actually check completeness + if (!valuesSet) { + r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INCOMPLETE_SET, "Missing required settings"); + return; + } + + // FIXME actually check consistency + if (!valuesSet) { + r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INCONSISTENT_SET, "Set is not consistent"); + return; + } + + const auto RESOURCE = PROTO::colorManagement->m_vImageDescriptions.emplace_back( + makeShared(makeShared(r->client(), r->version(), id))); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::colorManagement->m_vImageDescriptions.pop_back(); + return; + } + + // FIXME actually check support + if (!valuesSet) { + RESOURCE->resource()->sendFailed(XX_IMAGE_DESCRIPTION_V4_CAUSE_UNSUPPORTED, "unsupported"); + return; + } + + RESOURCE->self = RESOURCE; + RESOURCE->settings = settings; + RESOURCE->resource()->sendReady(id); + + PROTO::colorManagement->destroyResource(this); + }); + resource->setSetTfNamed([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t tf) { + LOGM(TRACE, "Set image description transfer function to {}", tf); + if (valuesSet & PC_TF) { + r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Transfer function already set"); + return; + } + + switch (tf) { + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_SRGB: break; + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ: break; + default: r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_TF, "Unsupported transfer function"); return; + } + + settings.transferFunction = (xxColorManagerV4TransferFunction)tf; + valuesSet |= PC_TF; + }); + resource->setSetTfPower([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t eexp) { + LOGM(TRACE, "Set image description tf power to {}", eexp); + if (valuesSet & PC_TF_POWER) { + r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Transfer function power already set"); + return; + } + settings.transferFunctionPower = eexp / 10000.0f; + valuesSet |= PC_TF_POWER; + }); + resource->setSetPrimariesNamed([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t primaries) { + LOGM(TRACE, "Set image description primaries by name {}", primaries); + if (valuesSet & PC_PRIMARIES) { + r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Primaries already set"); + return; + } + + switch (primaries) { + case XX_COLOR_MANAGER_V4_PRIMARIES_SRGB: + settings.primariesNameSet = true; + settings.primariesNamed = XX_COLOR_MANAGER_V4_PRIMARIES_SRGB; + settings.primaries = NColorPrimaries::BT709; + valuesSet |= PC_PRIMARIES; + break; + case XX_COLOR_MANAGER_V4_PRIMARIES_BT2020: + settings.primariesNameSet = true; + settings.primariesNamed = XX_COLOR_MANAGER_V4_PRIMARIES_BT2020; + settings.primaries = NColorPrimaries::BT2020; + valuesSet |= PC_PRIMARIES; + break; + default: r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_PRIMARIES, "Unsupported primaries"); + } + }); + resource->setSetPrimaries( + [this](CXxImageDescriptionCreatorParamsV4* r, int32_t r_x, int32_t r_y, int32_t g_x, int32_t g_y, int32_t b_x, int32_t b_y, int32_t w_x, int32_t w_y) { + LOGM(TRACE, "Set image description primaries by values r:{},{} g:{},{} b:{},{} w:{},{}", r_x, r_y, g_x, g_y, b_x, b_y, w_x, w_y); + if (valuesSet & PC_PRIMARIES) { + r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Primaries already set"); + return; + } + settings.primariesNameSet = false; + settings.primaries = + SImageDescription::SPCPRimaries{.red = {.x = r_x, .y = r_y}, .green = {.x = g_x, .y = g_y}, .blue = {.x = b_x, .y = b_y}, .white = {.x = w_x, .y = w_y}}; + valuesSet |= PC_PRIMARIES; + }); + resource->setSetLuminances([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t min_lum, uint32_t max_lum, uint32_t reference_lum) { + auto min = min_lum / 10000.0f; + LOGM(TRACE, "Set image description luminances to {} - {} ({})", min, max_lum, reference_lum); + if (valuesSet & PC_LUMINANCES) { + r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Luminances already set"); + return; + } + if (max_lum < reference_lum || reference_lum <= min) { + r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_LUMINANCE, "Invalid luminances"); + return; + } + settings.luminances = SImageDescription::SPCLuminances{.min = min, .max = max_lum, .reference = reference_lum}; + valuesSet |= PC_LUMINANCES; + }); + resource->setSetMasteringDisplayPrimaries( + [this](CXxImageDescriptionCreatorParamsV4* r, int32_t r_x, int32_t r_y, int32_t g_x, int32_t g_y, int32_t b_x, int32_t b_y, int32_t w_x, int32_t w_y) { + LOGM(TRACE, "Set image description mastering primaries by values r:{},{} g:{},{} b:{},{} w:{},{}", r_x, r_y, g_x, g_y, b_x, b_y, w_x, w_y); + // if (valuesSet & PC_MASTERING_PRIMARIES) { + // r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Mastering primaries already set"); + // return; + // } + settings.masteringPrimaries = + SImageDescription::SPCPRimaries{.red = {.x = r_x, .y = r_y}, .green = {.x = g_x, .y = g_y}, .blue = {.x = b_x, .y = b_y}, .white = {.x = w_x, .y = w_y}}; + valuesSet |= PC_MASTERING_PRIMARIES; + }); + resource->setSetMasteringLuminance([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t min_lum, uint32_t max_lum) { + auto min = min_lum / 10000.0f; + LOGM(TRACE, "Set image description mastering luminances to {} - {}", min, max_lum); + // if (valuesSet & PC_MASTERING_LUMINANCES) { + // r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Mastering luminances already set"); + // return; + // } + if (min > 0 && max_lum > 0 && max_lum <= min) { + r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_LUMINANCE, "Invalid luminances"); + return; + } + settings.masteringLuminances = SImageDescription::SPCMasteringLuminances{.min = min, .max = max_lum}; + valuesSet |= PC_MASTERING_LUMINANCES; + }); + resource->setSetMaxCll([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t max_cll) { + LOGM(TRACE, "Set image description max content light level to {}", max_cll); + // if (valuesSet & PC_CLL) { + // r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Max CLL already set"); + // return; + // } + settings.maxCLL = max_cll; + valuesSet |= PC_CLL; + }); + resource->setSetMaxFall([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t max_fall) { + LOGM(TRACE, "Set image description max frame-average light level to {}", max_fall); + // if (valuesSet & PC_FALL) { + // r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Max FALL already set"); + // return; + // } + settings.maxFALL = max_fall; + valuesSet |= PC_FALL; + }); +} + +bool CColorManagementParametricCreator::good() { + return resource->resource(); +} + +wl_client* CColorManagementParametricCreator::client() { + return pClient; +} + +CColorManagementImageDescription::CColorManagementImageDescription(SP resource_, bool allowGetInformation) : + m_resource(resource_), m_allowGetInformation(allowGetInformation) { + if (!good()) + return; + + pClient = m_resource->client(); + + m_resource->setDestroy([this](CXxImageDescriptionV4* r) { PROTO::colorManagement->destroyResource(this); }); + m_resource->setOnDestroy([this](CXxImageDescriptionV4* r) { PROTO::colorManagement->destroyResource(this); }); + + m_resource->setGetInformation([this](CXxImageDescriptionV4* r, uint32_t id) { + LOGM(TRACE, "Get image information for image={}, id={}", (uintptr_t)r, id); + if (!m_allowGetInformation) { + r->error(XX_IMAGE_DESCRIPTION_V4_ERROR_NO_INFORMATION, "Image descriptions doesn't allow get_information request"); + return; + } + + auto RESOURCE = makeShared(makeShared(r->client(), r->version(), id), settings); + + if (!RESOURCE->good()) + r->noMemory(); + + // CColorManagementImageDescriptionInfo should send everything in the constructor and be ready for destroying at this point + RESOURCE.reset(); + }); +} + +bool CColorManagementImageDescription::good() { + return m_resource->resource(); +} + +wl_client* CColorManagementImageDescription::client() { + return pClient; +} + +SP CColorManagementImageDescription::resource() { + return m_resource; +} + +CColorManagementImageDescriptionInfo::CColorManagementImageDescriptionInfo(SP resource_, const SImageDescription& settings_) : + m_resource(resource_), settings(settings_) { + if (!good()) + return; + + pClient = m_resource->client(); + + const auto toProto = [](float value) { return int32_t(std::round(value * 10000)); }; + + if (settings.iccFd >= 0) + m_resource->sendIccFile(settings.iccFd, settings.iccSize); + + // send preferred client paramateres + m_resource->sendPrimaries(toProto(settings.primaries.red.x), toProto(settings.primaries.red.y), toProto(settings.primaries.green.x), toProto(settings.primaries.green.y), + toProto(settings.primaries.blue.x), toProto(settings.primaries.blue.y), toProto(settings.primaries.white.x), toProto(settings.primaries.white.y)); + if (settings.primariesNameSet) + m_resource->sendPrimariesNamed(settings.primariesNamed); + m_resource->sendTfPower(std::round(settings.transferFunctionPower * 10000)); + m_resource->sendTfNamed(settings.transferFunction); + m_resource->sendLuminances(std::round(settings.luminances.min * 10000), settings.luminances.max, settings.luminances.reference); + + // send expexted display paramateres + m_resource->sendTargetPrimaries(toProto(settings.masteringPrimaries.red.x), toProto(settings.masteringPrimaries.red.y), toProto(settings.masteringPrimaries.green.x), + toProto(settings.masteringPrimaries.green.y), toProto(settings.masteringPrimaries.blue.x), toProto(settings.masteringPrimaries.blue.y), + toProto(settings.masteringPrimaries.white.x), toProto(settings.masteringPrimaries.white.y)); + m_resource->sendTargetLuminance(std::round(settings.masteringLuminances.min * 10000), settings.masteringLuminances.max); + m_resource->sendTargetMaxCll(settings.maxCLL); + m_resource->sendTargetMaxFall(settings.maxFALL); + + m_resource->sendDone(); +} + +bool CColorManagementImageDescriptionInfo::good() { + return m_resource->resource(); +} + +wl_client* CColorManagementImageDescriptionInfo::client() { + return pClient; +} + +CColorManagementProtocol::CColorManagementProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { + ; +} + +void CColorManagementProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); + + if (!RESOURCE->good()) { + wl_client_post_no_memory(client); + m_vManagers.pop_back(); + return; + } + + LOGM(TRACE, "New xx_color_manager at {:x}", (uintptr_t)RESOURCE.get()); +} + +void CColorManagementProtocol::onImagePreferredChanged() { + for (auto const& feedback : m_vFeedbackSurfaces) { + feedback->resource->sendPreferredChanged(); + } +} + +void CColorManagementProtocol::destroyResource(CColorManager* resource) { + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; }); +} + +void CColorManagementProtocol::destroyResource(CColorManagementOutput* resource) { + std::erase_if(m_vOutputs, [&](const auto& other) { return other.get() == resource; }); +} + +void CColorManagementProtocol::destroyResource(CColorManagementSurface* resource) { + std::erase_if(m_vSurfaces, [&](const auto& other) { return other.get() == resource; }); +} + +void CColorManagementProtocol::destroyResource(CColorManagementFeedbackSurface* resource) { + std::erase_if(m_vFeedbackSurfaces, [&](const auto& other) { return other.get() == resource; }); +} + +void CColorManagementProtocol::destroyResource(CColorManagementParametricCreator* resource) { + std::erase_if(m_vParametricCreators, [&](const auto& other) { return other.get() == resource; }); +} + +void CColorManagementProtocol::destroyResource(CColorManagementImageDescription* resource) { + std::erase_if(m_vImageDescriptions, [&](const auto& other) { return other.get() == resource; }); +} diff --git a/src/protocols/ColorManagement.hpp b/src/protocols/ColorManagement.hpp new file mode 100644 index 00000000..573abf69 --- /dev/null +++ b/src/protocols/ColorManagement.hpp @@ -0,0 +1,182 @@ +#pragma once + +#include +#include +#include +#include "WaylandProtocol.hpp" +#include "protocols/core/Compositor.hpp" +#include "xx-color-management-v4.hpp" +#include "types/ColorManagement.hpp" + +class CColorManager; +class CColorManagementOutput; +class CColorManagementImageDescription; +class CColorManagementProtocol; + +class CColorManager { + public: + CColorManager(SP resource_); + + bool good(); + + private: + SP resource; +}; + +class CColorManagementOutput { + public: + CColorManagementOutput(SP resource_); + + bool good(); + wl_client* client(); + + WP self; + WP imageDescription; + + private: + SP resource; + wl_client* pClient = nullptr; + + friend class CColorManagementProtocol; + friend class CColorManagementImageDescription; +}; + +class CColorManagementSurface { + public: + CColorManagementSurface(SP surface_); // temporary interface for frog CM + CColorManagementSurface(SP resource_, SP surface_); + + bool good(); + wl_client* client(); + + WP self; + WP surface; + + const SImageDescription& imageDescription(); + bool hasImageDescription(); + + private: + SP resource; + wl_client* pClient = nullptr; + SImageDescription m_imageDescription; + bool m_hasImageDescription = false; + + friend class CFrogColorManagementSurface; +}; + +class CColorManagementFeedbackSurface { + public: + CColorManagementFeedbackSurface(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 CColorManagementProtocol; +}; + +class CColorManagementParametricCreator { + public: + CColorManagementParametricCreator(SP resource_); + + bool good(); + wl_client* client(); + + WP self; + + SImageDescription settings; + + private: + enum eValuesSet : uint32_t { // NOLINT + PC_TF = (1 << 0), + PC_TF_POWER = (1 << 1), + PC_PRIMARIES = (1 << 2), + PC_LUMINANCES = (1 << 3), + PC_MASTERING_PRIMARIES = (1 << 4), + PC_MASTERING_LUMINANCES = (1 << 5), + PC_CLL = (1 << 6), + PC_FALL = (1 << 7), + }; + + SP resource; + wl_client* pClient = nullptr; + uint32_t valuesSet = 0; // enum eValuesSet +}; + +class CColorManagementImageDescription { + public: + CColorManagementImageDescription(SP resource_, bool allowGetInformation = false); + + bool good(); + wl_client* client(); + SP resource(); + + WP self; + + SImageDescription settings; + + private: + SP m_resource; + wl_client* pClient = nullptr; + bool m_allowGetInformation = false; + + friend class CColorManagementOutput; +}; + +class CColorManagementImageDescriptionInfo { + public: + CColorManagementImageDescriptionInfo(SP resource_, const SImageDescription& settings_); + + bool good(); + wl_client* client(); + + private: + SP m_resource; + wl_client* pClient = nullptr; + SImageDescription settings; +}; + +class CColorManagementProtocol : public IWaylandProtocol { + public: + CColorManagementProtocol(const wl_interface* iface, const int& ver, const std::string& name); + + virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); + + void onImagePreferredChanged(); + + private: + void destroyResource(CColorManager* resource); + void destroyResource(CColorManagementOutput* resource); + void destroyResource(CColorManagementSurface* resource); + void destroyResource(CColorManagementFeedbackSurface* resource); + void destroyResource(CColorManagementParametricCreator* resource); + void destroyResource(CColorManagementImageDescription* resource); + + std::vector> m_vManagers; + std::vector> m_vOutputs; + std::vector> m_vSurfaces; + std::vector> m_vFeedbackSurfaces; + std::vector> m_vParametricCreators; + std::vector> m_vImageDescriptions; + + friend class CColorManager; + friend class CColorManagementOutput; + friend class CColorManagementSurface; + friend class CColorManagementFeedbackSurface; + friend class CColorManagementParametricCreator; + friend class CColorManagementImageDescription; + + friend class CFrogColorManagementSurface; +}; + +namespace PROTO { + inline UP colorManagement; +}; diff --git a/src/protocols/FrogColorManagement.cpp b/src/protocols/FrogColorManagement.cpp new file mode 100644 index 00000000..3ea20405 --- /dev/null +++ b/src/protocols/FrogColorManagement.cpp @@ -0,0 +1,159 @@ +#include "FrogColorManagement.hpp" +#include "protocols/ColorManagement.hpp" +#include "protocols/core/Subcompositor.hpp" + +CFrogColorManager::CFrogColorManager(SP resource_) : resource(resource_) { + if (!good()) + return; + + resource->setDestroy([](CFrogColorManagementFactoryV1* r) { LOGM(TRACE, "Destroy frog_color_management at {:x} (generated default)", (uintptr_t)r); }); + resource->setOnDestroy([this](CFrogColorManagementFactoryV1* r) { PROTO::frogColorManagement->destroyResource(this); }); + + resource->setGetColorManagedSurface([](CFrogColorManagementFactoryV1* r, wl_resource* surface, uint32_t id) { + LOGM(TRACE, "Get surface for id={}, surface={}", id, (uintptr_t)surface); + auto SURF = CWLSurfaceResource::fromResource(surface); + + if (!SURF) { + LOGM(ERR, "No surface for resource {}", (uintptr_t)surface); + r->error(-1, "Invalid surface (2)"); + return; + } + + if (SURF->role->role() == SURFACE_ROLE_SUBSURFACE) + SURF = ((CSubsurfaceRole*)SURF->role.get())->subsurface->t1Parent(); + + const auto RESOURCE = PROTO::frogColorManagement->m_vSurfaces.emplace_back( + makeShared(makeShared(r->client(), r->version(), id), SURF)); + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::frogColorManagement->m_vSurfaces.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + }); +} + +bool CFrogColorManager::good() { + return resource->resource(); +} + +CFrogColorManagementSurface::CFrogColorManagementSurface(SP resource_, SP surface_) : surface(surface_), resource(resource_) { + if (!good()) + return; + + pClient = resource->client(); + + if (!surface->colorManagement.valid()) { + const auto RESOURCE = PROTO::colorManagement->m_vSurfaces.emplace_back(makeShared(surface_)); + if (!RESOURCE) { + resource->noMemory(); + PROTO::colorManagement->m_vSurfaces.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + + surface->colorManagement = RESOURCE; + + resource->setOnDestroy([this](CFrogColorManagedSurface* r) { + LOGM(TRACE, "Destroy frog cm and xx cm for surface {}", (uintptr_t)surface); + if (surface.valid()) + PROTO::colorManagement->destroyResource(surface->colorManagement.get()); + PROTO::frogColorManagement->destroyResource(this); + }); + } else + resource->setOnDestroy([this](CFrogColorManagedSurface* r) { + LOGM(TRACE, "Destroy frog cm surface {}", (uintptr_t)surface); + PROTO::frogColorManagement->destroyResource(this); + }); + + resource->setDestroy([this](CFrogColorManagedSurface* r) { + LOGM(TRACE, "Destroy frog cm surface {}", (uintptr_t)surface); + PROTO::frogColorManagement->destroyResource(this); + }); + + resource->setSetKnownTransferFunction([this](CFrogColorManagedSurface* r, frogColorManagedSurfaceTransferFunction tf) { + LOGM(TRACE, "Set frog cm transfer function {} for {}", (uint32_t)tf, surface->id()); + switch (tf) { + case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_ST2084_PQ: + surface->colorManagement->m_imageDescription.transferFunction = XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ; + break; + ; + case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_GAMMA_22: + if (pqIntentSent) { + LOGM(TRACE, + "FIXME: assuming broken enum value 2 (FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_GAMMA_22) referring to eotf value 2 (TRANSFER_FUNCTION_ST2084_PQ)"); + surface->colorManagement->m_imageDescription.transferFunction = XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ; + break; + }; // intended fall through + case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_UNDEFINED: + case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_SCRGB_LINEAR: LOGM(TRACE, std::format("FIXME: add tf support for {}", (uint32_t)tf)); // intended fall through + case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_SRGB: + surface->colorManagement->m_imageDescription.transferFunction = XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_SRGB; + + surface->colorManagement->m_hasImageDescription = true; + } + }); + resource->setSetKnownContainerColorVolume([this](CFrogColorManagedSurface* r, frogColorManagedSurfacePrimaries primariesName) { + LOGM(TRACE, "Set frog cm primaries {}", (uint32_t)primariesName); + switch (primariesName) { + case FROG_COLOR_MANAGED_SURFACE_PRIMARIES_UNDEFINED: + case FROG_COLOR_MANAGED_SURFACE_PRIMARIES_REC709: surface->colorManagement->m_imageDescription.primaries = NColorPrimaries::BT709; break; + case FROG_COLOR_MANAGED_SURFACE_PRIMARIES_REC2020: surface->colorManagement->m_imageDescription.primaries = NColorPrimaries::BT2020; break; + } + + surface->colorManagement->m_hasImageDescription = true; + }); + resource->setSetRenderIntent([this](CFrogColorManagedSurface* r, frogColorManagedSurfaceRenderIntent intent) { + LOGM(TRACE, "Set frog cm intent {}", (uint32_t)intent); + pqIntentSent = intent == FROG_COLOR_MANAGED_SURFACE_RENDER_INTENT_PERCEPTUAL; + surface->colorManagement->m_hasImageDescription = true; + }); + resource->setSetHdrMetadata([this](CFrogColorManagedSurface* r, uint32_t r_x, uint32_t r_y, uint32_t g_x, uint32_t g_y, uint32_t b_x, uint32_t b_y, uint32_t w_x, uint32_t w_y, + uint32_t max_lum, uint32_t min_lum, uint32_t cll, uint32_t fall) { + LOGM(TRACE, "Set frog primaries r:{},{} g:{},{} b:{},{} w:{},{} luminances {} - {} cll {} fall {}", r_x, r_y, g_x, g_y, b_x, b_y, w_x, w_y, min_lum, max_lum, cll, fall); + surface->colorManagement->m_imageDescription.masteringPrimaries = SImageDescription::SPCPRimaries{.red = {.x = r_x / 50000.0f, .y = r_y / 50000.0f}, + .green = {.x = g_x / 50000.0f, .y = g_y / 50000.0f}, + .blue = {.x = b_x / 50000.0f, .y = b_y / 50000.0f}, + .white = {.x = w_x / 50000.0f, .y = w_y / 50000.0f}}; + surface->colorManagement->m_imageDescription.masteringLuminances.min = min_lum / 10000.0f; + surface->colorManagement->m_imageDescription.masteringLuminances.max = max_lum; + surface->colorManagement->m_imageDescription.maxCLL = cll; + surface->colorManagement->m_imageDescription.maxFALL = fall; + + surface->colorManagement->m_hasImageDescription = true; + }); +} + +bool CFrogColorManagementSurface::good() { + return resource->resource(); +} + +wl_client* CFrogColorManagementSurface::client() { + return pClient; +} + +CFrogColorManagementProtocol::CFrogColorManagementProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { + ; +} + +void CFrogColorManagementProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); + + if (!RESOURCE->good()) { + wl_client_post_no_memory(client); + m_vManagers.pop_back(); + return; + } + + LOGM(TRACE, "New frog_color_management at {:x}", (uintptr_t)RESOURCE.get()); +} + +void CFrogColorManagementProtocol::destroyResource(CFrogColorManager* resource) { + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; }); +} + +void CFrogColorManagementProtocol::destroyResource(CFrogColorManagementSurface* resource) { + std::erase_if(m_vSurfaces, [&](const auto& other) { return other.get() == resource; }); +} diff --git a/src/protocols/FrogColorManagement.hpp b/src/protocols/FrogColorManagement.hpp new file mode 100644 index 00000000..6c00e38d --- /dev/null +++ b/src/protocols/FrogColorManagement.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include +#include +#include "WaylandProtocol.hpp" +#include "protocols/core/Compositor.hpp" +#include "frog-color-management-v1.hpp" + +class CFrogColorManager { + public: + CFrogColorManager(SP resource_); + + bool good(); + + private: + SP resource; +}; + +class CFrogColorManagementSurface { + public: + CFrogColorManagementSurface(SP resource_, SP surface_); + + bool good(); + wl_client* client(); + + WP self; + WP surface; + + bool pqIntentSent = false; + + private: + SP resource; + wl_client* pClient = nullptr; +}; + +class CFrogColorManagementProtocol : public IWaylandProtocol { + public: + CFrogColorManagementProtocol(const wl_interface* iface, const int& ver, const std::string& name); + + virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); + + private: + void destroyResource(CFrogColorManager* resource); + void destroyResource(CFrogColorManagementSurface* resource); + + std::vector> m_vManagers; + std::vector> m_vSurfaces; + + friend class CFrogColorManager; + friend class CFrogColorManagementSurface; +}; + +namespace PROTO { + inline UP frogColorManagement; +}; diff --git a/src/protocols/core/Compositor.hpp b/src/protocols/core/Compositor.hpp index 2a6f96f6..b347eb64 100644 --- a/src/protocols/core/Compositor.hpp +++ b/src/protocols/core/Compositor.hpp @@ -25,6 +25,8 @@ class CWLSurfaceResource; class CWLSubsurfaceResource; class CViewportResource; class CDRMSyncobjSurfaceResource; +class CColorManagementSurface; +class CFrogColorManagementSurface; class CWLCallbackResource { public: @@ -121,6 +123,7 @@ class CWLSurfaceResource { SP role; WP viewportResource; WP syncobj; // may not be present + WP colorManagement; void breadthfirst(std::function, const Vector2D&, void*)> fn, void* data); CRegion accumulateCurrentBufferDamage(); diff --git a/src/protocols/types/ColorManagement.hpp b/src/protocols/types/ColorManagement.hpp new file mode 100644 index 00000000..e18f0b84 --- /dev/null +++ b/src/protocols/types/ColorManagement.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include "xx-color-management-v4.hpp" + +struct SImageDescription { + int iccFd = -1; + uint32_t iccSize = 0; + + xxColorManagerV4TransferFunction transferFunction = XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_SRGB; + float transferFunctionPower = 1.0f; + + bool primariesNameSet = false; + xxColorManagerV4Primaries primariesNamed = XX_COLOR_MANAGER_V4_PRIMARIES_SRGB; + // primaries are stored as FP values with the same scale as standard defines (0.0 - 1.0) + // wayland protocol expects int32_t values multiplied by 10000 + // drm expects uint16_t values multiplied by 50000 + // frog protocol expects drm values + struct SPCPRimaries { + struct { + float x = 0; + float y = 0; + } red, green, blue, white; + } primaries, masteringPrimaries; + + // luminances in cd/m² + // protos and drm expect min * 10000 + struct SPCLuminances { + float min = 0.2; // 0.2 cd/m² + uint32_t max = 80; // 80 cd/m² + uint32_t reference = 80; // 80 cd/m² + } luminances; + struct SPCMasteringLuminances { + float min = 0; + uint32_t max = 0; + } masteringLuminances; + + uint32_t maxCLL = 0; + uint32_t maxFALL = 0; +}; + +namespace NColorPrimaries { + static const auto BT709 = + SImageDescription::SPCPRimaries{.red = {.x = 0.64, .y = 0.33}, .green = {.x = 0.30, .y = 0.60}, .blue = {.x = 0.15, .y = 0.06}, .white = {.x = 0.3127, .y = 0.3290}}; + + static const auto BT2020 = + SImageDescription::SPCPRimaries{.red = {.x = 0.708, .y = 0.292}, .green = {.x = 0.170, .y = 0.797}, .blue = {.x = 0.131, .y = 0.046}, .white = {.x = 0.3127, .y = 0.3290}}; +} \ No newline at end of file diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 4d93d92f..978384d5 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -25,6 +25,7 @@ #include "pass/RendererHintsPassElement.hpp" #include "pass/SurfacePassElement.hpp" #include "debug/Log.hpp" +#include "protocols/ColorManagement.hpp" #include using namespace Hyprutils::Utils; @@ -1374,6 +1375,82 @@ void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor) { } } +static const auto BT709 = Aquamarine::IOutput::SChromaticityCoords{ + .red = Aquamarine::IOutput::xy{.x = 0.64, .y = 0.33}, + .green = Aquamarine::IOutput::xy{.x = 0.30, .y = 0.60}, + .blue = Aquamarine::IOutput::xy{.x = 0.15, .y = 0.06}, + .white = Aquamarine::IOutput::xy{.x = 0.3127, .y = 0.3290}, +}; + +static hdr_output_metadata createHDRMetadata(uint8_t eotf, Aquamarine::IOutput::SParsedEDID edid) { + if (eotf == 0) + return hdr_output_metadata{.hdmi_metadata_type1 = hdr_metadata_infoframe{.eotf = 0}}; // empty metadata for SDR + + const auto toNits = [](float value) { return uint16_t(std::round(value)); }; + const auto to16Bit = [](float value) { return uint16_t(std::round(value * 50000)); }; + const auto colorimetry = edid.chromaticityCoords.value_or(BT709); + + Debug::log(TRACE, "ColorManagement primaries {},{} {},{} {},{} {},{}", colorimetry.red.x, colorimetry.red.y, colorimetry.green.x, colorimetry.green.y, colorimetry.blue.x, + colorimetry.blue.y, colorimetry.white.x, colorimetry.white.y); + Debug::log(TRACE, "ColorManagement max avg {}, min {}, max {}", edid.hdrMetadata->desiredMaxFrameAverageLuminance, edid.hdrMetadata->desiredContentMinLuminance, + edid.hdrMetadata->desiredContentMaxLuminance); + return hdr_output_metadata{ + .metadata_type = 0, + .hdmi_metadata_type1 = + hdr_metadata_infoframe{ + .eotf = eotf, + .metadata_type = 0, + .display_primaries = + { + {.x = to16Bit(colorimetry.red.x), .y = to16Bit(colorimetry.red.y)}, + {.x = to16Bit(colorimetry.green.x), .y = to16Bit(colorimetry.green.y)}, + {.x = to16Bit(colorimetry.blue.x), .y = to16Bit(colorimetry.blue.y)}, + }, + .white_point = {.x = to16Bit(colorimetry.white.x), .y = to16Bit(colorimetry.white.y)}, + .max_display_mastering_luminance = toNits(edid.hdrMetadata->desiredMaxFrameAverageLuminance), + .min_display_mastering_luminance = toNits(edid.hdrMetadata->desiredContentMinLuminance * 10000), + .max_cll = toNits(edid.hdrMetadata->desiredMaxFrameAverageLuminance), + .max_fall = toNits(edid.hdrMetadata->desiredMaxFrameAverageLuminance), + }, + }; +} + +static hdr_output_metadata createHDRMetadata(SImageDescription settings, Aquamarine::IOutput::SParsedEDID edid) { + if (settings.transferFunction != XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ) + return hdr_output_metadata{.hdmi_metadata_type1 = hdr_metadata_infoframe{.eotf = 0}}; // empty metadata for SDR + + const auto toNits = [](uint32_t value) { return uint16_t(std::round(value)); }; + const auto to16Bit = [](uint32_t value) { return uint16_t(std::round(value * 50000)); }; + + auto colorimetry = settings.primaries; + auto luminances = settings.masteringLuminances.max > 0 ? + settings.masteringLuminances : + SImageDescription::SPCMasteringLuminances{.min = edid.hdrMetadata->desiredContentMinLuminance, .max = edid.hdrMetadata->desiredContentMaxLuminance}; + + Debug::log(TRACE, "ColorManagement primaries {},{} {},{} {},{} {},{}", colorimetry.red.x, colorimetry.red.y, colorimetry.green.x, colorimetry.green.y, colorimetry.blue.x, + colorimetry.blue.y, colorimetry.white.x, colorimetry.white.y); + Debug::log(TRACE, "ColorManagement min {}, max {}, cll {}, fall {}", luminances.min, luminances.max, settings.maxCLL, settings.maxFALL); + return hdr_output_metadata{ + .metadata_type = 0, + .hdmi_metadata_type1 = + hdr_metadata_infoframe{ + .eotf = 2, + .metadata_type = 0, + .display_primaries = + { + {.x = to16Bit(colorimetry.red.x), .y = to16Bit(colorimetry.red.y)}, + {.x = to16Bit(colorimetry.green.x), .y = to16Bit(colorimetry.green.y)}, + {.x = to16Bit(colorimetry.blue.x), .y = to16Bit(colorimetry.blue.y)}, + }, + .white_point = {.x = to16Bit(colorimetry.white.x), .y = to16Bit(colorimetry.white.y)}, + .max_display_mastering_luminance = toNits(luminances.max), + .min_display_mastering_luminance = toNits(luminances.min * 10000), + .max_cll = toNits(settings.maxCLL), + .max_fall = toNits(settings.maxFALL), + }, + }; +} + bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) { // apply timelines for explicit sync // save inFD otherwise reset will reset it @@ -1382,6 +1459,26 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) { if (inFD >= 0) pMonitor->output->state->setExplicitInFence(inFD); + static auto PWIDE = CConfigValue("experimental:wide_color_gamut"); + if (pMonitor->output->state->state().wideColorGamut != *PWIDE) + Debug::log(TRACE, "Setting wide color gamut {}", *PWIDE ? "on" : "off"); + pMonitor->output->state->setWideColorGamut(*PWIDE); + + static auto PHDR = CConfigValue("experimental:hdr"); + + Debug::log(TRACE, "ColorManagement supportsBT2020 {}, supportsPQ {}", pMonitor->output->parsedEDID.supportsBT2020, pMonitor->output->parsedEDID.hdrMetadata->supportsPQ); + if (pMonitor->output->parsedEDID.supportsBT2020 && pMonitor->output->parsedEDID.hdrMetadata->supportsPQ) { + if (pMonitor->activeWorkspace && pMonitor->activeWorkspace->m_bHasFullscreenWindow && pMonitor->activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN) { + const auto WINDOW = pMonitor->activeWorkspace->getFullscreenWindow(); + const auto SURF = WINDOW->m_pWLSurface->resource(); + if (SURF->colorManagement.valid() && SURF->colorManagement->hasImageDescription()) + pMonitor->output->state->setHDRMetadata(createHDRMetadata(SURF->colorManagement.get()->imageDescription(), pMonitor->output->parsedEDID)); + else + pMonitor->output->state->setHDRMetadata(*PHDR ? createHDRMetadata(2, pMonitor->output->parsedEDID) : createHDRMetadata(0, pMonitor->output->parsedEDID)); + } else + pMonitor->output->state->setHDRMetadata(*PHDR ? createHDRMetadata(2, pMonitor->output->parsedEDID) : createHDRMetadata(0, pMonitor->output->parsedEDID)); + } + if (pMonitor->ctmUpdated) { pMonitor->ctmUpdated = false; pMonitor->output->state->setCTM(pMonitor->ctm); diff --git a/src/render/pass/SurfacePassElement.cpp b/src/render/pass/SurfacePassElement.cpp index f2f2627a..71aa26be 100644 --- a/src/render/pass/SurfacePassElement.cpp +++ b/src/render/pass/SurfacePassElement.cpp @@ -79,6 +79,8 @@ void CSurfacePassElement::draw(const CRegion& damage) { DELTALESSTHAN(windowBox.height, data.surface->current.bufferSize.y, 3) /* off by one-or-two */ && (!data.pWindow || (!data.pWindow->m_vRealSize->isBeingAnimated() && !INTERACTIVERESIZEINPROGRESS)) /* not window or not animated/resizing */; + if (data.surface->colorManagement.valid()) + Debug::log(TRACE, "FIXME: rendering surface with color management enabled, should apply necessary transformations"); g_pHyprRenderer->calculateUVForSurface(data.pWindow, data.surface, data.pMonitor->self.lock(), data.mainSurface, windowBox.size(), PROJSIZEUNSCALED, MISALIGNEDFSV1); // check for fractional scale surfaces misaligning the buffer size