1
// This file is part of QtUbuntu, a set of Qt components for Ubuntu.
2
// Copyright © 2013 Canonical Ltd.
4
// This program is free software: you can redistribute it and/or modify it under
5
// the terms of the GNU Lesser General Public License version 3, as published by
6
// the Free Software Foundation.
8
// This program is distributed in the hope that it will be useful, but WITHOUT
9
// ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10
// SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
// Lesser General Public License for more details.
13
// You should have received a copy of the GNU Lesser General Public License
14
// along with this program. If not, see <http://www.gnu.org/licenses/>.
2
* Copyright (C) 2014 Canonical, Ltd.
4
* This program is free software: you can redistribute it and/or modify it under
5
* the terms of the GNU Lesser General Public License version 3, as published by
6
* the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful, but WITHOUT
9
* ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10
* SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
* Lesser General Public License for more details.
13
* You should have received a copy of the GNU Lesser General Public License
14
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16
19
#include "window.h"
17
20
#include "screen.h"
18
21
#include "logging.h"
19
#include <qpa/qwindowsysteminterface.h>
21
QUbuntuBaseWindow::QUbuntuBaseWindow(QWindow* w, QUbuntuBaseScreen* screen)
24
, eglSurface_(EGL_NO_SURFACE) {
25
DASSERT(screen != NULL);
28
DLOG("QUbuntuBaseWindow::QUbuntuBaseWindow (this=%p, screen=%p)", this, screen);
31
QUbuntuBaseWindow::~QUbuntuBaseWindow() {
32
DLOG("QUbuntuBaseWindow::~QUbuntuBaseWindow");
35
void QUbuntuBaseWindow::createEGLSurface(EGLNativeWindowType nativeWindow) {
36
DLOG("QUbuntuBaseWindow::createEGLSurface (this=%p, nativeWindow=%p)", this, reinterpret_cast<void*>(nativeWindow));
37
ASSERT((eglSurface_ = eglCreateWindowSurface(
38
screen_->eglDisplay(), screen_->eglConfig(), nativeWindow, NULL)) != EGL_NO_SURFACE);
41
void QUbuntuBaseWindow::destroyEGLSurface() {
42
DLOG("QUbuntuBaseWindow::destroyEGLSurface (this=%p)", this);
43
if (eglSurface_ != EGL_NO_SURFACE) {
44
eglDestroySurface(screen_->eglDisplay(), eglSurface_);
45
eglSurface_ = EGL_NO_SURFACE;
24
#include <qpa/qwindowsysteminterface.h>
25
#include <qpa/qwindowsysteminterface.h>
27
#include <QMutexLocker>
32
#include <ubuntu/application/instance.h>
33
#include <ubuntu/application/ui/window.h>
37
#define IS_OPAQUE_FLAG 1
39
class UbuntuWindowPrivate
42
void createEGLSurface(EGLNativeWindowType nativeWindow);
43
void destroyEGLSurface();
46
EGLSurface eglSurface;
50
Qt::WindowState state;
52
UApplicationInstance* uaInstance;
53
UAUiWindowProperties* wProps;
55
QSize targetBufferSize;
57
int panelHeight; // FIXME - should be removed
60
static void eventCallback(void* context, const WindowEvent* event)
62
DLOG("eventCallback (context=%p, event=%p)", context, event);
63
DASSERT(context != NULL);
64
UbuntuWindow* platformWindow = static_cast<UbuntuWindow*>(context);
65
platformWindow->priv()->input->postEvent(platformWindow, event);
68
UbuntuWindow::UbuntuWindow(QWindow* w, UbuntuScreen* screen,
69
UbuntuInput* input, UApplicationInstance* instance)
70
: QObject(nullptr), QPlatformWindow(w)
72
DASSERT(screen != NULL);
74
d = new UbuntuWindowPrivate;
76
d->eglSurface = EGL_NO_SURFACE;
78
d->state = window()->windowState();
79
d->uaInstance = instance;
84
// Use client geometry if set explicitly, use available screen geometry otherwise.
85
d->geometry = window()->geometry() != screen->geometry() ?
86
window()->geometry() : screen->availableGeometry();
88
DLOG("UbuntuWindow::UbuntuWindow (this=%p, w=%p, screen=%p, input=%p)", this, w, screen, input);
90
// FIXME - in order to work around https://bugs.launchpad.net/mir/+bug/1346633
91
// we need to guess the panel height (3GU + 2DP)
92
const int defaultGridUnit = 8;
93
int gridUnit = defaultGridUnit;
94
QByteArray gridUnitString = qgetenv("GRID_UNIT_PX");
95
if (!gridUnitString.isEmpty()) {
97
gridUnit = gridUnitString.toInt(&ok);
99
gridUnit = defaultGridUnit;
102
qreal densityPixelRatio = static_cast<qreal>(gridUnit) / defaultGridUnit;
103
d->panelHeight = gridUnit * 3 + qFloor(densityPixelRatio) * 2;
106
UbuntuWindow::~UbuntuWindow()
108
DLOG("UbuntuWindow::~UbuntuWindow");
109
d->destroyEGLSurface();
110
ua_ui_window_destroy(d->window);
114
void UbuntuWindowPrivate::createEGLSurface(EGLNativeWindowType nativeWindow)
116
DLOG("UbuntuWindowPrivate::createEGLSurface (this=%p, nativeWindow=%p)",
117
this, reinterpret_cast<void*>(nativeWindow));
119
eglSurface = eglCreateWindowSurface(screen->eglDisplay(), screen->eglConfig(),
120
nativeWindow, nullptr);
122
DASSERT(eglSurface != EGL_NO_SURFACE);
125
void UbuntuWindowPrivate::destroyEGLSurface()
127
DLOG("UbuntuWindowPrivate::destroyEGLSurface (this=%p)", this);
128
if (eglSurface != EGL_NO_SURFACE) {
129
eglDestroySurface(screen->eglDisplay(), eglSurface);
130
eglSurface = EGL_NO_SURFACE;
134
void UbuntuWindow::createWindow()
136
DLOG("UbuntuWindow::createWindow (this=%p)", this);
138
// Get surface role and flags.
139
QVariant roleVariant = window()->property("role");
140
int role = roleVariant.isValid() ? roleVariant.toUInt() : 1; // 1 is the default role for apps.
141
QVariant opaqueVariant = window()->property("opaque");
142
uint flags = opaqueVariant.isValid() ?
143
opaqueVariant.toUInt() ? static_cast<uint>(IS_OPAQUE_FLAG) : 0 : 0;
145
// FIXME(loicm) Opaque flag is forced for now for non-system sessions (applications) for
146
// performance reasons.
147
flags |= static_cast<uint>(IS_OPAQUE_FLAG);
149
const QByteArray title = (!window()->title().isNull()) ? window()->title().toUtf8() : "Window 1"; // legacy title
151
#if !defined(QT_NO_DEBUG)
152
LOG("role: '%d'", role);
153
LOG("flags: '%s'", (flags & static_cast<uint>(1)) ? "Opaque" : "NotOpaque");
154
LOG("title: '%s'", title.constData());
157
// Get surface geometry.
159
if (d->state == Qt::WindowFullScreen) {
160
printf("UbuntuWindow - fullscreen geometry\n");
161
geometry = screen()->geometry();
162
} else if (d->state == Qt::WindowMaximized) {
163
printf("UbuntuWindow - maximized geometry\n");
164
geometry = screen()->availableGeometry();
166
printf("UbuntuWindow - regular geometry\n");
167
geometry = d->geometry;
170
DLOG("[ubuntumirclient QPA] creating surface at (%d, %d) with size (%d, %d) with title '%s'\n",
171
geometry.x(), geometry.y(), geometry.width(), geometry.height(), title.data());
173
// Setup platform window creation properties
174
d->wProps = ua_ui_window_properties_new_for_normal_window();
175
ua_ui_window_properties_set_titlen(d->wProps, title.data(), title.size());
176
ua_ui_window_properties_set_role(d->wProps, static_cast<UAUiWindowRole>(role));
177
ua_ui_window_properties_set_event_cb_and_ctx(d->wProps, &eventCallback, this);
178
ua_ui_window_properties_set_dimensions(d->wProps, geometry.width(), geometry.height());
180
// Create platform window
181
d->window = ua_ui_window_new_for_application_with_properties(d->uaInstance, d->wProps);
183
if (geometry.x() != 0 || geometry.y() != 0)
184
ua_ui_window_move(d->window, geometry.x(), geometry.y());
186
DASSERT(d->window != NULL);
187
d->createEGLSurface(ua_ui_window_get_native_type(d->window));
188
if (d->state == Qt::WindowFullScreen) {
189
ua_ui_window_request_fullscreen(d->window);
192
// Window manager can give us a final size different from what we asked for
193
// so let's check what we ended up getting
195
uint32_t width, height;
196
ua_ui_window_get_size(d->window, &width, &height);
197
geometry.setWidth(width);
198
geometry.setHeight(height);
201
DLOG("[ubuntumirclient QPA] created surface has size (%d, %d)",
202
geometry.width(), geometry.height());
204
// Assume that the buffer size matches the surface size at creation time
205
d->bufferSize = geometry.size();
207
// Tell Qt about the geometry.
208
QWindowSystemInterface::handleGeometryChange(window(), geometry);
209
QPlatformWindow::setGeometry(geometry);
212
void UbuntuWindow::moveResize(const QRect& rect)
214
fprintf(stderr, "\nQUbuntuWindow::moveResize (this=%p, x=%d, y=%d, w=%d, h=%d)\n", this,
215
rect.x(), rect.y(), rect.width(), rect.height());
216
LOG("UbuntuWindow::moveResize(width=%d, height=%d)", rect.width(), rect.height());
217
ua_ui_window_move(d->window, rect.x(), rect.y());
218
ua_ui_window_resize(d->window, rect.width(), rect.height());
219
QWindowSystemInterface::handleGeometryChange(window(), rect);
220
QPlatformWindow::setGeometry(rect);
223
void UbuntuWindow::handleSurfaceResize(int width, int height)
225
LOG("UbuntuWindow::handleSurfaceResize(width=%d, height=%d)", width, height);
227
// The current buffer size hasn't actually changed. so just render on it and swap
228
// buffers until we render on a buffer with the target size.
230
bool shouldSwapBuffers;
233
QMutexLocker(&d->mutex);
234
d->targetBufferSize.rwidth() = width;
235
d->targetBufferSize.rheight() = height;
237
shouldSwapBuffers = d->bufferSize != d->targetBufferSize;
240
if (shouldSwapBuffers) {
241
QWindowSystemInterface::handleExposeEvent(window(), geometry());
243
qWarning("[ubuntumirclient QPA] UbuntuWindow::handleSurfaceResize"
244
" current buffer already has the target size");
245
d->targetBufferSize = QSize();
249
void UbuntuWindow::handleSurfaceFocusChange(bool focused)
251
LOG("UbuntuWindow::handleSurfaceFocusChange(focused=%s)", focused ? "true" : "false");
252
QWindow *activatedWindow = focused ? window() : nullptr;
253
QWindowSystemInterface::handleWindowActivated(activatedWindow, Qt::ActiveWindowFocusReason);
256
void UbuntuWindow::handleBufferResize(int width, int height)
258
DLOG("UbuntuWindow::handleBufferResize(width=%d, height=%d)", width, height);
264
QMutexLocker(&d->mutex);
265
oldGeometry = geometry();
266
newGeometry = oldGeometry;
267
newGeometry.setWidth(width);
268
newGeometry.setHeight(height);
270
d->bufferSize.rwidth() = width;
271
d->bufferSize.rheight() = height;
272
d->geometry = newGeometry;
275
QPlatformWindow::setGeometry(newGeometry);
276
QWindowSystemInterface::handleGeometryChange(window(), newGeometry, oldGeometry);
277
QWindowSystemInterface::handleExposeEvent(window(), newGeometry);
280
void UbuntuWindow::forceRedraw()
282
QWindowSystemInterface::handleExposeEvent(window(), geometry());
285
void UbuntuWindow::setWindowState(Qt::WindowState state)
287
QMutexLocker(&d->mutex);
288
if (state == d->state)
292
case Qt::WindowNoState:
293
DLOG("setting window state: 'NoState'");
294
d->state = Qt::WindowNoState;
297
case Qt::WindowFullScreen:
298
DLOG("setting window state: 'FullScreen'");
299
ua_ui_window_request_fullscreen(d->window);
300
d->state = Qt::WindowFullScreen;
303
case Qt::WindowMaximized:
304
DLOG("setting window state: 'Maximized'");
305
d->state = Qt::WindowMaximized;
308
case Qt::WindowActive:
309
case Qt::WindowMinimized:
311
DLOG("setting window state: 'Active|Minimized'");
316
void UbuntuWindow::setGeometry(const QRect& rect)
318
DLOG("UbuntuWindow::setGeometry (this=%p)", this);
323
QMutexLocker(&d->mutex);
325
doMoveResize = d->state != Qt::WindowFullScreen && d->state != Qt::WindowMaximized;
333
void UbuntuWindow::setVisible(bool visible)
335
DLOG("UbuntuWindow::setVisible (this=%p, visible=%s)", this, visible ? "true" : "false");
338
ua_ui_window_show(d->window);
339
QWindowSystemInterface::handleExposeEvent(window(), QRect());
340
QWindowSystemInterface::flushWindowSystemEvents();
342
ua_ui_window_hide(d->window);
346
void* UbuntuWindow::eglSurface() const
348
return d->eglSurface;
351
WId UbuntuWindow::winId() const
356
void UbuntuWindow::onBuffersSwapped_threadSafe(int newBufferWidth, int newBufferHeight)
358
QMutexLocker(&d->mutex);
360
bool sizeKnown = newBufferWidth > 0 && newBufferHeight > 0;
362
if (sizeKnown && (d->bufferSize.width() != newBufferWidth ||
363
d->bufferSize.height() != newBufferHeight)) {
364
QMetaObject::invokeMethod(this, "handleBufferResize",
365
Qt::QueuedConnection,
366
Q_ARG(int, newBufferWidth), Q_ARG(int, newBufferHeight));
368
// buffer size hasn't changed
369
if (d->targetBufferSize.isValid()) {
370
if (d->bufferSize != d->targetBufferSize) {
371
// but we still didn't reach the promised buffer size from the mir resize event.
372
// thus keep swapping buffers
373
QMetaObject::invokeMethod(this, "forceRedraw", Qt::QueuedConnection);
375
// target met. we have just provided a render with the target size and
376
// can therefore finally rest.
377
d->targetBufferSize = QSize();
383
QPoint UbuntuWindow::mapToGlobal(const QPoint &position) const
386
* FIXME: Autopilot relies on being able to convert coordinates relative of the window
387
* into absolute screen coordinates. Mir does not allow this, see bug lp:1346633
388
* Until there's a correct way to perform this transformation agreed, this horrible hack
389
* guesses the transformation heuristically.
391
* Assumption: this method only used on phone devices!
394
if (d->state == Qt::WindowFullScreen)
397
// FIXME: update when enabling rotation in shell
398
return QPoint(position.x(), position.y() + d->panelHeight);