Implement gamma ramp adjustment

This commit is contained in:
Victor Tran 2023-10-15 23:24:08 +11:00
parent bd03fe54cc
commit 552760db8a
No known key found for this signature in database
10 changed files with 259 additions and 10 deletions

View file

@ -28,25 +28,29 @@ function(tdesktopenvironment_register_wayland_protocol_extension target)
foreach(_file ${REGISTER_WAYLAND_PROTOCOL_EXTENSIONS_FILES})
get_filename_component(_basename ${_file} NAME_WE)
if(NOT EXISTS ${_file})
set(_file ${CMAKE_CURRENT_SOURCE_DIR}/${_file})
endif()
# Wayland scanner
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/wayland-${_basename}-client-protocol.h"
COMMAND ${WAYLAND_SCANNER} client-header ${CMAKE_CURRENT_SOURCE_DIR}/${_file} "${CMAKE_CURRENT_BINARY_DIR}/wayland-${_basename}-client-protocol.h"
COMMAND ${WAYLAND_SCANNER} client-header ${_file} "${CMAKE_CURRENT_BINARY_DIR}/wayland-${_basename}-client-protocol.h"
DEPENDS ${_file}
VERBATIM)
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/wayland-${_basename}-client-protocol.c"
COMMAND ${WAYLAND_SCANNER} public-code ${CMAKE_CURRENT_SOURCE_DIR}/${_file} "${CMAKE_CURRENT_BINARY_DIR}/wayland-${_basename}-client-protocol.c"
COMMAND ${WAYLAND_SCANNER} public-code ${_file} "${CMAKE_CURRENT_BINARY_DIR}/wayland-${_basename}-client-protocol.c"
DEPENDS ${_file}
VERBATIM)
# Qt Wayland scanner
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/qwayland-${_basename}.h"
COMMAND ${QT_WAYLAND_SCANNER} client-header ${CMAKE_CURRENT_SOURCE_DIR}/${_file} > "${CMAKE_CURRENT_BINARY_DIR}/qwayland-${_basename}.h"
COMMAND ${QT_WAYLAND_SCANNER} client-header ${_file} > "${CMAKE_CURRENT_BINARY_DIR}/qwayland-${_basename}.h"
DEPENDS ${_file}
VERBATIM)
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/qwayland-${_basename}.cpp"
COMMAND ${QT_WAYLAND_SCANNER} client-code ${CMAKE_CURRENT_SOURCE_DIR}/${_file} > "${CMAKE_CURRENT_BINARY_DIR}/qwayland-${_basename}.cpp"
COMMAND ${QT_WAYLAND_SCANNER} client-code ${_file} > "${CMAKE_CURRENT_BINARY_DIR}/qwayland-${_basename}.cpp"
DEPENDS ${_file}
VERBATIM)
@ -65,4 +69,4 @@ endfunction()
add_subdirectory(wayland-layer-shell)
add_subdirectory(lib)
add_subdirectory(plugins)
add_subdirectory(plugins)

View file

@ -17,6 +17,8 @@ else()
Screens/waylandmode.cpp
Screens/waylandscreen.cpp
Screens/waylandscreenbackend.cpp
Screens/waylandgammacontrol.cpp
Screens/waylandoutput.cpp
)
set(HEADERS
@ -31,6 +33,8 @@ else()
Screens/waylandmode.h
Screens/waylandscreen.h
Screens/waylandscreenbackend.h
Screens/waylandgammacontrol.h
Screens/waylandoutput.h
)
set(PLUGIN_NAME wayland)
@ -47,6 +51,7 @@ target_sources(plugin-${PLUGIN_NAME} PRIVATE ${SOURCES} ${HEADERS})
wayland-protocols/tdesktopenvironment-protocols/tdesktopenvironment-accessibility-v1.xml
wayland-protocols/wlr-protocols/unstable/wlr-output-management-unstable-v1.xml
wayland-protocols/wlr-protocols/unstable/wlr-gamma-control-unstable-v1.xml
${CMAKE_INSTALL_FULL_DATADIR}/wayland/wayland.xml
)
install(DIRECTORY wayland-protocols/tdesktopenvironment-protocols
DESTINATION ${CMAKE_INSTALL_DATADIR}/libtdesktopenvironment/wayland-protocols

View file

@ -0,0 +1,62 @@
#include "waylandgammacontrol.h"
#include "twaylandregistry.h"
#include "waylandoutput.h"
#include <tlogger.h>
struct WaylandGammaControlPrivate {
QString name;
QString description;
tWaylandRegistry registry;
QSharedPointer<wl_output> output;
quint32 gammaSize;
};
WaylandGammaControl::WaylandGammaControl(QString name, QString description, QtWayland::zwlr_gamma_control_manager_v1* gammaControlManager, QObject* parent) :
QObject{parent} {
d = new WaylandGammaControlPrivate();
d->name = name;
d->description = description;
for (const auto& wlOutput : d->registry.interfaces<wl_output>(&wl_output_interface, 4)) {
WaylandOutput output(wlOutput.data());
tDebug("WaylandGammaControl") << "Found output name: " << output.name();
if (output.name() == name && output.description() == description) {
// We found the correct output
tDebug("WaylandGammaControl") << "Found the output for a gamma control";
d->output = wlOutput;
auto gammaControl = gammaControlManager->get_gamma_control(wlOutput.data());
this->QtWayland::zwlr_gamma_control_v1::init(gammaControl);
break;
}
}
}
WaylandGammaControl::~WaylandGammaControl() {
delete d;
}
quint32 WaylandGammaControl::rampSize() {
return d->gammaSize;
}
bool WaylandGammaControl::isReady() {
return this->isInitialized();
}
void WaylandGammaControl::setGamma(int32_t fd) {
this->set_gamma(fd);
auto display = reinterpret_cast<wl_display*>(qApp->platformNativeInterface()->nativeResourceForIntegration("display"));
wl_display_roundtrip(display);
}
void WaylandGammaControl::zwlr_gamma_control_v1_gamma_size(uint32_t size) {
d->gammaSize = size;
}
void WaylandGammaControl::zwlr_gamma_control_v1_failed() {
tWarn("WaylandGammaControl") << "Setting gamma ramps failed for display " << d->name;
}

View file

@ -0,0 +1,31 @@
#ifndef WAYLANDGAMMACONTROL_H
#define WAYLANDGAMMACONTROL_H
#include "qwayland-wlr-gamma-control-unstable-v1.h"
#include <QObject>
struct WaylandGammaControlPrivate;
class WaylandGammaControl : public QObject,
private QtWayland::zwlr_gamma_control_v1 {
Q_OBJECT
public:
explicit WaylandGammaControl(QString name, QString description, QtWayland::zwlr_gamma_control_manager_v1* gammaControlManager, QObject* parent = nullptr);
~WaylandGammaControl();
quint32 rampSize();
bool isReady();
void setGamma(int32_t fd);
signals:
private:
WaylandGammaControlPrivate* d;
// zwlr_gamma_control_v1 interface
protected:
void zwlr_gamma_control_v1_gamma_size(uint32_t size);
void zwlr_gamma_control_v1_failed();
};
#endif // WAYLANDGAMMACONTROL_H

View file

@ -0,0 +1,42 @@
#include "waylandoutput.h"
#include <QApplication>
#include <qpa/qplatformnativeinterface.h>
#include <tlogger.h>
struct WaylandOutputPrivate {
QString name;
QString description;
};
WaylandOutput::WaylandOutput(::wl_output* output, QObject* parent) :
QObject{parent}, QtWayland::wl_output(output) {
d = new WaylandOutputPrivate();
auto display = reinterpret_cast<wl_display*>(qApp->platformNativeInterface()->nativeResourceForIntegration("display"));
wl_display_roundtrip(display);
}
WaylandOutput::~WaylandOutput() {
delete d;
}
QString WaylandOutput::name() {
return d->name;
}
QString WaylandOutput::description() {
return d->description;
}
void WaylandOutput::output_name(const QString& name) {
d->name = name;
}
void WaylandOutput::output_description(const QString& description) {
d->description = description;
}
void WaylandOutput::output_done() {
}

View file

@ -0,0 +1,30 @@
#ifndef WAYLANDOUTPUT_H
#define WAYLANDOUTPUT_H
#include "qwayland-wayland.h"
#include <QObject>
struct WaylandOutputPrivate;
class WaylandOutput : public QObject,
public QtWayland::wl_output {
Q_OBJECT
public:
explicit WaylandOutput(::wl_output* output, QObject* parent = nullptr);
~WaylandOutput();
QString name();
QString description();
signals:
private:
WaylandOutputPrivate* d;
// wl_output interface
protected:
void output_name(const QString& name);
void output_description(const QString& description);
void output_done();
};
#endif // WAYLANDOUTPUT_H

View file

@ -20,10 +20,19 @@
#include "waylandscreen.h"
#include "Screens/screendaemon.h"
#include "waylandgammacontrol.h"
#include "waylandmode.h"
#include "waylandscreenbackend.h"
#include <QApplication>
#include <QMap>
#include <tlogger.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/memfd.h>
struct WaylandScreenPrivate {
WaylandScreenBackend* backend;
@ -43,6 +52,8 @@ struct WaylandScreenPrivate {
bool initialPowered;
int initialMode;
QPoint initialPoisition;
QMap<QString, SystemScreen::GammaRamps> gammaRamps;
};
WaylandScreen::WaylandScreen(::zwlr_output_head_v1* head, WaylandScreenBackend* backend) :
@ -76,6 +87,50 @@ void WaylandScreen::normaliseScreens() {
}
}
void WaylandScreen::updateGammaRamps() {
WaylandGammaControl gammaControl(d->name, d->description, d->backend);
if (!gammaControl.isReady()) {
tWarn("WaylandScreen") << "Unable to set gamma ramps for " << d->name << " because the wlr_gamma_control object could not be initialised";
return;
}
GammaRamps ramps;
if (d->gammaRamps.empty()) {
// Reset the gamma
ramps.red = 1;
ramps.green = 1;
ramps.blue = 1;
} else {
// Interpolate all the gamma values
auto allRamps = d->gammaRamps.values();
ramps = allRamps.front();
for (auto i = std::next(allRamps.begin()); i != allRamps.end(); i++) {
ramps.red *= i->red;
ramps.green *= i->green;
ramps.blue *= i->blue;
}
}
int num_channels = 3, num_entries = gammaControl.rampSize();
// Creates an unnamed, temporary file in memory
int fd = memfd_create("gamma-ramp", MFD_CLOEXEC | MFD_ALLOW_SEALING);
ftruncate(fd, num_channels * num_entries * sizeof(uint16_t));
uint16_t* table = static_cast<uint16_t*>(mmap(NULL, num_channels * num_entries * sizeof(uint16_t), PROT_WRITE, MAP_SHARED, fd, 0));
for (int i = 0; i < num_entries; i++) {
double factor = static_cast<double>(UINT16_MAX + 1) * i / num_entries;
table[i] = table[i + num_entries] = table[i + num_entries * 2] = static_cast<uint16_t>(factor * ramps.red + 0.5);
}
gammaControl.setGamma(fd);
// Clean up
munmap(table, num_channels * num_entries * sizeof(uint16_t));
close(fd);
}
void WaylandScreen::zwlr_output_head_v1_name(const QString& name) {
d->name = name;
}
@ -150,9 +205,13 @@ void WaylandScreen::setScreenBrightness(double screenBrightness) {
}
void WaylandScreen::adjustGammaRamps(QString adjustmentName, GammaRamps ramps) {
d->gammaRamps.insert(adjustmentName, ramps);
updateGammaRamps();
}
void WaylandScreen::removeGammaRamps(QString adjustmentName) {
d->gammaRamps.remove(adjustmentName);
updateGammaRamps();
}
bool WaylandScreen::powered() const {

View file

@ -40,6 +40,7 @@ class WaylandScreen : public SystemScreen,
WaylandScreenPrivate* d;
void normaliseScreens();
void updateGammaRamps();
// zwlr_output_head_v1 interface
protected:

View file

@ -45,7 +45,7 @@ struct WaylandBackendPrivate {
tWaylandRegistry registry;
wl_display* display;
wl_seat* seat;
QSharedPointer<wl_seat> seat;
quint64 nextKeygrabId = 0;
QMap<quint64, quint64> extKeygrab;
@ -100,7 +100,7 @@ wl_display* WaylandBackend::display() {
}
wl_seat* WaylandBackend::seat() {
return d->seat;
return d->seat.data();
}
void WaylandBackend::viewAdded(uint viewId) {

View file

@ -2,6 +2,7 @@
#define TWAYLANDREGISTRY_H
#include <QApplication>
#include <QCoroGenerator>
#include <QList>
#include <qpa/qplatformnativeinterface.h>
#include <wayland-client-protocol.h>
@ -50,20 +51,31 @@ class tWaylandRegistry {
return false;
}
wl_seat* seat() {
QSharedPointer<wl_seat> seat() {
return this->bind<wl_seat>(&wl_seat_interface, 1);
}
template<typename T> T* bind(const wl_interface* interface, quint32 version) {
template<typename T> QSharedPointer<T> bind(const wl_interface* interface, quint32 version) {
auto interfaceName = QString::fromLocal8Bit(interface->name);
for (const auto& item : this->items) {
if (item.interface == interfaceName) {
return static_cast<T*>(wl_registry_bind(item.registry, item.name, interface, std::min(version, static_cast<quint32>(1))));
auto bound = static_cast<T*>(wl_registry_bind(item.registry, item.name, interface, version));
return QSharedPointer<T>(bound, &tWaylandRegistry::resourceDeleter<T>);
}
}
return nullptr;
}
template<typename T> QCoro::Generator<QSharedPointer<T>> interfaces(const wl_interface* interface, quint32 version) {
auto interfaceName = QString::fromLocal8Bit(interface->name);
for (const auto& item : this->items) {
if (item.interface == interfaceName) {
auto bound = static_cast<T*>(wl_registry_bind(item.registry, item.name, interface, version));
co_yield QSharedPointer<T>(bound, &tWaylandRegistry::resourceDeleter<T>);
}
}
}
private:
struct RegistryItem {
wl_registry* registry;
@ -74,6 +86,9 @@ class tWaylandRegistry {
QList<RegistryItem> items;
wl_registry* registry;
template<typename T> static void resourceDeleter(T* obj) {
}
};
#endif // TWAYLANDREGISTRY_H