| 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 | |
| 52 | QT_BEGIN_NAMESPACE |
| 53 | |
| 54 | QFbScreen::QFbScreen() |
| 55 | : mUpdatePending(false), |
| 56 | mCursor(0), |
| 57 | mDepth(16), |
| 58 | mFormat(QImage::Format_RGB16), |
| 59 | mPainter(nullptr) |
| 60 | { |
| 61 | } |
| 62 | |
| 63 | QFbScreen::~QFbScreen() |
| 64 | { |
| 65 | delete mPainter; |
| 66 | } |
| 67 | |
| 68 | void QFbScreen::initializeCompositor() |
| 69 | { |
| 70 | mScreenImage = QImage(mGeometry.size(), mFormat); |
| 71 | scheduleUpdate(); |
| 72 | } |
| 73 | |
| 74 | bool 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 | |
| 84 | void 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 | |
| 106 | void 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 | |
| 115 | void 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 | |
| 127 | void 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 | |
| 139 | QWindow *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 | |
| 148 | QWindow *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 | |
| 157 | int QFbScreen::windowCount() const |
| 158 | { |
| 159 | return mWindowStack.count(); |
| 160 | } |
| 161 | |
| 162 | void 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 | |
| 170 | void QFbScreen::scheduleUpdate() |
| 171 | { |
| 172 | if (!mUpdatePending) { |
| 173 | mUpdatePending = true; |
| 174 | QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | void QFbScreen::setPhysicalSize(const QSize &size) |
| 179 | { |
| 180 | mPhysicalSize = size; |
| 181 | } |
| 182 | |
| 183 | void 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 | |
| 193 | bool QFbScreen::initialize() |
| 194 | { |
| 195 | return true; |
| 196 | } |
| 197 | |
| 198 | QRegion 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 | |
| 247 | QFbWindow *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 | |
| 256 | QFbScreen::Flags QFbScreen::flags() const |
| 257 | { |
| 258 | return { }; |
| 259 | } |
| 260 | |
| 261 | QT_END_NAMESPACE |
| 262 | |