Support dynamic output enabling/disabling from KScreen

Summary:
We need to keep the DrmOutput object to still have the
WaylandOutputDevice.

Screens currently start off enabled as before.

In order to keep KWin to have a correct index based list of screens we
need to store a second vector of currently enabled outputs for the
screens interface.

Test Plan:
Had dual screens.
Disabled/Enabled each one through the kscreen KCM

Reviewers: #plasma, graesslin

Reviewed By: #plasma, graesslin

Subscribers: ngraham, luebking, broulik, graesslin, plasma-devel, kwin, #kwin

Tags: #kwin

Differential Revision: https://phabricator.kde.org/D8796
This commit is contained in:
David Edmundson 2017-11-21 12:04:30 +00:00
parent 45ac8eed34
commit 01c1870e9d
5 changed files with 95 additions and 27 deletions

View file

@ -127,7 +127,7 @@ void DrmBackend::outputWentOff()
void DrmBackend::turnOutputsOn()
{
m_dpmsFilter.reset();
for (auto it = m_outputs.constBegin(), end = m_outputs.constEnd(); it != end; it++) {
for (auto it = m_enabledOutputs.constBegin(), end = m_enabledOutputs.constEnd(); it != end; it++) {
(*it)->setDpms(DrmOutput::DpmsMode::On);
}
}
@ -138,7 +138,7 @@ void DrmBackend::checkOutputsAreOn()
// already disabled, all outputs are on
return;
}
for (auto it = m_outputs.constBegin(), end = m_outputs.constEnd(); it != end; it++) {
for (auto it = m_enabledOutputs.constBegin(), end = m_enabledOutputs.constEnd(); it != end; it++) {
if (!(*it)->isDpmsEnabled()) {
// dpms still disabled, need to keep the filter
return;
@ -396,6 +396,7 @@ void DrmBackend::updateOutputs()
}
DrmOutput *removed = *it;
it = m_outputs.erase(it);
m_enabledOutputs.removeOne(removed);
emit outputRemoved(removed);
delete removed;
}
@ -475,6 +476,7 @@ void DrmBackend::updateOutputs()
}
std::sort(connectedOutputs.begin(), connectedOutputs.end(), [] (DrmOutput *a, DrmOutput *b) { return a->m_conn->id() < b->m_conn->id(); });
m_outputs = connectedOutputs;
m_enabledOutputs = connectedOutputs;
readOutputsConfiguration();
if (!m_outputs.isEmpty()) {
emit screensQueried();
@ -518,18 +520,50 @@ QByteArray DrmBackend::generateOutputConfigurationUuid() const
void DrmBackend::configurationChangeRequested(KWayland::Server::OutputConfigurationInterface *config)
{
const auto changes = config->changes();
for (auto it = changes.begin(); it != changes.end(); it++) {
bool countChanged = false;
//process all non-disabling changes
for (auto it = changes.begin(); it != changes.end(); it++) {
KWayland::Server::OutputChangeSet *changeset = it.value();
auto drmoutput = findOutput(it.key()->uuid());
if (drmoutput == nullptr) {
qCWarning(KWIN_DRM) << "Could NOT find DrmOutput matching " << it.key()->uuid();
return;
continue;
}
if (changeset->enabledChanged() && changeset->enabled() == KWayland::Server::OutputDeviceInterface::Enablement::Enabled) {
drmoutput->setEnabled(true);
m_enabledOutputs << drmoutput;
emit outputAdded(drmoutput);
countChanged = true;
}
drmoutput->setChanges(changeset);
}
emit screens()->changed();
//process any disable requests
for (auto it = changes.begin(); it != changes.end(); it++) {
KWayland::Server::OutputChangeSet *changeset = it.value();
if (changeset->enabledChanged() && changeset->enabled() == KWayland::Server::OutputDeviceInterface::Enablement::Disabled) {
if (m_enabledOutputs.count() == 1) {
qCWarning(KWIN_DRM) << "Not disabling final screen" << it.key()->uuid();
continue;
}
auto drmoutput = findOutput(it.key()->uuid());
if (drmoutput == nullptr) {
qCWarning(KWIN_DRM) << "Could NOT find DrmOutput matching " << it.key()->uuid();
continue;
}
drmoutput->setEnabled(false);
m_enabledOutputs.removeOne(drmoutput);
emit outputRemoved(drmoutput);
countChanged = true;
}
}
if (countChanged) {
emit screensQueried();
} else {
emit screens()->changed();
}
// KCoreAddons needs kwayland's 2b3f9509ac1 to not crash
if (KCoreAddons::version() >= QT_VERSION_CHECK(5, 39, 0)) {
config->setApplied();
@ -706,11 +740,11 @@ DrmSurfaceBuffer *DrmBackend::createBuffer(const std::shared_ptr<GbmSurface> &su
void DrmBackend::outputDpmsChanged()
{
if (m_outputs.isEmpty()) {
if (m_enabledOutputs.isEmpty()) {
return;
}
bool enabled = false;
for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) {
for (auto it = m_enabledOutputs.constBegin(); it != m_enabledOutputs.constEnd(); ++it) {
enabled = enabled || (*it)->isDpmsEnabled();
}
setOutputsEnabled(enabled);

View file

@ -92,6 +92,9 @@ public:
QVector<DrmOutput*> outputs() const {
return m_outputs;
}
QVector<DrmOutput*> enabledOutputs() const {
return m_enabledOutputs;
}
QVector<DrmPlane*> planes() const {
return m_planes;
}
@ -126,7 +129,13 @@ public Q_SLOTS:
void turnOutputsOn();
Q_SIGNALS:
/**
* Emitted whenever an output is removed/disabled
*/
void outputRemoved(KWin::DrmOutput *output);
/**
* Emitted whenever an output is added/enabled
*/
void outputAdded(KWin::DrmOutput *output);
protected:
@ -158,8 +167,11 @@ private:
QVector<DrmCrtc*> m_crtcs;
// all connectors
QVector<DrmConnector*> m_connectors;
// currently active output pipelines (planes + crtc + encoder + connector)
// active output pipelines (planes + crtc + encoder + connector)
QVector<DrmOutput*> m_outputs;
// active and enabled pipelines (above + wl_output)
QVector<DrmOutput*> m_enabledOutputs;
bool m_deleteBufferAfterPageFlip;
bool m_atomicModeSetting = false;
bool m_cursorEnabled = false;

View file

@ -183,6 +183,26 @@ qreal DrmOutput::scale() const
return m_scale;
}
void DrmOutput::setEnabled(bool enabled)
{
if (enabled == isEnabled()) {
return;
}
if (enabled) {
setDpms(DpmsMode::On);
initOutput();
} else {
setDpms(DpmsMode::Off);
delete m_waylandOutput.data();
}
m_waylandOutputDevice->setEnabled(enabled ?
KWayland::Server::OutputDeviceInterface::Enablement::Enabled : KWayland::Server::OutputDeviceInterface::Enablement::Disabled);
}
bool DrmOutput::isEnabled() const
{
return !m_waylandOutput.isNull();
}
static KWayland::Server::OutputInterface::DpmsMode toWaylandDpmsMode(DrmOutput::DpmsMode mode)
{
@ -272,8 +292,6 @@ bool DrmOutput::init(drmModeConnector *connector)
m_internal = connector->connector_type == DRM_MODE_CONNECTOR_LVDS || connector->connector_type == DRM_MODE_CONNECTOR_eDP;
setDpms(DpmsMode::On);
if (m_internal) {
connect(kwinApp(), &Application::screensCreated, this,
[this] {
@ -297,8 +315,8 @@ bool DrmOutput::init(drmModeConnector *connector)
m_physicalSize = physicalSize;
initOutputDevice(connector);
initOutput();
setEnabled(true);
return true;
}
@ -784,18 +802,13 @@ void DrmOutput::setChanges(KWayland::Server::OutputChangeSet *changes)
bool DrmOutput::commitChanges()
{
Q_ASSERT(!m_waylandOutputDevice.isNull());
Q_ASSERT(!m_waylandOutput.isNull());
if (m_changeset.isNull()) {
qCDebug(KWIN_DRM) << "no changes";
// No changes to an output is an entirely valid thing
return true;
}
if (m_changeset->enabledChanged()) {
qCDebug(KWIN_DRM) << "Setting enabled:";
m_waylandOutputDevice->setEnabled(m_changeset->enabled());
}
//enabledChanged is handled by drmbackend
if (m_changeset->modeChanged()) {
qCDebug(KWIN_DRM) << "Setting new mode:" << m_changeset->mode();
m_waylandOutputDevice->setCurrentMode(m_changeset->mode());

View file

@ -75,6 +75,15 @@ public:
bool present(DrmBuffer *buffer);
void pageFlipped();
/**
* Enable or disable the output.
* This differs from setDpms as it also
* removes the wl_output
* The default is on
*/
void setEnabled(bool enabled);
bool isEnabled() const;
/**
* This sets the changes and tests them against the DRM output
*/

View file

@ -43,7 +43,7 @@ void DrmScreens::init()
QRect DrmScreens::geometry(int screen) const
{
const auto outputs = m_backend->outputs();
const auto outputs = m_backend->enabledOutputs();
if (screen >= outputs.size()) {
return QRect();
}
@ -52,7 +52,7 @@ QRect DrmScreens::geometry(int screen) const
qreal DrmScreens::scale(int screen) const
{
const auto outputs = m_backend->outputs();
const auto outputs = m_backend->enabledOutputs();
if (screen >= outputs.size()) {
return 1;
}
@ -61,7 +61,7 @@ qreal DrmScreens::scale(int screen) const
QSize DrmScreens::size(int screen) const
{
const auto outputs = m_backend->outputs();
const auto outputs = m_backend->enabledOutputs();
if (screen >= outputs.size()) {
return QSize();
}
@ -70,14 +70,14 @@ QSize DrmScreens::size(int screen) const
void DrmScreens::updateCount()
{
setCount(m_backend->outputs().size());
setCount(m_backend->enabledOutputs().size());
}
int DrmScreens::number(const QPoint &pos) const
{
int bestScreen = 0;
int minDistance = INT_MAX;
const auto outputs = m_backend->outputs();
const auto outputs = m_backend->enabledOutputs();
for (int i = 0; i < outputs.size(); ++i) {
const QRect &geo = outputs.at(i)->geometry();
if (geo.contains(pos)) {
@ -97,7 +97,7 @@ int DrmScreens::number(const QPoint &pos) const
QString DrmScreens::name(int screen) const
{
const auto outputs = m_backend->outputs();
const auto outputs = m_backend->enabledOutputs();
if (screen >= outputs.size()) {
return Screens::name(screen);
}
@ -106,7 +106,7 @@ QString DrmScreens::name(int screen) const
float DrmScreens::refreshRate(int screen) const
{
const auto outputs = m_backend->outputs();
const auto outputs = m_backend->enabledOutputs();
if (screen >= outputs.size()) {
return Screens::refreshRate(screen);
}
@ -115,7 +115,7 @@ float DrmScreens::refreshRate(int screen) const
QSizeF DrmScreens::physicalSize(int screen) const
{
const auto outputs = m_backend->outputs();
const auto outputs = m_backend->enabledOutputs();
if (screen >= outputs.size()) {
return Screens::physicalSize(screen);
}
@ -124,7 +124,7 @@ QSizeF DrmScreens::physicalSize(int screen) const
bool DrmScreens::isInternal(int screen) const
{
const auto outputs = m_backend->outputs();
const auto outputs = m_backend->enabledOutputs();
if (screen >= outputs.size()) {
return false;
}
@ -133,7 +133,7 @@ bool DrmScreens::isInternal(int screen) const
bool DrmScreens::supportsTransformations(int screen) const
{
const auto outputs = m_backend->outputs();
const auto outputs = m_backend->enabledOutputs();
if (screen >= outputs.size()) {
return false;
}