Skip to content

Conversation

@misaka18931
Copy link
Collaborator

@misaka18931 misaka18931 commented Oct 21, 2025

This commit changes the following:

  • renamed src/modules/primary-output to src/modules/output-manager, for that protocol now handles more than setting primary output.
  • renamed the PrimaryOutputV1 class to OutputManagerV1, for the same reason above.
  • added implementation of the output_color_control_v1 interface in output-manager.
  • added setOutputGammaLUT method in WOutputViewport to set per channel gamma LUT through wlr_output_state_set_gamma_lut.
  • added brightness and color temperature settings in Output class, which uses backlight interface if available, otherwise uses gamma LUT.
  • added persistent settings for per-output brightness and color temperature in TreelandConfig, the Output class queries above settings on output initialization, and sets the values on change.
  • added example test_color_manager to demonstrate the usage of output_color_control_v1.
  • added Backlight base class in src/output/backlight, currently it contains no functionality. Currently, I can't find any reliable mechanism to query the backlight driver for an output from its drm connector found through wl_output. If such mechanism is found, implementation can follow simply by subclassing Backlight.

Summary by Sourcery

Implement brightness and color temperature controls via a new treeland_output_color_control_v1 interface, rename the primary-output module to output-manager, introduce a Backlight abstraction with gamma LUT fallback, persist settings, and add example and test updates.

New Features:

  • Add treeland_output_color_control_v1 protocol for runtime brightness and color temperature control
  • Expose Output brightness and colorTemperature properties and apply them via Backlight or gamma LUT
  • Implement Backlight abstraction with gamma LUT fallback for hardware brightness adjustments
  • Include test_color_manager example demonstrating the new color control interface

Enhancements:

  • Rename primary-output module and PrimaryOutputV1 to output-manager and OutputManagerV1
  • Persist per-output brightness and color temperature in TreelandConfig and restore on initialization
  • Add setOutputGammaLUT in WOutputViewport and corresponding helper methods to apply gamma ramps

Build:

  • Update CMakeLists to include output-manager module, backlight sources, and new test_color_manager example

Tests:

  • Update protocol unit tests to reference OutputManagerV1 instead of PrimaryOutputV1

@deepin-ci-robot
Copy link

Hi @misaka18931. Thanks for your PR.

I'm waiting for a linuxdeepin member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

@sourcery-ai
Copy link

sourcery-ai bot commented Oct 21, 2025

Reviewer's Guide

This PR refactors the primary-output module into a general output-manager, extends its Wayland protocol with a color control interface, enriches the core Output class with gamma‐LUT-based brightness and color temperature adjustments (falling back to backlight), persists settings in configuration, and updates server plumbing and examples accordingly.

Sequence diagram for brightness and color temperature setting via color control protocol

sequenceDiagram
    participant Client
    participant OutputManagerV1
    participant treeland_output_color_control_v1
    participant Output
    Client->>OutputManagerV1: get_color_control(output)
    OutputManagerV1->>treeland_output_color_control_v1: create for output
    treeland_output_color_control_v1->>Output: requestSetBrightness(brightness)
    Output->>Output: setBrightness(brightness)
    Output->>treeland_output_color_control_v1: brightnessChanged()
    treeland_output_color_control_v1->>Client: sendBrightness(brightness)
    treeland_output_color_control_v1->>Output: requestSetColorTemperature(temperature)
    Output->>Output: setColorTemperature(temperature)
    Output->>treeland_output_color_control_v1: colorTemperatureChanged()
    treeland_output_color_control_v1->>Client: sendColorTemperature(temperature)
Loading

Class diagram for Output and Backlight changes

classDiagram
    class Output {
        +qreal brightness
        +uint32_t colorTemperature
        +Backlight* m_backlight
        +qreal brightness() const
        +uint32_t colorTemperature() const
        +void setBrightness(qreal brightness)
        +void setColorTemperature(uint32_t colorTemperature)
        +signal brightnessChanged()
        +signal colorTemperatureChanged()
    }
    class Backlight {
        +qreal m_maxBrightness
        +qreal m_minBrightness
        +qreal m_brightness
        +Backlight()
        +~Backlight()
        +static Backlight* createForOutput(WOutput* output)
        +qreal brightness() const
        +qreal maxBrightness() const
        +qreal minBrightness() const
        +virtual qreal setBrightness(qreal brightness)
    }
    Output --> Backlight : composition
