1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtWidgets 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 https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://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 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qglobal.h" |
41 | #include "qdesktopwidget.h" |
42 | #include "qdesktopwidget_p.h" |
43 | #include "qscreen.h" |
44 | #include "qwidget_p.h" |
45 | #include "qwindow.h" |
46 | |
47 | #include <private/qhighdpiscaling_p.h> |
48 | #include <qpa/qplatformscreen.h> |
49 | |
50 | QT_BEGIN_NAMESPACE |
51 | |
52 | QDesktopScreenWidget::QDesktopScreenWidget(QScreen *screen, const QRect &geometry) |
53 | : QWidget(nullptr, Qt::Desktop), m_screen(screen) |
54 | { |
55 | setVisible(false); |
56 | if (QWindow *winHandle = windowHandle()) |
57 | winHandle->setScreen(screen); |
58 | setScreenGeometry(geometry); |
59 | } |
60 | |
61 | void QDesktopScreenWidget::setScreenGeometry(const QRect &geometry) |
62 | { |
63 | m_geometry = geometry; |
64 | setGeometry(geometry); |
65 | } |
66 | |
67 | int QDesktopScreenWidget::screenNumber() const |
68 | { |
69 | const QDesktopWidgetPrivate *desktopWidgetP |
70 | = static_cast<const QDesktopWidgetPrivate *>(qt_widget_private(widget: QApplication::desktop())); |
71 | return desktopWidgetP->screens.indexOf(t: const_cast<QDesktopScreenWidget *>(this)); |
72 | } |
73 | |
74 | const QRect QDesktopWidget::screenGeometry(const QWidget *widget) const |
75 | { |
76 | return QDesktopWidgetPrivate::screenGeometry(widget); |
77 | } |
78 | |
79 | const QRect QDesktopWidgetPrivate::screenGeometry(const QWidget *widget) |
80 | { |
81 | if (Q_UNLIKELY(!widget)) { |
82 | qWarning(msg: "QDesktopWidget::screenGeometry(): Attempt " |
83 | "to get the screen geometry of a null widget" ); |
84 | return QRect(); |
85 | } |
86 | QRect rect = QWidgetPrivate::screenGeometry(widget); |
87 | if (rect.isNull()) |
88 | return screenGeometry(screen: screenNumber(widget)); |
89 | else return rect; |
90 | } |
91 | |
92 | const QRect QDesktopWidget::availableGeometry(const QWidget *widget) const |
93 | { |
94 | return QDesktopWidgetPrivate::availableGeometry(widget); |
95 | } |
96 | |
97 | const QRect QDesktopWidgetPrivate::availableGeometry(const QWidget *widget) |
98 | { |
99 | if (Q_UNLIKELY(!widget)) { |
100 | qWarning(msg: "QDesktopWidget::availableGeometry(): Attempt " |
101 | "to get the available geometry of a null widget" ); |
102 | return QRect(); |
103 | } |
104 | QRect rect = QWidgetPrivate::screenGeometry(widget); |
105 | if (rect.isNull()) |
106 | return availableGeometry(screen: screenNumber(widget)); |
107 | else |
108 | return rect; |
109 | } |
110 | |
111 | QDesktopScreenWidget *QDesktopWidgetPrivate::widgetForScreen(QScreen *qScreen) const |
112 | { |
113 | foreach (QDesktopScreenWidget *widget, screens) { |
114 | if (widget->assignedScreen() == qScreen) |
115 | return widget; |
116 | } |
117 | return nullptr; |
118 | } |
119 | |
120 | void QDesktopWidgetPrivate::_q_updateScreens() |
121 | { |
122 | Q_Q(QDesktopWidget); |
123 | const QList<QScreen *> screenList = QGuiApplication::screens(); |
124 | const int targetLength = screenList.length(); |
125 | bool screenCountChanged = false; |
126 | |
127 | // Re-build our screens list. This is the easiest way to later compute which signals to emit. |
128 | // Create new screen widgets as necessary. While iterating, keep the old list in place so |
129 | // that widgetForScreen works. |
130 | // Furthermore, we note which screens have changed, and compute the overall virtual geometry. |
131 | QList<QDesktopScreenWidget *> newScreens; |
132 | QList<int> changedScreens; |
133 | QRegion virtualGeometry; |
134 | |
135 | for (int i = 0; i < targetLength; ++i) { |
136 | QScreen *qScreen = screenList.at(i); |
137 | const QRect screenGeometry = qScreen->geometry(); |
138 | QDesktopScreenWidget *screenWidget = widgetForScreen(qScreen); |
139 | if (screenWidget) { |
140 | // an old screen. update geometry and remember the index in the *new* list |
141 | if (screenGeometry != screenWidget->screenGeometry()) { |
142 | screenWidget->setScreenGeometry(screenGeometry); |
143 | changedScreens.push_back(t: i); |
144 | } |
145 | } else { |
146 | // a new screen, create a widget and connect the signals. |
147 | screenWidget = new QDesktopScreenWidget(qScreen, screenGeometry); |
148 | QObject::connect(sender: qScreen, SIGNAL(geometryChanged(QRect)), |
149 | receiver: q, SLOT(_q_updateScreens()), Qt::QueuedConnection); |
150 | QObject::connect(sender: qScreen, SIGNAL(availableGeometryChanged(QRect)), |
151 | receiver: q, SLOT(_q_availableGeometryChanged()), Qt::QueuedConnection); |
152 | QObject::connect(sender: qScreen, SIGNAL(destroyed()), |
153 | receiver: q, SLOT(_q_updateScreens()), Qt::QueuedConnection); |
154 | screenCountChanged = true; |
155 | } |
156 | // record all the screens and the overall geometry. |
157 | newScreens.push_back(t: screenWidget); |
158 | virtualGeometry += screenGeometry; |
159 | } |
160 | |
161 | // Now we apply the accumulated updates. |
162 | screens.swap(other&: newScreens); // now [newScreens] is the old screen list |
163 | Q_ASSERT(screens.size() == targetLength); |
164 | q->setGeometry(virtualGeometry.boundingRect()); |
165 | |
166 | // Delete the QDesktopScreenWidget that are not used any more. |
167 | foreach (QDesktopScreenWidget *screen, newScreens) { |
168 | if (!screens.contains(t: screen)) { |
169 | delete screen; |
170 | screenCountChanged = true; |
171 | } |
172 | } |
173 | |
174 | // Finally, emit the signals. |
175 | if (screenCountChanged) { |
176 | // Notice that we trigger screenCountChanged even if a screen was removed and another one added, |
177 | // in which case the total number of screens did not change. This is the only way for applications |
178 | // to notice that a screen was swapped out against another one. |
179 | #if QT_DEPRECATED_SINCE(5, 11) |
180 | QT_WARNING_PUSH |
181 | QT_WARNING_DISABLE_DEPRECATED |
182 | emit q->screenCountChanged(targetLength); |
183 | QT_WARNING_POP |
184 | #endif |
185 | } |
186 | #if QT_DEPRECATED_SINCE(5, 11) |
187 | foreach (int changedScreen, changedScreens) |
188 | QT_WARNING_PUSH |
189 | QT_WARNING_DISABLE_DEPRECATED |
190 | emit q->resized(changedScreen); |
191 | QT_WARNING_POP |
192 | #endif |
193 | } |
194 | |
195 | void QDesktopWidgetPrivate::_q_availableGeometryChanged() |
196 | { |
197 | #if QT_DEPRECATED_SINCE(5, 11) |
198 | Q_Q(QDesktopWidget); |
199 | if (QScreen *screen = qobject_cast<QScreen *>(object: q->sender())) |
200 | QT_WARNING_PUSH |
201 | QT_WARNING_DISABLE_DEPRECATED |
202 | emit q->workAreaResized(QGuiApplication::screens().indexOf(t: screen)); |
203 | QT_WARNING_POP |
204 | #endif |
205 | } |
206 | |
207 | QDesktopWidget::QDesktopWidget() |
208 | : QWidget(*new QDesktopWidgetPrivate, nullptr, Qt::Desktop) |
209 | { |
210 | Q_D(QDesktopWidget); |
211 | setObjectName(QLatin1String("desktop" )); |
212 | d->_q_updateScreens(); |
213 | connect(qApp, SIGNAL(screenAdded(QScreen*)), receiver: this, SLOT(_q_updateScreens())); |
214 | #if QT_DEPRECATED_SINCE(5, 11) |
215 | connect(qApp, SIGNAL(primaryScreenChanged(QScreen*)), receiver: this, SIGNAL(primaryScreenChanged())); |
216 | #endif |
217 | } |
218 | |
219 | QDesktopWidget::~QDesktopWidget() |
220 | { |
221 | } |
222 | |
223 | #if QT_DEPRECATED_SINCE(5, 11) |
224 | bool QDesktopWidget::isVirtualDesktop() const |
225 | { |
226 | return QDesktopWidgetPrivate::isVirtualDesktop(); |
227 | } |
228 | #endif |
229 | |
230 | bool QDesktopWidgetPrivate::isVirtualDesktop() |
231 | { |
232 | return QGuiApplication::primaryScreen()->virtualSiblings().size() > 1; |
233 | } |
234 | |
235 | QRect QDesktopWidgetPrivate::geometry() |
236 | { |
237 | return QGuiApplication::primaryScreen()->virtualGeometry(); |
238 | } |
239 | |
240 | QSize QDesktopWidgetPrivate::size() |
241 | { |
242 | return geometry().size(); |
243 | } |
244 | |
245 | int QDesktopWidgetPrivate::width() |
246 | { |
247 | return geometry().width(); |
248 | } |
249 | |
250 | int QDesktopWidgetPrivate::height() |
251 | { |
252 | return geometry().height(); |
253 | } |
254 | |
255 | #if QT_DEPRECATED_SINCE(5, 11) |
256 | int QDesktopWidget::primaryScreen() const |
257 | { |
258 | return QDesktopWidgetPrivate::primaryScreen(); |
259 | } |
260 | #endif |
261 | |
262 | int QDesktopWidgetPrivate::primaryScreen() |
263 | { |
264 | return 0; |
265 | } |
266 | |
267 | int QDesktopWidgetPrivate::numScreens() |
268 | { |
269 | return qMax(a: QGuiApplication::screens().size(), b: 1); |
270 | } |
271 | |
272 | #if QT_DEPRECATED_SINCE(5, 11) |
273 | int QDesktopWidget::numScreens() const |
274 | { |
275 | return QDesktopWidgetPrivate::numScreens(); |
276 | } |
277 | |
278 | QWidget *QDesktopWidget::screen(int screen) |
279 | { |
280 | Q_D(QDesktopWidget); |
281 | if (screen < 0 || screen >= d->screens.length()) |
282 | return d->screens.at(i: 0); |
283 | return d->screens.at(i: screen); |
284 | } |
285 | |
286 | const QRect QDesktopWidget::availableGeometry(int screenNo) const |
287 | { |
288 | return QDesktopWidgetPrivate::availableGeometry(screen: screenNo); |
289 | } |
290 | #endif |
291 | |
292 | const QRect QDesktopWidgetPrivate::availableGeometry(int screenNo) |
293 | { |
294 | const QScreen *screen = QDesktopWidgetPrivate::screen(screenNo); |
295 | return screen ? screen->availableGeometry() : QRect(); |
296 | } |
297 | |
298 | #if QT_DEPRECATED_SINCE(5, 11) |
299 | const QRect QDesktopWidget::screenGeometry(int screenNo) const |
300 | { |
301 | return QDesktopWidgetPrivate::screenGeometry(screen: screenNo); |
302 | } |
303 | #endif |
304 | |
305 | const QRect QDesktopWidgetPrivate::screenGeometry(int screenNo) |
306 | { |
307 | const QScreen *screen = QDesktopWidgetPrivate::screen(screenNo); |
308 | return screen ? screen->geometry() : QRect(); |
309 | } |
310 | |
311 | int QDesktopWidget::screenNumber(const QWidget *w) const |
312 | { |
313 | return QDesktopWidgetPrivate::screenNumber(widget: w); |
314 | } |
315 | |
316 | int QDesktopWidgetPrivate::screenNumber(const QWidget *w) |
317 | { |
318 | if (!w) |
319 | return primaryScreen(); |
320 | |
321 | const QList<QScreen *> allScreens = QGuiApplication::screens(); |
322 | QList<QScreen *> screens = allScreens; |
323 | if (screens.isEmpty()) // This should never happen |
324 | return primaryScreen(); |
325 | |
326 | // If there is more than one virtual desktop |
327 | if (screens.count() != screens.constFirst()->virtualSiblings().count()) { |
328 | // Find the root widget, get a QScreen from it and use the |
329 | // virtual siblings for checking the window position. |
330 | if (const QScreen *winScreen = qt_widget_private(widget: const_cast<QWidget *>(w))->associatedScreen()) |
331 | screens = winScreen->virtualSiblings(); |
332 | } |
333 | |
334 | // Get the screen number from window position using screen geometry |
335 | // and proper screens. |
336 | QRect frame = w->frameGeometry(); |
337 | if (!w->isWindow()) |
338 | frame.moveTopLeft(p: w->mapToGlobal(QPoint(0, 0))); |
339 | |
340 | QScreen *widgetScreen = nullptr; |
341 | int largestArea = 0; |
342 | foreach (QScreen *screen, screens) { |
343 | const QRect deviceIndependentScreenGeometry = |
344 | QHighDpi::fromNativePixels(value: screen->handle()->geometry(), context: screen); |
345 | const QRect intersected = deviceIndependentScreenGeometry.intersected(other: frame); |
346 | int area = intersected.width() * intersected.height(); |
347 | if (largestArea < area) { |
348 | widgetScreen = screen; |
349 | largestArea = area; |
350 | } |
351 | } |
352 | return allScreens.indexOf(t: widgetScreen); |
353 | } |
354 | |
355 | #if QT_DEPRECATED_SINCE(5, 11) |
356 | int QDesktopWidget::screenNumber(const QPoint &p) const |
357 | { |
358 | return QDesktopWidgetPrivate::screenNumber(p); |
359 | } |
360 | #endif |
361 | |
362 | int QDesktopWidgetPrivate::screenNumber(const QPoint &p) |
363 | { |
364 | QScreen *screen = QGuiApplication::screenAt(point: p); |
365 | return screen ? QGuiApplication::screens().indexOf(t: screen) : primaryScreen(); |
366 | } |
367 | |
368 | QScreen *QDesktopWidgetPrivate::screen(int screenNo) |
369 | { |
370 | QList<QScreen *> screens = QGuiApplication::screens(); |
371 | if (screenNo == -1) |
372 | screenNo = 0; |
373 | if (screenNo < 0 || screenNo >= screens.size()) |
374 | return nullptr; |
375 | return screens.at(i: screenNo); |
376 | } |
377 | |
378 | void QDesktopWidget::resizeEvent(QResizeEvent *) |
379 | { |
380 | } |
381 | |
382 | QT_END_NAMESPACE |
383 | |
384 | #include "moc_qdesktopwidget.cpp" |
385 | #include "moc_qdesktopwidget_p.cpp" |
386 | |