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 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 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 "qoffscreencommon.h"
41#include "qoffscreenintegration.h"
42#include "qoffscreenwindow.h"
43
44
45#include <QtGui/private/qpixmap_raster_p.h>
46#include <QtGui/private/qguiapplication_p.h>
47
48#include <qpa/qplatformcursor.h>
49#include <qpa/qplatformwindow.h>
50
51QT_BEGIN_NAMESPACE
52
53QPlatformWindow *QOffscreenScreen::windowContainingCursor = nullptr;
54
55
56QList<QPlatformScreen *> QOffscreenScreen::virtualSiblings() const
57{
58 return m_integration->screens();
59}
60
61class QOffscreenCursor : public QPlatformCursor
62{
63public:
64 QOffscreenCursor() : m_pos(10, 10) {}
65
66 QPoint pos() const override { return m_pos; }
67 void setPos(const QPoint &pos) override
68 {
69 m_pos = pos;
70 const QWindowList wl = QGuiApplication::topLevelWindows();
71 QWindow *containing = nullptr;
72 for (QWindow *w : wl) {
73 if (w->type() != Qt::Desktop && w->isExposed() && w->geometry().contains(pos)) {
74 containing = w;
75 break;
76 }
77 }
78
79 QPoint local = pos;
80 if (containing)
81 local -= containing->position();
82
83 QWindow *previous = QOffscreenScreen::windowContainingCursor ? QOffscreenScreen::windowContainingCursor->window() : nullptr;
84
85 if (containing != previous)
86 QWindowSystemInterface::handleEnterLeaveEvent(containing, previous, local, pos);
87
88 QWindowSystemInterface::handleMouseEvent(containing, local, pos, QGuiApplication::mouseButtons(), Qt::NoButton,
89 QEvent::MouseMove, QGuiApplication::keyboardModifiers(), Qt::MouseEventSynthesizedByQt);
90
91 QOffscreenScreen::windowContainingCursor = containing ? containing->handle() : nullptr;
92 }
93#ifndef QT_NO_CURSOR
94 void changeCursor(QCursor *windowCursor, QWindow *window) override
95 {
96 Q_UNUSED(windowCursor);
97 Q_UNUSED(window);
98 }
99#endif
100private:
101 QPoint m_pos;
102};
103
104QOffscreenScreen::QOffscreenScreen(const QOffscreenIntegration *integration)
105 : m_geometry(0, 0, 800, 600)
106 , m_cursor(new QOffscreenCursor)
107 , m_integration(integration)
108{
109}
110
111QPixmap QOffscreenScreen::grabWindow(WId id, int x, int y, int width, int height) const
112{
113 QRect rect(x, y, width, height);
114
115 QOffscreenWindow *window = QOffscreenWindow::windowForWinId(id);
116 if (!window || window->window()->type() == Qt::Desktop) {
117 const QWindowList wl = QGuiApplication::topLevelWindows();
118 QWindow *containing = nullptr;
119 for (QWindow *w : wl) {
120 if (w->type() != Qt::Desktop && w->isExposed() && w->geometry().contains(rect)) {
121 containing = w;
122 break;
123 }
124 }
125
126 if (!containing)
127 return QPixmap();
128
129 id = containing->winId();
130 rect = rect.translated(-containing->geometry().topLeft());
131 }
132
133 QOffscreenBackingStore *store = QOffscreenBackingStore::backingStoreForWinId(id);
134 if (store)
135 return store->grabWindow(id, rect);
136 return QPixmap();
137}
138
139QOffscreenBackingStore::QOffscreenBackingStore(QWindow *window)
140 : QPlatformBackingStore(window)
141{
142}
143
144QOffscreenBackingStore::~QOffscreenBackingStore()
145{
146 clearHash();
147}
148
149QPaintDevice *QOffscreenBackingStore::paintDevice()
150{
151 return &m_image;
152}
153
154void QOffscreenBackingStore::flush(QWindow *window, const QRegion &region, const QPoint &offset)
155{
156 Q_UNUSED(region);
157
158 if (m_image.size().isEmpty())
159 return;
160
161 QSize imageSize = m_image.size();
162
163 QRegion clipped = QRect(0, 0, window->width(), window->height());
164 clipped &= QRect(0, 0, imageSize.width(), imageSize.height()).translated(-offset);
165
166 QRect bounds = clipped.boundingRect().translated(offset);
167
168 if (bounds.isNull())
169 return;
170
171 WId id = window->winId();
172
173 m_windowAreaHash[id] = bounds;
174 m_backingStoreForWinIdHash[id] = this;
175}
176
177void QOffscreenBackingStore::resize(const QSize &size, const QRegion &)
178{
179 QImage::Format format = QGuiApplication::primaryScreen()->handle()->format();
180 if (m_image.size() != size)
181 m_image = QImage(size, format);
182 clearHash();
183}
184
185extern void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset);
186
187bool QOffscreenBackingStore::scroll(const QRegion &area, int dx, int dy)
188{
189 if (m_image.isNull())
190 return false;
191
192 for (const QRect &rect : area)
193 qt_scrollRectInImage(m_image, rect, QPoint(dx, dy));
194
195 return true;
196}
197
198QPixmap QOffscreenBackingStore::grabWindow(WId window, const QRect &rect) const
199{
200 QRect area = m_windowAreaHash.value(window, QRect());
201 if (area.isNull())
202 return QPixmap();
203
204 QRect adjusted = rect;
205 if (adjusted.width() <= 0)
206 adjusted.setWidth(area.width());
207 if (adjusted.height() <= 0)
208 adjusted.setHeight(area.height());
209
210 adjusted = adjusted.translated(area.topLeft()) & area;
211
212 if (adjusted.isEmpty())
213 return QPixmap();
214
215 return QPixmap::fromImage(m_image.copy(adjusted));
216}
217
218QOffscreenBackingStore *QOffscreenBackingStore::backingStoreForWinId(WId id)
219{
220 return m_backingStoreForWinIdHash.value(id, 0);
221}
222
223void QOffscreenBackingStore::clearHash()
224{
225 for (auto it = m_windowAreaHash.cbegin(), end = m_windowAreaHash.cend(); it != end; ++it) {
226 const auto it2 = qAsConst(m_backingStoreForWinIdHash).find(it.key());
227 if (it2.value() == this)
228 m_backingStoreForWinIdHash.erase(it2);
229 }
230 m_windowAreaHash.clear();
231}
232
233QHash<WId, QOffscreenBackingStore *> QOffscreenBackingStore::m_backingStoreForWinIdHash;
234
235QT_END_NAMESPACE
236