summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/xcb/qxcbscreen.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/xcb/qxcbscreen.cpp')
-rw-r--r--src/plugins/platforms/xcb/qxcbscreen.cpp184
1 files changed, 135 insertions, 49 deletions
diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp
index d92004b1ac..315c94bb56 100644
--- a/src/plugins/platforms/xcb/qxcbscreen.cpp
+++ b/src/plugins/platforms/xcb/qxcbscreen.cpp
@@ -43,38 +43,59 @@
#include "qxcbwindow.h"
#include "qxcbcursor.h"
#include "qxcbimage.h"
+#include "qnamespace.h"
#include <stdio.h>
#include <QDebug>
-#include <xcb/randr.h>
-
#include <qpa/qwindowsysteminterface.h>
QT_BEGIN_NAMESPACE
-QXcbScreen::QXcbScreen(QXcbConnection *connection, xcb_screen_t *screen, int number)
+QXcbScreen::QXcbScreen(QXcbConnection *connection, xcb_screen_t *scr,
+ xcb_randr_get_output_info_reply_t *output, QString outputName, int number)
: QXcbObject(connection)
- , m_screen(screen)
+ , m_screen(scr)
+ , m_crtc(output ? output->crtc : 0)
+ , m_outputName(outputName)
+ , m_sizeMillimeters(output ? QSize(output->mm_width, output->mm_height) : QSize())
+ , m_virtualSize(scr->width_in_pixels, scr->height_in_pixels)
+ , m_virtualSizeMillimeters(scr->width_in_millimeters, scr->height_in_millimeters)
+ , m_orientation(Qt::PrimaryOrientation)
, m_number(number)
, m_refreshRate(60)
{
if (connection->hasXRandr())
- xcb_randr_select_input(xcb_connection(), screen->root, true);
+ xcb_randr_select_input(xcb_connection(), screen()->root, true);
+ updateGeometry(output ? output->timestamp : 0);
updateRefreshRate();
+ // On VNC, it can be that physical size is unknown while
+ // virtual size is known (probably back-calculated from DPI and resolution)
+ if (m_sizeMillimeters.isEmpty())
+ m_sizeMillimeters = m_virtualSizeMillimeters;
+ if (m_geometry.isEmpty())
+ m_geometry = QRect(QPoint(), m_virtualSize);
+ if (m_availableGeometry.isEmpty())
+ m_availableGeometry = QRect(QPoint(), m_virtualSize);
+
#ifdef Q_XCB_DEBUG
qDebug();
- qDebug("Information of screen %d:", screen->root);
- qDebug(" width.........: %d", screen->width_in_pixels);
- qDebug(" height........: %d", screen->height_in_pixels);
- qDebug(" depth.........: %d", screen->root_depth);
- qDebug(" white pixel...: %x", screen->white_pixel);
- qDebug(" black pixel...: %x", screen->black_pixel);
+ qDebug("Screen %s:", m_outputName.toUtf8().constData());
+ qDebug(" width..........: %lf", m_sizeMillimeters.width());
+ qDebug(" height.........: %lf", m_sizeMillimeters.height());
+ qDebug(" geometry.......: %d x %d +%d +%d", m_geometry.width(), m_geometry.height(), m_geometry.x(), m_geometry.y());
+ qDebug(" virtual width..: %lf", m_virtualSizeMillimeters.width());
+ qDebug(" virtual height.: %lf", m_virtualSizeMillimeters.height());
+ qDebug(" virtual geom...: %d x %d", m_virtualSize.width(), m_virtualSize.height());
+ qDebug(" avail virt geom: %d x %d +%d +%d", m_availableGeometry.width(), m_availableGeometry.height(), m_availableGeometry.x(), m_availableGeometry.y());
+ qDebug(" depth..........: %d", screen()->root_depth);
+ qDebug(" white pixel....: %x", screen()->white_pixel);
+ qDebug(" black pixel....: %x", screen()->black_pixel);
qDebug(" refresh rate...: %d", m_refreshRate);
- qDebug();
+ qDebug(" root ID........: %x", screen()->root);
#endif
const quint32 mask = XCB_CW_EVENT_MASK;
@@ -85,11 +106,11 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, xcb_screen_t *screen, int num
| XCB_EVENT_MASK_PROPERTY_CHANGE
};
- xcb_change_window_attributes(xcb_connection(), screen->root, mask, values);
+ xcb_change_window_attributes(xcb_connection(), screen()->root, mask, values);
xcb_get_property_reply_t *reply =
xcb_get_property_reply(xcb_connection(),
- xcb_get_property_unchecked(xcb_connection(), false, screen->root,
+ xcb_get_property_unchecked(xcb_connection(), false, screen()->root,
atom(QXcbAtom::_NET_SUPPORTING_WM_CHECK),
XCB_ATOM_WINDOW, 0, 1024), NULL);
@@ -105,14 +126,14 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, xcb_screen_t *screen, int num
if (windowManagerReply && windowManagerReply->format == 8 && windowManagerReply->type == atom(QXcbAtom::UTF8_STRING)) {
m_windowManagerName = QString::fromUtf8((const char *)xcb_get_property_value(windowManagerReply), xcb_get_property_value_length(windowManagerReply));
#ifdef Q_XCB_DEBUG
- qDebug("Running window manager: %s", qPrintable(m_windowManagerName));
+ qDebug(" window manager.: %s", qPrintable(m_windowManagerName));
+ qDebug();
#endif
}
free(windowManagerReply);
}
}
-
free(reply);
const xcb_query_extension_reply_t *sync_reply = xcb_get_extension_data(xcb_connection(), &xcb_sync_id);
@@ -125,11 +146,11 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, xcb_screen_t *screen, int num
Q_XCB_CALL2(xcb_create_window(xcb_connection(),
XCB_COPY_FROM_PARENT,
m_clientLeader,
- m_screen->root,
+ screen()->root,
0, 0, 1, 1,
0,
XCB_WINDOW_CLASS_INPUT_OUTPUT,
- m_screen->root_visual,
+ screen()->root_visual,
0, 0), connection);
Q_XCB_CALL2(xcb_change_property(xcb_connection(),
@@ -142,7 +163,7 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, xcb_screen_t *screen, int num
&m_clientLeader), connection);
xcb_depth_iterator_t depth_iterator =
- xcb_screen_allowed_depths_iterator(screen);
+ xcb_screen_allowed_depths_iterator(screen());
while (depth_iterator.rem) {
xcb_depth_t *depth = depth_iterator.data;
@@ -214,34 +235,113 @@ const xcb_visualtype_t *QXcbScreen::visualForId(xcb_visualid_t visualid) const
return &*it;
}
-QRect QXcbScreen::geometry() const
-{
- return QRect(0, 0, m_screen->width_in_pixels, m_screen->height_in_pixels);
-}
-
-int QXcbScreen::depth() const
-{
- return m_screen->root_depth;
-}
-
QImage::Format QXcbScreen::format() const
{
return QImage::Format_RGB32;
}
-QSizeF QXcbScreen::physicalSize() const
+QPlatformCursor *QXcbScreen::cursor() const
{
- return QSizeF(m_screen->width_in_millimeters, m_screen->height_in_millimeters);
+ return m_cursor;
}
-QPlatformCursor *QXcbScreen::cursor() const
+/*!
+ \brief handle the XCB screen change event and update properties
+
+ On a mobile device, the ideal use case is that the accelerometer would
+ drive the orientation. This could be achieved by using QSensors to read the
+ accelerometer and adjusting the rotation in QML, or by reading the
+ orientation from the QScreen object and doing the same, or in many other
+ ways. However, on X we have the XRandR extension, which makes it possible
+ to have the whole screen rotated, so that individual apps DO NOT have to
+ rotate themselves. Apps could optionally use the
+ QScreen::primaryOrientation property to optimize layout though.
+ Furthermore, there is no support in X for accelerometer events anyway. So
+ it makes more sense on a Linux system running X to just run a daemon which
+ monitors the accelerometer and runs xrandr automatically to do the rotation,
+ then apps do not have to be aware of it (but probably the window manager
+ would resize them accordingly). updateGeometry() is written with this
+ design in mind. Therefore the physical geometry, available geometry,
+ virtual geometry, orientation and primaryOrientation should all change at
+ the same time. On a system which cannot rotate the whole screen, it would
+ be correct for only the orientation (not the primary orientation) to
+ change.
+*/
+void QXcbScreen::handleScreenChange(xcb_randr_screen_change_notify_event_t *change_event)
{
- return m_cursor;
+ updateGeometry(change_event->config_timestamp);
+
+ switch (change_event->rotation) {
+ case XCB_RANDR_ROTATION_ROTATE_0: // xrandr --rotate normal
+ m_orientation = Qt::LandscapeOrientation;
+ m_virtualSize.setWidth(change_event->width);
+ m_virtualSize.setHeight(change_event->height);
+ m_virtualSizeMillimeters.setWidth(change_event->mwidth);
+ m_virtualSizeMillimeters.setHeight(change_event->mheight);
+ break;
+ case XCB_RANDR_ROTATION_ROTATE_90: // xrandr --rotate left
+ m_orientation = Qt::PortraitOrientation;
+ m_virtualSize.setWidth(change_event->height);
+ m_virtualSize.setHeight(change_event->width);
+ m_virtualSizeMillimeters.setWidth(change_event->mheight);
+ m_virtualSizeMillimeters.setHeight(change_event->mwidth);
+ break;
+ case XCB_RANDR_ROTATION_ROTATE_180: // xrandr --rotate inverted
+ m_orientation = Qt::InvertedLandscapeOrientation;
+ m_virtualSize.setWidth(change_event->width);
+ m_virtualSize.setHeight(change_event->height);
+ m_virtualSizeMillimeters.setWidth(change_event->mwidth);
+ m_virtualSizeMillimeters.setHeight(change_event->mheight);
+ break;
+ case XCB_RANDR_ROTATION_ROTATE_270: // xrandr --rotate right
+ m_orientation = Qt::InvertedPortraitOrientation;
+ m_virtualSize.setWidth(change_event->height);
+ m_virtualSize.setHeight(change_event->width);
+ m_virtualSizeMillimeters.setWidth(change_event->mheight);
+ m_virtualSizeMillimeters.setHeight(change_event->mwidth);
+ break;
+ // We don't need to do anything with these, since QScreen doesn't store reflection state,
+ // and Qt-based applications probably don't need to care about it anyway.
+ case XCB_RANDR_ROTATION_REFLECT_X: break;
+ case XCB_RANDR_ROTATION_REFLECT_Y: break;
+ }
+
+ QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), geometry());
+ QWindowSystemInterface::handleScreenAvailableGeometryChange(QPlatformScreen::screen(), availableGeometry());
+ QWindowSystemInterface::handleScreenOrientationChange(QPlatformScreen::screen(), m_orientation);
}
-qreal QXcbScreen::refreshRate() const
+void QXcbScreen::updateGeometry(xcb_timestamp_t timestamp)
{
- return m_refreshRate;
+ if (connection()->hasXRandr()) {
+ xcb_randr_get_crtc_info_reply_t *crtc = xcb_randr_get_crtc_info_reply(xcb_connection(),
+ xcb_randr_get_crtc_info_unchecked(xcb_connection(), m_crtc, timestamp), NULL);
+ if (crtc) {
+ m_geometry = QRect(crtc->x, crtc->y, crtc->width, crtc->height);
+ m_availableGeometry = m_geometry;
+ free(crtc);
+ }
+ }
+
+ xcb_get_property_reply_t * workArea =
+ xcb_get_property_reply(xcb_connection(),
+ xcb_get_property_unchecked(xcb_connection(), false, screen()->root,
+ atom(QXcbAtom::_NET_WORKAREA),
+ XCB_ATOM_CARDINAL, 0, 1024), NULL);
+
+ if (workArea && workArea->type == XCB_ATOM_CARDINAL && workArea->format == 32 && workArea->value_len >= 4) {
+ // If workArea->value_len > 4, the remaining ones seem to be for virtual desktops.
+ // But QScreen doesn't know about that concept. In reality there could be a
+ // "docked" panel (with _NET_WM_STRUT_PARTIAL atom set) on just one desktop.
+ // But for now just assume the first 4 values give us the geometry of the
+ // "work area", AKA "available geometry"
+ uint32_t *geom = (uint32_t*)xcb_get_property_value(workArea);
+ QRect virtualAvailableGeometry(geom[0], geom[1], geom[2], geom[3]);
+ // Take the intersection of the desktop's available geometry with this screen's geometry
+ // to get the part of the available geometry which belongs to this screen.
+ m_availableGeometry = m_geometry & virtualAvailableGeometry;
+ }
+ free(workArea);
}
void QXcbScreen::updateRefreshRate()
@@ -267,11 +367,6 @@ void QXcbScreen::updateRefreshRate()
QWindowSystemInterface::handleScreenRefreshRateChange(QPlatformScreen::screen(), rate);
}
-int QXcbScreen::screenNumber() const
-{
- return m_number;
-}
-
QPixmap QXcbScreen::grabWindow(WId window, int x, int y, int width, int height) const
{
if (width == 0 || height == 0)
@@ -369,13 +464,4 @@ QPixmap QXcbScreen::grabWindow(WId window, int x, int y, int width, int height)
return result;
}
-QString QXcbScreen::name() const
-{
- QByteArray displayName = connection()->displayName();
- int dotPos = displayName.lastIndexOf('.');
- if (dotPos != -1)
- displayName.truncate(dotPos);
- return displayName + QLatin1Char('.') + QString::number(screenNumber());
-}
-
QT_END_NAMESPACE