1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the plugins of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
42 #include "qxcbscreen.h"
43 #include "qxcbwindow.h"
44 #include "qxcbcursor.h"
45 #include "qxcbimage.h"
46 #include "qnamespace.h"
52 #include <qpa/qwindowsysteminterface.h>
53 #include <private/qmath_p.h>
57 QXcbScreen::QXcbScreen(QXcbConnection *connection, xcb_screen_t *scr,
58 xcb_randr_get_output_info_reply_t *output, QString outputName, int number)
59 : QXcbObject(connection)
61 , m_crtc(output ? output->crtc : 0)
62 , m_outputName(outputName)
63 , m_sizeMillimeters(output ? QSize(output->mm_width, output->mm_height) : QSize())
64 , m_virtualSize(scr->width_in_pixels, scr->height_in_pixels)
65 , m_virtualSizeMillimeters(scr->width_in_millimeters, scr->height_in_millimeters)
66 , m_orientation(Qt::PrimaryOrientation)
71 if (connection->hasXRandr())
72 xcb_randr_select_input(xcb_connection(), screen()->root, true);
74 updateGeometry(output ? output->timestamp : 0);
77 // On VNC, it can be that physical size is unknown while
78 // virtual size is known (probably back-calculated from DPI and resolution)
79 if (m_sizeMillimeters.isEmpty())
80 m_sizeMillimeters = m_virtualSizeMillimeters;
81 if (m_geometry.isEmpty())
82 m_geometry = QRect(QPoint(), m_virtualSize);
83 if (m_availableGeometry.isEmpty())
84 m_availableGeometry = QRect(QPoint(), m_virtualSize);
91 qDebug("Screen output %s of xcb screen %d:", m_outputName.toUtf8().constData(), m_number);
92 qDebug(" width..........: %lf", m_sizeMillimeters.width());
93 qDebug(" height.........: %lf", m_sizeMillimeters.height());
94 qDebug(" geometry.......: %d x %d +%d +%d", m_geometry.width(), m_geometry.height(), m_geometry.x(), m_geometry.y());
95 qDebug(" virtual width..: %lf", m_virtualSizeMillimeters.width());
96 qDebug(" virtual height.: %lf", m_virtualSizeMillimeters.height());
97 qDebug(" virtual geom...: %d x %d", m_virtualSize.width(), m_virtualSize.height());
98 qDebug(" avail virt geom: %d x %d +%d +%d", m_availableGeometry.width(), m_availableGeometry.height(), m_availableGeometry.x(), m_availableGeometry.y());
99 qDebug(" depth..........: %d", screen()->root_depth);
100 qDebug(" white pixel....: %x", screen()->white_pixel);
101 qDebug(" black pixel....: %x", screen()->black_pixel);
102 qDebug(" refresh rate...: %d", m_refreshRate);
103 qDebug(" root ID........: %x", screen()->root);
106 const quint32 mask = XCB_CW_EVENT_MASK;
107 const quint32 values[] = {
109 XCB_EVENT_MASK_ENTER_WINDOW
110 | XCB_EVENT_MASK_LEAVE_WINDOW
111 | XCB_EVENT_MASK_PROPERTY_CHANGE
114 xcb_change_window_attributes(xcb_connection(), screen()->root, mask, values);
116 xcb_get_property_reply_t *reply =
117 xcb_get_property_reply(xcb_connection(),
118 xcb_get_property_unchecked(xcb_connection(), false, screen()->root,
119 atom(QXcbAtom::_NET_SUPPORTING_WM_CHECK),
120 XCB_ATOM_WINDOW, 0, 1024), NULL);
122 if (reply && reply->format == 32 && reply->type == XCB_ATOM_WINDOW) {
123 xcb_window_t windowManager = *((xcb_window_t *)xcb_get_property_value(reply));
125 if (windowManager != XCB_WINDOW_NONE) {
126 xcb_get_property_reply_t *windowManagerReply =
127 xcb_get_property_reply(xcb_connection(),
128 xcb_get_property_unchecked(xcb_connection(), false, windowManager,
129 atom(QXcbAtom::_NET_WM_NAME),
130 atom(QXcbAtom::UTF8_STRING), 0, 1024), NULL);
131 if (windowManagerReply && windowManagerReply->format == 8 && windowManagerReply->type == atom(QXcbAtom::UTF8_STRING)) {
132 m_windowManagerName = QString::fromUtf8((const char *)xcb_get_property_value(windowManagerReply), xcb_get_property_value_length(windowManagerReply));
134 qDebug(" window manager.: %s", qPrintable(m_windowManagerName));
139 free(windowManagerReply);
144 const xcb_query_extension_reply_t *sync_reply = xcb_get_extension_data(xcb_connection(), &xcb_sync_id);
145 if (!sync_reply || !sync_reply->present)
146 m_syncRequestSupported = false;
148 m_syncRequestSupported = m_windowManagerName != QLatin1String("KWin");
150 m_clientLeader = xcb_generate_id(xcb_connection());
151 Q_XCB_CALL2(xcb_create_window(xcb_connection(),
152 XCB_COPY_FROM_PARENT,
157 XCB_WINDOW_CLASS_INPUT_OUTPUT,
158 screen()->root_visual,
161 Q_XCB_CALL2(xcb_change_property(xcb_connection(),
162 XCB_PROP_MODE_REPLACE,
164 atom(QXcbAtom::WM_CLIENT_LEADER),
168 &m_clientLeader), connection);
170 xcb_depth_iterator_t depth_iterator =
171 xcb_screen_allowed_depths_iterator(screen());
173 while (depth_iterator.rem) {
174 xcb_depth_t *depth = depth_iterator.data;
175 xcb_visualtype_iterator_t visualtype_iterator =
176 xcb_depth_visuals_iterator(depth);
178 while (visualtype_iterator.rem) {
179 xcb_visualtype_t *visualtype = visualtype_iterator.data;
180 m_visuals.insert(visualtype->visual_id, *visualtype);
181 xcb_visualtype_next(&visualtype_iterator);
184 xcb_depth_next(&depth_iterator);
187 m_cursor = new QXcbCursor(connection, this);
190 QXcbScreen::~QXcbScreen()
196 QWindow *QXcbScreen::topLevelAt(const QPoint &p) const
198 xcb_window_t root = m_screen->root;
203 xcb_window_t parent = root;
204 xcb_window_t child = root;
207 xcb_translate_coordinates_cookie_t translate_cookie =
208 xcb_translate_coordinates_unchecked(xcb_connection(), parent, child, x, y);
210 xcb_translate_coordinates_reply_t *translate_reply =
211 xcb_translate_coordinates_reply(xcb_connection(), translate_cookie, NULL);
213 if (!translate_reply) {
218 child = translate_reply->child;
219 x = translate_reply->dst_x;
220 y = translate_reply->dst_y;
222 free(translate_reply);
224 if (!child || child == root)
227 QPlatformWindow *platformWindow = connection()->platformWindowFromId(child);
229 return platformWindow->window();
230 } while (parent != child);
235 const xcb_visualtype_t *QXcbScreen::visualForId(xcb_visualid_t visualid) const
237 QMap<xcb_visualid_t, xcb_visualtype_t>::const_iterator it = m_visuals.find(visualid);
238 if (it == m_visuals.constEnd())
243 QImage::Format QXcbScreen::format() const
245 return QImage::Format_RGB32;
248 QDpi QXcbScreen::logicalDpi() const
251 return QDpi(m_forcedDpi, m_forcedDpi);
253 return QDpi(Q_MM_PER_INCH * m_virtualSize.width() / m_virtualSizeMillimeters.width(),
254 Q_MM_PER_INCH * m_virtualSize.height() / m_virtualSizeMillimeters.height());
257 QPlatformCursor *QXcbScreen::cursor() const
263 \brief handle the XCB screen change event and update properties
265 On a mobile device, the ideal use case is that the accelerometer would
266 drive the orientation. This could be achieved by using QSensors to read the
267 accelerometer and adjusting the rotation in QML, or by reading the
268 orientation from the QScreen object and doing the same, or in many other
269 ways. However, on X we have the XRandR extension, which makes it possible
270 to have the whole screen rotated, so that individual apps DO NOT have to
271 rotate themselves. Apps could optionally use the
272 QScreen::primaryOrientation property to optimize layout though.
273 Furthermore, there is no support in X for accelerometer events anyway. So
274 it makes more sense on a Linux system running X to just run a daemon which
275 monitors the accelerometer and runs xrandr automatically to do the rotation,
276 then apps do not have to be aware of it (but probably the window manager
277 would resize them accordingly). updateGeometry() is written with this
278 design in mind. Therefore the physical geometry, available geometry,
279 virtual geometry, orientation and primaryOrientation should all change at
280 the same time. On a system which cannot rotate the whole screen, it would
281 be correct for only the orientation (not the primary orientation) to
284 void QXcbScreen::handleScreenChange(xcb_randr_screen_change_notify_event_t *change_event)
286 updateGeometry(change_event->config_timestamp);
288 switch (change_event->rotation) {
289 case XCB_RANDR_ROTATION_ROTATE_0: // xrandr --rotate normal
290 m_orientation = Qt::LandscapeOrientation;
291 m_virtualSize.setWidth(change_event->width);
292 m_virtualSize.setHeight(change_event->height);
293 m_virtualSizeMillimeters.setWidth(change_event->mwidth);
294 m_virtualSizeMillimeters.setHeight(change_event->mheight);
296 case XCB_RANDR_ROTATION_ROTATE_90: // xrandr --rotate left
297 m_orientation = Qt::PortraitOrientation;
298 m_virtualSize.setWidth(change_event->height);
299 m_virtualSize.setHeight(change_event->width);
300 m_virtualSizeMillimeters.setWidth(change_event->mheight);
301 m_virtualSizeMillimeters.setHeight(change_event->mwidth);
303 case XCB_RANDR_ROTATION_ROTATE_180: // xrandr --rotate inverted
304 m_orientation = Qt::InvertedLandscapeOrientation;
305 m_virtualSize.setWidth(change_event->width);
306 m_virtualSize.setHeight(change_event->height);
307 m_virtualSizeMillimeters.setWidth(change_event->mwidth);
308 m_virtualSizeMillimeters.setHeight(change_event->mheight);
310 case XCB_RANDR_ROTATION_ROTATE_270: // xrandr --rotate right
311 m_orientation = Qt::InvertedPortraitOrientation;
312 m_virtualSize.setWidth(change_event->height);
313 m_virtualSize.setHeight(change_event->width);
314 m_virtualSizeMillimeters.setWidth(change_event->mheight);
315 m_virtualSizeMillimeters.setHeight(change_event->mwidth);
317 // We don't need to do anything with these, since QScreen doesn't store reflection state,
318 // and Qt-based applications probably don't need to care about it anyway.
319 case XCB_RANDR_ROTATION_REFLECT_X: break;
320 case XCB_RANDR_ROTATION_REFLECT_Y: break;
323 QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), geometry());
324 QWindowSystemInterface::handleScreenAvailableGeometryChange(QPlatformScreen::screen(), availableGeometry());
325 QWindowSystemInterface::handleScreenOrientationChange(QPlatformScreen::screen(), m_orientation);
326 QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(QPlatformScreen::screen(),
327 Q_MM_PER_INCH * m_virtualSize.width() / m_virtualSizeMillimeters.width(),
328 Q_MM_PER_INCH * m_virtualSize.height() / m_virtualSizeMillimeters.height());
331 void QXcbScreen::updateGeometry(xcb_timestamp_t timestamp)
333 if (connection()->hasXRandr()) {
334 xcb_randr_get_crtc_info_reply_t *crtc = xcb_randr_get_crtc_info_reply(xcb_connection(),
335 xcb_randr_get_crtc_info_unchecked(xcb_connection(), m_crtc, timestamp), NULL);
337 m_geometry = QRect(crtc->x, crtc->y, crtc->width, crtc->height);
338 m_availableGeometry = m_geometry;
343 xcb_get_property_reply_t * workArea =
344 xcb_get_property_reply(xcb_connection(),
345 xcb_get_property_unchecked(xcb_connection(), false, screen()->root,
346 atom(QXcbAtom::_NET_WORKAREA),
347 XCB_ATOM_CARDINAL, 0, 1024), NULL);
349 if (workArea && workArea->type == XCB_ATOM_CARDINAL && workArea->format == 32 && workArea->value_len >= 4) {
350 // If workArea->value_len > 4, the remaining ones seem to be for virtual desktops.
351 // But QScreen doesn't know about that concept. In reality there could be a
352 // "docked" panel (with _NET_WM_STRUT_PARTIAL atom set) on just one desktop.
353 // But for now just assume the first 4 values give us the geometry of the
354 // "work area", AKA "available geometry"
355 uint32_t *geom = (uint32_t*)xcb_get_property_value(workArea);
356 QRect virtualAvailableGeometry(geom[0], geom[1], geom[2], geom[3]);
357 // Take the intersection of the desktop's available geometry with this screen's geometry
358 // to get the part of the available geometry which belongs to this screen.
359 m_availableGeometry = m_geometry & virtualAvailableGeometry;
364 void QXcbScreen::updateRefreshRate()
366 if (!connection()->hasXRandr())
369 int rate = m_refreshRate;
371 xcb_randr_get_screen_info_reply_t *screenInfoReply =
372 xcb_randr_get_screen_info_reply(xcb_connection(), xcb_randr_get_screen_info_unchecked(xcb_connection(), m_screen->root), 0);
374 if (screenInfoReply) {
375 rate = screenInfoReply->rate;
376 free(screenInfoReply);
379 if (rate == m_refreshRate)
382 m_refreshRate = rate;
384 QWindowSystemInterface::handleScreenRefreshRateChange(QPlatformScreen::screen(), rate);
387 QPixmap QXcbScreen::grabWindow(WId window, int x, int y, int width, int height) const
389 if (width == 0 || height == 0)
392 // TODO: handle multiple screens
393 QXcbScreen *screen = const_cast<QXcbScreen *>(this);
394 xcb_window_t root = screen->root();
399 xcb_get_geometry_cookie_t geometry_cookie = xcb_get_geometry_unchecked(xcb_connection(), window);
401 xcb_get_geometry_reply_t *reply =
402 xcb_get_geometry_reply(xcb_connection(), geometry_cookie, NULL);
409 width = reply->width - x;
411 height = reply->height - y;
413 geometry_cookie = xcb_get_geometry_unchecked(xcb_connection(), root);
414 xcb_get_geometry_reply_t *root_reply =
415 xcb_get_geometry_reply(xcb_connection(), geometry_cookie, NULL);
422 if (reply->depth == root_reply->depth) {
423 // if the depth of the specified window and the root window are the
424 // same, grab pixels from the root window (so that we get the any
425 // overlapping windows and window manager frames)
427 // map x and y to the root window
428 xcb_translate_coordinates_cookie_t translate_cookie =
429 xcb_translate_coordinates_unchecked(xcb_connection(), window, root, x, y);
431 xcb_translate_coordinates_reply_t *translate_reply =
432 xcb_translate_coordinates_reply(xcb_connection(), translate_cookie, NULL);
434 if (!translate_reply) {
440 x = translate_reply->dst_x;
441 y = translate_reply->dst_y;
445 free(translate_reply);
453 xcb_get_window_attributes_reply_t *attributes_reply =
454 xcb_get_window_attributes_reply(xcb_connection(), xcb_get_window_attributes_unchecked(xcb_connection(), window), NULL);
456 if (!attributes_reply) {
461 const xcb_visualtype_t *visual = screen->visualForId(attributes_reply->visual);
462 free(attributes_reply);
464 xcb_pixmap_t pixmap = xcb_generate_id(xcb_connection());
465 xcb_create_pixmap(xcb_connection(), reply->depth, pixmap, window, width, height);
467 uint32_t gc_value_mask = XCB_GC_SUBWINDOW_MODE;
468 uint32_t gc_value_list[] = { XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS };
470 xcb_gcontext_t gc = xcb_generate_id(xcb_connection());
471 xcb_create_gc(xcb_connection(), gc, pixmap, gc_value_mask, gc_value_list);
473 xcb_copy_area(xcb_connection(), window, pixmap, gc, x, y, 0, 0, width, height);
475 QPixmap result = qt_xcb_pixmapFromXPixmap(connection(), pixmap, width, height, reply->depth, visual);
478 xcb_free_gc(xcb_connection(), gc);
479 xcb_free_pixmap(xcb_connection(), pixmap);
484 void QXcbScreen::readXResources()
487 QByteArray resources;
489 xcb_get_property_reply_t *reply =
490 xcb_get_property_reply(xcb_connection(),
491 xcb_get_property_unchecked(xcb_connection(), false, screen()->root,
492 XCB_ATOM_RESOURCE_MANAGER,
493 XCB_ATOM_STRING, offset/4, 8192), NULL);
495 if (reply && reply->format == 8 && reply->type == XCB_ATOM_STRING) {
496 resources += QByteArray((const char *)xcb_get_property_value(reply), xcb_get_property_value_length(reply));
497 offset += xcb_get_property_value_length(reply);
498 more = reply->bytes_after != 0;
508 QList<QByteArray> split = resources.split('\n');
509 for (int i = 0; i < split.size(); ++i) {
510 const QByteArray &r = split.at(i);
511 if (r.startsWith("Xft.dpi:\t")) {
513 int dpi = r.mid(sizeof("Xft.dpi:")).toInt(&ok);