Merge lp:~gerboland/qtubuntu/active-decision-refactor into lp:qtubuntu

Proposed by Gerry Boland on 2016-10-13
Status: Merged
Approved by: Gerry Boland on 2016-10-26
Approved revision: 350
Merged at revision: 353
Proposed branch: lp:~gerboland/qtubuntu/active-decision-refactor
Merge into: lp:qtubuntu
Diff against target: 353 lines (+144/-48)
7 files modified
src/ubuntumirclient/appstatecontroller.cpp (+78/-0)
src/ubuntumirclient/appstatecontroller.h (+38/-0)
src/ubuntumirclient/integration.cpp (+10/-16)
src/ubuntumirclient/integration.h (+3/-0)
src/ubuntumirclient/ubuntumirclient.pro (+4/-2)
src/ubuntumirclient/window.cpp (+7/-29)
src/ubuntumirclient/window.h (+4/-1)
To merge this branch: bzr merge lp:~gerboland/qtubuntu/active-decision-refactor
Reviewer Review Type Date Requested Status
Unity8 CI Bot continuous-integration Approve on 2016-10-26
Nick Dedekind (community) 2016-10-13 Approve on 2016-10-13
Review via email: mp+308353@code.launchpad.net

Commit Message

Move all App State management logic to dedicated controller, use Timer to compress active-inactive-active invocations

To post a comment you must log in.
Unity8 CI Bot (unity8-ci-bot) wrote :

FAILED: Continuous integration, rev:350
https://unity8-jenkins.ubuntu.com/job/lp-qtubuntu-ci/144/
Executed test runs:

Click here to trigger a rebuild:
https://unity8-jenkins.ubuntu.com/job/lp-qtubuntu-ci/144/rebuild

review: Needs Fixing (continuous-integration)
Gerry Boland (gerboland) wrote :

To test, grab http://pastebin.ubuntu.com/23317914/

In your unity7 terminal, run:
mir_demo_server --x11-display=1000x740

and in another terminal, run:
QT_QPA_PLATFORM=ubuntumirclient qmlscene dual-window-active-test.qml

You'll get 2 windows, one red, one green. Clicking between them should not change the application active state. But starting another application and focusing its window should update the app active state.

Nick Dedekind (nick-dedekind) wrote :

Looks good

review: Approve
Unity8 CI Bot (unity8-ci-bot) wrote :

PASSED: Continuous integration, rev:
https://unity8-jenkins.ubuntu.com/job/lp-qtubuntu-ci/154/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build/3195
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/3223
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/3079
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/3079/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/3079
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/3079/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/3079
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/3079/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/3079
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/3079/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/3079
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/3079/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/3079
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/3079/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/3079
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/3079/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/3079
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/3079/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/3079
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/3079/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://unity8-jenkins.ubuntu.com/job/lp-qtubuntu-ci/154/rebuild

review: Approve (continuous-integration)
351. By Gerry Boland on 2016-11-04

Merge trunk and resolve conflicts

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'src/ubuntumirclient/appstatecontroller.cpp'
2--- src/ubuntumirclient/appstatecontroller.cpp 1970-01-01 00:00:00 +0000
3+++ src/ubuntumirclient/appstatecontroller.cpp 2016-11-04 13:21:25 +0000
4@@ -0,0 +1,78 @@
5+/*
6+ * Copyright (C) 2016 Canonical, Ltd.
7+ *
8+ * This program is free software: you can redistribute it and/or modify it under
9+ * the terms of the GNU Lesser General Public License version 3, as published by
10+ * the Free Software Foundation.
11+ *
12+ * This program is distributed in the hope that it will be useful, but WITHOUT
13+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
14+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+ * Lesser General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU Lesser General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ */
20+
21+#include "appstatecontroller.h"
22+
23+#include <qpa/qwindowsysteminterface.h>
24+
25+/*
26+ * UbuntuAppStateController - updates Qt's QApplication::applicationState property.
27+ *
28+ * Tries to avoid active-inactive-active invocations using a timer. The rapid state
29+ * change can confuse some applications.
30+ */
31+
32+UbuntuAppStateController::UbuntuAppStateController()
33+ : m_suspended(false)
34+ , m_lastActive(true)
35+{
36+ m_inactiveTimer.setSingleShot(true);
37+ m_inactiveTimer.setInterval(10);
38+ QObject::connect(&m_inactiveTimer, &QTimer::timeout, []()
39+ {
40+ QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationInactive);
41+ });
42+}
43+
44+void UbuntuAppStateController::setSuspended()
45+{
46+ m_inactiveTimer.stop();
47+ if (!m_suspended) {
48+ m_suspended = true;
49+
50+ QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationSuspended);
51+ }
52+}
53+
54+void UbuntuAppStateController::setResumed()
55+{
56+ m_inactiveTimer.stop();
57+ if (m_suspended) {
58+ m_suspended = false;
59+
60+ if (m_lastActive) {
61+ QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationActive);
62+ } else {
63+ QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationInactive);
64+ }
65+ }
66+}
67+
68+void UbuntuAppStateController::setWindowFocused(bool focused)
69+{
70+ if (m_suspended) {
71+ return;
72+ }
73+
74+ if (focused) {
75+ m_inactiveTimer.stop();
76+ QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationActive);
77+ } else {
78+ m_inactiveTimer.start();
79+ }
80+
81+ m_lastActive = focused;
82+}
83
84=== added file 'src/ubuntumirclient/appstatecontroller.h'
85--- src/ubuntumirclient/appstatecontroller.h 1970-01-01 00:00:00 +0000
86+++ src/ubuntumirclient/appstatecontroller.h 2016-11-04 13:21:25 +0000
87@@ -0,0 +1,38 @@
88+/*
89+ * Copyright (C) 2016 Canonical, Ltd.
90+ *
91+ * This program is free software: you can redistribute it and/or modify it under
92+ * the terms of the GNU Lesser General Public License version 3, as published by
93+ * the Free Software Foundation.
94+ *
95+ * This program is distributed in the hope that it will be useful, but WITHOUT
96+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
97+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
98+ * Lesser General Public License for more details.
99+ *
100+ * You should have received a copy of the GNU Lesser General Public License
101+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
102+ */
103+
104+#ifndef UBUNTUAPPSTATECONTROLLER_H
105+#define UBUNTUAPPSTATECONTROLLER_H
106+
107+#include <QTimer>
108+
109+class UbuntuAppStateController
110+{
111+public:
112+ UbuntuAppStateController();
113+
114+ void setSuspended();
115+ void setResumed();
116+
117+ void setWindowFocused(bool focused);
118+
119+private:
120+ bool m_suspended;
121+ bool m_lastActive;
122+ QTimer m_inactiveTimer;
123+};
124+
125+#endif // UBUNTUAPPSTATECONTROLLER_H
126
127=== modified file 'src/ubuntumirclient/integration.cpp'
128--- src/ubuntumirclient/integration.cpp 2016-10-04 16:20:07 +0000
129+++ src/ubuntumirclient/integration.cpp 2016-11-04 13:21:25 +0000
130@@ -47,30 +47,22 @@
131 #include <ubuntu/application/id.h>
132 #include <ubuntu/application/options.h>
133
134-static void resumedCallback(const UApplicationOptions *options, void* context)
135+static void resumedCallback(const UApplicationOptions */*options*/, void *context)
136 {
137- Q_UNUSED(options)
138- Q_UNUSED(context)
139- Q_ASSERT(context != NULL);
140- if (qGuiApp->focusWindow()) {
141- QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationActive);
142- } else {
143- QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationInactive);
144- }
145+ auto integration = static_cast<UbuntuClientIntegration*>(context);
146+ integration->appStateController()->setResumed();
147 }
148
149-static void aboutToStopCallback(UApplicationArchive *archive, void* context)
150+static void aboutToStopCallback(UApplicationArchive */*archive*/, void *context)
151 {
152- Q_UNUSED(archive)
153- Q_ASSERT(context != NULL);
154- UbuntuClientIntegration* integration = static_cast<UbuntuClientIntegration*>(context);
155- QPlatformInputContext *inputContext = integration->inputContext();
156+ auto integration = static_cast<UbuntuClientIntegration*>(context);
157+ auto inputContext = integration->inputContext();
158 if (inputContext) {
159 inputContext->hideInputPanel();
160 } else {
161 qCWarning(ubuntumirclient) << "aboutToStopCallback(): no input context";
162 }
163- QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationSuspended);
164+ integration->appStateController()->setSuspended();
165 }
166
167
168@@ -79,6 +71,7 @@
169 , mNativeInterface(new UbuntuNativeInterface(this))
170 , mFontDb(new QGenericUnixFontDatabase)
171 , mServices(new UbuntuPlatformServices)
172+ , mAppStateController(new UbuntuAppStateController)
173 , mScaleFactor(1.0)
174 {
175 {
176@@ -230,7 +223,8 @@
177 // Desktop windows should not be backed up by a mir surface as they don't draw anything (nor should).
178 return new UbuntuDesktopWindow(window);
179 } else {
180- return new UbuntuWindow(window, mInput, mNativeInterface, mEglDisplay, mMirConnection, mDebugExtension.data());
181+ return new UbuntuWindow(window, mInput, mNativeInterface, mAppStateController.data(),
182+ mEglDisplay, mMirConnection, mDebugExtension.data());
183 }
184 }
185
186
187=== modified file 'src/ubuntumirclient/integration.h'
188--- src/ubuntumirclient/integration.h 2016-09-22 19:45:30 +0000
189+++ src/ubuntumirclient/integration.h 2016-11-04 13:21:25 +0000
190@@ -20,6 +20,7 @@
191 #include <qpa/qplatformintegration.h>
192 #include <QSharedPointer>
193
194+#include "appstatecontroller.h"
195 #include "platformservices.h"
196 #include "screenobserver.h"
197
198@@ -64,6 +65,7 @@
199 MirConnection *mirConnection() const { return mMirConnection; }
200 EGLDisplay eglDisplay() const { return mEglDisplay; }
201 EGLNativeDisplayType eglNativeDisplay() const { return mEglNativeDisplay; }
202+ UbuntuAppStateController *appStateController() const { return mAppStateController.data(); }
203 UbuntuScreenObserver *screenObserver() const { return mScreenObserver.data(); }
204 UbuntuDebugExtension *debugExtension() const { return mDebugExtension.data(); }
205
206@@ -85,6 +87,7 @@
207 QPlatformInputContext* mInputContext;
208 QScopedPointer<UbuntuDebugExtension> mDebugExtension;
209 QScopedPointer<UbuntuScreenObserver> mScreenObserver;
210+ QScopedPointer<UbuntuAppStateController> mAppStateController;
211 qreal mScaleFactor;
212
213 MirConnection *mMirConnection;
214
215=== modified file 'src/ubuntumirclient/ubuntumirclient.pro'
216--- src/ubuntumirclient/ubuntumirclient.pro 2016-10-04 16:20:07 +0000
217+++ src/ubuntumirclient/ubuntumirclient.pro 2016-11-04 13:21:25 +0000
218@@ -29,7 +29,8 @@
219 screen.cpp \
220 screenobserver.cpp \
221 theme.cpp \
222- window.cpp
223+ window.cpp \
224+ appstatecontroller.cpp
225
226 HEADERS = \
227 backingstore.h \
228@@ -48,7 +49,8 @@
229 screenobserver.h \
230 screen.h \
231 theme.h \
232- window.h
233+ window.h \
234+ appstatecontroller.h
235
236 # Installation path
237 target.path += $$[QT_INSTALL_PLUGINS]/platforms
238
239=== modified file 'src/ubuntumirclient/window.cpp'
240--- src/ubuntumirclient/window.cpp 2016-10-24 11:32:31 +0000
241+++ src/ubuntumirclient/window.cpp 2016-11-04 13:21:25 +0000
242@@ -29,7 +29,6 @@
243 #include <qpa/qwindowsysteminterface.h>
244 #include <QMutexLocker>
245 #include <QSize>
246-#include <QAtomicInt>
247 #include <QtMath>
248 #include <private/qeglconvenience_p.h>
249 #include <private/qguiapplication_p.h>
250@@ -402,7 +401,6 @@
251 QSurfaceFormat format() const { return mFormat; }
252
253 bool mNeedsExposeCatchup;
254- QAtomicInt mPendingFocusGainedEvents;
255
256 QString persistentSurfaceId();
257
258@@ -567,14 +565,6 @@
259 QMutexLocker lock(&mTargetSizeMutex);
260 mTargetSize.rwidth() = width;
261 mTargetSize.rheight() = height;
262- } else if (mir_event_type_surface == eventType) {
263- auto surfaceEvent = mir_event_get_surface_event(event);
264- if (mir_surface_attrib_focus == mir_surface_event_get_attribute(surfaceEvent)) {
265- const bool focused = mir_surface_event_get_attribute_value(surfaceEvent) == mir_surface_focused;
266- if (focused) {
267- mPendingFocusGainedEvents++;
268- }
269- }
270 }
271
272 mInput->postEvent(mPlatformWindow, event);
273@@ -601,7 +591,8 @@
274 }
275
276 UbuntuWindow::UbuntuWindow(QWindow *w, UbuntuInput *input, UbuntuNativeInterface *native,
277- EGLDisplay eglDisplay, MirConnection *mirConnection, UbuntuDebugExtension *debugExt)
278+ UbuntuAppStateController *appState, EGLDisplay eglDisplay,
279+ MirConnection *mirConnection, UbuntuDebugExtension *debugExt)
280 : QObject(nullptr)
281 , QPlatformWindow(w)
282 , mId(makeId())
283@@ -610,6 +601,7 @@
284 , mWindowVisible(false)
285 , mDebugExtention(debugExt)
286 , mNativeInterface(native)
287+ , mAppStateController(appState)
288 , mSurface(new UbuntuSurface{this, eglDisplay, input, mirConnection})
289 , mScale(1.0)
290 , mFormFactor(mir_form_factor_unknown)
291@@ -663,28 +655,14 @@
292
293 void UbuntuWindow::handleSurfaceFocusChanged(bool focused)
294 {
295- qCDebug(ubuntumirclient, "handleSurfaceFocusChanged(window=%p, focused=%d, pending=%d)", window(), focused, mSurface->mPendingFocusGainedEvents.load());
296+ qCDebug(ubuntumirclient, "handleSurfaceFocusChanged(window=%p, focused=%d)", window(), focused);
297
298- // Mir may have sent a pair of focus lost/gained events, so we need to "peek" into the queue
299- // so that we don't deactivate windows prematurely.
300 if (focused) {
301- mSurface->mPendingFocusGainedEvents--;
302+ mAppStateController->setWindowFocused(true);
303 QWindowSystemInterface::handleWindowActivated(window(), Qt::ActiveWindowFocusReason);
304-
305- // NB: Since processing of system events is queued, never check qGuiApp->applicationState()
306- // as it might be outdated. Always call handleApplicationStateChanged() with the latest
307- // state regardless.
308- QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationActive);
309-
310- // Flush events so that we update QGuiApplicationPrivate::focus_window immediately
311- QWindowSystemInterface::flushWindowSystemEvents();
312-
313- } else if (mSurface->mPendingFocusGainedEvents == 0 && window() == QGuiApplicationPrivate::focus_window) {
314+ } else {
315 QWindowSystemInterface::handleWindowActivated(nullptr, Qt::ActiveWindowFocusReason);
316- QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationInactive);
317-
318- // Flush events so that we update QGuiApplicationPrivate::focus_window immediately
319- QWindowSystemInterface::flushWindowSystemEvents();
320+ mAppStateController->setWindowFocused(false);
321 }
322 }
323
324
325=== modified file 'src/ubuntumirclient/window.h'
326--- src/ubuntumirclient/window.h 2016-10-04 16:04:53 +0000
327+++ src/ubuntumirclient/window.h 2016-11-04 13:21:25 +0000
328@@ -27,6 +27,7 @@
329
330 #include <EGL/egl.h>
331
332+class UbuntuAppStateController;
333 class UbuntuDebugExtension;
334 class UbuntuNativeInterface;
335 class UbuntuInput;
336@@ -39,7 +40,8 @@
337 {
338 Q_OBJECT
339 public:
340- UbuntuWindow(QWindow *w, UbuntuInput *input, UbuntuNativeInterface* native, EGLDisplay eglDisplay,
341+ UbuntuWindow(QWindow *w, UbuntuInput *input, UbuntuNativeInterface *native,
342+ UbuntuAppStateController *appState, EGLDisplay eglDisplay,
343 MirConnection *mirConnection, UbuntuDebugExtension *debugExt);
344 virtual ~UbuntuWindow();
345
346@@ -83,6 +85,7 @@
347 bool mWindowExposed;
348 UbuntuDebugExtension *mDebugExtention;
349 UbuntuNativeInterface *mNativeInterface;
350+ UbuntuAppStateController *mAppStateController;
351 std::unique_ptr<UbuntuSurface> mSurface;
352 float mScale;
353 MirFormFactor mFormFactor;

Subscribers

People subscribed via source and target branches