From 741c75d907916d760163f910e65186d410e27874 Mon Sep 17 00:00:00 2001 From: Vaxry Date: Mon, 22 Apr 2024 18:21:03 +0100 Subject: [PATCH] gamma-control: move to new impl --- CMakeLists.txt | 1 + protocols/meson.build | 1 + protocols/wlr-gamma-control-unstable-v1.xml | 126 ++++++++++++++ src/Compositor.cpp | 4 - src/Compositor.hpp | 1 - src/events/Events.hpp | 3 - src/events/Misc.cpp | 17 -- src/helpers/Monitor.cpp | 12 +- src/helpers/Monitor.hpp | 9 +- src/managers/ProtocolManager.cpp | 3 + src/protocols/GammaControl.cpp | 177 ++++++++++++++++++++ src/protocols/GammaControl.hpp | 58 +++++++ src/render/Renderer.cpp | 19 --- 13 files changed, 383 insertions(+), 48 deletions(-) create mode 100644 protocols/wlr-gamma-control-unstable-v1.xml create mode 100644 src/protocols/GammaControl.cpp create mode 100644 src/protocols/GammaControl.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b530b86b..d8aed5f2 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -269,6 +269,7 @@ protocolNew("unstable/idle-inhibit/idle-inhibit-unstable-v1.xml" "idle-inhibit-u protocolNew("unstable/relative-pointer/relative-pointer-unstable-v1.xml" "relative-pointer-unstable-v1" false) protocolNew("unstable/xdg-decoration/xdg-decoration-unstable-v1.xml" "xdg-decoration-unstable-v1" false) protocolNew("staging/alpha-modifier/alpha-modifier-v1.xml" "alpha-modifier-v1" false) +protocolNew("protocols/wlr-gamma-control-unstable-v1.xml" "wlr-gamma-control-unstable-v1" true) # tools add_subdirectory(hyprctl) diff --git a/protocols/meson.build b/protocols/meson.build index 8247d26a..eaeaada0 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -39,6 +39,7 @@ protocols = [ ] new_protocols = [ + ['wlr-gamma-control-unstable-v1.xml'], [wl_protocol_dir, 'staging/tearing-control/tearing-control-v1.xml'], [wl_protocol_dir, 'staging/fractional-scale/fractional-scale-v1.xml'], [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], diff --git a/protocols/wlr-gamma-control-unstable-v1.xml b/protocols/wlr-gamma-control-unstable-v1.xml new file mode 100644 index 00000000..16e0be8b --- /dev/null +++ b/protocols/wlr-gamma-control-unstable-v1.xml @@ -0,0 +1,126 @@ + + + + Copyright © 2015 Giulio camuffo + Copyright © 2018 Simon Ser + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + This protocol allows a privileged client to set the gamma tables for + outputs. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible changes + may be added together with the corresponding interface version bump. + Backward incompatible changes are done by bumping the version number in + the protocol and interface names and resetting the interface version. + Once the protocol is to be declared stable, the 'z' prefix and the + version number in the protocol and interface names are removed and the + interface version number is reset. + + + + + This interface is a manager that allows creating per-output gamma + controls. + + + + + Create a gamma control that can be used to adjust gamma tables for the + provided output. + + + + + + + + All objects created by the manager will still remain valid, until their + appropriate destroy request has been called. + + + + + + + This interface allows a client to adjust gamma tables for a particular + output. + + The client will receive the gamma size, and will then be able to set gamma + tables. At any time the compositor can send a failed event indicating that + this object is no longer valid. + + There can only be at most one gamma control object per output, which + has exclusive access to this particular output. When the gamma control + object is destroyed, the gamma table is restored to its original value. + + + + + Advertise the size of each gamma ramp. + + This event is sent immediately when the gamma control object is created. + + + + + + + + + + + Set the gamma table. The file descriptor can be memory-mapped to provide + the raw gamma table, which contains successive gamma ramps for the red, + green and blue channels. Each gamma ramp is an array of 16-byte unsigned + integers which has the same length as the gamma size. + + The file descriptor data must have the same length as three times the + gamma size. + + + + + + + This event indicates that the gamma control is no longer valid. This + can happen for a number of reasons, including: + - The output doesn't support gamma tables + - Setting the gamma tables failed + - Another client already has exclusive gamma control for this output + - The compositor has transferred gamma control to another client + + Upon receiving this event, the client should destroy this object. + + + + + + Destroys the gamma control object. If the object is still valid, this + restores the original gamma tables. + + + + diff --git a/src/Compositor.cpp b/src/Compositor.cpp index 2997bf6e..aafb4fcb 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -188,8 +188,6 @@ void CCompositor::initServer() { wlr_primary_selection_v1_device_manager_create(m_sWLDisplay); wlr_viewporter_create(m_sWLDisplay); - m_sWLRGammaCtrlMgr = wlr_gamma_control_manager_v1_create(m_sWLDisplay); - m_sWLROutputLayout = wlr_output_layout_create(m_sWLDisplay); m_sWLROutputPowerMgr = wlr_output_power_manager_v1_create(m_sWLDisplay); @@ -299,7 +297,6 @@ void CCompositor::initAllSignals() { addWLSignal(&m_sWLRTextInputMgr->events.text_input, &Events::listen_newTextInput, m_sWLRTextInputMgr, "TextInputMgr"); addWLSignal(&m_sWLRActivation->events.request_activate, &Events::listen_activateXDG, m_sWLRActivation, "ActivationV1"); addWLSignal(&m_sWLRSessionLockMgr->events.new_lock, &Events::listen_newSessionLock, m_sWLRSessionLockMgr, "SessionLockMgr"); - addWLSignal(&m_sWLRGammaCtrlMgr->events.set_gamma, &Events::listen_setGamma, m_sWLRGammaCtrlMgr, "GammaCtrlMgr"); addWLSignal(&m_sWLRKbShInhibitMgr->events.new_inhibitor, &Events::listen_newShortcutInhibitor, m_sWLRKbShInhibitMgr, "ShortcutInhibitMgr"); if (m_sWRLDRMLeaseMgr) @@ -349,7 +346,6 @@ void CCompositor::removeAllSignals() { removeWLSignal(&Events::listen_newTextInput); removeWLSignal(&Events::listen_activateXDG); removeWLSignal(&Events::listen_newSessionLock); - removeWLSignal(&Events::listen_setGamma); removeWLSignal(&Events::listen_newShortcutInhibitor); if (m_sWRLDRMLeaseMgr) diff --git a/src/Compositor.hpp b/src/Compositor.hpp index af777d03..c6a38ebf 100644 --- a/src/Compositor.hpp +++ b/src/Compositor.hpp @@ -77,7 +77,6 @@ class CCompositor { wlr_linux_dmabuf_v1* m_sWLRLinuxDMABuf; wlr_backend* m_sWLRHeadlessBackend; wlr_session_lock_manager_v1* m_sWLRSessionLockMgr; - wlr_gamma_control_manager_v1* m_sWLRGammaCtrlMgr; // ------------------------------------------------- // std::string m_szWLDisplaySocket = ""; diff --git a/src/events/Events.hpp b/src/events/Events.hpp index 74b81b00..3a03f3d0 100644 --- a/src/events/Events.hpp +++ b/src/events/Events.hpp @@ -133,9 +133,6 @@ namespace Events { // Session Lock LISTENER(newSessionLock); - // Gamma control - LISTENER(setGamma); - // Shortcut inhibitor LISTENER(newShortcutInhibitor); }; diff --git a/src/events/Misc.cpp b/src/events/Misc.cpp index 3952920a..6381937c 100644 --- a/src/events/Misc.cpp +++ b/src/events/Misc.cpp @@ -222,23 +222,6 @@ void Events::listener_newSessionLock(wl_listener* listener, void* data) { g_pSessionLockManager->onNewSessionLock((wlr_session_lock_v1*)data); } -void Events::listener_setGamma(wl_listener* listener, void* data) { - Debug::log(LOG, "New Gamma event at {:x}", (uintptr_t)data); - - const auto E = (wlr_gamma_control_manager_v1_set_gamma_event*)data; - - const auto PMONITOR = g_pCompositor->getMonitorFromOutput(E->output); - - if (!PMONITOR) { - Debug::log(ERR, "Gamma event object references non-existent output {:x} ?", (uintptr_t)E->output); - return; - } - - PMONITOR->gammaChanged = true; - - g_pCompositor->scheduleFrameForMonitor(PMONITOR); -} - void Events::listener_newShortcutInhibitor(wl_listener* listener, void* data) { const auto INHIBITOR = (wlr_keyboard_shortcuts_inhibitor_v1*)data; diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index ceb021c8..f7d2274c 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -1,10 +1,8 @@ #include "Monitor.hpp" - #include "MiscFunctions.hpp" - #include "../Compositor.hpp" - #include "../config/ConfigValue.hpp" +#include "../protocols/GammaControl.hpp" int ratHandler(void* data) { g_pHyprRenderer->renderMonitor((CMonitor*)data); @@ -26,6 +24,8 @@ CMonitor::~CMonitor() { hyprListener_monitorNeedsFrame.removeCallback(); hyprListener_monitorCommit.removeCallback(); hyprListener_monitorBind.removeCallback(); + + events.destroy.emit(); } void CMonitor::onConnect(bool noRule) { @@ -214,6 +214,10 @@ void CMonitor::onConnect(bool noRule) { renderTimer = wl_event_loop_add_timer(g_pCompositor->m_sWLEventLoop, ratHandler, this); g_pCompositor->scheduleFrameForMonitor(this); + + PROTO::gamma->applyGammaToState(this); + + events.connect.emit(); } void CMonitor::onDisconnect(bool destroy) { @@ -228,6 +232,8 @@ void CMonitor::onDisconnect(bool destroy) { Debug::log(LOG, "onDisconnect called for {}", output->name); + events.disconnect.emit(); + // Cleanup everything. Move windows back, snap cursor, shit. CMonitor* BACKUPMON = nullptr; for (auto& m : g_pCompositor->m_vMonitors) { diff --git a/src/helpers/Monitor.hpp b/src/helpers/Monitor.hpp index 909abb44..4dd58a4e 100644 --- a/src/helpers/Monitor.hpp +++ b/src/helpers/Monitor.hpp @@ -11,6 +11,8 @@ #include "Region.hpp" #include +#include "signal/Signal.hpp" + struct SMonitorRule { std::string name = ""; Vector2D resolution = Vector2D(1280, 720); @@ -83,7 +85,6 @@ class CMonitor { bool noFrameSchedule = false; bool scheduledRecalc = false; wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; - bool gammaChanged = false; float xwaylandScale = 1.f; std::array projMatrix = {0}; std::optional forceSize; @@ -120,6 +121,12 @@ class CMonitor { bool frameScheduledWhileBusy = false; } tearingState; + struct { + CSignal destroy; + CSignal connect; + CSignal disconnect; + } events; + std::array>, 4> m_aLayerSurfaceLayers; DYNLISTENER(monitorFrame); diff --git a/src/managers/ProtocolManager.cpp b/src/managers/ProtocolManager.cpp index 368c7bb2..96a0707b 100644 --- a/src/managers/ProtocolManager.cpp +++ b/src/managers/ProtocolManager.cpp @@ -8,6 +8,7 @@ #include "../protocols/RelativePointer.hpp" #include "../protocols/XDGDecoration.hpp" #include "../protocols/AlphaModifier.hpp" +#include "../protocols/GammaControl.hpp" #include "tearing-control-v1.hpp" #include "fractional-scale-v1.hpp" @@ -17,6 +18,7 @@ #include "relative-pointer-unstable-v1.hpp" #include "xdg-decoration-unstable-v1.hpp" #include "alpha-modifier-v1.hpp" +#include "wlr-gamma-control-unstable-v1.hpp" CProtocolManager::CProtocolManager() { @@ -28,6 +30,7 @@ CProtocolManager::CProtocolManager() { PROTO::relativePointer = std::make_unique(&zwp_relative_pointer_manager_v1_interface, 1, "RelativePointer"); PROTO::xdgDecoration = std::make_unique(&zxdg_decoration_manager_v1_interface, 1, "XDGDecoration"); PROTO::alphaModifier = std::make_unique(&wp_alpha_modifier_v1_interface, 1, "AlphaModifier"); + PROTO::gamma = std::make_unique(&zwlr_gamma_control_manager_v1_interface, 1, "GammaControl"); // Old protocol implementations. // TODO: rewrite them to use hyprwayland-scanner. diff --git a/src/protocols/GammaControl.cpp b/src/protocols/GammaControl.cpp new file mode 100644 index 00000000..fa7fddd7 --- /dev/null +++ b/src/protocols/GammaControl.cpp @@ -0,0 +1,177 @@ +#include "GammaControl.hpp" +#include +#include +#include "../helpers/Monitor.hpp" +#include "../Compositor.hpp" + +CGammaControl::CGammaControl(SP resource_, wl_resource* output) : resource(resource_) { + if (!resource_->resource()) + return; + + wlr_output* wlrOutput = wlr_output_from_resource(output); + + if (!wlrOutput) { + Debug::log(ERR, "[gamma] No wlr_output"); + resource->sendFailed(); + return; + } + + pMonitor = g_pCompositor->getRealMonitorFromOutput(wlrOutput); + + if (!pMonitor) { + Debug::log(ERR, "[gamma] No CMonitor"); + resource->sendFailed(); + return; + } + + for (auto& g : PROTO::gamma->m_vGammaControllers) { + if (g->pMonitor == pMonitor) { + resource->sendFailed(); + return; + } + } + + gammaSize = wlr_output_get_gamma_size(wlrOutput); + + if (gammaSize <= 0) { + Debug::log(ERR, "[gamma] Output {} doesn't support gamma", pMonitor->szName); + resource->sendFailed(); + return; + } + + gammaTable.resize(gammaSize * 3); + + resource->setDestroy([this](CZwlrGammaControlV1* gamma) { PROTO::gamma->destroyGammaControl(this); }); + resource->setOnDestroy([this](CZwlrGammaControlV1* gamma) { PROTO::gamma->destroyGammaControl(this); }); + + resource->setSetGamma([this](CZwlrGammaControlV1* gamma, int32_t fd) { + Debug::log(LOG, "[gamma] setGamma for {}", pMonitor->szName); + + int fdFlags = fcntl(fd, F_GETFL, 0); + if (fdFlags < 0) { + Debug::log(ERR, "[gamma] Failed to get fd flags"); + resource->sendFailed(); + close(fd); + return; + } + + if (fcntl(fd, F_SETFL, fdFlags | O_NONBLOCK) < 0) { + Debug::log(ERR, "[gamma] Failed to set fd flags"); + resource->sendFailed(); + close(fd); + return; + } + + ssize_t readBytes = pread(fd, gammaTable.data(), gammaTable.size() * sizeof(uint16_t), 0); + if (readBytes < 0 || (size_t)readBytes != gammaTable.size() * sizeof(uint16_t)) { + Debug::log(ERR, "[gamma] Failed to read bytes"); + close(fd); + + if ((size_t)readBytes != gammaTable.size() * sizeof(uint16_t)) { + wl_resource_post_error(gamma->resource(), ZWLR_GAMMA_CONTROL_V1_ERROR_INVALID_GAMMA, "Gamma ramps size mismatch"); + return; + } + + resource->sendFailed(); + return; + } + + gammaTableSet = true; + close(fd); + applyToMonitor(); + }); + + resource->sendGammaSize(gammaSize); + + listeners.monitorDestroy = pMonitor->events.destroy.registerListener([this](std::any) { this->onMonitorDestroy(); }); + listeners.monitorDisconnect = pMonitor->events.destroy.registerListener([this](std::any) { this->onMonitorDestroy(); }); +} + +CGammaControl::~CGammaControl() { + if (!gammaTableSet || !pMonitor) + return; + + // reset the LUT if the client dies for whatever reason and doesn't unset the gamma + wlr_output_state_set_gamma_lut(pMonitor->state.wlr(), 0, nullptr, nullptr, nullptr); +} + +bool CGammaControl::good() { + return resource->resource(); +} + +void CGammaControl::applyToMonitor() { + if (!pMonitor) + return; // ?? + + Debug::log(LOG, "[gamma] setting to monitor {}", pMonitor->szName); + + if (!gammaTableSet) { + wlr_output_state_set_gamma_lut(pMonitor->state.wlr(), 0, nullptr, nullptr, nullptr); + return; + } + + uint16_t* red = &gammaTable.at(0); + uint16_t* green = &gammaTable.at(gammaSize); + uint16_t* blue = &gammaTable.at(gammaSize * 2); + + wlr_output_state_set_gamma_lut(pMonitor->state.wlr(), gammaSize, red, green, blue); + + if (!pMonitor->state.test()) { + Debug::log(LOG, "[gamma] setting to monitor {} failed", pMonitor->szName); + wlr_output_state_set_gamma_lut(pMonitor->state.wlr(), 0, nullptr, nullptr, nullptr); + } + + g_pHyprRenderer->damageMonitor(pMonitor); +} + +CMonitor* CGammaControl::getMonitor() { + return pMonitor; +} + +void CGammaControl::onMonitorDestroy() { + resource->sendFailed(); + pMonitor = nullptr; +} + +CGammaControlProtocol::CGammaControlProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { + ; +} + +void CGammaControlProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { + const auto RESOURCE = m_vManagers.emplace_back(std::make_unique(client, ver, id)).get(); + RESOURCE->setOnDestroy([this](CZwlrGammaControlManagerV1* p) { this->onManagerResourceDestroy(p->resource()); }); + + RESOURCE->setDestroy([this](CZwlrGammaControlManagerV1* pMgr) { this->onManagerResourceDestroy(pMgr->resource()); }); + RESOURCE->setGetGammaControl([this](CZwlrGammaControlManagerV1* pMgr, uint32_t id, wl_resource* output) { this->onGetGammaControl(pMgr, id, output); }); +} + +void CGammaControlProtocol::onManagerResourceDestroy(wl_resource* res) { + std::erase_if(m_vManagers, [&](const auto& other) { return other->resource() == res; }); +} + +void CGammaControlProtocol::destroyGammaControl(CGammaControl* gamma) { + std::erase_if(m_vGammaControllers, [&](const auto& other) { return other.get() == gamma; }); +} + +void CGammaControlProtocol::onGetGammaControl(CZwlrGammaControlManagerV1* pMgr, uint32_t id, wl_resource* output) { + const auto CLIENT = wl_resource_get_client(pMgr->resource()); + const auto RESOURCE = + m_vGammaControllers.emplace_back(std::make_unique(std::make_shared(CLIENT, wl_resource_get_version(pMgr->resource()), id), output)) + .get(); + + if (!RESOURCE->good()) { + wl_resource_post_no_memory(pMgr->resource()); + m_vGammaControllers.pop_back(); + return; + } +} + +void CGammaControlProtocol::applyGammaToState(CMonitor* pMonitor) { + for (auto& g : m_vGammaControllers) { + if (g->getMonitor() != pMonitor) + continue; + + g->applyToMonitor(); + break; + } +} \ No newline at end of file diff --git a/src/protocols/GammaControl.hpp b/src/protocols/GammaControl.hpp new file mode 100644 index 00000000..074a51f0 --- /dev/null +++ b/src/protocols/GammaControl.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#include +#include "WaylandProtocol.hpp" +#include "wlr-gamma-control-unstable-v1.hpp" +#include "../helpers/signal/Listener.hpp" + +class CMonitor; + +class CGammaControl { + public: + CGammaControl(SP resource_, wl_resource* output); + ~CGammaControl(); + + bool good(); + void applyToMonitor(); + CMonitor* getMonitor(); + + private: + SP resource; + CMonitor* pMonitor = nullptr; + size_t gammaSize = 0; + bool gammaTableSet = false; + std::vector gammaTable; + + void onMonitorDestroy(); + + struct { + CHyprSignalListener monitorDisconnect; + CHyprSignalListener monitorDestroy; + } listeners; +}; + +class CGammaControlProtocol : public IWaylandProtocol { + public: + CGammaControlProtocol(const wl_interface* iface, const int& ver, const std::string& name); + + virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); + + void applyGammaToState(CMonitor* pMonitor); + + private: + void onManagerResourceDestroy(wl_resource* res); + void destroyGammaControl(CGammaControl* gamma); + void onGetGammaControl(CZwlrGammaControlManagerV1* pMgr, uint32_t id, wl_resource* output); + + // + std::vector> m_vManagers; + std::vector> m_vGammaControllers; + + friend class CGammaControl; +}; + +namespace PROTO { + inline UP gamma; +}; diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index f183bf44..9a8819e1 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -1151,25 +1151,6 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) { g_pLayoutManager->getCurrentLayout()->recalculateMonitor(pMonitor->ID); } - // gamma stuff - if (pMonitor->gammaChanged) { - pMonitor->gammaChanged = false; - - const auto PGAMMACTRL = wlr_gamma_control_manager_v1_get_control(g_pCompositor->m_sWLRGammaCtrlMgr, pMonitor->output); - - if (!wlr_gamma_control_v1_apply(PGAMMACTRL, pMonitor->state.wlr())) { - Debug::log(ERR, "Could not apply gamma control to {}", pMonitor->szName); - return; - } - - if (!wlr_output_test_state(pMonitor->output, pMonitor->state.wlr())) { - Debug::log(ERR, "Output test failed for setting gamma to {}", pMonitor->szName); - // aka rollback - wlr_gamma_control_v1_apply(nullptr, pMonitor->state.wlr()); - wlr_gamma_control_v1_send_failed_and_destroy(PGAMMACTRL); - } - } - // tearing and DS first bool shouldTear = false; if (pMonitor->tearingState.nextRenderTorn) {