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