Backport Night Color feature to X11

Summary:
The color correction manager doesn't make any specific assumptions about
underlying platform, e.g. whether it's x11, etc. The platform just
has to be capable of setting gamma ramps. Given that, there are no any
significant technical blockers for making this feature work on x.

Reviewers: #kwin, davidedmundson, romangg

Reviewed By: #kwin, davidedmundson, romangg

Subscribers: romangg, neobrain, GB_2, filipf, davidedmundson, ngraham, kwin

Tags: #kwin

Differential Revision: https://phabricator.kde.org/D21345
This commit is contained in:
Vlad Zagorodniy 2019-06-17 12:07:19 +03:00
parent a39c74059e
commit 0d381846f1
12 changed files with 184 additions and 90 deletions

View file

@ -17,16 +17,53 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "abstract_output.h"
// KF5
#include <KLocalizedString>
#include <cmath>
namespace KWin
{
GammaRamp::GammaRamp(uint32_t size)
: m_table(3 * size)
, m_size(size)
{
}
uint32_t GammaRamp::size() const
{
return m_size;
}
uint16_t *GammaRamp::red()
{
return m_table.data();
}
const uint16_t *GammaRamp::red() const
{
return m_table.data();
}
uint16_t *GammaRamp::green()
{
return m_table.data() + m_size;
}
const uint16_t *GammaRamp::green() const
{
return m_table.data() + m_size;
}
uint16_t *GammaRamp::blue()
{
return m_table.data() + 2 * m_size;
}
const uint16_t *GammaRamp::blue() const
{
return m_table.data() + 2 * m_size;
}
AbstractOutput::AbstractOutput(QObject *parent)
: QObject(parent)
{

View file

@ -25,13 +25,64 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QObject>
#include <QRect>
#include <QSize>
#include <QVector>
namespace KWin
{
namespace ColorCorrect {
struct GammaRamp;
}
class KWIN_EXPORT GammaRamp
{
public:
GammaRamp(uint32_t size);
/**
* Returns the size of the gamma ramp.
**/
uint32_t size() const;
/**
* Returns pointer to the first red component in the gamma ramp.
*
* The returned pointer can be used for altering the red component
* in the gamma ramp.
**/
uint16_t *red();
/**
* Returns pointer to the first red component in the gamma ramp.
**/
const uint16_t *red() const;
/**
* Returns pointer to the first green component in the gamma ramp.
*
* The returned pointer can be used for altering the green component
* in the gamma ramp.
**/
uint16_t *green();
/**
* Returns pointer to the first green component in the gamma ramp.
**/
const uint16_t *green() const;
/**
* Returns pointer to the first blue component in the gamma ramp.
*
* The returned pointer can be used for altering the blue component
* in the gamma ramp.
**/
uint16_t *blue();
/**
* Returns pointer to the first blue component in the gamma ramp.
**/
const uint16_t *blue() const;
private:
QVector<uint16_t> m_table;
uint32_t m_size;
};
/**
* Generic output representation in a Wayland session
@ -64,10 +115,10 @@ public:
return Qt::PrimaryOrientation;
}
virtual int getGammaRampSize() const {
virtual int gammaRampSize() const {
return 0;
}
virtual bool setGammaRamp(const ColorCorrect::GammaRamp &gamma) {
virtual bool setGammaRamp(const GammaRamp &gamma) {
Q_UNUSED(gamma);
return false;
}

View file

@ -1,53 +0,0 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright 2017 Roman Gilg <subdiff@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#ifndef KWIN_GAMMARAMP_H
#define KWIN_GAMMARAMP_H
namespace KWin
{
namespace ColorCorrect
{
struct GammaRamp {
GammaRamp(int _size) {
size = _size;
red = new uint16_t[3 * _size];
green = red + _size;
blue = green + _size;
}
~GammaRamp() {
delete[] red;
red = green = blue = nullptr;
}
uint32_t size = 0;
uint16_t *red = nullptr;
uint16_t *green = nullptr;
uint16_t *blue = nullptr;
private:
Q_DISABLE_COPY(GammaRamp)
};
}
}
#endif // KWIN_GAMMARAMP_H

View file

@ -20,7 +20,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "manager.h"
#include "colorcorrectdbusinterface.h"
#include "suncalc.h"
#include "gammaramp.h"
#include <colorcorrect_logging.h>
#include <main.h>
@ -513,20 +512,23 @@ void Manager::commitGammaRamps(int temperature)
const auto outs = kwinApp()->platform()->outputs();
for (auto *o : outs) {
int rampsize = o->getGammaRampSize();
int rampsize = o->gammaRampSize();
GammaRamp ramp(rampsize);
/*
* The gamma calculation below is based on the Redshift app:
* https://github.com/jonls/redshift
*/
uint16_t *red = ramp.red();
uint16_t *green = ramp.green();
uint16_t *blue = ramp.blue();
// linear default state
for (int i = 0; i < rampsize; i++) {
uint16_t value = (double)i / rampsize * (UINT16_MAX + 1);
ramp.red[i] = value;
ramp.green[i] = value;
ramp.blue[i] = value;
red[i] = value;
green[i] = value;
blue[i] = value;
}
// approximate white point
@ -538,9 +540,9 @@ void Manager::commitGammaRamps(int temperature)
whitePoint[2] = (1. - alpha) * blackbodyColor[bbCIndex + 2] + alpha * blackbodyColor[bbCIndex + 5];
for (int i = 0; i < rampsize; i++) {
ramp.red[i] = (double)ramp.red[i] / (UINT16_MAX+1) * whitePoint[0] * (UINT16_MAX+1);
ramp.green[i] = (double)ramp.green[i] / (UINT16_MAX+1) * whitePoint[1] * (UINT16_MAX+1);
ramp.blue[i] = (double)ramp.blue[i] / (UINT16_MAX+1) * whitePoint[2] * (UINT16_MAX+1);
red[i] = qreal(red[i]) / (UINT16_MAX+1) * whitePoint[0] * (UINT16_MAX+1);
green[i] = qreal(green[i]) / (UINT16_MAX+1) * whitePoint[1] * (UINT16_MAX+1);
blue[i] = qreal(blue[i]) / (UINT16_MAX+1) * whitePoint[2] * (UINT16_MAX+1);
}
if (o->setGammaRamp(ramp)) {

View file

@ -23,7 +23,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "drm_buffer.h"
#include "drm_pointer.h"
#include "logging.h"
#include <colorcorrection/gammaramp.h>
namespace KWin
{
@ -114,9 +113,15 @@ bool DrmCrtc::blank()
return false;
}
bool DrmCrtc::setGammaRamp(const ColorCorrect::GammaRamp &gamma) {
bool isError = drmModeCrtcSetGamma(m_backend->fd(), m_id, gamma.size,
gamma.red, gamma.green, gamma.blue);
bool DrmCrtc::setGammaRamp(const GammaRamp &gamma)
{
uint16_t *red = const_cast<uint16_t *>(gamma.red());
uint16_t *green = const_cast<uint16_t *>(gamma.green());
uint16_t *blue = const_cast<uint16_t *>(gamma.blue());
const bool isError = drmModeCrtcSetGamma(m_backend->fd(), m_id,
gamma.size(), red, green, blue);
return !isError;
}

View file

@ -25,13 +25,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
namespace KWin
{
namespace ColorCorrect {
struct GammaRamp;
}
class DrmBackend;
class DrmBuffer;
class DrmDumbBuffer;
class GammaRamp;
class DrmCrtc : public DrmObject
{
@ -47,7 +44,7 @@ public:
Active,
Count
};
bool initProps();
int resIndex() const {
@ -67,10 +64,10 @@ public:
void flipBuffer();
bool blank();
int getGammaRampSize() const {
int gammaRampSize() const {
return m_gammaRampSize;
}
bool setGammaRamp(const ColorCorrect::GammaRamp &gamma);
bool setGammaRamp(const GammaRamp &gamma);
private:
int m_resIndex;

View file

@ -1201,12 +1201,12 @@ void DrmOutput::automaticRotation()
emit screens()->changed();
}
int DrmOutput::getGammaRampSize() const
int DrmOutput::gammaRampSize() const
{
return m_crtc->getGammaRampSize();
return m_crtc->gammaRampSize();
}
bool DrmOutput::setGammaRamp(const ColorCorrect::GammaRamp &gamma)
bool DrmOutput::setGammaRamp(const GammaRamp &gamma)
{
return m_crtc->setGammaRamp(gamma);
}

View file

@ -133,8 +133,8 @@ private:
void transform(KWayland::Server::OutputDeviceInterface::Transform transform) override;
void automaticRotation();
int getGammaRampSize() const override;
bool setGammaRamp(const ColorCorrect::GammaRamp &gamma) override;
int gammaRampSize() const override;
bool setGammaRamp(const GammaRamp &gamma) override;
QMatrix4x4 matrixDisplay(const QSize &s) const;
DrmBackend *m_backend;

View file

@ -41,10 +41,10 @@ public:
void setGeometry(const QRect &geo);
int getGammaRampSize() const override {
int gammaRampSize() const override {
return m_gammaSize;
}
bool setGammaRamp(const ColorCorrect::GammaRamp &gamma) override {
bool setGammaRamp(const GammaRamp &gamma) override {
Q_UNUSED(gamma);
return m_gammaResult;
}

View file

@ -61,4 +61,31 @@ void X11Output::setRefreshRate(int set)
m_refreshRate = set;
}
int X11Output::gammaRampSize() const
{
return m_gammaRampSize;
}
bool X11Output::setGammaRamp(const GammaRamp &gamma)
{
if (m_crtc == XCB_NONE) {
return false;
}
xcb_randr_set_crtc_gamma(connection(), m_crtc, gamma.size(), gamma.red(),
gamma.green(), gamma.blue());
return true;
}
void X11Output::setCrtc(xcb_randr_crtc_t crtc)
{
m_crtc = crtc;
}
void X11Output::setGammaRampSize(int size)
{
m_gammaRampSize = size;
}
}

View file

@ -26,6 +26,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QObject>
#include <QRect>
#include <xcb/randr.h>
namespace KWin
{
@ -35,6 +37,7 @@ namespace KWin
class KWIN_EXPORT X11Output : public AbstractOutput
{
Q_OBJECT
public:
explicit X11Output(QObject *parent = nullptr);
virtual ~X11Output() = default;
@ -53,10 +56,23 @@ public:
int refreshRate() const override;
void setRefreshRate(int set);
/**
* The size of gamma lookup table.
**/
int gammaRampSize() const override;
bool setGammaRamp(const GammaRamp &gamma) override;
private:
void setCrtc(xcb_randr_crtc_t crtc);
void setGammaRampSize(int size);
xcb_randr_crtc_t m_crtc = XCB_NONE;
QString m_name;
QRect m_geometry;
int m_gammaRampSize;
int m_refreshRate;
friend class X11StandalonePlatform;
};
}

View file

@ -81,6 +81,8 @@ X11StandalonePlatform::X11StandalonePlatform(QObject *parent)
}
}
);
setSupportsGammaControl(true);
}
X11StandalonePlatform::~X11StandalonePlatform()
@ -456,6 +458,7 @@ void X11StandalonePlatform::doUpdateOutputs()
{
auto fallback = [this]() {
auto *o = new X11Output(this);
o->setGammaRampSize(0);
o->setRefreshRate(-1.0f);
o->setName(QStringLiteral("Xinerama"));
m_outputs << o;
@ -514,14 +517,23 @@ void X11StandalonePlatform::doUpdateOutputs()
const QRect geo = info.rect();
if (geo.isValid()) {
xcb_randr_crtc_t crtc = crtcs[i];
// TODO: Perhaps the output has to save the inherited gamma ramp and
// restore it during tear down. Currently neither standalone x11 nor
// drm platform do this.
Xcb::RandR::CrtcGamma gamma(crtc);
auto *o = new X11Output(this);
o->setCrtc(crtc);
o->setGammaRampSize(gamma.isNull() ? 0 : gamma->size);
o->setGeometry(geo);
o->setRefreshRate(refreshRate);
QString name;
for (int j = 0; j < info->num_outputs; ++j) {
Xcb::RandR::OutputInfo outputInfo(outputInfos.at(j));
if (crtcs[i] == outputInfo->crtc) {
if (crtc == outputInfo->crtc) {
name = outputInfo.name();
break;
}