Add PLUGIN_CLASS_NAME to qtbase plugins
[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 {
70     if (connection->hasXRandr())
71         xcb_randr_select_input(xcb_connection(), screen()->root, true);
72
73     updateGeometry(output ? output->timestamp : 0);
74     updateRefreshRate();
75
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);
84
85 #ifdef Q_XCB_DEBUG
86     qDebug();
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);
100 #endif
101
102     const quint32 mask = XCB_CW_EVENT_MASK;
103     const quint32 values[] = {
104         // XCB_CW_EVENT_MASK
105         XCB_EVENT_MASK_ENTER_WINDOW
106         | XCB_EVENT_MASK_LEAVE_WINDOW
107         | XCB_EVENT_MASK_PROPERTY_CHANGE
108     };
109
110     xcb_change_window_attributes(xcb_connection(), screen()->root, mask, values);
111
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);
117
118     if (reply && reply->format == 32 && reply->type == XCB_ATOM_WINDOW) {
119         xcb_window_t windowManager = *((xcb_window_t *)xcb_get_property_value(reply));
120
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));
129 #ifdef Q_XCB_DEBUG
130                 qDebug("  window manager.: %s", qPrintable(m_windowManagerName));
131                 qDebug();
132 #endif
133             }
134
135             free(windowManagerReply);
136         }
137     }
138     free(reply);
139
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;
143     else
144         m_syncRequestSupported = m_windowManagerName != QLatin1String("KWin");
145
146     m_clientLeader = xcb_generate_id(xcb_connection());
147     Q_XCB_CALL2(xcb_create_window(xcb_connection(),
148                                   XCB_COPY_FROM_PARENT,
149                                   m_clientLeader,
150                                   screen()->root,
151                                   0, 0, 1, 1,
152                                   0,
153                                   XCB_WINDOW_CLASS_INPUT_OUTPUT,
154                                   screen()->root_visual,
155                                   0, 0), connection);
156
157     Q_XCB_CALL2(xcb_change_property(xcb_connection(),
158                                     XCB_PROP_MODE_REPLACE,
159                                     m_clientLeader,
160                                     atom(QXcbAtom::WM_CLIENT_LEADER),
161                                     XCB_ATOM_WINDOW,
162                                     32,
163                                     1,
164                                     &m_clientLeader), connection);
165
166     xcb_depth_iterator_t depth_iterator =
167         xcb_screen_allowed_depths_iterator(screen());
168
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);
173
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);
178         }
179
180         xcb_depth_next(&depth_iterator);
181     }
182
183     m_cursor = new QXcbCursor(connection, this);
184 }
185
186 QXcbScreen::~QXcbScreen()
187 {
188     delete m_cursor;
189 }
190
191
192 QWindow *QXcbScreen::topLevelAt(const QPoint &p) const
193 {
194     xcb_window_t root = m_screen->root;
195
196     int x = p.x();
197     int y = p.y();
198
199     xcb_window_t parent = root;
200     xcb_window_t child = root;
201
202     do {
203         xcb_translate_coordinates_cookie_t translate_cookie =
204             xcb_translate_coordinates_unchecked(xcb_connection(), parent, child, x, y);
205
206         xcb_translate_coordinates_reply_t *translate_reply =
207             xcb_translate_coordinates_reply(xcb_connection(), translate_cookie, NULL);
208
209         if (!translate_reply) {
210             return 0;
211         }
212
213         parent = child;
214         child = translate_reply->child;
215         x = translate_reply->dst_x;
216         y = translate_reply->dst_y;
217
218         free(translate_reply);
219
220         if (!child || child == root)
221             return 0;
222
223         QPlatformWindow *platformWindow = connection()->platformWindowFromId(child);
224         if (platformWindow)
225             return platformWindow->window();
226     } while (parent != child);
227
228     return 0;
229 }
230
231 const xcb_visualtype_t *QXcbScreen::visualForId(xcb_visualid_t visualid) const
232 {
233     QMap<xcb_visualid_t, xcb_visualtype_t>::const_iterator it = m_visuals.find(visualid);
234     if (it == m_visuals.constEnd())
235         return 0;
236     return &*it;
237 }
238
239 QImage::Format QXcbScreen::format() const
240 {
241     return QImage::Format_RGB32;
242 }
243
244 QDpi QXcbScreen::logicalDpi() const
245 {
246     return QDpi(Q_MM_PER_INCH * m_virtualSize.width() / m_virtualSizeMillimeters.width(),
247                 Q_MM_PER_INCH * m_virtualSize.height() / m_virtualSizeMillimeters.height());
248 }
249
250 QPlatformCursor *QXcbScreen::cursor() const
251 {
252     return m_cursor;
253 }
254
255 /*!
256     \brief handle the XCB screen change event and update properties
257
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
275     change.
276 */
277 void QXcbScreen::handleScreenChange(xcb_randr_screen_change_notify_event_t *change_event)
278 {
279     updateGeometry(change_event->config_timestamp);
280
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);
288         break;
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);
295         break;
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);
302         break;
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);
309         break;
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;
314     }
315
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());
322 }
323
324 void QXcbScreen::updateGeometry(xcb_timestamp_t timestamp)
325 {
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);
329         if (crtc) {
330             m_geometry = QRect(crtc->x, crtc->y, crtc->width, crtc->height);
331             m_availableGeometry = m_geometry;
332             free(crtc);
333         }
334     }
335
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);
341
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;
353     }
354     free(workArea);
355 }
356
357 void QXcbScreen::updateRefreshRate()
358 {
359     if (!connection()->hasXRandr())
360         return;
361
362     int rate = m_refreshRate;
363
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);
366
367     if (screenInfoReply) {
368         rate = screenInfoReply->rate;
369         free(screenInfoReply);
370     }
371
372     if (rate == m_refreshRate)
373         return;
374
375     m_refreshRate = rate;
376
377     QWindowSystemInterface::handleScreenRefreshRateChange(QPlatformScreen::screen(), rate);
378 }
379
380 QPixmap QXcbScreen::grabWindow(WId window, int x, int y, int width, int height) const
381 {
382     if (width == 0 || height == 0)
383         return QPixmap();
384
385     // TODO: handle multiple screens
386     QXcbScreen *screen = const_cast<QXcbScreen *>(this);
387     xcb_window_t root = screen->root();
388
389     if (window == 0)
390         window = root;
391
392     xcb_get_geometry_cookie_t geometry_cookie = xcb_get_geometry_unchecked(xcb_connection(), window);
393
394     xcb_get_geometry_reply_t *reply =
395         xcb_get_geometry_reply(xcb_connection(), geometry_cookie, NULL);
396
397     if (!reply) {
398         return QPixmap();
399     }
400
401     if (width < 0)
402         width = reply->width - x;
403     if (height < 0)
404         height = reply->height - y;
405
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);
409
410     if (!root_reply) {
411         free(reply);
412         return QPixmap();
413     }
414
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)
419
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);
423
424         xcb_translate_coordinates_reply_t *translate_reply =
425             xcb_translate_coordinates_reply(xcb_connection(), translate_cookie, NULL);
426
427         if (!translate_reply) {
428             free(reply);
429             free(root_reply);
430             return QPixmap();
431         }
432
433         x = translate_reply->dst_x;
434         y = translate_reply->dst_y;
435
436         window = root;
437
438         free(translate_reply);
439         free(reply);
440         reply = root_reply;
441     } else {
442         free(root_reply);
443         root_reply = 0;
444     }
445
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);
448
449     if (!attributes_reply) {
450         free(reply);
451         return QPixmap();
452     }
453
454     const xcb_visualtype_t *visual = screen->visualForId(attributes_reply->visual);
455     free(attributes_reply);
456
457     xcb_pixmap_t pixmap = xcb_generate_id(xcb_connection());
458     xcb_create_pixmap(xcb_connection(), reply->depth, pixmap, window, width, height);
459
460     uint32_t gc_value_mask = XCB_GC_SUBWINDOW_MODE;
461     uint32_t gc_value_list[] = { XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS };
462
463     xcb_gcontext_t gc = xcb_generate_id(xcb_connection());
464     xcb_create_gc(xcb_connection(), gc, pixmap, gc_value_mask, gc_value_list);
465
466     xcb_copy_area(xcb_connection(), window, pixmap, gc, x, y, 0, 0, width, height);
467
468     QPixmap result = qt_xcb_pixmapFromXPixmap(connection(), pixmap, width, height, reply->depth, visual);
469
470     free(reply);
471     xcb_free_gc(xcb_connection(), gc);
472     xcb_free_pixmap(xcb_connection(), pixmap);
473
474     return result;
475 }
476
477 QT_END_NAMESPACE