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 "qfbscreen_p.h"
41#include "qfbcursor_p.h"
42#include "qfbwindow_p.h"
43#include "qfbbackingstore_p.h"
44
45#include <QtGui/QPainter>
46#include <QtCore/QCoreApplication>
47#include <qpa/qwindowsysteminterface.h>
48
49#include <QtCore/QDebug>
50#include <QtCore/QElapsedTimer>
51
52QT_BEGIN_NAMESPACE
53
54QFbScreen::QFbScreen()
55 : mUpdatePending(false),
56 mCursor(0),
57 mDepth(16),
58 mFormat(QImage::Format_RGB16),
59 mPainter(nullptr)
60{
61}
62
63QFbScreen::~QFbScreen()
64{
65 delete mPainter;
66}
67
68void QFbScreen::initializeCompositor()
69{
70 mScreenImage = QImage(mGeometry.size(), mFormat);
71 scheduleUpdate();
72}
73
74bool QFbScreen::event(QEvent *event)
75{
76 if (event->type() == QEvent::UpdateRequest) {
77 doRedraw();
78 mUpdatePending = false;
79 return true;
80 }
81 return QObject::event(event);
82}
83
84void QFbScreen::addWindow(QFbWindow *window)
85{
86 mWindowStack.prepend(window);
87 if (!mPendingBackingStores.isEmpty()) {
88 //check if we have a backing store for this window
89 for (int i = 0; i < mPendingBackingStores.size(); ++i) {
90 QFbBackingStore *bs = mPendingBackingStores.at(i);
91 // this gets called during QWindow::create() at a point where the
92 // invariant (window->handle()->window() == window) is broken
93 if (bs->window() == window->window()) {
94 window->setBackingStore(bs);
95 mPendingBackingStores.removeAt(i);
96 break;
97 }
98 }
99 }
100 setDirty(window->geometry());
101 QWindow *w = topWindow();
102 QWindowSystemInterface::handleWindowActivated(w);
103 topWindowChanged(w);
104}
105
106void QFbScreen::removeWindow(QFbWindow *window)
107{
108 mWindowStack.removeOne(window);
109 setDirty(window->geometry());
110 QWindow *w = topWindow();
111 QWindowSystemInterface::handleWindowActivated(w);
112 topWindowChanged(w);
113}
114
115void QFbScreen::raise(QFbWindow *window)
116{
117 int index = mWindowStack.indexOf(window);
118 if (index <= 0)
119 return;
120 mWindowStack.move(index, 0);
121 setDirty(window->geometry());
122 QWindow *w = topWindow();
123 QWindowSystemInterface::handleWindowActivated(w);
124 topWindowChanged(w);
125}
126
127void QFbScreen::lower(QFbWindow *window)
128{
129 int index = mWindowStack.indexOf(window);
130 if (index == -1 || index == (mWindowStack.size() - 1))
131 return;
132 mWindowStack.move(index, mWindowStack.size() - 1);
133 setDirty(window->geometry());
134 QWindow *w = topWindow();
135 QWindowSystemInterface::handleWindowActivated(w);
136 topWindowChanged(w);
137}
138
139QWindow *QFbScreen::topWindow() const
140{
141 for (QFbWindow *fbw : mWindowStack) {
142 if (fbw->window()->type() == Qt::Window || fbw->window()->type() == Qt::Dialog)
143 return fbw->window();
144 }
145 return nullptr;
146}
147
148QWindow *QFbScreen::topLevelAt(const QPoint & p) const
149{
150 for (QFbWindow *fbw : mWindowStack) {
151 if (fbw->geometry().contains(p, false) && fbw->window()->isVisible())
152 return fbw->window();
153 }
154 return nullptr;
155}
156
157int QFbScreen::windowCount() const
158{
159 return mWindowStack.count();
160}
161
162void QFbScreen::setDirty(const QRect &rect)
163{
164 const QRect intersection = rect.intersected(mGeometry);
165 const QPoint screenOffset = mGeometry.topLeft();
166 mRepaintRegion += intersection.translated(-screenOffset); // global to local translation
167 scheduleUpdate();
168}
169
170void QFbScreen::scheduleUpdate()
171{
172 if (!mUpdatePending) {
173 mUpdatePending = true;
174 QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));
175 }
176}
177
178void QFbScreen::setPhysicalSize(const QSize &size)
179{
180 mPhysicalSize = size;
181}
182
183void QFbScreen::setGeometry(const QRect &rect)
184{
185 delete mPainter;
186 mPainter = nullptr;
187 mGeometry = rect;
188 mScreenImage = QImage(mGeometry.size(), mFormat);
189 QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), geometry(), availableGeometry());
190 resizeMaximizedWindows();
191}
192
193bool QFbScreen::initialize()
194{
195 return true;
196}
197
198QRegion QFbScreen::doRedraw()
199{
200 const QPoint screenOffset = mGeometry.topLeft();
201
202 QRegion touchedRegion;
203 if (mCursor && mCursor->isDirty() && mCursor->isOnScreen()) {
204 const QRect lastCursor = mCursor->dirtyRect();
205 mRepaintRegion += lastCursor;
206 }
207 if (mRepaintRegion.isEmpty() && (!mCursor || !mCursor->isDirty()))
208 return touchedRegion;
209
210 if (!mPainter)
211 mPainter = new QPainter(&mScreenImage);
212
213 const QRect screenRect = mGeometry.translated(-screenOffset);
214 for (QRect rect : mRepaintRegion) {
215 rect = rect.intersected(screenRect);
216 if (rect.isEmpty())
217 continue;
218
219 mPainter->setCompositionMode(QPainter::CompositionMode_Source);
220 mPainter->fillRect(rect, mScreenImage.hasAlphaChannel() ? Qt::transparent : Qt::black);
221
222 for (int layerIndex = mWindowStack.size() - 1; layerIndex != -1; layerIndex--) {
223 if (!mWindowStack[layerIndex]->window()->isVisible())
224 continue;
225
226 const QRect windowRect = mWindowStack[layerIndex]->geometry().translated(-screenOffset);
227 const QRect windowIntersect = rect.translated(-windowRect.left(), -windowRect.top());
228 QFbBackingStore *backingStore = mWindowStack[layerIndex]->backingStore();
229 if (backingStore) {
230 backingStore->lock();
231 mPainter->drawImage(rect, backingStore->image(), windowIntersect);
232 backingStore->unlock();
233 }
234 }
235 }
236
237 if (mCursor && (mCursor->isDirty() || mRepaintRegion.intersects(mCursor->lastPainted()))) {
238 mPainter->setCompositionMode(QPainter::CompositionMode_SourceOver);
239 touchedRegion += mCursor->drawCursor(*mPainter);
240 }
241 touchedRegion += mRepaintRegion;
242 mRepaintRegion = QRegion();
243
244 return touchedRegion;
245}
246
247QFbWindow *QFbScreen::windowForId(WId wid) const
248{
249 for (int i = 0; i < mWindowStack.count(); ++i) {
250 if (mWindowStack[i]->winId() == wid)
251 return mWindowStack[i];
252 }
253 return nullptr;
254}
255
256QFbScreen::Flags QFbScreen::flags() const
257{
258 return { };
259}
260
261QT_END_NAMESPACE
262