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)
70 if (connection->hasXRandr())
71 xcb_randr_select_input(xcb_connection(), screen()->root, true);
73 updateGeometry(output ? output->timestamp : 0);
76 // On VNC, it can be that physical size is unknown while
77 // virtual size is known (probably back-calculated from DPI and resolution)
78 if (m_sizeMillimeters.isEmpty())
79 m_sizeMillimeters = m_virtualSizeMillimeters;
80 if (m_geometry.isEmpty())
81 m_geometry = QRect(QPoint(), m_virtualSize);
82 if (m_availableGeometry.isEmpty())
83 m_availableGeometry = QRect(QPoint(), m_virtualSize);
87 qDebug("Screen output %s of xcb screen %d:", m_outputName.toUtf8().constData(), m_number);
88 qDebug(" width..........: %lf", m_sizeMillimeters.width());
89 qDebug(" height.........: %lf", m_sizeMillimeters.height());
90 qDebug(" geometry.......: %d x %d +%d +%d", m_geometry.width(), m_geometry.height(), m_geometry.x(), m_geometry.y());
91 qDebug(" virtual width..: %lf", m_virtualSizeMillimeters.width());
92 qDebug(" virtual height.: %lf", m_virtualSizeMillimeters.height());
93 qDebug(" virtual geom...: %d x %d", m_virtualSize.width(), m_virtualSize.height());
94 qDebug(" avail virt geom: %d x %d +%d +%d", m_availableGeometry.width(), m_availableGeometry.height(), m_availableGeometry.x(), m_availableGeometry.y());
95 qDebug(" depth..........: %d", screen()->root_depth);
96 qDebug(" white pixel....: %x", screen()->white_pixel);
97 qDebug(" black pixel....: %x", screen()->black_pixel);
98 qDebug(" refresh rate...: %d", m_refreshRate);
99 qDebug(" root ID........: %x", screen()->root);
102 const quint32 mask = XCB_CW_EVENT_MASK;
103 const quint32 values[] = {
105 XCB_EVENT_MASK_ENTER_WINDOW
106 | XCB_EVENT_MASK_LEAVE_WINDOW
107 | XCB_EVENT_MASK_PROPERTY_CHANGE
110 xcb_change_window_attributes(xcb_connection(), screen()->root, mask, values);
112 xcb_get_property_reply_t *reply =
113 xcb_get_property_reply(xcb_connection(),
114 xcb_get_property_unchecked(xcb_connection(), false, screen()->root,
115 atom(QXcbAtom::_NET_SUPPORTING_WM_CHECK),
116 XCB_ATOM_WINDOW, 0, 1024), NULL);
118 if (reply && reply->format == 32 && reply->type == XCB_ATOM_WINDOW) {
119 xcb_window_t windowManager = *((xcb_window_t *)xcb_get_property_value(reply));
121 if (windowManager != XCB_WINDOW_NONE) {
122 xcb_get_property_reply_t *windowManagerReply =
123 xcb_get_property_reply(xcb_connection(),
124 xcb_get_property_unchecked(xcb_connection(), false, windowManager,
125 atom(QXcbAtom::_NET_WM_NAME),
126 atom(QXcbAtom::UTF8_STRING), 0, 1024), NULL);
127 if (windowManagerReply && windowManagerReply->format == 8 && windowManagerReply->type == atom(QXcbAtom::UTF8_STRING)) {
128 m_windowManagerName = QString::fromUtf8((const char *)xcb_get_property_value(windowManagerReply), xcb_get_property_value_length(windowManagerReply));
130 qDebug(" window manager.: %s", qPrintable(m_windowManagerName));
135 free(windowManagerReply);
140 const xcb_query_extension_reply_t *sync_reply = xcb_get_extension_data(xcb_connection(), &xcb_sync_id);
141 if (!sync_reply || !sync_reply->present)
142 m_syncRequestSupported = false;
144 m_syncRequestSupported = m_windowManagerName != QLatin1String("KWin");
146 m_clientLeader = xcb_generate_id(xcb_connection());
147 Q_XCB_CALL2(xcb_create_window(xcb_connection(),
148 XCB_COPY_FROM_PARENT,
153 XCB_WINDOW_CLASS_INPUT_OUTPUT,
154 screen()->root_visual,
157 Q_XCB_CALL2(xcb_change_property(xcb_connection(),
158 XCB_PROP_MODE_REPLACE,
160 atom(QXcbAtom::WM_CLIENT_LEADER),
164 &m_clientLeader), connection);
166 xcb_depth_iterator_t depth_iterator =
167 xcb_screen_allowed_depths_iterator(screen());
169 while (depth_iterator.rem) {
170 xcb_depth_t *depth = depth_iterator.data;
171 xcb_visualtype_iterator_t visualtype_iterator =
172 xcb_depth_visuals_iterator(depth);
174 while (visualtype_iterator.rem) {
175 xcb_visualtype_t *visualtype = visualtype_iterator.data;
176 m_visuals.insert(visualtype->visual_id, *visualtype);
177 xcb_visualtype_next(&visualtype_iterator);
180 xcb_depth_next(&depth_iterator);
183 m_cursor = new QXcbCursor(connection, this);
186 QXcbScreen::~QXcbScreen()
192 QWindow *QXcbScreen::topLevelAt(const QPoint &p) const
194 xcb_window_t root = m_screen->root;
199 xcb_window_t parent = root;
200 xcb_window_t child = root;
203 xcb_translate_coordinates_cookie_t translate_cookie =
204 xcb_translate_coordinates_unchecked(xcb_connection(), parent, child, x, y);
206 xcb_translate_coordinates_reply_t *translate_reply =
207 xcb_translate_coordinates_reply(xcb_connection(), translate_cookie, NULL);
209 if (!translate_reply) {
214 child = translate_reply->child;
215 x = translate_reply->dst_x;
216 y = translate_reply->dst_y;
218 free(translate_reply);
220 if (!child || child == root)
223 QPlatformWindow *platformWindow = connection()->platformWindowFromId(child);
225 return platformWindow->window();
226 } while (parent != child);
231 const xcb_visualtype_t *QXcbScreen::visualForId(xcb_visualid_t visualid) const
233 QMap<xcb_visualid_t, xcb_visualtype_t>::const_iterator it = m_visuals.find(visualid);
234 if (it == m_visuals.constEnd())
239 QImage::Format QXcbScreen::format() const
241 return QImage::Format_RGB32;
244 QDpi QXcbScreen::logicalDpi() const
246 return QDpi(Q_MM_PER_INCH * m_virtualSize.width() / m_virtualSizeMillimeters.width(),
247 Q_MM_PER_INCH * m_virtualSize.height() / m_virtualSizeMillimeters.height());
250 QPlatformCursor *QXcbScreen::cursor() const
256 \brief handle the XCB screen change event and update properties
258 On a mobile device, the ideal use case is that the accelerometer would
259 drive the orientation. This could be achieved by using QSensors to read the
260 accelerometer and adjusting the rotation in QML, or by reading the
261 orientation from the QScreen object and doing the same, or in many other
262 ways. However, on X we have the XRandR extension, which makes it possible
263 to have the whole screen rotated, so that individual apps DO NOT have to
264 rotate themselves. Apps could optionally use the
265 QScreen::primaryOrientation property to optimize layout though.
266 Furthermore, there is no support in X for accelerometer events anyway. So
267 it makes more sense on a Linux system running X to just run a daemon which
268 monitors the accelerometer and runs xrandr automatically to do the rotation,
269 then apps do not have to be aware of it (but probably the window manager
270 would resize them accordingly). updateGeometry() is written with this
271 design in mind. Therefore the physical geometry, available geometry,
272 virtual geometry, orientation and primaryOrientation should all change at
273 the same time. On a system which cannot rotate the whole screen, it would
274 be correct for only the orientation (not the primary orientation) to
277 void QXcbScreen::handleScreenChange(xcb_randr_screen_change_notify_event_t *change_event)
279 updateGeometry(change_event->config_timestamp);
281 switch (change_event->rotation) {
282 case XCB_RANDR_ROTATION_ROTATE_0: // xrandr --rotate normal
283 m_orientation = Qt::LandscapeOrientation;
284 m_virtualSize.setWidth(change_event->width);
285 m_virtualSize.setHeight(change_event->height);
286 m_virtualSizeMillimeters.setWidth(change_event->mwidth);
287 m_virtualSizeMillimeters.setHeight(change_event->mheight);
289 case XCB_RANDR_ROTATION_ROTATE_90: // xrandr --rotate left
290 m_orientation = Qt::PortraitOrientation;
291 m_virtualSize.setWidth(change_event->height);
292 m_virtualSize.setHeight(change_event->width);
293 m_virtualSizeMillimeters.setWidth(change_event->mheight);
294 m_virtualSizeMillimeters.setHeight(change_event->mwidth);
296 case XCB_RANDR_ROTATION_ROTATE_180: // xrandr --rotate inverted
297 m_orientation = Qt::InvertedLandscapeOrientation;
298 m_virtualSize.setWidth(change_event->width);
299 m_virtualSize.setHeight(change_event->height);
300 m_virtualSizeMillimeters.setWidth(change_event->mwidth);
301 m_virtualSizeMillimeters.setHeight(change_event->mheight);
303 case XCB_RANDR_ROTATION_ROTATE_270: // xrandr --rotate right
304 m_orientation = Qt::InvertedPortraitOrientation;
305 m_virtualSize.setWidth(change_event->height);
306 m_virtualSize.setHeight(change_event->width);
307 m_virtualSizeMillimeters.setWidth(change_event->mheight);
308 m_virtualSizeMillimeters.setHeight(change_event->mwidth);
310 // We don't need to do anything with these, since QScreen doesn't store reflection state,
311 // and Qt-based applications probably don't need to care about it anyway.
312 case XCB_RANDR_ROTATION_REFLECT_X: break;
313 case XCB_RANDR_ROTATION_REFLECT_Y: break;
316 QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), geometry());
317 QWindowSystemInterface::handleScreenAvailableGeometryChange(QPlatformScreen::screen(), availableGeometry());
318 QWindowSystemInterface::handleScreenOrientationChange(QPlatformScreen::screen(), m_orientation);
319 QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(QPlatformScreen::screen(),
320 Q_MM_PER_INCH * m_virtualSize.width() / m_virtualSizeMillimeters.width(),
321 Q_MM_PER_INCH * m_virtualSize.height() / m_virtualSizeMillimeters.height());
324 void QXcbScreen::updateGeometry(xcb_timestamp_t timestamp)
326 if (connection()->hasXRandr()) {
327 xcb_randr_get_crtc_info_reply_t *crtc = xcb_randr_get_crtc_info_reply(xcb_connection(),
328 xcb_randr_get_crtc_info_unchecked(xcb_connection(), m_crtc, timestamp), NULL);
330 m_geometry = QRect(crtc->x, crtc->y, crtc->width, crtc->height);
331 m_availableGeometry = m_geometry;
336 xcb_get_property_reply_t * workArea =
337 xcb_get_property_reply(xcb_connection(),
338 xcb_get_property_unchecked(xcb_connection(), false, screen()->root,
339 atom(QXcbAtom::_NET_WORKAREA),
340 XCB_ATOM_CARDINAL, 0, 1024), NULL);
342 if (workArea && workArea->type == XCB_ATOM_CARDINAL && workArea->format == 32 && workArea->value_len >= 4) {
343 // If workArea->value_len > 4, the remaining ones seem to be for virtual desktops.
344 // But QScreen doesn't know about that concept. In reality there could be a
345 // "docked" panel (with _NET_WM_STRUT_PARTIAL atom set) on just one desktop.
346 // But for now just assume the first 4 values give us the geometry of the
347 // "work area", AKA "available geometry"
348 uint32_t *geom = (uint32_t*)xcb_get_property_value(workArea);
349 QRect virtualAvailableGeometry(geom[0], geom[1], geom[2], geom[3]);
350 // Take the intersection of the desktop's available geometry with this screen's geometry
351 // to get the part of the available geometry which belongs to this screen.
352 m_availableGeometry = m_geometry & virtualAvailableGeometry;
357 void QXcbScreen::updateRefreshRate()
359 if (!connection()->hasXRandr())
362 int rate = m_refreshRate;
364 xcb_randr_get_screen_info_reply_t *screenInfoReply =
365 xcb_randr_get_screen_info_reply(xcb_connection(), xcb_randr_get_screen_info_unchecked(xcb_connection(), m_screen->root), 0);
367 if (screenInfoReply) {
368 rate = screenInfoReply->rate;
369 free(screenInfoReply);
372 if (rate == m_refreshRate)
375 m_refreshRate = rate;
377 QWindowSystemInterface::handleScreenRefreshRateChange(QPlatformScreen::screen(), rate);
380 QPixmap QXcbScreen::grabWindow(WId window, int x, int y, int width, int height) const
382 if (width == 0 || height == 0)
385 // TODO: handle multiple screens
386 QXcbScreen *screen = const_cast<QXcbScreen *>(this);
387 xcb_window_t root = screen->root();
392 xcb_get_geometry_cookie_t geometry_cookie = xcb_get_geometry_unchecked(xcb_connection(), window);
394 xcb_get_geometry_reply_t *reply =
395 xcb_get_geometry_reply(xcb_connection(), geometry_cookie, NULL);
402 width = reply->width - x;
404 height = reply->height - y;
406 geometry_cookie = xcb_get_geometry_unchecked(xcb_connection(), root);
407 xcb_get_geometry_reply_t *root_reply =
408 xcb_get_geometry_reply(xcb_connection(), geometry_cookie, NULL);
415 if (reply->depth == root_reply->depth) {
416 // if the depth of the specified window and the root window are the
417 // same, grab pixels from the root window (so that we get the any
418 // overlapping windows and window manager frames)
420 // map x and y to the root window
421 xcb_translate_coordinates_cookie_t translate_cookie =
422 xcb_translate_coordinates_unchecked(xcb_connection(), window, root, x, y);
424 xcb_translate_coordinates_reply_t *translate_reply =
425 xcb_translate_coordinates_reply(xcb_connection(), translate_cookie, NULL);
427 if (!translate_reply) {
433 x = translate_reply->dst_x;
434 y = translate_reply->dst_y;
438 free(translate_reply);
446 xcb_get_window_attributes_reply_t *attributes_reply =
447 xcb_get_window_attributes_reply(xcb_connection(), xcb_get_window_attributes_unchecked(xcb_connection(), window), NULL);
449 if (!attributes_reply) {
454 const xcb_visualtype_t *visual = screen->visualForId(attributes_reply->visual);
455 free(attributes_reply);
457 xcb_pixmap_t pixmap = xcb_generate_id(xcb_connection());
458 xcb_create_pixmap(xcb_connection(), reply->depth, pixmap, window, width, height);
460 uint32_t gc_value_mask = XCB_GC_SUBWINDOW_MODE;
461 uint32_t gc_value_list[] = { XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS };
463 xcb_gcontext_t gc = xcb_generate_id(xcb_connection());
464 xcb_create_gc(xcb_connection(), gc, pixmap, gc_value_mask, gc_value_list);
466 xcb_copy_area(xcb_connection(), window, pixmap, gc, x, y, 0, 0, width, height);
468 QPixmap result = qt_xcb_pixmapFromXPixmap(connection(), pixmap, width, height, reply->depth, visual);
471 xcb_free_gc(xcb_connection(), gc);
472 xcb_free_pixmap(xcb_connection(), pixmap);