Commit ed49d7c5 authored by Vlad Zahorodnii's avatar Vlad Zahorodnii
Browse files

Refactor output state setting

If multiple properties that affect the geometry change, then the
Output::geometryChanged() signal will be emitted multiple times, which
in its turn may force the Workspace to re-arrange windows, etc.

With this, the geometryChanged signal will be emitted in more expected
fashion only once as long as relevant property changes are batched.
parent e9cd8be3
......@@ -57,4 +57,11 @@ DrmGpu *DrmAbstractOutput::gpu() const
return m_gpu;
}
void DrmAbstractOutput::updateEnabled(bool enabled)
{
State next = m_state;
next.enabled = enabled;
setState(next);
}
}
......@@ -32,6 +32,8 @@ public:
virtual bool present() = 0;
virtual DrmOutputLayer *outputLayer() const = 0;
void updateEnabled(bool enabled);
protected:
friend class DrmGpu;
......
......@@ -318,12 +318,12 @@ void DrmBackend::addOutput(DrmAbstractOutput *o)
{
m_outputs.append(o);
Q_EMIT outputAdded(o);
o->setEnabled(true);
o->updateEnabled(true);
}
void DrmBackend::removeOutput(DrmAbstractOutput *o)
{
o->setEnabled(false);
o->updateEnabled(false);
m_outputs.removeOne(o);
Q_EMIT outputRemoved(o);
}
......
......@@ -305,7 +305,7 @@ bool DrmGpu::updateOutputs()
pipeline->applyPendingChanges();
if (pipeline->output() && !pipeline->crtc()) {
pipeline->setEnable(false);
pipeline->output()->setEnabled(false);
pipeline->output()->updateEnabled(false);
}
}
} else if (err == DrmPipeline::Error::NoPermission) {
......@@ -328,7 +328,7 @@ bool DrmGpu::updateOutputs()
pipeline->revertPendingChanges();
}
for (const auto &output : qAsConst(addedOutputs)) {
output->setEnabled(false);
output->updateEnabled(false);
output->pipeline()->setEnable(false);
output->pipeline()->applyPendingChanges();
}
......@@ -589,8 +589,7 @@ const QVector<DrmPipeline *> DrmGpu::pipelines() const
DrmVirtualOutput *DrmGpu::createVirtualOutput(const QString &name, const QSize &size, double scale, VirtualOutputType type)
{
auto output = new DrmVirtualOutput(name, this, size, type);
output->setScale(scale);
auto output = new DrmVirtualOutput(name, this, size, scale, type);
m_outputs << output;
Q_EMIT outputAdded(output);
return output;
......
......@@ -50,9 +50,11 @@ DrmOutput::DrmOutput(DrmPipeline *pipeline, KWaylandServer::DrmLeaseDeviceV1Inte
m_renderLoop->setRefreshRate(m_pipeline->mode()->refreshRate());
Capabilities capabilities = Capability::Dpms;
State initialState;
if (conn->hasOverscan()) {
capabilities |= Capability::Overscan;
setOverscanInternal(conn->overscan());
initialState.overscan = conn->overscan();
}
if (conn->vrrCapable()) {
capabilities |= Capability::Vrr;
......@@ -60,7 +62,7 @@ DrmOutput::DrmOutput(DrmPipeline *pipeline, KWaylandServer::DrmLeaseDeviceV1Inte
}
if (conn->hasRgbRange()) {
capabilities |= Capability::RgbRange;
setRgbRangeInternal(conn->rgbRange());
initialState.rgbRange = conn->rgbRange();
}
const Edid *edid = conn->edid();
......@@ -79,12 +81,13 @@ DrmOutput::DrmOutput(DrmPipeline *pipeline, KWaylandServer::DrmLeaseDeviceV1Inte
.nonDesktop = conn->isNonDesktop(),
});
const QList<std::shared_ptr<OutputMode>> modes = getModes();
std::shared_ptr<OutputMode> currentMode = m_pipeline->mode();
if (!currentMode) {
currentMode = modes.constFirst();
initialState.modes = getModes();
initialState.currentMode = m_pipeline->mode();
if (!initialState.currentMode) {
initialState.currentMode = initialState.modes.constFirst();
}
setModesInternal(modes, currentMode);
setState(initialState);
m_turnOffTimer.setSingleShot(true);
m_turnOffTimer.setInterval(dimAnimationTime());
......@@ -261,13 +264,13 @@ bool DrmOutput::setDrmDpmsMode(DpmsMode mode)
bool active = mode == DpmsMode::On;
bool isActive = dpmsMode() == DpmsMode::On;
if (active == isActive) {
setDpmsModeInternal(mode);
updateDpmsMode(mode);
return true;
}
m_pipeline->setActive(active);
if (DrmPipeline::commitPipelines({m_pipeline}, active ? DrmPipeline::CommitMode::TestAllowModeset : DrmPipeline::CommitMode::CommitModeset) == DrmPipeline::Error::None) {
m_pipeline->applyPendingChanges();
setDpmsModeInternal(mode);
updateDpmsMode(mode);
if (active) {
m_gpu->platform()->checkOutputsAreOn();
m_renderLoop->uninhibit();
......@@ -314,7 +317,8 @@ DrmPlane::Transformations outputToPlaneTransform(DrmOutput::Transform transform)
void DrmOutput::updateModes()
{
const QList<std::shared_ptr<OutputMode>> modes = getModes();
State next = m_state;
next.modes = getModes();
if (m_pipeline->crtc()) {
const auto currentMode = m_pipeline->connector()->findMode(m_pipeline->crtc()->queryCurrentMode());
......@@ -331,12 +335,19 @@ void DrmOutput::updateModes()
}
}
std::shared_ptr<OutputMode> currentMode = m_pipeline->mode();
if (!currentMode) {
currentMode = modes.constFirst();
next.currentMode = m_pipeline->mode();
if (!next.currentMode) {
next.currentMode = next.modes.constFirst();
}
setModesInternal(modes, currentMode);
setState(next);
}
void DrmOutput::updateDpmsMode(DpmsMode dpmsMode)
{
State next = m_state;
next.dpmsMode = dpmsMode;
setState(next);
}
bool DrmOutput::present()
......@@ -407,22 +418,26 @@ void DrmOutput::applyQueuedChanges(const OutputConfiguration &config)
m_pipeline->applyPendingChanges();
auto props = config.constChangeSet(this);
setEnabled(props->enabled && m_pipeline->crtc());
State next = m_state;
next.enabled = props->enabled && m_pipeline->crtc();
next.position = props->pos;
next.scale = props->scale;
next.transform = props->transform;
next.currentMode = m_pipeline->mode();
next.overscan = m_pipeline->overscan();
next.rgbRange = m_pipeline->rgbRange();
setState(next);
setVrrPolicy(props->vrrPolicy);
if (!isEnabled() && m_pipeline->needsModeset()) {
m_gpu->maybeModeset();
}
moveTo(props->pos);
setScale(props->scale);
setTransformInternal(props->transform);
const auto mode = m_pipeline->mode();
setCurrentModeInternal(mode);
m_renderLoop->setRefreshRate(mode->refreshRate());
setOverscanInternal(m_pipeline->overscan());
setRgbRangeInternal(m_pipeline->rgbRange());
setVrrPolicy(props->vrrPolicy);
m_renderLoop->setRefreshRate(refreshRate());
m_renderLoop->scheduleRepaint();
Q_EMIT changed();
updateCursor();
......
......@@ -55,6 +55,7 @@ public:
void applyQueuedChanges(const OutputConfiguration &config);
void revertQueuedChanges();
void updateModes();
void updateDpmsMode(DpmsMode dpmsMode);
bool usesSoftwareCursor() const override;
void updateCursor();
......
......@@ -20,14 +20,13 @@
namespace KWin
{
DrmVirtualOutput::DrmVirtualOutput(const QString &name, DrmGpu *gpu, const QSize &size, VirtualOutputType type)
DrmVirtualOutput::DrmVirtualOutput(const QString &name, DrmGpu *gpu, const QSize &size, qreal scale, VirtualOutputType type)
: DrmAbstractOutput(gpu)
, m_vsyncMonitor(SoftwareVsyncMonitor::create())
{
connect(m_vsyncMonitor.get(), &VsyncMonitor::vblankOccurred, this, &DrmVirtualOutput::vblank);
auto mode = std::make_shared<OutputMode>(size, 60000, OutputMode::Flag::Preferred);
setModesInternal({mode}, mode);
m_renderLoop->setRefreshRate(mode->refreshRate());
setInformation(Information{
......@@ -36,6 +35,12 @@ DrmVirtualOutput::DrmVirtualOutput(const QString &name, DrmGpu *gpu, const QSize
.placeholder = type == VirtualOutputType::Placeholder,
});
setState(State{
.scale = scale,
.modes = {mode},
.currentMode = mode,
});
recreateSurface();
}
......@@ -60,7 +65,9 @@ void DrmVirtualOutput::vblank(std::chrono::nanoseconds timestamp)
void DrmVirtualOutput::setDpmsMode(DpmsMode mode)
{
setDpmsModeInternal(mode);
State next = m_state;
next.dpmsMode = mode;
setState(next);
}
DrmOutputLayer *DrmVirtualOutput::outputLayer() const
......
......@@ -27,7 +27,7 @@ class DrmVirtualOutput : public DrmAbstractOutput
Q_OBJECT
public:
DrmVirtualOutput(const QString &name, DrmGpu *gpu, const QSize &size, VirtualOutputType type);
DrmVirtualOutput(const QString &name, DrmGpu *gpu, const QSize &size, qreal scale, VirtualOutputType type);
~DrmVirtualOutput() override;
bool present() override;
......
......@@ -55,7 +55,7 @@ bool VirtualBackend::initialize()
dummyOutput->init(QPoint(0, 0), initialWindowSize());
m_outputs << dummyOutput;
Q_EMIT outputAdded(dummyOutput);
dummyOutput->setEnabled(true);
dummyOutput->updateEnabled(true);
}
setReady(true);
......@@ -104,15 +104,15 @@ void VirtualBackend::setVirtualOutputs(int count, QVector<QRect> geometries, QVe
sumWidth += initialWindowSize().width();
}
if (scales.size()) {
vo->setScale(scales.at(i));
vo->updateScale(scales.at(i));
}
m_outputs.append(vo);
Q_EMIT outputAdded(vo);
vo->setEnabled(true);
vo->updateEnabled(true);
}
for (VirtualOutput *output : removed) {
output->setEnabled(false);
output->updateEnabled(false);
m_outputs.removeOne(output);
Q_EMIT outputRemoved(output);
delete output;
......@@ -124,7 +124,7 @@ void VirtualBackend::setVirtualOutputs(int count, QVector<QRect> geometries, QVe
void VirtualBackend::removeOutput(Output *output)
{
VirtualOutput *virtualOutput = static_cast<VirtualOutput *>(output);
virtualOutput->setEnabled(false);
virtualOutput->updateEnabled(false);
m_outputs.removeOne(virtualOutput);
Q_EMIT outputRemoved(virtualOutput);
......
......@@ -56,8 +56,27 @@ void VirtualOutput::init(const QPoint &logicalPosition, const QSize &pixelSize)
void VirtualOutput::setGeometry(const QRect &geo)
{
auto mode = std::make_shared<OutputMode>(geo.size(), m_vsyncMonitor->refreshRate());
setModesInternal({mode}, mode);
moveTo(geo.topLeft());
State next = m_state;
next.modes = {mode};
next.currentMode = mode;
next.position = geo.topLeft();
setState(next);
}
void VirtualOutput::updateScale(qreal scale)
{
State next = m_state;
next.scale = scale;
setState(next);
}
void VirtualOutput::updateEnabled(bool enabled)
{
State next = m_state;
next.enabled = enabled;
setState(next);
}
void VirtualOutput::vblank(std::chrono::nanoseconds timestamp)
......
......@@ -32,6 +32,8 @@ public:
void init(const QPoint &logicalPosition, const QSize &pixelSize);
void setGeometry(const QRect &geo);
void updateScale(qreal scale);
void updateEnabled(bool enabled);
private:
void vblank(std::chrono::nanoseconds timestamp);
......
......@@ -826,7 +826,7 @@ void WaylandBackend::destroyOutputs()
{
while (!m_outputs.isEmpty()) {
WaylandOutput *output = m_outputs.takeLast();
output->setEnabled(false);
output->updateEnabled(false);
Q_EMIT outputRemoved(output);
delete output;
}
......@@ -940,7 +940,7 @@ void WaylandBackend::addConfiguredOutput(WaylandOutput *output)
{
m_outputs << output;
Q_EMIT outputAdded(output);
output->setEnabled(true);
output->updateEnabled(true);
m_pendingInitialOutputs--;
if (m_pendingInitialOutputs == 0) {
......@@ -976,7 +976,7 @@ void WaylandBackend::removeVirtualOutput(Output *output)
{
WaylandOutput *waylandOutput = dynamic_cast<WaylandOutput *>(output);
if (waylandOutput && m_outputs.removeAll(waylandOutput)) {
waylandOutput->setEnabled(false);
waylandOutput->updateEnabled(false);
Q_EMIT outputRemoved(waylandOutput);
delete waylandOutput;
}
......
......@@ -41,7 +41,7 @@ WaylandOutput::WaylandOutput(const QString &name, Surface *surface, WaylandBacke
m_turnOffTimer.setSingleShot(true);
m_turnOffTimer.setInterval(dimAnimationTime());
connect(&m_turnOffTimer, &QTimer::timeout, this, [this] {
setDpmsModeInternal(DpmsMode::Off);
updateDpmsMode(DpmsMode::Off);
});
}
......@@ -61,14 +61,23 @@ void WaylandOutput::init(const QSize &pixelSize)
m_renderLoop->setRefreshRate(s_refreshRate);
auto mode = std::make_shared<OutputMode>(pixelSize, s_refreshRate);
setModesInternal({mode}, mode);
setScale(backend()->initialOutputScale());
State initialState;
initialState.modes = {mode};
initialState.currentMode = mode;
initialState.scale = m_backend->initialOutputScale();
setState(initialState);
}
void WaylandOutput::resize(const QSize &pixelSize)
{
auto mode = std::make_shared<OutputMode>(pixelSize, s_refreshRate);
setModesInternal({mode}, mode);
State next = m_state;
next.modes = {mode};
next.currentMode = mode;
setState(next);
Q_EMIT m_backend->screensQueried();
}
......@@ -85,12 +94,26 @@ void WaylandOutput::setDpmsMode(DpmsMode mode)
m_backend->clearDpmsFilter();
if (mode != dpmsMode()) {
setDpmsModeInternal(mode);
updateDpmsMode(mode);
Q_EMIT wakeUp();
}
}
}
void WaylandOutput::updateDpmsMode(DpmsMode dpmsMode)
{
State next = m_state;
next.dpmsMode = dpmsMode;
setState(next);
}
void WaylandOutput::updateEnabled(bool enabled)
{
State next = m_state;
next.enabled = enabled;
setState(next);
}
XdgShellOutput::XdgShellOutput(const QString &name, Surface *surface, XdgShell *xdgShell, WaylandBackend *backend, int number, bool placeholder)
: WaylandOutput(name, surface, backend, placeholder)
, m_number(number)
......
......@@ -66,6 +66,9 @@ public:
void setDpmsMode(DpmsMode mode) override;
void updateDpmsMode(DpmsMode dpmsMode);
void updateEnabled(bool enabled);
Q_SIGNALS:
void sizeChanged(const QSize &size);
void frameRendered();
......
......@@ -63,13 +63,11 @@ bool X11Output::usesSoftwareCursor() const
return false;
}
void X11Output::setMode(const QSize &size, uint32_t refreshRate)
void X11Output::updateEnabled(bool enabled)
{
const auto current = currentMode();
if (!current || current->size() != size || current->refreshRate() != refreshRate) {
auto mode = std::make_shared<OutputMode>(size, refreshRate);
setModesInternal({mode}, mode);
}
State next = m_state;
next.enabled = enabled;
setState(next);
}
} // namespace KWin
......@@ -32,6 +32,8 @@ class KWIN_EXPORT X11Output : public Output
public:
explicit X11Output(X11StandalonePlatform *backend, QObject *parent = nullptr);
void updateEnabled(bool enabled);
bool usesSoftwareCursor() const override;
RenderLoop *renderLoop() const override;
......@@ -42,8 +44,6 @@ public:
void setColorTransformation(const std::shared_ptr<ColorTransformation> &transformation) override;
void setMode(const QSize &size, uint32_t refreshRate);
private:
void setCrtc(xcb_randr_crtc_t crtc);
void setGammaRampSize(int size);
......
......@@ -22,11 +22,15 @@ X11PlaceholderOutput::X11PlaceholderOutput(X11StandalonePlatform *backend, QObje
}
auto mode = std::make_shared<OutputMode>(pixelSize, 60000);
setModesInternal({mode}, mode);
setInformation(Information{
.name = QStringLiteral("Placeholder-0"),
});
setState(State{
.modes = {mode},
.currentMode = mode,
});
}
RenderLoop *X11PlaceholderOutput::renderLoop() const
......@@ -34,4 +38,11 @@ RenderLoop *X11PlaceholderOutput::renderLoop() const
return m_backend->renderLoop();
}
void X11PlaceholderOutput::updateEnabled(bool enabled)
{
State next = m_state;
next.enabled = enabled;
setState(next);
}
} // namespace KWin
......@@ -22,6 +22,8 @@ public:
RenderLoop *renderLoop() const override;
void updateEnabled(bool enabled);
private:
X11StandalonePlatform *m_backend;
};
......
......@@ -528,8 +528,6 @@ void X11StandalonePlatform::doUpdateOutputs()
output->setRenderLoop(m_renderLoop.get());
output->setCrtc(crtc);
output->setGammaRampSize(gamma.isNull() ? 0 : gamma->size);
output->setMode(geometry.size(), refreshRate * 1000);
output->moveTo(geometry.topLeft());
auto it = std::find(crtcs.begin(), crtcs.end(), crtc);
int crtcIndex = std::distance(crtcs.begin(), it);
output->setXineramaNumber(crtcIndex);
......@@ -564,7 +562,15 @@ void X11StandalonePlatform::doUpdateOutputs()
}
}
auto mode = std::make_shared<OutputMode>(geometry.size(), refreshRate * 1000);
X11Output::State state = output->m_state;
state.modes = {mode};
state.currentMode = mode;
state.position = geometry.topLeft();
output->setInformation(information);
output->setState(state);
break;
}
}
......@@ -577,20 +583,28 @@ void X11StandalonePlatform::doUpdateOutputs()
auto dummyOutput = new X11PlaceholderOutput(this);
m_outputs << dummyOutput;
Q_EMIT outputAdded(dummyOutput);
dummyOutput->setEnabled(true);
dummyOutput->updateEnabled(true);
}
// Process new outputs. Note new outputs must be introduced before removing any other outputs.
for (Output *output : qAsConst(added)) {
m_outputs.append(output);
Q_EMIT outputAdded(output);
output->setEnabled(true);
if (auto placeholderOutput = qobject_cast<X11PlaceholderOutput *>(output)) {
placeholderOutput->updateEnabled(true);
} else if (auto nativeOutput = qobject_cast<X11Output *>(output)) {
nativeOutput->updateEnabled(true);
}
}
// Outputs have to be removed last to avoid the case where there are no enabled outputs.
for (Output *output : qAsConst(removed)) {
m_outputs.removeOne(output);
output->setEnabled(false);
if (auto placeholderOutput = qobject_cast<X11PlaceholderOutput *>(output)) {
placeholderOutput->updateEnabled(false);
} else if (auto nativeOutput = qobject_cast<X11Output *>(output)) {
nativeOutput->updateEnabled(false);
}
Q_EMIT outputRemoved(output);
delete output;
}
......
......@@ -299,7 +299,7 @@ void X11WindowedBackend::createOutputs()
m_outputs << output;
Q_EMIT outputAdded(output);
output->setEnabled(true);
output->updateEnabled(true);
}
updateWindowTitle();
......@@ -497,7 +497,7 @@ void X11WindowedBackend::handleClientMessage(xcb_client_message_event_t *event)
auto removedOutput = *it;
it = m_outputs.erase(it);
removedOutput->setEnabled(false);
removedOutput->updateEnabled(false);
Q_EMIT outputRemoved(removedOutput);
delete removedOutput;
Q_EMIT screensQueried();
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment