eglfs: Configurable screen order in the virtual desktop

Say one wants a virtual desktop with the display on HDMI above
the display on DisplayPort:

{
  "device": "drm-nvdc",
  "virtualDesktopOrientation": "vertical",
  "outputs": [
    {
      "name": "HDMI1",
      "virtualIndex": 0
    },
    {
      "name": "DP1"
    }
  ]
}

Undefined virtualIndex values map to INT_MAX and will go after the
explicitly specified ones. However, the sorting is stable so the original
order from the DRM connector list is preserved between such outputs.

Task-number: QTBUG-55188
Change-Id: I204fb08205ea7dbfbcdefd1d22ed22f5387f3e8c
Reviewed-by: Andy Nichols <andy.nichols@qt.io>
This commit is contained in:
Laszlo Agocs 2016-08-09 10:08:46 +02:00
parent ce2419af89
commit 2afead0211
13 changed files with 84 additions and 74 deletions

View File

@ -142,10 +142,10 @@ void QEglFSKmsGbmDevice::handleDrmEvent()
drmHandleEvent(fd(), &drmEvent);
}
QEglFSKmsScreen *QEglFSKmsGbmDevice::createScreen(QEglFSKmsIntegration *integration, QEglFSKmsDevice *device, QEglFSKmsOutput output, QPoint position)
QEglFSKmsScreen *QEglFSKmsGbmDevice::createScreen(QEglFSKmsIntegration *integration, QEglFSKmsDevice *device, QEglFSKmsOutput output)
{
static bool firstScreen = true;
QEglFSKmsGbmScreen *screen = new QEglFSKmsGbmScreen(integration, device, output, position);
QEglFSKmsGbmScreen *screen = new QEglFSKmsGbmScreen(integration, device, output);
if (firstScreen && integration->hwCursor()) {
m_globalCursor = new QEglFSKmsGbmCursor(screen);

View File

@ -68,8 +68,7 @@ public:
virtual QEglFSKmsScreen *createScreen(QEglFSKmsIntegration *integration,
QEglFSKmsDevice *device,
QEglFSKmsOutput output,
QPoint position) Q_DECL_OVERRIDE;
QEglFSKmsOutput output) Q_DECL_OVERRIDE;
private:
Q_DISABLE_COPY(QEglFSKmsGbmDevice)

View File

@ -94,9 +94,8 @@ QEglFSKmsGbmScreen::FrameBuffer *QEglFSKmsGbmScreen::framebufferForBufferObject(
QEglFSKmsGbmScreen::QEglFSKmsGbmScreen(QEglFSKmsIntegration *integration,
QEglFSKmsDevice *device,
QEglFSKmsOutput output,
QPoint position)
: QEglFSKmsScreen(integration, device, output, position)
QEglFSKmsOutput output)
: QEglFSKmsScreen(integration, device, output)
, m_gbm_surface(Q_NULLPTR)
, m_gbm_bo_current(Q_NULLPTR)
, m_gbm_bo_next(Q_NULLPTR)

View File

@ -56,8 +56,7 @@ class QEglFSKmsGbmScreen : public QEglFSKmsScreen
public:
QEglFSKmsGbmScreen(QEglFSKmsIntegration *integration,
QEglFSKmsDevice *device,
QEglFSKmsOutput output,
QPoint position);
QEglFSKmsOutput output);
~QEglFSKmsGbmScreen();
QPlatformCursor *cursor() const Q_DECL_OVERRIDE;

View File

@ -83,9 +83,9 @@ EGLNativeDisplayType QEglFSKmsEglDevice::nativeDisplay() const
}
QEglFSKmsScreen *QEglFSKmsEglDevice::createScreen(QEglFSKmsIntegration *integration, QEglFSKmsDevice *device,
QEglFSKmsOutput output, QPoint position)
QEglFSKmsOutput output)
{
QEglFSKmsScreen *screen = new QEglFSKmsEglDeviceScreen(integration, device, output, position);
QEglFSKmsScreen *screen = new QEglFSKmsEglDeviceScreen(integration, device, output);
if (!m_globalCursor && !integration->separateScreens()) {
qCDebug(qLcEglfsKmsDebug, "Creating new global mouse cursor");

View File

@ -58,8 +58,7 @@ public:
virtual QEglFSKmsScreen *createScreen(QEglFSKmsIntegration *integration,
QEglFSKmsDevice *device,
QEglFSKmsOutput output,
QPoint position) Q_DECL_OVERRIDE;
QEglFSKmsOutput output) Q_DECL_OVERRIDE;
QPlatformCursor *globalCursor() { return m_globalCursor; }
void destroyGlobalCursor();

View File

@ -43,8 +43,8 @@
QT_BEGIN_NAMESPACE
QEglFSKmsEglDeviceScreen::QEglFSKmsEglDeviceScreen(QEglFSKmsIntegration *integration, QEglFSKmsDevice *device, QEglFSKmsOutput output, QPoint position)
: QEglFSKmsScreen(integration, device, output, position)
QEglFSKmsEglDeviceScreen::QEglFSKmsEglDeviceScreen(QEglFSKmsIntegration *integration, QEglFSKmsDevice *device, QEglFSKmsOutput output)
: QEglFSKmsScreen(integration, device, output)
{
}

View File

@ -49,8 +49,7 @@ class QEglFSKmsEglDeviceScreen : public QEglFSKmsScreen
public:
QEglFSKmsEglDeviceScreen(QEglFSKmsIntegration *integration,
QEglFSKmsDevice *device,
QEglFSKmsOutput output,
QPoint position);
QEglFSKmsOutput output);
~QEglFSKmsEglDeviceScreen();
QPlatformCursor *cursor() const Q_DECL_OVERRIDE;

View File

@ -159,7 +159,7 @@ static bool parseModeline(const QByteArray &text, drmModeModeInfoPtr mode)
return true;
}
QEglFSKmsScreen *QEglFSKmsDevice::createScreenForConnector(drmModeResPtr resources, drmModeConnectorPtr connector, QPoint pos)
QEglFSKmsScreen *QEglFSKmsDevice::createScreenForConnector(drmModeResPtr resources, drmModeConnectorPtr connector, int *virtualIndex)
{
const QByteArray connectorName = nameForConnector(connector);
@ -192,6 +192,8 @@ QEglFSKmsScreen *QEglFSKmsDevice::createScreenForConnector(drmModeResPtr resourc
qWarning("Invalid mode \"%s\" for output %s", mode.constData(), connectorName.constData());
configuration = OutputConfigPreferred;
}
if (virtualIndex)
*virtualIndex = userConnectorConfig.value(QStringLiteral("virtualIndex"), INT_MAX).toInt();
const uint32_t crtc_id = resources->crtcs[crtc];
@ -303,6 +305,7 @@ QEglFSKmsScreen *QEglFSKmsDevice::createScreenForConnector(drmModeResPtr resourc
physSize.setHeight(connector->mmHeight);
}
}
qCDebug(qLcEglfsKmsDebug) << "Physical size is" << physSize << "mm" << "for output" << connectorName;
QEglFSKmsOutput output = {
QString::fromUtf8(connectorName),
@ -320,7 +323,7 @@ QEglFSKmsScreen *QEglFSKmsDevice::createScreenForConnector(drmModeResPtr resourc
m_crtc_allocator |= (1 << output.crtc_id);
m_connector_allocator |= (1 << output.connector_id);
return createScreen(m_integration, this, output, pos);
return createScreen(m_integration, this, output);
}
drmModePropertyPtr QEglFSKmsDevice::connectorProperty(drmModeConnectorPtr connector, const QByteArray &name)
@ -352,6 +355,26 @@ QEglFSKmsDevice::~QEglFSKmsDevice()
{
}
struct OrderedScreen
{
OrderedScreen() : screen(nullptr), index(-1) { }
OrderedScreen(QEglFSKmsScreen *screen, int index) : screen(screen), index(index) { }
QEglFSKmsScreen *screen;
int index;
};
QDebug operator<<(QDebug dbg, const OrderedScreen &s)
{
QDebugStateSaver saver(dbg);
dbg.nospace() << "OrderedScreen(" << s.screen << " : " << s.index << ")";
return dbg;
}
static bool orderedScreenLessThan(const OrderedScreen &a, const OrderedScreen &b)
{
return a.index < b.index;
}
void QEglFSKmsDevice::createScreens()
{
drmModeResPtr resources = drmModeGetResources(m_dri_fd);
@ -360,38 +383,46 @@ void QEglFSKmsDevice::createScreens()
return;
}
QEglFSKmsScreen *primaryScreen = Q_NULLPTR;
QList<QPlatformScreen *> siblings;
QPoint pos(0, 0);
QEglFSIntegration *integration = static_cast<QEglFSIntegration *>(QGuiApplicationPrivate::platformIntegration());
QVector<OrderedScreen> screens;
for (int i = 0; i < resources->count_connectors; i++) {
drmModeConnectorPtr connector = drmModeGetConnector(m_dri_fd, resources->connectors[i]);
if (!connector)
continue;
QEglFSKmsScreen *screen = createScreenForConnector(resources, connector, pos);
if (screen) {
integration->addScreen(screen);
if (m_integration->virtualDesktopLayout() == QEglFSKmsIntegration::VirtualDesktopLayoutVertical)
pos.ry() += screen->geometry().height();
else
pos.rx() += screen->geometry().width();
siblings << screen;
if (!primaryScreen)
primaryScreen = screen;
}
int virtualIndex;
QEglFSKmsScreen *screen = createScreenForConnector(resources, connector, &virtualIndex);
if (screen)
screens.append(OrderedScreen(screen, virtualIndex));
drmModeFreeConnector(connector);
}
drmModeFreeResources(resources);
// Use stable sort to preserve the original order for outputs with unspecified indices.
std::stable_sort(screens.begin(), screens.end(), orderedScreenLessThan);
qCDebug(qLcEglfsKmsDebug) << "Sorted screen list:" << screens;
QPoint pos(0, 0);
QList<QPlatformScreen *> siblings;
QEglFSIntegration *qpaIntegration = static_cast<QEglFSIntegration *>(QGuiApplicationPrivate::platformIntegration());
for (const OrderedScreen &orderedScreen : screens) {
QEglFSKmsScreen *s = orderedScreen.screen;
// set up a horizontal or vertical virtual desktop
s->setVirtualPosition(pos);
if (m_integration->virtualDesktopLayout() == QEglFSKmsIntegration::VirtualDesktopLayoutVertical)
pos.ry() += s->geometry().height();
else
pos.rx() += s->geometry().width();
qCDebug(qLcEglfsKmsDebug) << "Adding screen" << s << "to QPA with geometry" << s->geometry();
qpaIntegration->addScreen(s);
siblings << s;
}
if (!m_integration->separateScreens()) {
// set up a virtual desktop
// enable the virtual desktop
Q_FOREACH (QPlatformScreen *screen, siblings)
static_cast<QEglFSKmsScreen *>(screen)->setVirtualSiblings(siblings);
}
@ -407,9 +438,9 @@ QString QEglFSKmsDevice::devicePath() const
return m_path;
}
QEglFSKmsScreen *QEglFSKmsDevice::createScreen(QEglFSKmsIntegration *integration, QEglFSKmsDevice *device, QEglFSKmsOutput output, QPoint position)
QEglFSKmsScreen *QEglFSKmsDevice::createScreen(QEglFSKmsIntegration *integration, QEglFSKmsDevice *device, QEglFSKmsOutput output)
{
return new QEglFSKmsScreen(integration, device, output, position);
return new QEglFSKmsScreen(integration, device, output);
}
void QEglFSKmsDevice::setFd(int fd)