Loading

Class diagram for OutputManagerV1 and color control protocol

classDiagram
    class OutputManagerV1 {
        +void sendPrimaryOutput(const char *name)
        +void onColorControlCreated(treeland_output_color_control_v1 *control)
        +signal requestSetPrimaryOutput(const char *name)
    }
    class treeland_output_manager_v1 {
        +void set_primary_output(const char *name)
        +signal requestSetPrimaryOutput(const char *name)
        +signal colorControlCreated(treeland_output_color_control_v1 *control)
    }
    class treeland_output_color_control_v1 {
        +void sendColorTemperature(uint32_t temperature)
        +void sendBrightness(uint32_t brightness)
        +signal requestSetColorTemperature(uint32_t temperature)
        +signal requestSetBrightness(uint32_t brightness)
    }
    OutputManagerV1 --> treeland_output_manager_v1 : composition
    treeland_output_manager_v1 --> treeland_output_color_control_v1 : creates
Loading

File-Level Changes

Change Details Files
Module and class renaming for expanded output management
  • rename src/modules/primary-output to src/modules/output-manager and update CMakeLists
  • rename PrimaryOutputV1 class to OutputManagerV1 and adjust interfaces
  • update Helper and tests to reference OutputManagerV1 instead of PrimaryOutputV1
src/modules/primary-output/outputmanagement.h
src/modules/output-manager/outputmanagement.h
src/seat/helper.cpp
tests/test_protocol_primary-output/main.cpp
src/modules/CMakeLists.txt
Add color control interface to output-manager protocol
  • define treeland_output_color_control_v1 interface with set_brightness and set_color_temperature
  • emit colorControlCreated signal on protocol bind
  • handle client requests and forward via requestSetBrightness/Temperature signals
src/modules/output-manager/impl/output_manager_impl.h
src/modules/output-manager/impl/output_manager_impl.cpp
src/modules/output-manager/outputmanagement.cpp
Enhance Output class with brightness and color temperature support
  • add m_brightness, m_colorTemperature, getters, signals, and Q_PROPERTY declarations
  • load and persist per-output settings via Helper::config
  • implement setBrightness and setColorTemperature with gamma LUT generation
src/output/output.cpp
src/output/output.h
Introduce gamma LUT plumbing and Backlight abstraction
  • add kelvinToRGB, sRGB/linear conversion and generateGammaLUT functions
  • extend WOutputHelper, WOutputRenderWindow, and WOutputViewport with setGammaLUT methods
  • add Backlight base class with createForOutput stub for driver integration
waylib/src/server/qtquick/woutputhelper.cpp
waylib/src/server/qtquick/woutputrenderwindow.h
waylib/src/server/qtquick/woutputrenderwindow.cpp
waylib/src/server/qtquick/woutputviewport.cpp
src/output/backlight/backlight.h
src/output/backlight/backlight.cpp
Update build configuration and add usage examples
  • add test_color_manager example and update examples/test_primary_output
  • adjust top-level and module CMakeLists to include output-manager and backlight
  • generate Wayland client code for treeland-output-manager-v1 in tests and examples
examples/test_color_manager/main.cpp
examples/test_color_manager/CMakeLists.txt
examples/test_primary_output/main.cpp
examples/CMakeLists.txt
src/modules/output-manager/CMakeLists.txt
src/CMakeLists.txt

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes - here's some feedback:

  • The generateGammaLUT and kelvin‐to‐RGB math is fairly heavy—consider moving it into its own utility or class so it can be unit-tested and avoids cluttering Output.cpp.
  • treeland_output_manager_v1 currently uses one wl_resource list for both primary‐output and color‐control resources; splitting those into separate lists will prevent iterating color-control resources when sending primary-output events.
  • In setBrightness and setColorTemperature, validate and clamp incoming values (e.g. brightness to [0,1], temperature to supported range) before applying or persisting them.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The generateGammaLUT and kelvin‐to‐RGB math is fairly heavy—consider moving it into its own utility or class so it can be unit-tested and avoids cluttering Output.cpp.
