mirror of
https://github.com/hyprwm/Hyprland
synced 2024-11-06 04:05:58 +01:00
177 lines
5.9 KiB
C++
177 lines
5.9 KiB
C++
|
#include "GammaControl.hpp"
|
||
|
#include <fcntl.h>
|
||
|
#include <unistd.h>
|
||
|
#include "../helpers/Monitor.hpp"
|
||
|
#include "../Compositor.hpp"
|
||
|
|
||
|
CGammaControl::CGammaControl(SP<CZwlrGammaControlV1> 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<CZwlrGammaControlManagerV1>(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<CGammaControl>(std::make_shared<CZwlrGammaControlV1>(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;
|
||
|
}
|
||
|
}
|