View File

@ -68,8 +68,7 @@ public:
protected:
virtual QEglFSKmsScreen *createScreen(QEglFSKmsIntegration *integration,
QEglFSKmsDevice *device,
QEglFSKmsOutput output,
QPoint position);
QEglFSKmsOutput output);
void setFd(int fd);
QEglFSKmsIntegration *m_integration;
@ -80,7 +79,7 @@ protected:
quint32 m_connector_allocator;
int crtcForConnector(drmModeResPtr resources, drmModeConnectorPtr connector);
QEglFSKmsScreen *createScreenForConnector(drmModeResPtr resources, drmModeConnectorPtr connector, QPoint pos);
QEglFSKmsScreen *createScreenForConnector(drmModeResPtr resources, drmModeConnectorPtr connector, int *virtualIndex);
drmModePropertyPtr connectorProperty(drmModeConnectorPtr connector, const QByteArray &name);
static void pageFlipHandler(int fd,

View File

@ -201,7 +201,7 @@ void QEglFSKmsIntegration::loadConfig()
else if (vdOriString == QLatin1String("vertical"))
m_virtualDesktopLayout = VirtualDesktopLayoutVertical;
else
qCWarning(qLcEglfsKmsDebug) << "Unknown virtualDesktop value" << vdOriString;
qCWarning(qLcEglfsKmsDebug) << "Unknown virtualDesktopOrientation value" << vdOriString;
}
const QJsonArray outputs = object.value(QLatin1String("outputs")).toArray();

View File

@ -71,13 +71,11 @@ private:
QEglFSKmsScreen::QEglFSKmsScreen(QEglFSKmsIntegration *integration,
QEglFSKmsDevice *device,
QEglFSKmsOutput output,
QPoint position)
QEglFSKmsOutput output)
: QEglFSScreen(eglGetDisplay(device->nativeDisplay()))
, m_integration(integration)
, m_device(device)
, m_output(output)
, m_pos(position)
, m_powerState(PowerStateOn)
, m_interruptHandler(new QEglFSKmsInterruptHandler(this))
{
@ -98,34 +96,19 @@ QEglFSKmsScreen::~QEglFSKmsScreen()
delete m_interruptHandler;
}
void QEglFSKmsScreen::setVirtualPosition(const QPoint &pos)
{
m_pos = pos;
}
// Reimplement rawGeometry(), not geometry(). The base class implementation of
// geometry() calls rawGeometry() and may apply additional transforms.
QRect QEglFSKmsScreen::rawGeometry() const
{
const int mode = m_output.mode;
QRect r(m_pos.x(), m_pos.y(),
return QRect(m_pos.x(), m_pos.y(),
m_output.modes[mode].hdisplay,
m_output.modes[mode].vdisplay);
static int rotation = qEnvironmentVariableIntValue("QT_QPA_EGLFS_ROTATION");
switch (rotation) {
case 0:
case 180:
case -180:
break;
case 90:
case -90: {
int h = r.height();
r.setHeight(r.width());
r.setWidth(h);
break;
}
default:
qWarning("Invalid rotation %d specified in QT_QPA_EGLFS_ROTATION", rotation);
break;
}
return r;
}
int QEglFSKmsScreen::depth() const

View File

@ -74,11 +74,13 @@ class Q_EGLFS_EXPORT QEglFSKmsScreen : public QEglFSScreen
public:
QEglFSKmsScreen(QEglFSKmsIntegration *integration,
QEglFSKmsDevice *device,
QEglFSKmsOutput output,
QPoint position);
QEglFSKmsOutput output);
~QEglFSKmsScreen();
void setVirtualPosition(const QPoint &pos);
QRect rawGeometry() const Q_DECL_OVERRIDE;
int depth() const Q_DECL_OVERRIDE;
QImage::Format format() const Q_DECL_OVERRIDE;