1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module 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 The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "qapplication.h"
43 #include "qdesktopwidget.h"
44 #include "qlibrary.h"
45 #include "qt_x11_p.h"
46 #include "qvariant.h"
47 #include "qwidget_p.h"
48 #include "qx11info_x11.h"
49 #include <limits.h>
50 
51 QT_BEGIN_NAMESPACE
52 
53 // defined in qwidget_x11.cpp
54 extern int qt_x11_create_desktop_on_screen;
55 
56 
57 // function to update the workarea of the screen
58 static bool qt_desktopwidget_workarea_dirty = true;
qt_desktopwidget_update_workarea()59 void qt_desktopwidget_update_workarea()
60 {
61     qt_desktopwidget_workarea_dirty = true;
62 }
63 
64 
65 class QSingleDesktopWidget : public QWidget
66 {
67 public:
68     QSingleDesktopWidget();
69     ~QSingleDesktopWidget();
70 };
71 
QSingleDesktopWidget()72 QSingleDesktopWidget::QSingleDesktopWidget()
73     : QWidget(0, Qt::Desktop)
74 {
75 }
76 
~QSingleDesktopWidget()77 QSingleDesktopWidget::~QSingleDesktopWidget()
78 {
79     const QObjectList &childList = children();
80     for (int i = childList.size(); i > 0 ;) {
81         --i;
82         childList.at(i)->setParent(0);
83     }
84 }
85 
86 
87 class QDesktopWidgetPrivate : public QWidgetPrivate
88 {
89 public:
90     QDesktopWidgetPrivate();
91     ~QDesktopWidgetPrivate();
92 
93     void init();
94 
95     bool use_xinerama;
96     int defaultScreen;
97     int screenCount;
98 
99     QWidget **screens;
100     QRect *rects;
101     QRect *workareas;
102 };
103 
QDesktopWidgetPrivate()104 QDesktopWidgetPrivate::QDesktopWidgetPrivate()
105     : use_xinerama(false), defaultScreen(0), screenCount(1),
106       screens(0), rects(0), workareas(0)
107 {
108 }
109 
~QDesktopWidgetPrivate()110 QDesktopWidgetPrivate::~QDesktopWidgetPrivate()
111 {
112     if (screens) {
113         for (int i = 0; i < screenCount; ++i) {
114             if (i == defaultScreen) continue;
115             delete screens[i];
116             screens[i] = 0;
117         }
118 
119         free (screens);
120     }
121 
122     if (rects)     delete [] rects;
123     if (workareas) delete [] workareas;
124 }
125 
init()126 void QDesktopWidgetPrivate::init()
127 {
128     // get the screen count
129     int newScreenCount = ScreenCount(X11->display);
130 #ifndef QT_NO_XINERAMA
131 
132     XineramaScreenInfo *xinerama_screeninfo = 0;
133 
134     // we ignore the Xinerama extension when using the display is
135     // using traditional multi-screen (with multiple root windows)
136     if (newScreenCount == 1
137         && X11->ptrXineramaQueryExtension
138         && X11->ptrXineramaIsActive
139         && X11->ptrXineramaQueryScreens) {
140         int unused;
141         use_xinerama = (X11->ptrXineramaQueryExtension(X11->display, &unused, &unused)
142                         && X11->ptrXineramaIsActive(X11->display));
143     }
144 
145     if (use_xinerama) {
146         xinerama_screeninfo =
147             X11->ptrXineramaQueryScreens(X11->display, &newScreenCount);
148     }
149 
150     if (xinerama_screeninfo) {
151         defaultScreen = 0;
152      } else
153 #endif // QT_NO_XINERAMA
154     {
155         defaultScreen = DefaultScreen(X11->display);
156         newScreenCount = ScreenCount(X11->display);
157         use_xinerama = false;
158     }
159 
160     delete [] rects;
161     rects     = new QRect[newScreenCount];
162     delete [] workareas;
163     workareas = new QRect[newScreenCount];
164 
165     // get the geometry of each screen
166     int i, j, x, y, w, h;
167     for (i = 0, j = 0; i < newScreenCount; i++, j++) {
168 
169 #ifndef QT_NO_XINERAMA
170         if (use_xinerama) {
171             x = xinerama_screeninfo[i].x_org;
172             y = xinerama_screeninfo[i].y_org;
173             w = xinerama_screeninfo[i].width;
174             h = xinerama_screeninfo[i].height;
175         } else
176 #endif // QT_NO_XINERAMA
177             {
178                 x = 0;
179                 y = 0;
180                 w = WidthOfScreen(ScreenOfDisplay(X11->display, i));
181                 h = HeightOfScreen(ScreenOfDisplay(X11->display, i));
182             }
183 
184         rects[j].setRect(x, y, w, h);
185 
186         if (use_xinerama && j > 0 && rects[j-1].intersects(rects[j])) {
187             // merge a "cloned" screen with the previous, hiding all crtcs
188             // that are currently showing a sub-rect of the previous screen
189             if ((rects[j].width()*rects[j].height()) >
190                 (rects[j-1].width()*rects[j-1].height()))
191                 rects[j-1] = rects[j];
192             j--;
193         }
194 
195         workareas[i] = QRect();
196     }
197 
198     if (screens) {
199         // leaks QWidget* pointers on purpose, can't delete them as pointer escapes
200         screens = q_check_ptr((QWidget**) realloc(screens, j * sizeof(QWidget*)));
201         if (j > screenCount)
202             memset(&screens[screenCount], 0, (j-screenCount) * sizeof(QWidget*));
203     }
204 
205     screenCount = j;
206 
207 #ifndef QT_NO_XINERAMA
208     if (use_xinerama && screenCount == 1)
209         use_xinerama = false;
210 
211     if (xinerama_screeninfo)
212         XFree(xinerama_screeninfo);
213 #endif // QT_NO_XINERAMA
214 
215 }
216 
217 // the QDesktopWidget itself will be created on the default screen
218 // as qt_x11_create_desktop_on_screen defaults to -1
QDesktopWidget()219 QDesktopWidget::QDesktopWidget()
220     : QWidget(*new QDesktopWidgetPrivate, 0, Qt::Desktop)
221 {
222     Q_D(QDesktopWidget);
223     d->init();
224 }
225 
~QDesktopWidget()226 QDesktopWidget::~QDesktopWidget()
227 {
228 }
229 
isVirtualDesktop() const230 bool QDesktopWidget::isVirtualDesktop() const
231 {
232     Q_D(const QDesktopWidget);
233     return d->use_xinerama;
234 }
235 
primaryScreen() const236 int QDesktopWidget::primaryScreen() const
237 {
238     Q_D(const QDesktopWidget);
239     return d->defaultScreen;
240 }
241 
numScreens() const242 int QDesktopWidget::numScreens() const
243 {
244     Q_D(const QDesktopWidget);
245     return d->screenCount;
246 }
247 
screen(int screen)248 QWidget *QDesktopWidget::screen(int screen)
249 {
250     Q_D(QDesktopWidget);
251     if (d->use_xinerama)
252         return this;
253 
254     if (screen < 0 || screen >= d->screenCount)
255         screen = d->defaultScreen;
256 
257     if (! d->screens) {
258         d->screens = (QWidget**) calloc( d->screenCount, sizeof(QWidget*));
259         d->screens[d->defaultScreen] = this;
260     }
261 
262     if (! d->screens[screen] ||               // not created yet
263         ! (d->screens[screen]->windowType() == Qt::Desktop)) { // reparented away
264         qt_x11_create_desktop_on_screen = screen;
265         d->screens[screen] = new QSingleDesktopWidget;
266         qt_x11_create_desktop_on_screen = -1;
267     }
268 
269     return d->screens[screen];
270 }
271 
availableGeometry(int screen) const272 const QRect QDesktopWidget::availableGeometry(int screen) const
273 {
274     Q_D(const QDesktopWidget);
275     if (qt_desktopwidget_workarea_dirty) {
276         // the workareas are dirty, invalidate them
277         for (int i = 0; i < d->screenCount; ++i)
278             d->workareas[i] = QRect();
279         qt_desktopwidget_workarea_dirty = false;
280     }
281 
282     if (screen < 0 || screen >= d->screenCount)
283         screen = d->defaultScreen;
284 
285     if (d->workareas[screen].isValid())
286         return d->workareas[screen];
287 
288     if (X11->isSupportedByWM(ATOM(_NET_WORKAREA))) {
289         int x11Screen = isVirtualDesktop() ? DefaultScreen(X11->display) : screen;
290 
291         Atom ret;
292         int format, e;
293         unsigned char *data = 0;
294         unsigned long nitems, after;
295 
296         e = XGetWindowProperty(X11->display,
297                                QX11Info::appRootWindow(x11Screen),
298                                ATOM(_NET_WORKAREA), 0, 4, False, XA_CARDINAL,
299                                &ret, &format, &nitems, &after, &data);
300 
301         QRect workArea;
302         if (e == Success && ret == XA_CARDINAL &&
303             format == 32 && nitems == 4) {
304             long *workarea = (long *) data;
305             workArea = QRect(workarea[0], workarea[1], workarea[2], workarea[3]);
306         } else {
307             workArea = screenGeometry(screen);
308         }
309 
310         if (isVirtualDesktop()) {
311             // intersect the workarea (which spawns all Xinerama screens) with the rect for the
312             // requested screen
313             workArea &= screenGeometry(screen);
314         }
315 
316         d->workareas[screen] = workArea;
317 
318         if (data)
319             XFree(data);
320     } else {
321         d->workareas[screen] = screenGeometry(screen);
322     }
323 
324     return d->workareas[screen];
325 }
326 
screenGeometry(int screen) const327 const QRect QDesktopWidget::screenGeometry(int screen) const
328 {
329     Q_D(const QDesktopWidget);
330     if (screen < 0 || screen >= d->screenCount)
331         screen = d->defaultScreen;
332 
333     return d->rects[screen];
334 }
335 
screenNumber(const QWidget * widget) const336 int QDesktopWidget::screenNumber(const QWidget *widget) const
337 {
338     Q_D(const QDesktopWidget);
339     if (!widget)
340         return d->defaultScreen;
341 
342 #ifndef QT_NO_XINERAMA
343     if (d->use_xinerama) {
344         // this is how we do it for xinerama
345         QRect frame = widget->frameGeometry();
346         if (!widget->isWindow())
347             frame.moveTopLeft(widget->mapToGlobal(QPoint(0, 0)));
348 
349         int maxSize = -1;
350         int maxScreen = -1;
351 
352         for (int i = 0; i < d->screenCount; ++i) {
353             QRect sect = d->rects[i].intersected(frame);
354             int size = sect.width() * sect.height();
355             if (size > maxSize && sect.width() > 0 && sect.height() > 0) {
356                 maxSize = size;
357                 maxScreen = i;
358             }
359         }
360         return maxScreen;
361     }
362 #endif // QT_NO_XINERAMA
363 
364     return widget->x11Info().screen();
365 }
366 
screenNumber(const QPoint & point) const367 int QDesktopWidget::screenNumber(const QPoint &point) const
368 {
369     Q_D(const QDesktopWidget);
370     int closestScreen = -1;
371     int shortestDistance = INT_MAX;
372     for (int i = 0; i < d->screenCount; ++i) {
373         int thisDistance = d->pointToRect(point, d->rects[i]);
374         if (thisDistance < shortestDistance) {
375             shortestDistance = thisDistance;
376             closestScreen = i;
377         }
378     }
379     return closestScreen;
380 }
381 
resizeEvent(QResizeEvent * event)382 void QDesktopWidget::resizeEvent(QResizeEvent *event)
383 {
384     Q_D(QDesktopWidget);
385     int oldScreenCount = d->screenCount;
386     QVector<QRect> oldRects(oldScreenCount);
387     for (int i = 0; i < oldScreenCount; ++i) {
388         oldRects[i] = d->rects[i];
389     }
390 
391     d->init();
392 
393     for (int i = 0; i < qMin(oldScreenCount, d->screenCount); ++i) {
394         if (oldRects.at(i) != d->rects[i])
395             emit resized(i);
396     }
397 
398     if (oldScreenCount != d->screenCount) {
399         emit screenCountChanged(d->screenCount);
400     }
401 
402     qt_desktopwidget_workarea_dirty = true;
403     QWidget::resizeEvent(event);
404 }
405 
406 QT_END_NAMESPACE
407