Update spec to build Qt 5.0
[profile/ivi/qtbase.git] / src / plugins / platforms / xcb / qxcbscreen.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
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.
16 **
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.
24 **
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.
28 **
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.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qxcbscreen.h"
43 #include "qxcbwindow.h"
44 #include "qxcbcursor.h"
45 #include "qxcbimage.h"
46 #include "qnamespace.h"
47
48 #include <stdio.h>
49
50 #include <QDebug>
51
52 #include <qpa/qwindowsysteminterface.h>
53 #include <private/qmath_p.h>
54
55 QT_BEGIN_NAMESPACE
56
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)
60     , m_screen(scr)
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)
67     , m_number(number)
68     , m_refreshRate(60)
69     , m_forcedDpi(-1)
70 {
71     if (connection->hasXRandr())
72         xcb_randr_select_input(xcb_connection(), screen()->root, true);
73
74     updateGeometry(output ? output->timestamp : 0);
75     updateRefreshRate();
76
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);
85
86     readXResources();
87
88
89 #ifdef Q_XCB_DEBUG
90     qDebug();
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);
104 #endif
105
106     const quint32 mask = XCB_CW_EVENT_MASK;
107     const quint32 values[] = {
108         // XCB_CW_EVENT_MASK
109         XCB_EVENT_MASK_ENTER_WINDOW
110         | XCB_EVENT_MASK_LEAVE_WINDOW
111         | XCB_EVENT_MASK_PROPERTY_CHANGE
112     };
113
114     xcb_change_window_attributes(xcb_connection(), screen()->root, mask, values);
115
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);
121
122     if (reply && reply->format == 32 && reply->type == XCB_ATOM_WINDOW) {
123         xcb_window_t windowManager = *((xcb_window_t *)xcb_get_property_value(reply));
124
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));
133 #ifdef Q_XCB_DEBUG
134                 qDebug("  window manager.: %s", qPrintable(m_windowManagerName));
135                 qDebug();
136 #endif
137             }
138
139             free(windowManagerReply);
140         }
141     }
142     free(reply);
143
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;
147     else
148         m_syncRequestSupported = m_windowManagerName != QLatin1String("KWin");
149
150     m_clientLeader = xcb_generate_id(xcb_connection());
151     Q_XCB_CALL2(xcb_create_window(xcb_connection(),
152                                   XCB_COPY_FROM_PARENT,
153                                   m_clientLeader,
154                                   screen()->root,
155                                   0, 0, 1, 1,
156                                   0,
157                                   XCB_WINDOW_CLASS_INPUT_OUTPUT,
158                                   screen()->root_visual,
159                                   0, 0), connection);
160
161     Q_XCB_CALL2(xcb_change_property(xcb_connection(),
162                                     XCB_PROP_MODE_REPLACE,
163                                     m_clientLeader,
164                                     atom(QXcbAtom::WM_CLIENT_LEADER),
165                                     XCB_ATOM_WINDOW,
166                                     32,
167                                     1,
168                                     &m_clientLeader), connection);
169
170     xcb_depth_iterator_t depth_iterator =
171         xcb_screen_allowed_depths_iterator(screen());
172
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);
177
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);
182         }
183
184         xcb_depth_next(&depth_iterator);
185     }
186
187     m_cursor = new QXcbCursor(connection, this);
188 }
189
190 QXcbScreen::~QXcbScreen()
191 {
192     delete m_cursor;
193 }
194
195
196 QWindow *QXcbScreen::topLevelAt(const QPoint &p) const
197 {
198     xcb_window_t root = m_screen->root;
199
200     int x = p.x();
201     int y = p.y();
202
203     xcb_window_t parent = root;
204     xcb_window_t child = root;
205
206     do {
207         xcb_translate_coordinates_cookie_t translate_cookie =
208             xcb_translate_coordinates_unchecked(xcb_connection(), parent, child, x, y);
209
210         xcb_translate_coordinates_reply_t *translate_reply =
211             xcb_translate_coordinates_reply(xcb_connection(), translate_cookie, NULL);
212
213         if (!translate_reply) {
214             return 0;
215         }
216
217         parent = child;
218         child = translate_reply->child;
219         x = translate_reply->dst_x;
220         y = translate_reply->dst_y;
221
222         free(translate_reply);
223
224         if (!child || child == root)
225             return 0;
226
227         QPlatformWindow *platformWindow = connection()->platformWindowFromId(child);
228         if (platformWindow)
229             return platformWindow->window();
230     } while (parent != child);
231
232     return 0;
233 }
234
235 const xcb_visualtype_t *QXcbScreen::visualForId(xcb_visualid_t visualid) const
236 {
237     QMap<xcb_visualid_t, xcb_visualtype_t>::const_iterator it = m_visuals.find(visualid);
238     if (it == m_visuals.constEnd())
239         return 0;
240     return &*it;
241 }
242
243 QImage::Format QXcbScreen::format() const
244 {
245     return QImage::Format_RGB32;
246 }
247
248 QDpi QXcbScreen::logicalDpi() const
249 {
250     if (m_forcedDpi > 0)
251         return QDpi(m_forcedDpi, m_forcedDpi);
252
253     return QDpi(Q_MM_PER_INCH * m_virtualSize.width() / m_virtualSizeMillimeters.width(),
254                 Q_MM_PER_INCH * m_virtualSize.height() / m_virtualSizeMillimeters.height());
255 }
256
257 QPlatformCursor *QXcbScreen::cursor() const
258 {
259     return m_cursor;
260 }
261
262 /*!
263     \brief handle the XCB screen change event and update properties
264
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
282     change.
283 */
284 void QXcbScreen::handleScreenChange(xcb_randr_screen_change_notify_event_t *change_event)
285 {
286     updateGeometry(change_event->config_timestamp);
287
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);
295         break;
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);
302         break;
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);
309         break;
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);
316         break;
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;
321     }
322
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());
329 }
330
331 void QXcbScreen::updateGeometry(xcb_timestamp_t timestamp)
332 {
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);
336         if (crtc) {
337             m_geometry = QRect(crtc->x, crtc->y, crtc->width, crtc->height);
338             m_availableGeometry = m_geometry;
339             free(crtc);
340         }
341     }
342
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);
348
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;
360     }
361     free(workArea);
362 }
363
364 void QXcbScreen::updateRefreshRate()
365 {
366     if (!connection()->hasXRandr())
367         return;
368
369     int rate = m_refreshRate;
370
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);
373
374     if (screenInfoReply) {
375         rate = screenInfoReply->rate;
376         free(screenInfoReply);
377     }
378
379     if (rate == m_refreshRate)
380         return;
381
382     m_refreshRate = rate;
383
384     QWindowSystemInterface::handleScreenRefreshRateChange(QPlatformScreen::screen(), rate);
385 }
386
387 QPixmap QXcbScreen::grabWindow(WId window, int x, int y, int width, int height) const
388 {
389     if (width == 0 || height == 0)
390         return QPixmap();
391
392     // TODO: handle multiple screens
393     QXcbScreen *screen = const_cast<QXcbScreen *>(this);
394     xcb_window_t root = screen->root();
395
396     if (window == 0)
397         window = root;
398
399     xcb_get_geometry_cookie_t geometry_cookie = xcb_get_geometry_unchecked(xcb_connection(), window);
400
401     xcb_get_geometry_reply_t *reply =
402         xcb_get_geometry_reply(xcb_connection(), geometry_cookie, NULL);
403
404     if (!reply) {
405         return QPixmap();
406     }
407
408     if (width < 0)
409         width = reply->width - x;
410     if (height < 0)
411         height = reply->height - y;
412
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);
416
417     if (!root_reply) {
418         free(reply);
419         return QPixmap();
420     }
421
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)
426
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);
430
431         xcb_translate_coordinates_reply_t *translate_reply =
432             xcb_translate_coordinates_reply(xcb_connection(), translate_cookie, NULL);
433
434         if (!translate_reply) {
435             free(reply);
436             free(root_reply);
437             return QPixmap();
438         }
439
440         x = translate_reply->dst_x;
441         y = translate_reply->dst_y;
442
443         window = root;
444
445         free(translate_reply);
446         free(reply);
447         reply = root_reply;
448     } else {
449         free(root_reply);
450         root_reply = 0;
451     }
452
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);
455
456     if (!attributes_reply) {
457         free(reply);
458         return QPixmap();
459     }
460
461     const xcb_visualtype_t *visual = screen->visualForId(attributes_reply->visual);
462     free(attributes_reply);
463
464     xcb_pixmap_t pixmap = xcb_generate_id(xcb_connection());
465     xcb_create_pixmap(xcb_connection(), reply->depth, pixmap, window, width, height);
466
467     uint32_t gc_value_mask = XCB_GC_SUBWINDOW_MODE;
468     uint32_t gc_value_list[] = { XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS };
469
470     xcb_gcontext_t gc = xcb_generate_id(xcb_connection());
471     xcb_create_gc(xcb_connection(), gc, pixmap, gc_value_mask, gc_value_list);
472
473     xcb_copy_area(xcb_connection(), window, pixmap, gc, x, y, 0, 0, width, height);
474
475     QPixmap result = qt_xcb_pixmapFromXPixmap(connection(), pixmap, width, height, reply->depth, visual);
476
477     free(reply);
478     xcb_free_gc(xcb_connection(), gc);
479     xcb_free_pixmap(xcb_connection(), pixmap);
480
481     return result;
482 }
483
484 void QXcbScreen::readXResources()
485 {
486     int offset = 0;
487     QByteArray resources;
488     while(1) {
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);
494         bool more = false;
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;
499         }
500
501         if (reply)
502             free(reply);
503
504         if (!more)
505             break;
506     }
507
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")) {
512             bool ok;
513             int dpi = r.mid(sizeof("Xft.dpi:")).toInt(&ok);
514             if (ok)
515                 m_forcedDpi = dpi;
516             break;
517         }
518     }
519 }
520
521 QT_END_NAMESPACE