summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/mirclient/qmirclientwindow.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/mirclient/qmirclientwindow.cpp')
-rw-r--r--src/plugins/platforms/mirclient/qmirclientwindow.cpp662
1 files changed, 662 insertions, 0 deletions
diff --git a/src/plugins/platforms/mirclient/qmirclientwindow.cpp b/src/plugins/platforms/mirclient/qmirclientwindow.cpp
new file mode 100644
index 0000000000..9a72c2f9dc
--- /dev/null
+++ b/src/plugins/platforms/mirclient/qmirclientwindow.cpp
@@ -0,0 +1,662 @@
+/****************************************************************************
+**
+** Copyright (C) 2014-2015 Canonical, Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+// Local
+#include "qmirclientwindow.h"
+#include "qmirclientclipboard.h"
+#include "qmirclientinput.h"
+#include "qmirclientscreen.h"
+#include "qmirclientlogging.h"
+
+#include <mir_toolkit/mir_client_library.h>
+
+// Qt
+#include <qpa/qwindowsysteminterface.h>
+#include <QMutexLocker>
+#include <QSize>
+#include <QtMath>
+
+// Platform API
+#include <ubuntu/application/instance.h>
+
+#include <EGL/egl.h>
+
+namespace
+{
+
+// FIXME: this used to be defined by platform-api, but it's been removed in v3. Change ubuntu-keyboard to use
+// a different enum for window roles.
+enum UAUiWindowRole {
+ U_MAIN_ROLE = 1,
+ U_DASH_ROLE,
+ U_INDICATOR_ROLE,
+ U_NOTIFICATIONS_ROLE,
+ U_GREETER_ROLE,
+ U_LAUNCHER_ROLE,
+ U_ON_SCREEN_KEYBOARD_ROLE,
+ U_SHUTDOWN_DIALOG_ROLE,
+};
+
+struct MirSpecDeleter
+{
+ void operator()(MirSurfaceSpec *spec) { mir_surface_spec_release(spec); }
+};
+
+using Spec = std::unique_ptr<MirSurfaceSpec, MirSpecDeleter>;
+
+EGLNativeWindowType nativeWindowFor(MirSurface *surf)
+{
+ auto stream = mir_surface_get_buffer_stream(surf);
+ return reinterpret_cast<EGLNativeWindowType>(mir_buffer_stream_get_egl_native_window(stream));
+}
+
+MirSurfaceState qtWindowStateToMirSurfaceState(Qt::WindowState state)
+{
+ switch (state) {
+ case Qt::WindowNoState:
+ return mir_surface_state_restored;
+ case Qt::WindowFullScreen:
+ return mir_surface_state_fullscreen;
+ case Qt::WindowMaximized:
+ return mir_surface_state_maximized;
+ case Qt::WindowMinimized:
+ return mir_surface_state_minimized;
+ default:
+ LOG("Unexpected Qt::WindowState: %d", state);
+ return mir_surface_state_restored;
+ }
+}
+
+#if !defined(QT_NO_DEBUG)
+const char *qtWindowStateToStr(Qt::WindowState state)
+{
+ switch (state) {
+ case Qt::WindowNoState:
+ return "NoState";
+ case Qt::WindowFullScreen:
+ return "FullScreen";
+ case Qt::WindowMaximized:
+ return "Maximized";
+ case Qt::WindowMinimized:
+ return "Minimized";
+ default:
+ return "!?";
+ }
+}
+#endif
+
+WId makeId()
+{
+ static int id = 1;
+ return id++;
+}
+
+MirPixelFormat defaultPixelFormatFor(MirConnection *connection)
+{
+ MirPixelFormat format;
+ unsigned int nformats;
+ mir_connection_get_available_surface_formats(connection, &format, 1, &nformats);
+ return format;
+}
+
+UAUiWindowRole roleFor(QWindow *window)
+{
+ QVariant roleVariant = window->property("role");
+ if (!roleVariant.isValid())
+ return U_MAIN_ROLE;
+
+ uint role = roleVariant.toUInt();
+ if (role < U_MAIN_ROLE || role > U_SHUTDOWN_DIALOG_ROLE)
+ return U_MAIN_ROLE;
+
+ return static_cast<UAUiWindowRole>(role);
+}
+
+QMirClientWindow *transientParentFor(QWindow *window)
+{
+ QWindow *parent = window->transientParent();
+ return parent ? static_cast<QMirClientWindow *>(parent->handle()) : nullptr;
+}
+
+Spec makeSurfaceSpec(QWindow *window, QMirClientInput *input, MirConnection *connection)
+{
+ const auto geom = window->geometry();
+ const int width = geom.width() > 0 ? geom.width() : 1;
+ const int height = geom.height() > 0 ? geom.height() : 1;
+ const auto pixelFormat = defaultPixelFormatFor(connection);
+
+ if (U_ON_SCREEN_KEYBOARD_ROLE == roleFor(window)) {
+ DLOG("[ubuntumirclient QPA] makeSurfaceSpec(window=%p) - creating input method surface (width=%d, height=%d", window, width, height);
+ return Spec{mir_connection_create_spec_for_input_method(connection, width, height, pixelFormat)};
+ }
+
+ const Qt::WindowType type = window->type();
+ if (type == Qt::Popup) {
+ auto parent = transientParentFor(window);
+ if (parent == nullptr) {
+ //NOTE: We cannot have a parentless popup -
+ //try using the last surface to receive input as that will most likely be
+ //the one that caused this popup to be created
+ parent = input->lastFocusedWindow();
+ }
+ if (parent) {
+ auto pos = geom.topLeft();
+ pos -= parent->geometry().topLeft();
+ MirRectangle location{pos.x(), pos.y(), 0, 0};
+ DLOG("[ubuntumirclient QPA] makeSurfaceSpec(window=%p) - creating menu surface(width:%d, height:%d)", window, width, height);
+ return Spec{mir_connection_create_spec_for_menu(
+ connection, width, height, pixelFormat, parent->mirSurface(),
+ &location, mir_edge_attachment_any)};
+ } else {
+ DLOG("[ubuntumirclient QPA] makeSurfaceSpec(window=%p) - cannot create a menu without a parent!", window);
+ }
+ } else if (type == Qt::Dialog) {
+ auto parent = transientParentFor(window);
+ if (parent) {
+ // Modal dialog
+ DLOG("[ubuntumirclient QPA] makeSurfaceSpec(window=%p) - creating modal dialog (width=%d, height=%d", window, width, height);
+ return Spec{mir_connection_create_spec_for_modal_dialog(connection, width, height, pixelFormat, parent->mirSurface())};
+ } else {
+ // TODO: do Qt parentless dialogs have the same semantics as mir?
+ DLOG("[ubuntumirclient QPA] makeSurfaceSpec(window=%p) - creating parentless dialog (width=%d, height=%d)", window, width, height);
+ return Spec{mir_connection_create_spec_for_dialog(connection, width, height, pixelFormat)};
+ }
+ }
+ DLOG("[ubuntumirclient QPA] makeSurfaceSpec(window=%p) - creating normal surface(type=0x%x, width=%d, height=%d)", window, type, width, height);
+ return Spec{mir_connection_create_spec_for_normal_surface(connection, width, height, pixelFormat)};
+}
+
+void setSizingConstraints(MirSurfaceSpec *spec, const QSize& minSize, const QSize& maxSize, const QSize& increment)
+{
+ mir_surface_spec_set_min_width(spec, minSize.width());
+ mir_surface_spec_set_min_height(spec, minSize.height());
+ if (maxSize.width() >= minSize.width()) {
+ mir_surface_spec_set_max_width(spec, maxSize.width());
+ }
+ if (maxSize.height() >= minSize.height()) {
+ mir_surface_spec_set_max_height(spec, maxSize.height());
+ }
+ if (increment.width() > 0) {
+ mir_surface_spec_set_width_increment(spec, increment.width());
+ }
+ if (increment.height() > 0) {
+ mir_surface_spec_set_height_increment(spec, increment.height());
+ }
+}
+
+MirSurface *createMirSurface(QWindow *window, QMirClientScreen *screen, QMirClientInput *input, MirConnection *connection)
+{
+ auto spec = makeSurfaceSpec(window, input, connection);
+ const auto title = window->title().toUtf8();
+ mir_surface_spec_set_name(spec.get(), title.constData());
+
+ setSizingConstraints(spec.get(), window->minimumSize(), window->maximumSize(), window->sizeIncrement());
+
+ if (window->windowState() == Qt::WindowFullScreen) {
+ mir_surface_spec_set_fullscreen_on_output(spec.get(), screen->mirOutputId());
+ }
+
+ auto surface = mir_surface_create_sync(spec.get());
+ Q_ASSERT(mir_surface_is_valid(surface));
+ return surface;
+}
+
+// FIXME - in order to work around https://bugs.launchpad.net/mir/+bug/1346633
+// we need to guess the panel height (3GU)
+int panelHeight()
+{
+ if (qEnvironmentVariableIsSet("QT_MIRCLIENT_IGNORE_PANEL"))
+ return 0;
+ const int defaultGridUnit = 8;
+ int gridUnit = defaultGridUnit;
+ QByteArray gridUnitString = qgetenv("GRID_UNIT_PX");
+ if (!gridUnitString.isEmpty()) {
+ bool ok;
+ gridUnit = gridUnitString.toInt(&ok);
+ if (!ok) {
+ gridUnit = defaultGridUnit;
+ }
+ }
+ return gridUnit * 3;
+}
+
+} //namespace
+
+class QMirClientSurface
+{
+public:
+ QMirClientSurface(QMirClientWindow *platformWindow, QMirClientScreen *screen, QMirClientInput *input, MirConnection *connection)
+ : mWindow(platformWindow->window())
+ , mPlatformWindow(platformWindow)
+ , mInput(input)
+ , mConnection(connection)
+ , mMirSurface(createMirSurface(mWindow, screen, input, connection))
+ , mEglDisplay(screen->eglDisplay())
+ , mEglSurface(eglCreateWindowSurface(mEglDisplay, screen->eglConfig(), nativeWindowFor(mMirSurface), nullptr))
+ , mVisible(false)
+ , mNeedsRepaint(false)
+ , mParented(mWindow->transientParent() || mWindow->parent())
+ , mWindowState(mWindow->windowState())
+
+ {
+ mir_surface_set_event_handler(mMirSurface, surfaceEventCallback, this);
+
+ // Window manager can give us a final size different from what we asked for
+ // so let's check what we ended up getting
+ MirSurfaceParameters parameters;
+ mir_surface_get_parameters(mMirSurface, &parameters);
+
+ auto geom = mWindow->geometry();
+ geom.setWidth(parameters.width);
+ geom.setHeight(parameters.height);
+ if (mWindowState == Qt::WindowFullScreen) {
+ geom.setY(0);
+ } else {
+ geom.setY(panelHeight());
+ }
+
+ // Assume that the buffer size matches the surface size at creation time
+ mBufferSize = geom.size();
+ platformWindow->QPlatformWindow::setGeometry(geom);
+ QWindowSystemInterface::handleGeometryChange(mWindow, geom);
+
+ DLOG("[ubuntumirclient QPA] created surface at (%d, %d) with size (%d, %d), title '%s', role: '%d'\n",
+ geom.x(), geom.y(), geom.width(), geom.height(), mWindow->title().toUtf8().constData(), roleFor(mWindow));
+ }
+
+ ~QMirClientSurface()
+ {
+ if (mEglSurface != EGL_NO_SURFACE)
+ eglDestroySurface(mEglDisplay, mEglSurface);
+ if (mMirSurface)
+ mir_surface_release_sync(mMirSurface);
+ }
+
+ QMirClientSurface(QMirClientSurface const&) = delete;
+ QMirClientSurface& operator=(QMirClientSurface const&) = delete;
+
+ void resize(const QSize& newSize);
+ void setState(Qt::WindowState newState);
+ void setVisible(bool state);
+ void updateTitle(const QString& title);
+ void setSizingConstraints(const QSize& minSize, const QSize& maxSize, const QSize& increment);
+
+ void onSwapBuffersDone();
+ void handleSurfaceResized(int width, int height);
+ int needsRepaint() const;
+
+ EGLSurface eglSurface() const { return mEglSurface; }
+ MirSurface *mirSurface() const { return mMirSurface; }
+
+private:
+ static void surfaceEventCallback(MirSurface* surface, const MirEvent *event, void* context);
+ void postEvent(const MirEvent *event);
+ void updateSurface();
+
+ QWindow * const mWindow;
+ QMirClientWindow * const mPlatformWindow;
+ QMirClientInput * const mInput;
+ MirConnection * const mConnection;
+
+ MirSurface * const mMirSurface;
+ const EGLDisplay mEglDisplay;
+ const EGLSurface mEglSurface;
+
+ bool mVisible;
+ bool mNeedsRepaint;
+ bool mParented;
+ Qt::WindowState mWindowState;
+ QSize mBufferSize;
+
+ QMutex mTargetSizeMutex;
+ QSize mTargetSize;
+};
+
+void QMirClientSurface::resize(const QSize& size)
+{
+ DLOG("[ubuntumirclient QPA] resize(window=%p, width=%d, height=%d)", mWindow, size.width(), size.height());
+
+ if (mWindowState == Qt::WindowFullScreen || mWindowState == Qt::WindowMaximized) {
+ DLOG("[ubuntumirclient QPA] resize(window=%p) - not resizing, window is maximized or fullscreen", mWindow);
+ return;
+ }
+
+ if (size.isEmpty()) {
+ DLOG("[ubuntumirclient QPA] resize(window=%p) - not resizing, size is empty", mWindow);
+ return;
+ }
+
+ Spec spec{mir_connection_create_spec_for_changes(mConnection)};
+ mir_surface_spec_set_width(spec.get(), size.width());
+ mir_surface_spec_set_height(spec.get(), size.height());
+ mir_surface_apply_spec(mMirSurface, spec.get());
+}
+
+void QMirClientSurface::setState(Qt::WindowState newState)
+{
+ mir_wait_for(mir_surface_set_state(mMirSurface, qtWindowStateToMirSurfaceState(newState)));
+ mWindowState = newState;
+}
+
+void QMirClientSurface::setVisible(bool visible)
+{
+ if (mVisible == visible)
+ return;
+
+ mVisible = visible;
+
+ if (mVisible)
+ updateSurface();
+
+ // TODO: Use the new mir_surface_state_hidden state instead of mir_surface_state_minimized.
+ // Will have to change qtmir and unity8 for that.
+ const auto newState = visible ? qtWindowStateToMirSurfaceState(mWindowState) : mir_surface_state_minimized;
+ mir_wait_for(mir_surface_set_state(mMirSurface, newState));
+}
+
+void QMirClientSurface::updateTitle(const QString& newTitle)
+{
+ const auto title = newTitle.toUtf8();
+ Spec spec{mir_connection_create_spec_for_changes(mConnection)};
+ mir_surface_spec_set_name(spec.get(), title.constData());
+ mir_surface_apply_spec(mMirSurface, spec.get());
+}
+
+void QMirClientSurface::setSizingConstraints(const QSize& minSize, const QSize& maxSize, const QSize& increment)
+{
+ Spec spec{mir_connection_create_spec_for_changes(mConnection)};
+ ::setSizingConstraints(spec.get(), minSize, maxSize, increment);
+ mir_surface_apply_spec(mMirSurface, spec.get());
+}
+
+void QMirClientSurface::handleSurfaceResized(int width, int height)
+{
+ QMutexLocker lock(&mTargetSizeMutex);
+
+ // mir's resize event is mainly a signal that we need to redraw our content. We use the
+ // width/height as identifiers to figure out if this is the latest surface resize event
+ // that has posted, discarding any old ones. This avoids issuing too many redraw events.
+ // see TODO in postEvent as the ideal way we should handle this.
+ // The actual buffer size may or may have not changed at this point, so let the rendering
+ // thread drive the window geometry updates.
+ mNeedsRepaint = mTargetSize.width() == width && mTargetSize.height() == height;
+}
+
+int QMirClientSurface::needsRepaint() const
+{
+ if (mNeedsRepaint) {
+ if (mTargetSize != mBufferSize) {
+ //If the buffer hasn't changed yet, we need at least two redraws,
+ //once to get the new buffer size and propagate the geometry changes
+ //and the second to redraw the content at the new size
+ return 2;
+ } else {
+ // The buffer size has already been updated so we only need one redraw
+ // to render at the new size
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void QMirClientSurface::onSwapBuffersDone()
+{
+#if !defined(QT_NO_DEBUG)
+ static int sFrameNumber = 0;
+ ++sFrameNumber;
+#endif
+
+ EGLint eglSurfaceWidth = -1;
+ EGLint eglSurfaceHeight = -1;
+ eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &eglSurfaceWidth);
+ eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &eglSurfaceHeight);
+
+ const bool validSize = eglSurfaceWidth > 0 && eglSurfaceHeight > 0;
+
+ if (validSize && (mBufferSize.width() != eglSurfaceWidth || mBufferSize.height() != eglSurfaceHeight)) {
+
+ DLOG("[ubuntumirclient QPA] onSwapBuffersDone(window=%p) [%d] - size changed (%d, %d) => (%d, %d)",
+ mWindow, sFrameNumber, mBufferSize.width(), mBufferSize.height(), eglSurfaceWidth, eglSurfaceHeight);
+
+ mBufferSize.rwidth() = eglSurfaceWidth;
+ mBufferSize.rheight() = eglSurfaceHeight;
+
+ QRect newGeometry = mPlatformWindow->geometry();
+ newGeometry.setSize(mBufferSize);
+
+ mPlatformWindow->QPlatformWindow::setGeometry(newGeometry);
+ QWindowSystemInterface::handleGeometryChange(mWindow, newGeometry);
+ } else {
+#if 0
+ DLOG("[ubuntumirclient QPA] onSwapBuffersDone(window=%p) [%d] - buffer size (%d,%d)",
+ mWindow, sFrameNumber, mBufferSize.width(), mBufferSize.height());
+#endif
+ }
+}
+
+void QMirClientSurface::surfaceEventCallback(MirSurface *surface, const MirEvent *event, void* context)
+{
+ Q_UNUSED(surface);
+ Q_ASSERT(context != nullptr);
+
+ auto s = static_cast<QMirClientSurface *>(context);
+ s->postEvent(event);
+}
+
+void QMirClientSurface::postEvent(const MirEvent *event)
+{
+ if (mir_event_type_resize == mir_event_get_type(event)) {
+ // TODO: The current event queue just accumulates all resize events;
+ // It would be nicer if we could update just one event if that event has not been dispatched.
+ // As a workaround, we use the width/height as an identifier of this latest event
+ // so the event handler (handleSurfaceResized) can discard/ignore old ones.
+ const auto resizeEvent = mir_event_get_resize_event(event);
+ const auto width = mir_resize_event_get_width(resizeEvent);
+ const auto height = mir_resize_event_get_height(resizeEvent);
+ DLOG("[ubuntumirclient QPA] resizeEvent(window=%p, width=%d, height=%d)", mWindow, width, height);
+
+ QMutexLocker lock(&mTargetSizeMutex);
+ mTargetSize.rwidth() = width;
+ mTargetSize.rheight() = height;
+ }
+
+ mInput->postEvent(mPlatformWindow, event);
+}
+
+void QMirClientSurface::updateSurface()
+{
+ DLOG("[ubuntumirclient QPA] updateSurface(window=%p)", mWindow);
+
+ if (!mParented && mWindow->type() == Qt::Dialog) {
+ // The dialog may have been parented after creation time
+ // so morph it into a modal dialog
+ auto parent = transientParentFor(mWindow);
+ if (parent) {
+ DLOG("[ubuntumirclient QPA] updateSurface(window=%p) dialog now parented", mWindow);
+ mParented = true;
+ Spec spec{mir_connection_create_spec_for_changes(mConnection)};
+ mir_surface_spec_set_parent(spec.get(), parent->mirSurface());
+ mir_surface_apply_spec(mMirSurface, spec.get());
+ }
+ }
+}
+
+QMirClientWindow::QMirClientWindow(QWindow *w, const QSharedPointer<QMirClientClipboard> &clipboard, QMirClientScreen *screen,
+ QMirClientInput *input, MirConnection *connection)
+ : QObject(nullptr)
+ , QPlatformWindow(w)
+ , mId(makeId())
+ , mClipboard(clipboard)
+ , mSurface(new QMirClientSurface{this, screen, input, connection})
+{
+ DLOG("[ubuntumirclient QPA] QMirClientWindow(window=%p, screen=%p, input=%p, surf=%p)", w, screen, input, mSurface.get());
+}
+
+QMirClientWindow::~QMirClientWindow()
+{
+ DLOG("[ubuntumirclient QPA] ~QMirClientWindow(window=%p)", this);
+}
+
+void QMirClientWindow::handleSurfaceResized(int width, int height)
+{
+ QMutexLocker lock(&mMutex);
+ DLOG("[ubuntumirclient QPA] handleSurfaceResize(window=%p, width=%d, height=%d)", window(), width, height);
+
+ mSurface->handleSurfaceResized(width, height);
+
+ // This resize event could have occurred just after the last buffer swap for this window.
+ // This means the client may still be holding a buffer with the older size. The first redraw call
+ // will then render at the old size. After swapping the client now will get a new buffer with the
+ // updated size but it still needs re-rendering so another redraw may be needed.
+ // A mir API to drop the currently held buffer would help here, so that we wouldn't have to redraw twice
+ auto const numRepaints = mSurface->needsRepaint();
+ DLOG("[ubuntumirclient QPA] handleSurfaceResize(window=%p) redraw %d times", window(), numRepaints);
+ for (int i = 0; i < numRepaints; i++) {
+ DLOG("[ubuntumirclient QPA] handleSurfaceResize(window=%p) repainting width=%d, height=%d", window(), geometry().size().width(), geometry().size().height());
+ QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(), geometry().size()));
+ }
+}
+
+void QMirClientWindow::handleSurfaceFocused()
+{
+ DLOG("[ubuntumirclient QPA] handleSurfaceFocused(window=%p)", window());
+
+ // System clipboard contents might have changed while this window was unfocused and without
+ // this process getting notified about it because it might have been suspended (due to
+ // application lifecycle policies), thus unable to listen to any changes notified through
+ // D-Bus.
+ // Therefore let's ensure we are up to date with the system clipboard now that we are getting
+ // focused again.
+ mClipboard->requestDBusClipboardContents();
+}
+
+void QMirClientWindow::setWindowState(Qt::WindowState state)
+{
+ QMutexLocker lock(&mMutex);
+ DLOG("[ubuntumirclient QPA] setWindowState(window=%p, %s)", this, qtWindowStateToStr(state));
+ mSurface->setState(state);
+
+ updatePanelHeightHack(state);
+}
+
+/*
+ FIXME: Mir does not let clients know the position of their windows in the virtual
+ desktop space. So we have this ugly hack that assumes a phone situation where the
+ window is always on the top-left corner, right below the indicators panel if not
+ in fullscreen.
+ */
+void QMirClientWindow::updatePanelHeightHack(Qt::WindowState state)
+{
+ if (state == Qt::WindowFullScreen && geometry().y() != 0) {
+ QRect newGeometry = geometry();
+ newGeometry.setY(0);
+ QPlatformWindow::setGeometry(newGeometry);
+ QWindowSystemInterface::handleGeometryChange(window(), newGeometry);
+ } else if (geometry().y() == 0) {
+ QRect newGeometry = geometry();
+ newGeometry.setY(panelHeight());
+ QPlatformWindow::setGeometry(newGeometry);
+ QWindowSystemInterface::handleGeometryChange(window(), newGeometry);
+ }
+}
+
+void QMirClientWindow::setGeometry(const QRect& rect)
+{
+ QMutexLocker lock(&mMutex);
+ DLOG("[ubuntumirclient QPA] setGeometry (window=%p, x=%d, y=%d, width=%d, height=%d)",
+ window(), rect.x(), rect.y(), rect.width(), rect.height());
+
+ //NOTE: mir surfaces cannot be moved by the client so ignore the topLeft coordinates
+ const auto newSize = rect.size();
+ auto newGeometry = geometry();
+ newGeometry.setSize(newSize);
+ QPlatformWindow::setGeometry(newGeometry);
+
+ mSurface->resize(newSize);
+}
+
+void QMirClientWindow::setVisible(bool visible)
+{
+ QMutexLocker lock(&mMutex);
+ DLOG("[ubuntumirclient QPA] setVisible (window=%p, visible=%s)", window(), visible ? "true" : "false");
+
+ mSurface->setVisible(visible);
+ const QRect& exposeRect = visible ? QRect(QPoint(), geometry().size()) : QRect();
+
+ lock.unlock();
+ QWindowSystemInterface::handleExposeEvent(window(), exposeRect);
+ QWindowSystemInterface::flushWindowSystemEvents();
+}
+
+void QMirClientWindow::setWindowTitle(const QString& title)
+{
+ QMutexLocker lock(&mMutex);
+ DLOG("[ubuntumirclient QPA] setWindowTitle(window=%p) title=%s)", window(), title.toUtf8().constData());
+ mSurface->updateTitle(title);
+}
+
+void QMirClientWindow::propagateSizeHints()
+{
+ QMutexLocker lock(&mMutex);
+ const auto win = window();
+ DLOG("[ubuntumirclient QPA] propagateSizeHints(window=%p) min(%d,%d), max(%d,%d) increment(%d, %d)",
+ win, win->minimumSize().width(), win->minimumSize().height(),
+ win->maximumSize().width(), win->maximumSize().height(),
+ win->sizeIncrement().width(), win->sizeIncrement().height());
+ mSurface->setSizingConstraints(win->minimumSize(), win->maximumSize(), win->sizeIncrement());
+}
+
+void* QMirClientWindow::eglSurface() const
+{
+ return mSurface->eglSurface();
+}
+
+MirSurface *QMirClientWindow::mirSurface() const
+{
+ return mSurface->mirSurface();
+}
+
+WId QMirClientWindow::winId() const
+{
+ return mId;
+}
+
+void QMirClientWindow::onSwapBuffersDone()
+{
+ QMutexLocker lock(&mMutex);
+ mSurface->onSwapBuffersDone();
+}