[drm] Support configuring absolute output position

Begin of proper multiscreen support!

We load configuration sets for the connected outputs. Each set of
screens represents a unique configuration. For that we use the md5
sum of the edid+connector as uuid of an output. Each of the md5 sums
is then used to create a uuid of the output set. We can be quite certain
that this will generate unique ids for the use cases we will face.

The uuids are used as group names. And from there we read the global
position.

The uuids are considered internal information. It is not intended for
users to configure manually in the config file. The intended way to
configure will be the OutputManagementInterface which recently got added
to KWayland. Once KWin applies a configuration it will store it to config
so that it can be loaded on next startup.

The configuration looks like:
[DrmOutputs][abcdef0123][0123abcdef]
Position=0,0

[DrmOutputs][abcdef0123][fbca3bcdef]
Position=1280,0

This is an example for two outputs set next to each other.

Reviewed-By: Sebastian Kügler
This commit is contained in:
Martin Gräßlin 2015-11-02 09:34:32 +01:00
parent a8ff9d39a7
commit cbbd684430
2 changed files with 65 additions and 1 deletions

View file

@ -39,6 +39,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <KLocalizedString>
#include <KSharedConfig>
// Qt
#include <QCryptographicHash>
#include <QSocketNotifier>
#include <QPainter>
// system
@ -285,8 +286,10 @@ void DrmBackend::queryResources()
}
drmOutput->m_connector = connector->connector_id;
drmOutput->init(connector.data());
qCDebug(KWIN_DRM) << "Found new output with uuid" << drmOutput->uuid();
connectedOutputs << drmOutput;
}
std::sort(connectedOutputs.begin(), connectedOutputs.end(), [] (DrmOutput *a, DrmOutput *b) { return a->m_connector < b->m_connector; });
// check for outputs which got removed
auto it = m_outputs.begin();
while (it != m_outputs.end()) {
@ -305,8 +308,41 @@ void DrmBackend::queryResources()
}
}
m_outputs = connectedOutputs;
readOutputsConfiguration();
emit screensQueried();
// TODO: install global space
}
void DrmBackend::readOutputsConfiguration()
{
if (m_outputs.isEmpty()) {
return;
}
const QByteArray uuid = generateOutputConfigurationUuid();
const auto outputGroup = KSharedConfig::openConfig(KWIN_CONFIG)->group("DrmOutputs");
const auto configGroup = outputGroup.group(uuid);
qCDebug(KWIN_DRM) << "Reading output configuration for" << uuid;
// default position goes from left to right
QPoint pos(0, 0);
for (auto it = m_outputs.begin(); it != m_outputs.end(); ++it) {
const auto outputConfig = configGroup.group((*it)->uuid());
(*it)->setGlobalPos(outputConfig.readEntry<QPoint>("Position", pos));
// TODO: add mode
pos.setX(pos.x() + (*it)->size().width());
}
}
QByteArray DrmBackend::generateOutputConfigurationUuid() const
{
auto it = m_outputs.constBegin();
if (m_outputs.size() == 1) {
// special case: one output
return (*it)->uuid();
}
QCryptographicHash hash(QCryptographicHash::Md5);
for (; it != m_outputs.constEnd(); ++it) {
hash.addData((*it)->uuid());
}
return hash.result().toHex().left(10);
}
DrmOutput *DrmBackend::findOutput(quint32 connector)
@ -650,6 +686,7 @@ void DrmOutput::init(drmModeConnector *connector)
{
initEdid(connector);
initDpms(connector);
initUuid();
m_savedCrtc.reset(drmModeGetCrtc(m_backend->fd(), m_crtcId));
blank();
setDpms(DpmsMode::On);
@ -731,6 +768,16 @@ void DrmOutput::init(drmModeConnector *connector)
m_waylandOutput->create();
}
void DrmOutput::initUuid()
{
QCryptographicHash hash(QCryptographicHash::Md5);
hash.addData(QByteArray::number(m_connector));
hash.addData(m_edid.eisaId);
hash.addData(m_edid.monitorName);
hash.addData(m_edid.serialNumber);
m_uuid = hash.result().toHex().left(10);
}
bool DrmOutput::isCurrentMode(const drmModeModeInfo *mode) const
{
return mode->clock == m_mode.clock
@ -996,6 +1043,14 @@ int DrmOutput::currentRefreshRate() const
return m_waylandOutput->refreshRate();
}
void DrmOutput::setGlobalPos(const QPoint &pos)
{
m_globalPos = pos;
if (m_waylandOutput) {
m_waylandOutput->setGlobalPosition(pos);
}
}
DrmBuffer::DrmBuffer(DrmBackend *backend, const QSize &size)
: m_backend(backend)
, m_size(size)

View file

@ -107,6 +107,8 @@ private:
quint32 findCrtc(drmModeRes *res, drmModeConnector *connector, bool *ok = nullptr);
bool crtcIsUsed(quint32 crtc);
void outputDpmsChanged();
void readOutputsConfiguration();
QByteArray generateOutputConfigurationUuid() const;
DrmOutput *findOutput(quint32 connector);
QScopedPointer<Udev> m_udev;
QScopedPointer<UdevMonitor> m_udevMonitor;
@ -155,6 +157,10 @@ public:
return m_dpmsMode == DpmsMode::On;
}
QByteArray uuid() const {
return m_uuid;
}
Q_SIGNALS:
void dpmsChanged();
@ -167,6 +173,8 @@ private:
void initDpms(drmModeConnector *connector);
bool isCurrentMode(const drmModeModeInfo *mode) const;
void reenableDpms();
void initUuid();
void setGlobalPos(const QPoint &pos);
DrmBackend *m_backend;
QPoint m_globalPos;
@ -186,6 +194,7 @@ private:
QPointer<KWayland::Server::OutputInterface> m_waylandOutput;
ScopedDrmPointer<_drmModeProperty, &drmModeFreeProperty> m_dpms;
DpmsMode m_dpmsMode = DpmsMode::On;
QByteArray m_uuid;
};
class DrmBuffer