- treeland_output_manager_v1 currently uses one wl_resource list for both primary‐output and color‐control resources; splitting those into separate lists will prevent iterating color-control resources when sending primary-output events.
- In setBrightness and setColorTemperature, validate and clamp incoming values (e.g. brightness to [0,1], temperature to supported range) before applying or persisting them.

## Individual Comments

### Comment 1
<location> `src/output/output.cpp:147-149` </location>
<code_context>
+    auto brightnessMap = Helper::instance()->config()->perOutputBrightness();
+    auto colorTempMap = Helper::instance()->config()->perOutputColorTemperature();
+    QString outputName = output->output()->name();
+    if (brightnessMap.contains(outputName)) {
+        auto confBrightness = brightnessMap.value(outputName);
+        if (confBrightness.canConvert<qreal>()) {
+            m_brightness = confBrightness.toDouble();
+        }
</code_context>

<issue_to_address>
**suggestion:** Consider handling invalid or out-of-range brightness values.

The code assigns m_brightness without validating its range. Please ensure brightness is clamped or checked to stay within acceptable limits (e.g., 0.0 to 1.0).

Suggested implementation:

```cpp
        if (confBrightness.canConvert<qreal>()) {
            m_brightness = confBrightness.toDouble();
            // Clamp brightness to [0.0, 1.0]
            m_brightness = std::clamp(m_brightness, 0.0, 1.0);
        }

```

If your project does not use C++17 or does not have access to `std::clamp`, replace the clamp line with:
```cpp
m_brightness = qBound(0.0, m_brightness, 1.0);
```
or implement a manual clamp:
```cpp
m_brightness = (m_brightness < 0.0) ? 0.0 : (m_brightness > 1.0) ? 1.0 : m_brightness;
```
Make sure to include the appropriate headers for `std::clamp` (`#include <algorithm>`) or `qBound` (`#include <QtGlobal>`).
</issue_to_address>

### Comment 2
<location> `src/output/output.cpp:155-157` </location>
<code_context>
+            m_brightness = confBrightness.toDouble();
+        }
+    }
+    if (colorTempMap.contains(outputName)) {
+        auto confColorTemp = colorTempMap.value(outputName);
+        if (confColorTemp.canConvert<int>()) {
+            m_colorTemperature = confColorTemp.toInt();
+        }
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Color temperature should be validated to avoid out-of-range values.

Assigning m_colorTemperature without range validation may allow invalid values. Please add checks to ensure the value stays within the acceptable range (e.g., 1000K–40000K).

```suggestion
        if (confColorTemp.canConvert<int>()) {
            int temp = confColorTemp.toInt();
            if (temp >= 1000 && temp <= 40000) {
                m_colorTemperature = temp;
            } else {
                // Optionally, handle out-of-range value (e.g., log a warning or set to default)
            }
        }
```
</issue_to_address>

### Comment 3
<location> `src/output/output.cpp:962-963` </location>
<code_context>
+
+    qreal brightnessCorrection = 1.0;
+
+    if (m_backlight) {
+        brightnessCorrection = brightness / m_backlight->setBrightness(brightness);
+    } else {
+        brightnessCorrection = brightness;
</code_context>

<issue_to_address>
**issue (bug_risk):** Division by zero risk in brightness correction calculation.

Add a check to ensure m_backlight->setBrightness(brightness) does not return zero before performing the division.
</issue_to_address>

### Comment 4
<location> `tests/test_protocol_primary-output/main.cpp:30` </location>
<code_context>

     void testCreate()
     {
-        QVERIFY(m_server->attach<PrimaryOutputV1>(m_server) != nullptr);
</code_context>

<issue_to_address>
**suggestion (testing):** Test only verifies protocol object creation, not new color control features.

Please add tests for the new color control features, including brightness, color temperature, and their related signals and state changes.

Suggested implementation:

```cpp
    void testCreate()
    {
        QVERIFY(m_server->attach<OutputManagerV1>(m_server) != nullptr);
    }

    void verifyPrimaryOutput()
    {
        auto protocol = m_server->findChild<OutputManagerV1 *>();
        QVERIFY(protocol != nullptr);
    }

    void testBrightnessControl()
    {
        auto protocol = m_server->findChild<OutputManagerV1 *>();
        QVERIFY(protocol != nullptr);

        // Assume protocol has setBrightness/getBrightness and brightnessChanged signal
        QSignalSpy brightnessSpy(protocol, SIGNAL(brightnessChanged(int)));

        int initialBrightness = protocol->brightness();
        protocol->setBrightness(80);
        QCOMPARE(protocol->brightness(), 80);
        QVERIFY(brightnessSpy.count() == 1);
        QList<QVariant> arguments = brightnessSpy.takeFirst();
        QCOMPARE(arguments.at(0).toInt(), 80);

        // Restore initial state
        protocol->setBrightness(initialBrightness);
    }

    void testColorTemperatureControl()
    {
        auto protocol = m_server->findChild<OutputManagerV1 *>();
        QVERIFY(protocol != nullptr);

        // Assume protocol has setColorTemperature/getColorTemperature and colorTemperatureChanged signal
        QSignalSpy colorTempSpy(protocol, SIGNAL(colorTemperatureChanged(int)));

        int initialColorTemp = protocol->colorTemperature();
        protocol->setColorTemperature(6500);
        QCOMPARE(protocol->colorTemperature(), 6500);
        QVERIFY(colorTempSpy.count() == 1);
        QList<QVariant> arguments = colorTempSpy.takeFirst();
        QCOMPARE(arguments.at(0).toInt(), 6500);

        // Restore initial state
        protocol->setColorTemperature(initialColorTemp);
    }

```

- You may need to ensure that `OutputManagerV1` exposes `setBrightness`, `brightness`, `brightnessChanged`, `setColorTemperature`, `colorTemperature`, and `colorTemperatureChanged` as public methods/signals.
- If the signal names or method signatures differ, adjust them to match your implementation.
- Register these new test functions with your test runner if required.
</issue_to_address>

### Comment 5
<location> `tests/test_protocol_primary-output/main.cpp:35` </location>
<code_context>
+        QVERIFY(m_server->attach<OutputManagerV1>(m_server) != nullptr);
     }

     void verifyPrimaryOutput()
     {
-        auto protocol = m_server->findChild<PrimaryOutputV1 *>();
</code_context>

<issue_to_address>
**suggestion (testing):** No edge case or error condition tests for protocol interactions.

Please add tests for invalid output names, missing outputs, and error handling to ensure the protocol behaves correctly under unexpected conditions.

Suggested implementation:

```cpp
    void verifyPrimaryOutput()
    {
        auto protocol = m_server->findChild<OutputManagerV1 *>();
        QVERIFY(protocol != nullptr);
    }

    void testInvalidOutputName()
    {
        auto protocol = m_server->findChild<OutputManagerV1 *>();
        QVERIFY(protocol != nullptr);

        // Try to get an output with an invalid name
        auto invalidOutput = protocol->getOutputByName("non_existent_output");
        QVERIFY(invalidOutput == nullptr);
    }

    void testMissingOutput()
    {
        auto protocol = m_server->findChild<OutputManagerV1 *>();
        QVERIFY(protocol != nullptr);

        // Simulate missing output scenario
        QString missingName = "missing_output";
        auto missingOutput = protocol->getOutputByName(missingName);
        QVERIFY(missingOutput == nullptr);
    }

    void testErrorHandling()
    {
        auto protocol = m_server->findChild<OutputManagerV1 *>();
        QVERIFY(protocol != nullptr);

        // Pass a null or malformed parameter
        auto nullOutput = protocol->getOutputByName(QString());
        QVERIFY(nullOutput == nullptr);

        // Optionally, test for exception or error code if protocol supports it
        // QVERIFY_EXCEPTION_THROWN(protocol->getOutputByName(""), SomeExceptionType);
    }

```

- Ensure that `OutputManagerV1` has a method `getOutputByName(const QString&)` or similar. If not, you may need to implement or mock this method for the tests to compile and run.
- Register these new test functions with your test runner (e.g., add them to the test suite if using QTest).
- If your protocol uses error codes or exceptions for error handling, adjust the testErrorHandling function to check for those instead of nullptr.
</issue_to_address>

### Comment 6
<location> `src/output/output.cpp:893` </location>
<code_context>
+}
+
+namespace {
+static inline void kelvinToRGB(double kelvin, double &r, double &g, double &b)
+{
+    kelvin = std::clamp(kelvin, 1000.0, 40000.0) / 100.0;
</code_context>

<issue_to_address>
**issue (complexity):** Consider extracting the color temperature and gamma LUT logic into a dedicated utility file to simplify Output.cpp.

```suggestion
// 1) Extract all the temperature→RGB, linear/sRGB conversions, and LUT‐generation
//    into a small utility in its own file (e.g. ColorGammaUtil). This removes ~100 LOC
//    from Output.cpp and makes Output focus on “when” to call it, not “how”.

// File: src/color/ColorGammaUtil.h
#pragma once
#include <QVector>
#include <cstdint>
#include <cstddef>
#include <QtGlobal>

namespace ColorGammaUtil {
    // Fill r/g/b LUTs for given colorTemp & brightness
    void generateGammaLUT(uint32_t colorTemperature,
                          qreal    brightness,
                          size_t   gammaSize,
                          QVector<uint16_t> &r,
                          QVector<uint16_t> &g,
                          QVector<uint16_t> &b);
}

// File: src/color/ColorGammaUtil.cpp
#include "ColorGammaUtil.h"
#include <algorithm>
#include <cmath>

namespace {
    static inline void kelvinToRGB(double kelvin, double &r, double &g, double &b) {
        // … existing kelvin→rgb code …
    }
    static inline double sRGBToLinear(double v) {
        return (v <= 0.04045)
            ? v / 12.92
            : std::pow((v + 0.055) / 1.055, 2.4);
    }
    static inline double linearToSRGB(double v) {
        return (v <= 0.0031308)
            ? 12.92 * v
            : 1.055 * std::pow(v, 1.0/2.4) - 0.055;
    }
}

void ColorGammaUtil::generateGammaLUT(uint32_t colorTemperature,
                                      qreal    brightness,
                                      size_t   gammaSize,
                                      QVector<uint16_t> &r,
                                      QVector<uint16_t> &g,
                                      QVector<uint16_t> &b)
{
    if (gammaSize == 0) return;
    double cr, cg, cb;
    kelvinToRGB(colorTemperature, cr, cg, cb);
    cr = sRGBToLinear(cr); cg = sRGBToLinear(cg); cb = sRGBToLinear(cb);

    for (size_t i = 0; i < gammaSize; ++i) {
        double norm = double(i) / double(gammaSize - 1);
        double lin = std::pow(norm, 2.2);
        auto clamp01 = [](double x){ return std::clamp(x, 0.0, 1.0); };
        double rv = linearToSRGB(clamp01(lin * cr * brightness));
        double gv = linearToSRGB(clamp01(lin * cg * brightness));
        double bv = linearToSRGB(clamp01(lin * cb * brightness));
        r[i] = uint16_t(std::round(rv * 65535.0));
        g[i] = uint16_t(std::round(gv * 65535.0));
        b[i] = uint16_t(std::round(bv * 65535.0));
    }
}

// 2) In Output.cpp simply include & call it:

// … at the top of Output.cpp …
#include "color/ColorGammaUtil.h"

// … inside setBrightness() or setColorTemperature() …
size_t gammaSize = wlr_output_get_gamma_size(output()->nativeHandle());
QVector<uint16_t> r(gammaSize), g(gammaSize), b(gammaSize);
ColorGammaUtil::generateGammaLUT(m_colorTemperature,
                                 brightnessCorrection,
                                 gammaSize,
                                 r, g, b);
screenViewport()->setOutputGammaLUT(r, g, b);

// This keeps all functionality but collapses ~150 LOC of inline math
// into a single, testable utility. You can unit‐test ColorGammaUtil
// separately and keep Output::_only_ orchestrating calls.
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines 147 to 149
if (brightnessMap.contains(outputName)) {
auto confBrightness = brightnessMap.value(outputName);
if (confBrightness.canConvert<qreal>()) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Consider handling invalid or out-of-range brightness values.

The code assigns m_brightness without validating its range. Please ensure brightness is clamped or checked to stay within acceptable limits (e.g., 0.0 to 1.0).

Suggested implementation:

        if (confBrightness.canConvert<qreal>()) {
            m_brightness = confBrightness.toDouble();
            // Clamp brightness to [0.0, 1.0]
            m_brightness = std::clamp(m_brightness, 0.0, 1.0);
        }

If your project does not use C++17 or does not have access to std::clamp, replace the clamp line with:

m_brightness = qBound(0.0, m_brightness, 1.0);

or implement a manual clamp:

m_brightness = (m_brightness < 0.0) ? 0.0 : (m_brightness > 1.0) ? 1.0 : m_brightness;

Make sure to include the appropriate headers for std::clamp (#include <algorithm>) or qBound (#include <QtGlobal>).

}

namespace {
static inline void kelvinToRGB(double kelvin, double &r, double &g, double &b)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider extracting the color temperature and gamma LUT logic into a dedicated utility file to simplify Output.cpp.

Suggested change
static inline void kelvinToRGB(double kelvin, double &r, double &g, double &b)
// 1) Extract all the temperature→RGB, linear/sRGB conversions, and LUT‐generation
// into a small utility in its own file (e.g. ColorGammaUtil). This removes ~100 LOC
// from Output.cpp and makes Output focus on “when” to call it, not “how”.
// File: src/color/ColorGammaUtil.h
#pragma once
#include <QVector>
#include <cstdint>
#include <cstddef>
#include <QtGlobal>
namespace ColorGammaUtil {
// Fill r/g/b LUTs for given colorTemp & brightness
void generateGammaLUT(uint32_t colorTemperature,
qreal brightness,
size_t gammaSize,
QVector<uint16_t> &r,
QVector<uint16_t> &g,
QVector<uint16_t> &b);
}
// File: src/color/ColorGammaUtil.cpp
#include "ColorGammaUtil.h"
#include <algorithm>
#include <cmath>
namespace {
static inline void kelvinToRGB(double kelvin, double &r, double &g, double &b) {
// … existing kelvin→rgb code …
}
static inline double sRGBToLinear(double v) {
return (v <= 0.04045)
? v / 12.92
: std::pow((v + 0.055) / 1.055, 2.4);
}
static inline double linearToSRGB(double v) {
return (v <= 0.0031308)
? 12.92 * v
: 1.055 * std::pow(v, 1.0/2.4) - 0.055;
}
}
void ColorGammaUtil::generateGammaLUT(uint32_t colorTemperature,
qreal brightness,
size_t gammaSize,
QVector<uint16_t> &r,
QVector<uint16_t> &g,
QVector<uint16_t> &b)
{
if (gammaSize == 0) return;
double cr, cg, cb;
kelvinToRGB(colorTemperature, cr, cg, cb);
cr = sRGBToLinear(cr); cg = sRGBToLinear(cg); cb = sRGBToLinear(cb);
for (size_t i = 0; i < gammaSize; ++i) {
double norm = double(i) / double(gammaSize - 1);
double lin = std::pow(norm, 2.2);
auto clamp01 = [](double x){ return std::clamp(x, 0.0, 1.0); };
double rv = linearToSRGB(clamp01(lin * cr * brightness));
double gv = linearToSRGB(clamp01(lin * cg * brightness));
double bv = linearToSRGB(clamp01(lin * cb * brightness));
r[i] = uint16_t(std::round(rv * 65535.0));
g[i] = uint16_t(std::round(gv * 65535.0));
b[i] = uint16_t(std::round(bv * 65535.0));
}
}
// 2) In Output.cpp simply include & call it:
// … at the top of Output.cpp …
#include "color/ColorGammaUtil.h"
// … inside setBrightness() or setColorTemperature() …
size_t gammaSize = wlr_output_get_gamma_size(output()->nativeHandle());
QVector<uint16_t> r(gammaSize), g(gammaSize), b(gammaSize);
ColorGammaUtil::generateGammaLUT(m_colorTemperature,
brightnessCorrection,
gammaSize,
r, g, b);
screenViewport()->setOutputGammaLUT(r, g, b);
// This keeps all functionality but collapses ~150 LOC of inline math
// into a single, testable utility. You can unit‐test ColorGammaUtil
// separately and keep Output::_only_ orchestrating calls.

Copy link
Member

@zccrs zccrs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

还没有看完,下午继续

@zccrs zccrs requested a review from Copilot October 21, 2025 05:41
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR extends the treeland output manager protocol to support per-output brightness and color temperature control, beyond just setting the primary output. The implementation includes both hardware backlight support (with fallback to gamma LUT) and persistent configuration storage.

Key changes:

  • Renamed PrimaryOutputV1 to OutputManagerV1 and relocated from src/modules/primary-output to src/modules/output-manager
  • Added treeland_output_color_control_v1 interface for runtime brightness and color temperature adjustment
  • Implemented gamma LUT-based brightness/color temperature control with backlight abstraction layer

Reviewed Changes

Copilot reviewed 27 out of 27 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
waylib/src/server/qtquick/woutputviewport.* Added setOutputGammaLUT method to apply per-channel gamma LUT
waylib/src/server/qtquick/woutputrenderwindow.* Added setOutputGammaLUT to propagate gamma LUT settings to output helper
waylib/src/server/qtquick/woutputhelper.* Implemented setGammaLUT using wlr_output_state_set_gamma_lut
src/output/output.* Added brightness/color temperature properties with gamma LUT generation and config persistence
src/output/backlight/* Created Backlight abstraction (currently a no-op stub)
src/modules/output-manager/* Renamed and extended module with color control interface implementation
src/seat/helper.* Updated to use renamed OutputManagerV1 class
tests/test_protocol_primary-output/main.cpp Updated tests to reference OutputManagerV1
examples/test_color_manager/* Added example demonstrating color control usage
misc/dconfig/org.deepin.treeland.json Added persistent config keys for per-output brightness and color temperature

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

public:
Backlight();
virtual ~Backlight();
static Backlight* createForOutput(WOutput* output);
Copy link

Copilot AI Oct 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The factory method returns a raw pointer without clear ownership semantics. Consider returning std::unique_ptr<Backlight> to make ownership explicit and prevent potential memory leaks.

Copilot uses AI. Check for mistakes.
@deepin-bot
Copy link

deepin-bot bot commented Oct 30, 2025

TAG Bot

New tag: 0.7.6
DISTRIBUTION: unstable
Suggest: synchronizing this PR through rebase #611

@misaka18931 misaka18931 force-pushed the master branch 2 times, most recently from f6792a3 to 52f3ed4 Compare November 2, 2025 15:58
@zccrs zccrs requested a review from Copilot November 5, 2025 05:08
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 28 out of 28 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@deepin-bot
Copy link

deepin-bot bot commented Nov 7, 2025

TAG Bot

New tag: 0.7.7
DISTRIBUTION: unstable
Suggest: synchronizing this PR through rebase #619

@misaka18931 misaka18931 force-pushed the master branch 3 times, most recently from 9b6636c to 0815d41 Compare November 10, 2025 00:37
@misaka18931 misaka18931 force-pushed the master branch 2 times, most recently from b89389b to 882b19c Compare November 10, 2025 07:41
…-output-manager-v1

This commit changes the following:
- renamed src/modules/primary-output to src/modules/output-manager, for
  that protocol now handles more than setting primary output.
- renamed the PrimaryOutputV1 class to OutputManagerV1, for the same reason above.
- added implementation of the output_color_control_v1 interface in output-manager.
- added setOutputGammaLUT method in WOutputRenderWindow to set per channel gamma LUT
  through wlr_output_state_set_gamma_lut.
- added new DConfig class OutputConfig, with description in
  `misc/dconfig/org.deepin.treeland.output.json`, for per-output persistent
  settings including brightness and color temperature. The dconfig settings
  are stored in `org.deepin.treeland.outputs` under subpath of output's
  connector name (e.g. `/eDP-1`) and exposed under Output by method `config()`.
- added Qt slot method `setOutputColor(double brightness, int colorTemperature)`
  for atomic update of brightness and color temperature settings.
- added a backlight helper class in src/output/backlight using sysfs
  backlight interface.
  `Backlight::createForOutput(WOutput*)` tries to find a backlight device
  for the given output based on drm connector id and heuristics.
  The Backlight class writes to /sys/class/backlight/<device>/brightness
  to set brightness if the file is writeable, otherwise it will fail
  semi-silently, printing a warning message.
- added example test_color_control to demonstrate the usage of output_color_control_v1.
@deepin-ci-robot
Copy link

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: misaka18931, zccrs

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@zccrs zccrs merged commit cfaea20 into linuxdeepin:master Nov 10, 2025
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants