~gerboland/qtubuntu/fix_1351024

« back to all changes in this revision

Viewing changes to src/ubuntumirclient/window.cpp

  • Committer: CI bot
  • Author(s): Gerry Boland, Daniel d'Andrada
  • Date: 2014-07-28 02:37:08 UTC
  • mfrom: (231.1.14 ubuntumirclient-only)
  • Revision ID: ps-jenkins@lists.canonical.com-20140728023708-iv11mcwrd0pzjv0c
Major refactor: remove SurfaceFlinger & Mir in-server-process-client support, flatten class hierarchy. Only Mir client QPA remains


 
Approved by: Michael Terry, Ricardo Mendoza

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
// This file is part of QtUbuntu, a set of Qt components for Ubuntu.
2
 
// Copyright © 2013 Canonical Ltd.
3
 
//
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.
7
 
//
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.
12
 
//
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/>.
 
1
/*
 
2
 * Copyright (C) 2014 Canonical, Ltd.
 
3
 *
 
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.
 
7
 *
 
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.
 
12
 *
 
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/>.
 
15
 */
15
16
 
 
17
// Local
 
18
#include "input.h"
16
19
#include "window.h"
17
20
#include "screen.h"
18
21
#include "logging.h"
19
 
#include <qpa/qwindowsysteminterface.h>
20
 
 
21
 
QUbuntuBaseWindow::QUbuntuBaseWindow(QWindow* w, QUbuntuBaseScreen* screen)
22
 
    : QPlatformWindow(w)
23
 
    , screen_(screen)
24
 
    , eglSurface_(EGL_NO_SURFACE) {
25
 
  DASSERT(screen != NULL);
26
 
  static int id = 1;
27
 
  id_ = id++;
28
 
  DLOG("QUbuntuBaseWindow::QUbuntuBaseWindow (this=%p, screen=%p)", this, screen);
29
 
}
30
 
 
31
 
QUbuntuBaseWindow::~QUbuntuBaseWindow() {
32
 
  DLOG("QUbuntuBaseWindow::~QUbuntuBaseWindow");
33
 
}
34
 
 
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);
39
 
}
40
 
 
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;
 
22
 
 
23
// Qt
 
24
#include <qpa/qwindowsysteminterface.h>
 
25
#include <qpa/qwindowsysteminterface.h>
 
26
#include <QMutex>
 
27
#include <QMutexLocker>
 
28
#include <QSize>
 
29
#include <QtMath>
 
30
 
 
31
// Platform API
 
32
#include <ubuntu/application/instance.h>
 
33
#include <ubuntu/application/ui/window.h>
 
34
 
 
35
#include <EGL/egl.h>
 
36
 
 
37
#define IS_OPAQUE_FLAG 1
 
38
 
 
39
class UbuntuWindowPrivate
 
40
{
 
41
public:
 
42
    void createEGLSurface(EGLNativeWindowType nativeWindow);
 
43
    void destroyEGLSurface();
 
44
 
 
45
    UbuntuScreen* screen;
 
46
    EGLSurface eglSurface;
 
47
    WId id;
 
48
    UbuntuInput* input;
 
49
    UAUiWindow* window;
 
50
    Qt::WindowState state;
 
51
    QRect geometry;
 
52
    UApplicationInstance* uaInstance;
 
53
    UAUiWindowProperties* wProps;
 
54
    QSize bufferSize;
 
55
    QSize targetBufferSize;
 
56
    QMutex mutex;
 
57
    int panelHeight; // FIXME - should be removed
 
58
};
 
59
 
 
60
static void eventCallback(void* context, const WindowEvent* event)
 
61
{
 
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);
 
66
}
 
67
 
 
68
UbuntuWindow::UbuntuWindow(QWindow* w, UbuntuScreen* screen,
 
69
                           UbuntuInput* input, UApplicationInstance* instance)
 
70
    : QObject(nullptr), QPlatformWindow(w)
 
71
{
 
72
    DASSERT(screen != NULL);
 
73
 
 
74
    d = new UbuntuWindowPrivate;
 
75
    d->screen = screen;
 
76
    d->eglSurface = EGL_NO_SURFACE;
 
77
    d->input = input;
 
78
    d->state = window()->windowState();
 
79
    d->uaInstance = instance;
 
80
 
 
81
    static int id = 1;
 
82
    d->id = id++;
 
83
 
 
84
    // Use client geometry if set explicitly, use available screen geometry otherwise.
 
85
    d->geometry = window()->geometry() != screen->geometry() ?
 
86
        window()->geometry() : screen->availableGeometry();
 
87
    createWindow();
 
88
    DLOG("UbuntuWindow::UbuntuWindow (this=%p, w=%p, screen=%p, input=%p)", this, w, screen, input);
 
89
 
 
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()) {
 
96
        bool ok;
 
97
        gridUnit = gridUnitString.toInt(&ok);
 
98
        if (!ok) {
 
99
            gridUnit = defaultGridUnit;
 
100
        }
 
101
    }
 
102
    qreal densityPixelRatio = static_cast<qreal>(gridUnit) / defaultGridUnit;
 
103
    d->panelHeight = gridUnit * 3 + qFloor(densityPixelRatio) * 2;
 
104
}
 
105
 
 
106
UbuntuWindow::~UbuntuWindow()
 
107
{
 
108
    DLOG("UbuntuWindow::~UbuntuWindow");
 
109
    d->destroyEGLSurface();
 
110
    ua_ui_window_destroy(d->window);
 
111
    delete d;
 
112
}
 
113
 
 
114
void UbuntuWindowPrivate::createEGLSurface(EGLNativeWindowType nativeWindow)
 
115
{
 
116
  DLOG("UbuntuWindowPrivate::createEGLSurface (this=%p, nativeWindow=%p)",
 
117
          this, reinterpret_cast<void*>(nativeWindow));
 
118
 
 
119
  eglSurface = eglCreateWindowSurface(screen->eglDisplay(), screen->eglConfig(),
 
120
          nativeWindow, nullptr);
 
121
 
 
122
  DASSERT(eglSurface != EGL_NO_SURFACE);
 
123
}
 
124
 
 
125
void UbuntuWindowPrivate::destroyEGLSurface()
 
126
{
 
127
    DLOG("UbuntuWindowPrivate::destroyEGLSurface (this=%p)", this);
 
128
    if (eglSurface != EGL_NO_SURFACE) {
 
129
        eglDestroySurface(screen->eglDisplay(), eglSurface);
 
130
        eglSurface = EGL_NO_SURFACE;
 
131
    }
 
132
}
 
133
 
 
134
void UbuntuWindow::createWindow()
 
135
{
 
136
    DLOG("UbuntuWindow::createWindow (this=%p)", this);
 
137
 
 
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;
 
144
 
 
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);
 
148
 
 
149
    const QByteArray title = (!window()->title().isNull()) ? window()->title().toUtf8() : "Window 1"; // legacy title
 
150
 
 
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());
 
155
    #endif
 
156
 
 
157
    // Get surface geometry.
 
158
    QRect 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();
 
165
    } else {
 
166
        printf("UbuntuWindow - regular geometry\n");
 
167
        geometry = d->geometry;
 
168
    }
 
169
 
 
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());
 
172
 
 
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());
 
179
 
 
180
    // Create platform window
 
181
    d->window = ua_ui_window_new_for_application_with_properties(d->uaInstance, d->wProps);
 
182
 
 
183
    if (geometry.x() != 0 || geometry.y() != 0)
 
184
        ua_ui_window_move(d->window, geometry.x(), geometry.y());
 
185
 
 
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);
 
190
    }
 
191
 
 
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
 
194
    {
 
195
        uint32_t width, height;
 
196
        ua_ui_window_get_size(d->window, &width, &height);
 
197
        geometry.setWidth(width);
 
198
        geometry.setHeight(height);
 
199
    }
 
200
 
 
201
    DLOG("[ubuntumirclient QPA] created surface has size (%d, %d)",
 
202
            geometry.width(), geometry.height());
 
203
 
 
204
    // Assume that the buffer size matches the surface size at creation time
 
205
    d->bufferSize = geometry.size();
 
206
 
 
207
    // Tell Qt about the geometry.
 
208
    QWindowSystemInterface::handleGeometryChange(window(), geometry);
 
209
    QPlatformWindow::setGeometry(geometry);
 
210
}
 
211
 
 
212
void UbuntuWindow::moveResize(const QRect& rect)
 
213
{
 
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);
 
221
}
 
222
 
 
223
void UbuntuWindow::handleSurfaceResize(int width, int height)
 
224
{
 
225
    LOG("UbuntuWindow::handleSurfaceResize(width=%d, height=%d)", width, height);
 
226
 
 
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.
 
229
 
 
230
    bool shouldSwapBuffers;
 
231
 
 
232
    {
 
233
        QMutexLocker(&d->mutex);
 
234
        d->targetBufferSize.rwidth() = width;
 
235
        d->targetBufferSize.rheight() = height;
 
236
 
 
237
        shouldSwapBuffers = d->bufferSize != d->targetBufferSize;
 
238
    }
 
239
 
 
240
    if (shouldSwapBuffers) {
 
241
        QWindowSystemInterface::handleExposeEvent(window(), geometry());
 
242
    } else {
 
243
        qWarning("[ubuntumirclient QPA] UbuntuWindow::handleSurfaceResize"
 
244
                 " current buffer already has the target size");
 
245
        d->targetBufferSize = QSize();
 
246
    }
 
247
}
 
248
 
 
249
void UbuntuWindow::handleSurfaceFocusChange(bool focused)
 
250
{
 
251
    LOG("UbuntuWindow::handleSurfaceFocusChange(focused=%s)", focused ? "true" : "false");
 
252
    QWindow *activatedWindow = focused ? window() : nullptr;
 
253
    QWindowSystemInterface::handleWindowActivated(activatedWindow, Qt::ActiveWindowFocusReason);
 
254
}
 
255
 
 
256
void UbuntuWindow::handleBufferResize(int width, int height)
 
257
{
 
258
    DLOG("UbuntuWindow::handleBufferResize(width=%d, height=%d)", width, height);
 
259
 
 
260
    QRect oldGeometry;
 
261
    QRect newGeometry;
 
262
 
 
263
    {
 
264
        QMutexLocker(&d->mutex);
 
265
        oldGeometry = geometry();
 
266
        newGeometry = oldGeometry;
 
267
        newGeometry.setWidth(width);
 
268
        newGeometry.setHeight(height);
 
269
 
 
270
        d->bufferSize.rwidth() = width;
 
271
        d->bufferSize.rheight() = height;
 
272
        d->geometry = newGeometry;
 
273
    }
 
274
 
 
275
    QPlatformWindow::setGeometry(newGeometry);
 
276
    QWindowSystemInterface::handleGeometryChange(window(), newGeometry, oldGeometry);
 
277
    QWindowSystemInterface::handleExposeEvent(window(), newGeometry);
 
278
}
 
279
 
 
280
void UbuntuWindow::forceRedraw()
 
281
{
 
282
    QWindowSystemInterface::handleExposeEvent(window(), geometry());
 
283
}
 
284
 
 
285
void UbuntuWindow::setWindowState(Qt::WindowState state)
 
286
{
 
287
    QMutexLocker(&d->mutex);
 
288
    if (state == d->state)
 
289
        return;
 
290
 
 
291
    switch (state) {
 
292
    case Qt::WindowNoState:
 
293
        DLOG("setting window state: 'NoState'");
 
294
        d->state = Qt::WindowNoState;
 
295
        break;
 
296
 
 
297
    case Qt::WindowFullScreen:
 
298
        DLOG("setting window state: 'FullScreen'");
 
299
        ua_ui_window_request_fullscreen(d->window);
 
300
        d->state = Qt::WindowFullScreen;
 
301
        break;
 
302
 
 
303
    case Qt::WindowMaximized:
 
304
        DLOG("setting window state: 'Maximized'");
 
305
        d->state = Qt::WindowMaximized;
 
306
        break;
 
307
 
 
308
    case Qt::WindowActive:
 
309
    case Qt::WindowMinimized:
 
310
    default:
 
311
        DLOG("setting window state: 'Active|Minimized'");
 
312
        break;
 
313
    }
 
314
}
 
315
 
 
316
void UbuntuWindow::setGeometry(const QRect& rect)
 
317
{
 
318
    DLOG("UbuntuWindow::setGeometry (this=%p)", this);
 
319
 
 
320
    bool doMoveResize;
 
321
 
 
322
    {
 
323
        QMutexLocker(&d->mutex);
 
324
        d->geometry = rect;
 
325
        doMoveResize = d->state != Qt::WindowFullScreen && d->state != Qt::WindowMaximized;
 
326
    }
 
327
 
 
328
    if (doMoveResize) {
 
329
        moveResize(rect);
 
330
    }
 
331
}
 
332
 
 
333
void UbuntuWindow::setVisible(bool visible)
 
334
{
 
335
  DLOG("UbuntuWindow::setVisible (this=%p, visible=%s)", this, visible ? "true" : "false");
 
336
 
 
337
  if (visible) {
 
338
    ua_ui_window_show(d->window);
 
339
    QWindowSystemInterface::handleExposeEvent(window(), QRect());
 
340
    QWindowSystemInterface::flushWindowSystemEvents();
 
341
  } else {
 
342
    ua_ui_window_hide(d->window);
46
343
  }
47
344
}
 
345
 
 
346
void* UbuntuWindow::eglSurface() const
 
347
{
 
348
    return d->eglSurface;
 
349
}
 
350
 
 
351
WId UbuntuWindow::winId() const
 
352
{
 
353
    return d->id;
 
354
}
 
355
 
 
356
void UbuntuWindow::onBuffersSwapped_threadSafe(int newBufferWidth, int newBufferHeight)
 
357
{
 
358
    QMutexLocker(&d->mutex);
 
359
 
 
360
    bool sizeKnown = newBufferWidth > 0 && newBufferHeight > 0;
 
361
 
 
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));
 
367
    } else {
 
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);
 
374
            } else {
 
375
                // target met. we have just provided a render with the target size and
 
376
                // can therefore finally rest.
 
377
                d->targetBufferSize = QSize();
 
378
            }
 
379
        }
 
380
    }
 
381
}
 
382
 
 
383
QPoint UbuntuWindow::mapToGlobal(const QPoint &position) const
 
384
{
 
385
    /*
 
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.
 
390
     *
 
391
     * Assumption: this method only used on phone devices!
 
392
     */
 
393
 
 
394
    if (d->state == Qt::WindowFullScreen)
 
395
        return position;
 
396
 
 
397
    // FIXME: update when enabling rotation in shell
 
398
    return QPoint(position.x(), position.y() + d->panelHeight);
 
399
}