Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/plugin-display/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.18)

find_package(${QT_NS} REQUIRED COMPONENTS WaylandClient Concurrent)
find_package(PkgConfig REQUIRED)
find_package(TreelandProtocols REQUIRED)
find_package(TreelandProtocols 0.6 REQUIRED)
pkg_check_modules(WaylandClient REQUIRED IMPORTED_TARGET wayland-client)
pkg_check_modules(WLR_PROTOCOLS REQUIRED wlr-protocols)

Expand All @@ -25,7 +25,7 @@ add_library(display-wayland-client
qt_generate_wayland_protocol_client_sources(display-wayland-client
FILES
${WLR_PROTOCOLS_XML_DIR}/unstable/wlr-output-management-unstable-v1.xml
${TREELAND_PROTOCOLS_DATA_DIR}/treeland-output-manager-v1.xml
${TREELAND_PROTOCOLS_DATA_DIR}/treeland-output-manager-unstable-v2.xml
${TREELAND_PROTOCOLS_DATA_DIR}/treeland-virtual-output-manager-v1.xml
${TREELAND_PROTOCOLS_DATA_DIR}/treeland-wallpaper-manager-unstable-v1.xml
)
Expand Down
25 changes: 22 additions & 3 deletions src/plugin-display/operation/private/displayworker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -753,8 +753,27 @@ void DisplayWorker::setMonitorRotate(Monitor *mon, const quint16 rotate)
void DisplayWorker::setPrimary(const QString &name)
{
if (WQt::Utils::isTreeland()) {
if (m_treelandOutputMgr)
m_treelandOutputMgr->setPrimaryOutput(name.toStdString().c_str());
if (m_treelandOutputMgr) {
// v2 protocol takes a wl_output* instead of an output name string;
// resolve the name to the wl_output owned by the matching QScreen.
struct wl_output *output = nullptr;
for (auto it(m_screen_outputs.cbegin()); it != m_screen_outputs.cend(); ++it) {
if (it.key()->name() == name) {
output = it.value();
break;
}
}
// The v2 set_primary_output request does not declare allow-null for
// its output argument, so do not send a null object — the compositor
// would reject it (or worse, treat it as a protocol error). This
// happens when the screen has not been registered yet; the primary
// output is left unchanged and the user can retry later.
if (!output) {
qCWarning(DdcDisplayWorker) << "cannot set primary output: no wl_output found for" << name;
return;
}
m_treelandOutputMgr->setPrimaryOutput(output);
}
} else {
m_displayInter->SetPrimary(name);
}
Expand Down Expand Up @@ -1399,7 +1418,7 @@ void DisplayWorker::updateControl()
}

if (controlContext == m_control_monitors.end()) {
auto *control = m_treelandOutputMgr->getColorControl(it.value());
auto *control = m_treelandOutputMgr->getPictureControl(it.value());
if (control) {
connect(control, &WQt::ColorControl::brightnessChanged, this, [this, control](double brightness) {
onBrightnessChanged(control, brightness);
Expand Down
81 changes: 54 additions & 27 deletions src/plugin-display/wayland/client/TreeLandOutputManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,99 +7,126 @@
#include "WayQtLogging.h"
#include "WayQtUtils.h"

#include <wayland-client.h>

Check warning on line 10 in src/plugin-display/wayland/client/TreeLandOutputManager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <wayland-client.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include <QDebug>

Check warning on line 12 in src/plugin-display/wayland/client/TreeLandOutputManager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QDebug> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QGuiApplication>

Check warning on line 13 in src/plugin-display/wayland/client/TreeLandOutputManager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QGuiApplication> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QLoggingCategory>

Check warning on line 14 in src/plugin-display/wayland/client/TreeLandOutputManager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QLoggingCategory> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QScreen>

Check warning on line 15 in src/plugin-display/wayland/client/TreeLandOutputManager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QScreen> not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include "wayland-treeland-output-manager-unstable-v2-client-protocol.h"

Check warning on line 17 in src/plugin-display/wayland/client/TreeLandOutputManager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "wayland-treeland-output-manager-unstable-v2-client-protocol.h" not found.

// ── WQt::ColorControl ──────────────────────────────────────────────

WQt::ColorControl::ColorControl(::treeland_output_color_control_v1 *obj, QObject *parent)
: QWaylandClientExtensionTemplate<WQt::ColorControl>(1)
, QtWayland::treeland_output_color_control_v1(obj)
WQt::ColorControl::ColorControl(::treeland_output_picture_control_v2 *obj, QObject *parent)
: QWaylandClientExtensionTemplate<WQt::ColorControl>(treeland_output_picture_control_v2_interface.version)
, QtWayland::treeland_output_picture_control_v2(obj)
{
setParent(parent);
}

WQt::ColorControl::~ColorControl()
{
if (isInitialized())
QtWayland::treeland_output_color_control_v1::destroy();
QtWayland::treeland_output_picture_control_v2::destroy();
}

void WQt::ColorControl::setBrightness(double brightness)

Check warning on line 34 in src/plugin-display/wayland/client/TreeLandOutputManager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

The function 'setBrightness' is never used.
{
// An out-of-range value is a fatal protocol error that terminates the
// connection, so clamp before sending.
if (brightness < 0.0 || brightness > 100.0) {
qCWarning(DccWayQt) << "clamping brightness" << brightness << "to [0.0, 100.0]";
brightness = qBound(0.0, brightness, 100.0);
}
qCDebug(DccWayQt) << "ColorControl::setBrightness" << brightness;
QtWayland::treeland_output_color_control_v1::set_brightness(wl_fixed_from_double(brightness));
QtWayland::treeland_output_picture_control_v2::set_brightness(wl_fixed_from_double(brightness));
commit();
}

void WQt::ColorControl::setColorTemperature(uint32_t temperature)

Check warning on line 47 in src/plugin-display/wayland/client/TreeLandOutputManager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

The function 'setColorTemperature' is never used.
{
// An out-of-range value is a fatal protocol error that terminates the
// connection, so clamp before sending.
if (temperature < 1000 || temperature > 20000) {
qCWarning(DccWayQt) << "clamping color temperature" << temperature << "to [1000, 20000]";
temperature = qBound<uint32_t>(1000, temperature, 20000);
}
qCDebug(DccWayQt) << "ColorControl::setColorTemperature" << temperature;
QtWayland::treeland_output_color_control_v1::set_color_temperature(temperature);
QtWayland::treeland_output_picture_control_v2::set_color_temperature(temperature);
commit();
}

void WQt::ColorControl::treeland_output_color_control_v1_result(uint32_t success)
void WQt::ColorControl::treeland_output_picture_control_v2_result(uint32_t commitResult)

Check warning on line 60 in src/plugin-display/wayland/client/TreeLandOutputManager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

The function 'treeland_output_picture_control_v2_result' is never used.
{
Q_EMIT result(success);
// The parameter cannot be named "result": it would shadow the
// result(uint32_t) signal and break the emit below.
Q_EMIT result(commitResult);
}

void WQt::ColorControl::treeland_output_color_control_v1_color_temperature(uint32_t temperature)
void WQt::ColorControl::treeland_output_picture_control_v2_color_temperature(uint32_t temperature)

Check warning on line 67 in src/plugin-display/wayland/client/TreeLandOutputManager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

The function 'treeland_output_picture_control_v2_color_temperature' is never used.
{
Q_EMIT colorTemperatureChanged(temperature);
}

void WQt::ColorControl::treeland_output_color_control_v1_brightness(int32_t brightness)
void WQt::ColorControl::treeland_output_picture_control_v2_brightness(int32_t brightness)
{
Q_EMIT brightnessChanged(wl_fixed_to_double(brightness));
}

// ── WQt::TreeLandOutputManager ─────────────────────────────────────

WQt::TreeLandOutputManager::TreeLandOutputManager(QObject *parent)
: QWaylandClientExtensionTemplate<WQt::TreeLandOutputManager>(2)
: QWaylandClientExtensionTemplate<WQt::TreeLandOutputManager>(treeland_output_manager_v2_interface.version)
{
setParent(parent);
}

WQt::TreeLandOutputManager::~TreeLandOutputManager()
{
// The destroy request was introduced in version 2; older compositors
// reject it as an unknown method.
if (isInitialized() && QtWayland::treeland_output_manager_v1::version() >= 2)
QtWayland::treeland_output_manager_v1::destroy();
if (isInitialized())
QtWayland::treeland_output_manager_v2::destroy();
}

void WQt::TreeLandOutputManager::setPrimaryOutput(const char *name)
void WQt::TreeLandOutputManager::setPrimaryOutput(struct wl_output *output)
{
qCDebug(DccWayQt) << "TreeLandOutputManager::setPrimaryOutput" << name;
QtWayland::treeland_output_manager_v1::set_primary_output(name);
qCDebug(DccWayQt) << "TreeLandOutputManager::setPrimaryOutput" << output;
QtWayland::treeland_output_manager_v2::set_primary_output(output);
}

WQt::ColorControl *WQt::TreeLandOutputManager::getColorControl(struct wl_output *output)
WQt::ColorControl *WQt::TreeLandOutputManager::getPictureControl(struct wl_output *output)
{
if (!output)
return nullptr;

// Same guard as WallpaperManager::getWallpaper: a removed output turns this
// request into a fatal protocol error.
if (!WQt::Utils::isOutputAlive(output)) {
qCWarning(DccWayQt) << "skipping get_color_control for a removed output" << output;
qCWarning(DccWayQt) << "skipping get_picture_control for a removed output" << output;
return nullptr;
}

auto *colorControl = get_color_control(output);
if (!colorControl)
auto *pictureControl = get_picture_control(output);
if (!pictureControl)
return nullptr;

return new WQt::ColorControl(colorControl, this);
return new WQt::ColorControl(pictureControl, this);
}

void WQt::TreeLandOutputManager::treeland_output_manager_v2_primary_output(struct wl_output *output)
{
QString name;
if (output) {
auto *screen = WQt::Utils::qScreenFromWlOutput(output);
if (screen)
name = screen->name();
}
qCDebug(DccWayQt) << "TreeLandOutputManager::primary output changed" << name;
mPrimaryOutput = name;
Q_EMIT primaryOutputChanged(name);
}

void WQt::TreeLandOutputManager::treeland_output_manager_v1_primary_output(const QString &output_name)
void WQt::TreeLandOutputManager::treeland_output_manager_v2_primary_output_failed(uint32_t reason)
{
qCDebug(DccWayQt) << "TreeLandOutputManager::primary output changed" << output_name;
mPrimaryOutput = output_name;
Q_EMIT primaryOutputChanged(output_name);
qCWarning(DccWayQt) << "TreeLandOutputManager::set_primary_output rejected, reason" << reason;
}
30 changes: 19 additions & 11 deletions src/plugin-display/wayland/client/TreeLandOutputManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

#pragma once

#include "qwayland-treeland-output-manager-v1.h"
#include "qwayland-treeland-output-manager-unstable-v2.h"

#include <QMap>
#include <QObject>
Expand All @@ -20,29 +20,36 @@ class TreeLandOutputManager;
class ColorControl;
} // namespace WQt

class WQt::ColorControl : public QWaylandClientExtensionTemplate<WQt::ColorControl>, public QtWayland::treeland_output_color_control_v1
class WQt::ColorControl : public QWaylandClientExtensionTemplate<WQt::ColorControl>, public QtWayland::treeland_output_picture_control_v2
{
Q_OBJECT

public:
ColorControl(::treeland_output_color_control_v1 *obj, QObject *parent = nullptr);
ColorControl(::treeland_output_picture_control_v2 *obj, QObject *parent = nullptr);
~ColorControl() override;

void setBrightness(double brightness);
void setColorTemperature(uint32_t temperature);

protected:
void treeland_output_color_control_v1_result(uint32_t success) override;
void treeland_output_color_control_v1_color_temperature(uint32_t temperature) override;
void treeland_output_color_control_v1_brightness(int32_t brightness) override;
void treeland_output_picture_control_v2_result(uint32_t result) override;
void treeland_output_picture_control_v2_color_temperature(uint32_t temperature) override;
void treeland_output_picture_control_v2_brightness(int32_t brightness) override;

Q_SIGNALS:
void result(uint32_t success);
// v2 commit_result enum: 0 = success, 1 = failed, 2 = unsupported,
// 3 = invalid_output. Out-of-range set_* values never reach a commit:
// they are rejected at request time with a fatal protocol error that
// terminates the connection (see the clamps in setBrightness and
// setColorTemperature).
// (v1 used uint success with 1 = success / 0 = failure — the polarity is
// inverted in v2, so do not treat a non-zero value as success.)
void result(uint32_t result);
void colorTemperatureChanged(uint32_t temperature);
void brightnessChanged(double brightness);
};

class WQt::TreeLandOutputManager : public QWaylandClientExtensionTemplate<WQt::TreeLandOutputManager>, public QtWayland::treeland_output_manager_v1
class WQt::TreeLandOutputManager : public QWaylandClientExtensionTemplate<WQt::TreeLandOutputManager>, public QtWayland::treeland_output_manager_v2
{
Q_OBJECT
Q_PROPERTY(bool active READ isActive NOTIFY activeChanged)
Expand All @@ -51,13 +58,14 @@ class WQt::TreeLandOutputManager : public QWaylandClientExtensionTemplate<WQt::T
TreeLandOutputManager(QObject *parent = nullptr);
~TreeLandOutputManager() override;

void setPrimaryOutput(const char *);
WQt::ColorControl *getColorControl(struct wl_output *output);
void setPrimaryOutput(struct wl_output *output);
WQt::ColorControl *getPictureControl(struct wl_output *output);

QString mPrimaryOutput;

protected:
void treeland_output_manager_v1_primary_output(const QString &output_name) override;
void treeland_output_manager_v2_primary_output(struct wl_output *output) override;
void treeland_output_manager_v2_primary_output_failed(uint32_t reason) override;

Q_SIGNALS:
void primaryOutputChanged(const QString &);
Expand Down
13 changes: 13 additions & 0 deletions src/plugin-display/wayland/client/WayQtUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@ wl_output *WQt::Utils::wlOutputFromQScreen(QScreen *screen)
return native->output();
}

QScreen *WQt::Utils::qScreenFromWlOutput(wl_output *output)
{
if (!output)
return nullptr;

for (QScreen *screen : QGuiApplication::screens()) {
if (wlOutputFromQScreen(screen) == output)
return screen;
}

return nullptr;
}

bool WQt::Utils::isOutputAlive(wl_output *output)
{
if (!output)
Expand Down
9 changes: 9 additions & 0 deletions src/plugin-display/wayland/client/WayQtUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ namespace Utils {
*/
struct wl_output *wlOutputFromQScreen(QScreen *screen);

/**
* The QScreen whose Qt Wayland platform screen owns @a output, or nullptr
* if no current screen backs this wl_output.
*
* Reverse lookup of wlOutputFromQScreen; used to recover a screen name
* from a wl_output object received in a Wayland event.
*/
QScreen *qScreenFromWlOutput(struct wl_output *output);

/**
* Whether @a output still backs one of the screens Qt currently knows about.
*
Expand Down
Loading