diff --git a/src/plugin-display/CMakeLists.txt b/src/plugin-display/CMakeLists.txt index fd1403f4ee..d9c0e1cf42 100644 --- a/src/plugin-display/CMakeLists.txt +++ b/src/plugin-display/CMakeLists.txt @@ -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) @@ -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 ) diff --git a/src/plugin-display/operation/private/displayworker.cpp b/src/plugin-display/operation/private/displayworker.cpp index bebae04931..fc871ad63c 100644 --- a/src/plugin-display/operation/private/displayworker.cpp +++ b/src/plugin-display/operation/private/displayworker.cpp @@ -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); } @@ -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); diff --git a/src/plugin-display/wayland/client/TreeLandOutputManager.cpp b/src/plugin-display/wayland/client/TreeLandOutputManager.cpp index 587b3fb16e..6287709c10 100644 --- a/src/plugin-display/wayland/client/TreeLandOutputManager.cpp +++ b/src/plugin-display/wayland/client/TreeLandOutputManager.cpp @@ -10,13 +10,17 @@ #include #include +#include #include +#include + +#include "wayland-treeland-output-manager-unstable-v2-client-protocol.h" // ── WQt::ColorControl ────────────────────────────────────────────── -WQt::ColorControl::ColorControl(::treeland_output_color_control_v1 *obj, QObject *parent) - : QWaylandClientExtensionTemplate(1) - , QtWayland::treeland_output_color_control_v1(obj) +WQt::ColorControl::ColorControl(::treeland_output_picture_control_v2 *obj, QObject *parent) + : QWaylandClientExtensionTemplate(treeland_output_picture_control_v2_interface.version) + , QtWayland::treeland_output_picture_control_v2(obj) { setParent(parent); } @@ -24,34 +28,48 @@ WQt::ColorControl::ColorControl(::treeland_output_color_control_v1 *obj, QObject 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) { + // 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) { + // 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(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) { - 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) { 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)); } @@ -59,26 +77,24 @@ void WQt::ColorControl::treeland_output_color_control_v1_brightness(int32_t brig // ── WQt::TreeLandOutputManager ───────────────────────────────────── WQt::TreeLandOutputManager::TreeLandOutputManager(QObject *parent) - : QWaylandClientExtensionTemplate(2) + : QWaylandClientExtensionTemplate(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; @@ -86,20 +102,31 @@ WQt::ColorControl *WQt::TreeLandOutputManager::getColorControl(struct wl_output // 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; } diff --git a/src/plugin-display/wayland/client/TreeLandOutputManager.h b/src/plugin-display/wayland/client/TreeLandOutputManager.h index 49dcc3605e..dbcaba675b 100644 --- a/src/plugin-display/wayland/client/TreeLandOutputManager.h +++ b/src/plugin-display/wayland/client/TreeLandOutputManager.h @@ -4,7 +4,7 @@ #pragma once -#include "qwayland-treeland-output-manager-v1.h" +#include "qwayland-treeland-output-manager-unstable-v2.h" #include #include @@ -20,29 +20,36 @@ class TreeLandOutputManager; class ColorControl; } // namespace WQt -class WQt::ColorControl : public QWaylandClientExtensionTemplate, public QtWayland::treeland_output_color_control_v1 +class WQt::ColorControl : public QWaylandClientExtensionTemplate, 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, public QtWayland::treeland_output_manager_v1 +class WQt::TreeLandOutputManager : public QWaylandClientExtensionTemplate, public QtWayland::treeland_output_manager_v2 { Q_OBJECT Q_PROPERTY(bool active READ isActive NOTIFY activeChanged) @@ -51,13 +58,14 @@ class WQt::TreeLandOutputManager : public QWaylandClientExtensionTemplateoutput(); } +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) diff --git a/src/plugin-display/wayland/client/WayQtUtils.h b/src/plugin-display/wayland/client/WayQtUtils.h index a3432b12e2..b229b864f9 100644 --- a/src/plugin-display/wayland/client/WayQtUtils.h +++ b/src/plugin-display/wayland/client/WayQtUtils.h @@ -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. *