Merge lp:~dandrader/qtmir/missingTouchEnd-lp1295623 into lp:qtmir

Proposed by Daniel d'Andrada
Status: Merged
Approved by: Gerry Boland
Approved revision: 245
Merged at revision: 244
Proposed branch: lp:~dandrader/qtmir/missingTouchEnd-lp1295623
Merge into: lp:qtmir
Diff against target: 984 lines (+608/-62)
18 files modified
src/common/debughelpers.cpp (+38/-6)
src/common/debughelpers.h (+2/-1)
src/modules/Unity/Application/Application.pro (+3/-3)
src/modules/Unity/Application/application.cpp (+3/-1)
src/modules/Unity/Application/mirsurfaceitem.cpp (+3/-1)
src/modules/Unity/Application/mirsurfacemanager.cpp (+3/-1)
src/platforms/mirserver/logging.h (+1/-0)
src/platforms/mirserver/mirserver.pro (+4/-0)
src/platforms/mirserver/qteventfeeder.cpp (+175/-18)
src/platforms/mirserver/qteventfeeder.h (+31/-1)
tests/google-mock.pri (+24/-0)
tests/mirserver/QtEventFeeder/QtEventFeeder.pro (+16/-0)
tests/mirserver/QtEventFeeder/mock_qtwindowsystem.h (+65/-0)
tests/mirserver/QtEventFeeder/qteventfeeder_test.cpp (+224/-0)
tests/mirserver/mirserver.pro (+2/-0)
tests/modules/common/common.pri (+4/-29)
tests/test-includes.pri (+9/-0)
tests/tests.pro (+1/-1)
To merge this branch: bzr merge lp:~dandrader/qtmir/missingTouchEnd-lp1295623
Reviewer Review Type Date Requested Status
Gerry Boland (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+232410@code.launchpad.net

Commit message

QtEventFeeder: validate touches before sending them to Qt

Make sure we send a tidy touch event stream to Qt as some of it
(QQuickWindow) is vulnerable to things like missing touch releases.

Also properly ignore unsupported motion event actions such as hovering.

Description of the change

Should fix https://launchpad.net/bugs/1295623

There's also this simple desktop application that exemplifies the situation that makes QQuickWindow stop generating mouse events, which is believed to be the cause of the bug above.
https://code.launchpad.net/~dandrader/+junk/touchToMouse

It captures raw mouse events and send synthesized touch events to the QQuickWindow out of them.
- Left button mouse clicks are translated to touch taps.
- Middle button mouse clicks are translated to touch taps as well with the difference that it omits the corresponding TouchEnd (thus emulating the bogus situation).

Left-click all rectangles and see that they happily blink. Then middle-click once a MouseArea. From that point onwards no mouse area will *ever* react to any left-click anymore. They get stuck forever. The touch area though will continue working normally.

I've made a patch for Qt to make QQuickWindow more robust, thus avoid entering in such bogus state, but I decided to solve it in the platform abstraction level as the qt patch is not a crowd pleaser.
https://codereview.qt-project.org/93002

* Are there any related MPs required for this MP to build/function as expected? Please list.
No.

* Did you perform an exploratory manual test run of your code change and any related functionality?
Yes.

* If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
Not applicable.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Gerry Boland (gerboland) wrote :

Things I'd like clarified in a big comment in this code:
1. this is a workaround for a bug in Qt? If yes, please add the QTBUG number, plus a short textual description of that bug. It/when it gets fixed upstream, presumably we can remove this?
2. is this also processing slightly screwed up events from Mir so that they make sense to Qt? Other toolkits may also choke on this, so we should have a Mir bug on this topic - and reference it in this code too.

Code review:
+++ src/platforms/mirserver/qteventfeeder.h
+ class QtWindowSystem {
This is actually a interface, please call it QtWindowSystemInterface - hopefully it doesn't clash with Qt's defined one. Else QtMirWindowSystemInterface maybe? Is it a Window really? :)

+++ src/platforms/mirserver/qteventfeeder.cpp
+ class RealQtWindowSystem
if you do the above rename, then you can remove "Real" from the name - it confused me when I saw it first (though I understand now you mean not-Mock)

Class is very single-monitor specific, please add a "FIXME - won't work with multimonitor" somewhere - I know that the QtEventFeeder has the same issue, but I just want to document it clearly for ourselves when we start thinking about desktop.

+ int mirMotionAction = event.action & MirEventActionMask;
const?

+ // Qt needs a happy, sane stream of touch events. So let's make sure we're not forwarding
+ // any insanity.
Add QTBUG here perhaps?

+ qCWarning(QTMIR_MIR_MESSAGES)
I think a dedicated logging category for this would make sense. QTMIR_INPUT_PROCESSING -> qtmir.mir.input maybe?

+void QtEventFeederTest::setIrrelevantMockWindowSystemExpectations()
+{
+ EXPECT_CALL(*mockWindowSystem, hasTargetWindow())
+ .Times(AnyNumber())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mockWindowSystem, targetWindowGeometry())
+ .Times(AnyNumber())
+ .WillRepeatedly(Return(QRect(0,0,100,100)));
+}
Nice method name :D They're not exactly irrelevant though. Since you call it before tests run, can't it be run as part of SetUp()?

+ .Times(1);
I don't like it alone on it's own line. Almost looks like a statement of its own.

+ setIrrelevantMockWindowSystemExpectations();
why do you keep calling this? You use .Times(AnyNumber()).WillRepeatedly(Return(true)) so it should not need resetting

+TEST_F(QtEventFeederTest, GenerateMissingTouchEnd)
This test generates a Press event at (10,10). You check press event passed to Qt. Next generate Press event at (20, 20). You ensure this event also generates a Release event for the previous event.
- would help to add this comment to the test. Sorry, but I'm considering somebody other than you or I reading this code!

Otherwise looks good.

Will do functional testing now

review: Needs Fixing (code)
Revision history for this message
Gerry Boland (gerboland) wrote :

Desktop tests ok

Revision history for this message
Gerry Boland (gerboland) wrote :

I don't see any regression on the phone either.

I still can make apps hang by doing lots of stupid multi-finger gestures. Which confuses me, as I expected your event processing to fix that.

review: Approve
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> I don't see any regression on the phone either.
>
> I still can make apps hang by doing lots of stupid multi-finger gestures.
> Which confuses me, as I expected your event processing to fix that.

That patch ensures that *unity8* receives a clean stream of touch events.
But whether unity8 on its part sends a clean stream of touch events to applications, that's another story

241. By Daniel d'Andrada

QtEventFeeder: s/QtWindowSystem/QtWindowSystemInterface

242. By Daniel d'Andrada

Add a TODO notice to that new internal interface

243. By Daniel d'Andrada

Declare mirMotionAction a const as it's not meant to be changed

244. By Daniel d'Andrada

Using a new logging category

245. By Daniel d'Andrada

Comment/explain QtEventFeederTest::GenerateMissingTouchEnd

Revision history for this message
Daniel d'Andrada (dandrader) wrote :
Download full text (4.1 KiB)

> Things I'd like clarified in a big comment in this code:
> 1. this is a workaround for a bug in Qt? If yes, please add the QTBUG number,
> plus a short textual description of that bug. It/when it gets fixed upstream,
> presumably we can remove this?
> 2. is this also processing slightly screwed up events from Mir so that they
> make sense to Qt? Other toolkits may also choke on this, so we should have a
> Mir bug on this topic - and reference it in this code too.
>
>
> Code review:
> +++ src/platforms/mirserver/qteventfeeder.h
> + class QtWindowSystem {
> This is actually a interface, please call it QtWindowSystemInterface -
> hopefully it doesn't clash with Qt's defined one. Else
> QtMirWindowSystemInterface maybe? Is it a Window really? :)

It's impossible to have a clash with anything coming with because it's defined *inside* QtEventFeeder class. So it's inside QtEventFeeder's namespace sort of. It's also prefixed with "Qt", whereas Qt itself uses just "Q" for prefixing its classes.

Changed it to adhere to the "FooInterface" convention.

>
> +++ src/platforms/mirserver/qteventfeeder.cpp
> + class RealQtWindowSystem
> if you do the above rename, then you can remove "Real" from the name - it
> confused me when I saw it first (though I understand now you mean not-Mock)

Done.

>
> Class is very single-monitor specific, please add a "FIXME - won't work with
> multimonitor" somewhere - I know that the QtEventFeeder has the same issue,
> but I just want to document it clearly for ourselves when we start thinking
> about desktop.

Done.

>
> + int mirMotionAction = event.action & MirEventActionMask;
> const?
>

Done.

> + // Qt needs a happy, sane stream of touch events. So let's make sure we're
> not forwarding
> + // any insanity.
> Add QTBUG here perhaps?

hmm... Not necessarily a bug as you cannot really assume that Qt should cope with a messed up platform abstraction. It would be great if it did, but not a necessity.

>
> + qCWarning(QTMIR_MIR_MESSAGES)
> I think a dedicated logging category for this would make sense.
> QTMIR_INPUT_PROCESSING -> qtmir.mir.input maybe?

Done.

>
> +void QtEventFeederTest::setIrrelevantMockWindowSystemExpectations()
> +{
> + EXPECT_CALL(*mockWindowSystem, hasTargetWindow())
> + .Times(AnyNumber())
> + .WillRepeatedly(Return(true));
> + EXPECT_CALL(*mockWindowSystem, targetWindowGeometry())
> + .Times(AnyNumber())
> + .WillRepeatedly(Return(QRect(0,0,100,100)));
> +}
> Nice method name :D They're not exactly irrelevant though. Since you call it
> before tests run, can't it be run as part of SetUp()?

They are irrelevant because the tests don't care about them. They are like uninteresting implementation details needed to get things working, from the tests point of view.

They can't be part of SetUp() because they have to be done more than once during a test. Everytime Mock::VerifyAndClearExpectations(mockWindowSystem) is called, all expectations are cleared/removed, as the method name says. So you have to set those expectations again after it.

>
> + .Times(1);
> I don't like it alone on it's own line. Almost looks like a statement of its
> own.

It's standard google mock coding style but ok, changed...

Read more...

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> Things I'd like clarified in a big comment in this code:
> 1. this is a workaround for a bug in Qt?

I wouldn't say it's a bug in Qt. It's just that Qt is more sensible to garbage coming from platform abstraction than we would like. So it would be a nice improvement if it would be more resilient/robust.

> If yes, please add the QTBUG number,
> plus a short textual description of that bug. It/when it gets fixed upstream,
> presumably we can remove this?
> 2. is this also processing slightly screwed up events from Mir so that they
> make sense to Qt? Other toolkits may also choke on this, so we should have a
> Mir bug on this topic - and reference it in this code too.

It's not only about protecting against possible garbage coming from Mir, but also about sanity checking what QtEventFeeder itself does before finally sending the generated touch events down to Qt. Previously, for instance, there was a good chance that QtEventFeeder was sending some (possibly inconsistent) touch events in reaction to mir hover events.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Gerry Boland (gerboland) wrote :

Perfect, thank you

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added directory 'src/common'
=== renamed file 'src/modules/Unity/Application/debughelpers.cpp' => 'src/common/debughelpers.cpp'
--- src/modules/Unity/Application/debughelpers.cpp 2014-07-25 15:29:20 +0000
+++ src/common/debughelpers.cpp 2014-08-28 13:13:15 +0000
@@ -17,22 +17,24 @@
17#include "debughelpers.h"17#include "debughelpers.h"
18#include <QTouchEvent>18#include <QTouchEvent>
1919
20#include <mir_toolkit/event.h>
21
20// Unity API22// Unity API
21#include <unity/shell/application/ApplicationInfoInterface.h>23#include <unity/shell/application/ApplicationInfoInterface.h>
2224
23QString touchPointStateToString(Qt::TouchPointState state)25const char *touchPointStateToString(Qt::TouchPointState state)
24{26{
25 switch (state) {27 switch (state) {
26 case Qt::TouchPointPressed:28 case Qt::TouchPointPressed:
27 return QString("pressed");29 return "pressed";
28 case Qt::TouchPointMoved:30 case Qt::TouchPointMoved:
29 return QString("moved");31 return "moved";
30 case Qt::TouchPointStationary:32 case Qt::TouchPointStationary:
31 return QString("stationary");33 return "stationary";
32 case Qt::TouchPointReleased:34 case Qt::TouchPointReleased:
33 return QString("released");35 return "released";
34 default:36 default:
35 return QString("UNKNOWN!");37 return "UNKNOWN!";
36 }38 }
37}39}
3840
@@ -174,6 +176,36 @@
174 }176 }
175}177}
176178
179const char *mirMotionActionToStr(int value)
180{
181 switch (value) {
182 case mir_motion_action_move:
183 return "move";
184 case mir_motion_action_down:
185 return "down";
186 case mir_motion_action_up:
187 return "up";
188 case mir_motion_action_pointer_down:
189 return "pointer_down";
190 case mir_motion_action_cancel:
191 return "cancel";
192 case mir_motion_action_pointer_up:
193 return "pointer_up";
194 case mir_motion_action_outside:
195 return "outside";
196 case mir_motion_action_hover_move:
197 return "hover_move";
198 case mir_motion_action_scroll:
199 return "scroll";
200 case mir_motion_action_hover_enter:
201 return "hover_enter";
202 case mir_motion_action_hover_exit:
203 return "hover_exit";
204 default:
205 return "???";
206 }
207}
208
177using namespace unity::shell::application;209using namespace unity::shell::application;
178210
179const char *applicationStateToStr(int state)211const char *applicationStateToStr(int state)
180212
=== renamed file 'src/modules/Unity/Application/debughelpers.h' => 'src/common/debughelpers.h'
--- src/modules/Unity/Application/debughelpers.h 2014-07-25 15:29:20 +0000
+++ src/common/debughelpers.h 2014-08-28 13:13:15 +0000
@@ -23,7 +23,7 @@
2323
24class QTouchEvent;24class QTouchEvent;
2525
26QString touchPointStateToString(Qt::TouchPointState state);26const char *touchPointStateToString(Qt::TouchPointState state);
27QString touchEventToString(const QTouchEvent *ev);27QString touchEventToString(const QTouchEvent *ev);
2828
29QString mirSurfaceAttribAndValueToString(MirSurfaceAttrib attrib, int value);29QString mirSurfaceAttribAndValueToString(MirSurfaceAttrib attrib, int value);
@@ -31,6 +31,7 @@
31const char *mirSurfaceStateToStr(int value);31const char *mirSurfaceStateToStr(int value);
32const char *mirSurfaceFocusStateToStr(int value);32const char *mirSurfaceFocusStateToStr(int value);
33const char *mirSurfaceVisibilityToStr(int value);33const char *mirSurfaceVisibilityToStr(int value);
34const char *mirMotionActionToStr(int value);
3435
35const char *applicationStateToStr(int state);36const char *applicationStateToStr(int state);
3637
3738
=== modified file 'src/modules/Unity/Application/Application.pro'
--- src/modules/Unity/Application/Application.pro 2014-08-05 09:14:13 +0000
+++ src/modules/Unity/Application/Application.pro 2014-08-28 13:13:15 +0000
@@ -12,7 +12,7 @@
1212
13PKGCONFIG += mirserver glib-2.0 process-cpp ubuntu-app-launch-213PKGCONFIG += mirserver glib-2.0 process-cpp ubuntu-app-launch-2
1414
15INCLUDEPATH += ../../../platforms/mirserver15INCLUDEPATH += ../../../platforms/mirserver ../../../common
16LIBS += -L../../../platforms/mirserver -lqpa-mirserver16LIBS += -L../../../platforms/mirserver -lqpa-mirserver
17QMAKE_RPATHDIR += $$[QT_INSTALL_PLUGINS]/platforms # where libqpa-mirserver.so is installed17QMAKE_RPATHDIR += $$[QT_INSTALL_PLUGINS]/platforms # where libqpa-mirserver.so is installed
1818
@@ -24,7 +24,7 @@
2424
25SOURCES += application_manager.cpp \25SOURCES += application_manager.cpp \
26 application.cpp \26 application.cpp \
27 debughelpers.cpp \27 ../../../common/debughelpers.cpp \
28 desktopfilereader.cpp \28 desktopfilereader.cpp \
29 plugin.cpp \29 plugin.cpp \
30 applicationscreenshotprovider.cpp \30 applicationscreenshotprovider.cpp \
@@ -42,7 +42,7 @@
42HEADERS += application_manager.h \42HEADERS += application_manager.h \
43 applicationcontroller.h \43 applicationcontroller.h \
44 application.h \44 application.h \
45 debughelpers.h \45 ../../../common/debughelpers.h \
46 desktopfilereader.h \46 desktopfilereader.h \
47 applicationscreenshotprovider.h \47 applicationscreenshotprovider.h \
48 dbuswindowstack.h \48 dbuswindowstack.h \
4949
=== modified file 'src/modules/Unity/Application/application.cpp'
--- src/modules/Unity/Application/application.cpp 2014-08-22 22:45:47 +0000
+++ src/modules/Unity/Application/application.cpp 2014-08-28 13:13:15 +0000
@@ -17,10 +17,12 @@
17// local17// local
18#include "application.h"18#include "application.h"
19#include "application_manager.h"19#include "application_manager.h"
20#include "debughelpers.h"
21#include "desktopfilereader.h"20#include "desktopfilereader.h"
22#include "taskcontroller.h"21#include "taskcontroller.h"
2322
23// common
24#include <debughelpers.h>
25
24// QPA mirserver26// QPA mirserver
25#include "logging.h"27#include "logging.h"
2628
2729
=== modified file 'src/modules/Unity/Application/mirsurfaceitem.cpp'
--- src/modules/Unity/Application/mirsurfaceitem.cpp 2014-08-12 18:23:06 +0000
+++ src/modules/Unity/Application/mirsurfaceitem.cpp 2014-08-28 13:13:15 +0000
@@ -20,11 +20,13 @@
2020
21// local21// local
22#include "application.h"22#include "application.h"
23#include "debughelpers.h"
24#include "mirbuffersgtexture.h"23#include "mirbuffersgtexture.h"
25#include "mirsurfaceitem.h"24#include "mirsurfaceitem.h"
26#include "logging.h"25#include "logging.h"
2726
27// common
28#include <debughelpers.h>
29
28// Qt30// Qt
29#include <QDebug>31#include <QDebug>
30#include <QQmlEngine>32#include <QQmlEngine>
3133
=== modified file 'src/modules/Unity/Application/mirsurfacemanager.cpp'
--- src/modules/Unity/Application/mirsurfacemanager.cpp 2014-08-08 10:37:50 +0000
+++ src/modules/Unity/Application/mirsurfacemanager.cpp 2014-08-28 13:13:15 +0000
@@ -19,10 +19,12 @@
19#include <QMutexLocker>19#include <QMutexLocker>
2020
21// local21// local
22#include "debughelpers.h"
23#include "mirsurfacemanager.h"22#include "mirsurfacemanager.h"
24#include "application_manager.h"23#include "application_manager.h"
2524
25// common
26#include <debughelpers.h>
27
26// QPA mirserver28// QPA mirserver
27#include "nativeinterface.h"29#include "nativeinterface.h"
28#include "mirserverconfiguration.h"30#include "mirserverconfiguration.h"
2931
=== modified file 'src/platforms/mirserver/logging.h'
--- src/platforms/mirserver/logging.h 2014-07-01 13:38:06 +0000
+++ src/platforms/mirserver/logging.h 2014-08-28 13:13:15 +0000
@@ -21,5 +21,6 @@
21Q_DECLARE_LOGGING_CATEGORY(QTMIR_APPLICATIONS)21Q_DECLARE_LOGGING_CATEGORY(QTMIR_APPLICATIONS)
22Q_DECLARE_LOGGING_CATEGORY(QTMIR_SURFACES)22Q_DECLARE_LOGGING_CATEGORY(QTMIR_SURFACES)
23Q_DECLARE_LOGGING_CATEGORY(QTMIR_MIR_MESSAGES)23Q_DECLARE_LOGGING_CATEGORY(QTMIR_MIR_MESSAGES)
24Q_DECLARE_LOGGING_CATEGORY(QTMIR_MIR_INPUT)
2425
25#endif // UBUNTU_APPLICATION_PLUGIN_LOGGING_H26#endif // UBUNTU_APPLICATION_PLUGIN_LOGGING_H
2627
=== modified file 'src/platforms/mirserver/mirserver.pro'
--- src/platforms/mirserver/mirserver.pro 2014-08-05 09:14:13 +0000
+++ src/platforms/mirserver/mirserver.pro 2014-08-28 13:13:15 +0000
@@ -14,6 +14,8 @@
14QMAKE_CXXFLAGS = -std=c++11 -Werror -Wall14QMAKE_CXXFLAGS = -std=c++11 -Werror -Wall
15QMAKE_LFLAGS = -std=c++11 -Wl,-no-undefined15QMAKE_LFLAGS = -std=c++11 -Wl,-no-undefined
1616
17INCLUDEPATH += ../../common
18
17CONFIG += link_pkgconfig19CONFIG += link_pkgconfig
18PKGCONFIG += mirserver protobuf egl xkbcommon url-dispatcher-120PKGCONFIG += mirserver protobuf egl xkbcommon url-dispatcher-1
1921
@@ -21,6 +23,7 @@
2123
22SOURCES += \24SOURCES += \
23 connectioncreator.cpp \25 connectioncreator.cpp \
26 ../../common/debughelpers.cpp \
24 focussetter.cpp \27 focussetter.cpp \
25 qteventfeeder.cpp \28 qteventfeeder.cpp \
26 plugin.cpp \29 plugin.cpp \
@@ -47,6 +50,7 @@
4750
48HEADERS += \51HEADERS += \
49 connectioncreator.h \52 connectioncreator.h \
53 ../../common/debughelpers.h \
50 focussetter.h \54 focussetter.h \
51 qteventfeeder.h \55 qteventfeeder.h \
52 plugin.h \56 plugin.h \
5357
=== modified file 'src/platforms/mirserver/qteventfeeder.cpp'
--- src/platforms/mirserver/qteventfeeder.cpp 2014-07-29 15:01:34 +0000
+++ src/platforms/mirserver/qteventfeeder.cpp 2014-08-28 13:13:15 +0000
@@ -18,10 +18,10 @@
18 */18 */
1919
20#include "qteventfeeder.h"20#include "qteventfeeder.h"
21#include "logging.h"
2122
22#include <qpa/qplatforminputcontext.h>23#include <qpa/qplatforminputcontext.h>
23#include <qpa/qplatformintegration.h>24#include <qpa/qplatformintegration.h>
24#include <qpa/qwindowsysteminterface.h>
25#include <QGuiApplication>25#include <QGuiApplication>
26#include <private/qguiapplication_p.h>26#include <private/qguiapplication_p.h>
2727
@@ -30,6 +30,8 @@
3030
31#include <QDebug>31#include <QDebug>
3232
33Q_LOGGING_CATEGORY(QTMIR_MIR_INPUT, "qtmir.mir.input")
34
33// from android-input AMOTION_EVENT_ACTION_*, hidden inside mir bowels35// from android-input AMOTION_EVENT_ACTION_*, hidden inside mir bowels
34// mir headers should define them36// mir headers should define them
35const int QtEventFeeder::MirEventActionMask = 0xff;37const int QtEventFeeder::MirEventActionMask = 0xff;
@@ -133,9 +135,61 @@
133 return toupper(sym);135 return toupper(sym);
134}136}
135137
136138namespace {
137QtEventFeeder::QtEventFeeder()139
140class QtWindowSystem : public QtEventFeeder::QtWindowSystemInterface {
141
142 bool hasTargetWindow() override
143 {
144 if (mTopLevelWindow.isNull() && !QGuiApplication::topLevelWindows().isEmpty()) {
145 mTopLevelWindow = QGuiApplication::topLevelWindows().first();
146 }
147 return !mTopLevelWindow.isNull();
148 }
149
150 QRect targetWindowGeometry() override
151 {
152 Q_ASSERT(!mTopLevelWindow.isNull());
153 return mTopLevelWindow->geometry();
154 }
155
156 void registerTouchDevice(QTouchDevice *device) override
157 {
158 QWindowSystemInterface::registerTouchDevice(device);
159 }
160
161 void handleExtendedKeyEvent(ulong timestamp, QEvent::Type type, int key,
162 Qt::KeyboardModifiers modifiers,
163 quint32 nativeScanCode, quint32 nativeVirtualKey,
164 quint32 nativeModifiers,
165 const QString& text, bool autorep, ushort count) override
166 {
167 Q_ASSERT(!mTopLevelWindow.isNull());
168 QWindowSystemInterface::handleExtendedKeyEvent(mTopLevelWindow.data(), timestamp, type, key, modifiers,
169 nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count);
170 }
171
172 void handleTouchEvent(ulong timestamp, QTouchDevice *device,
173 const QList<struct QWindowSystemInterface::TouchPoint> &points, Qt::KeyboardModifiers mods) override
174 {
175 Q_ASSERT(!mTopLevelWindow.isNull());
176 QWindowSystemInterface::handleTouchEvent(mTopLevelWindow.data(), timestamp, device, points, mods);
177 }
178private:
179 QPointer<QWindow> mTopLevelWindow;
180};
181
182} // anonymous namespace
183
184
185QtEventFeeder::QtEventFeeder(QtEventFeeder::QtWindowSystemInterface *windowSystem)
138{186{
187 if (windowSystem) {
188 mQtWindowSystem = windowSystem;
189 } else {
190 mQtWindowSystem = new QtWindowSystem;
191 }
192
139 // Initialize touch device. Hardcoded just like in qtubuntu193 // Initialize touch device. Hardcoded just like in qtubuntu
140 // TODO: Create them from info gathered from Mir and store things like device id and source194 // TODO: Create them from info gathered from Mir and store things like device id and source
141 // in a QTouchDevice-derived class created by us. So that we can properly assemble back195 // in a QTouchDevice-derived class created by us. So that we can properly assemble back
@@ -145,7 +199,12 @@
145 mTouchDevice->setCapabilities(199 mTouchDevice->setCapabilities(
146 QTouchDevice::Position | QTouchDevice::Area | QTouchDevice::Pressure |200 QTouchDevice::Position | QTouchDevice::Area | QTouchDevice::Pressure |
147 QTouchDevice::NormalizedPosition);201 QTouchDevice::NormalizedPosition);
148 QWindowSystemInterface::registerTouchDevice(mTouchDevice);202 mQtWindowSystem->registerTouchDevice(mTouchDevice);
203}
204
205QtEventFeeder::~QtEventFeeder()
206{
207 delete mQtWindowSystem;
149}208}
150209
151void QtEventFeeder::dispatch(MirEvent const& event)210void QtEventFeeder::dispatch(MirEvent const& event)
@@ -171,11 +230,9 @@
171230
172void QtEventFeeder::dispatchKey(MirKeyEvent const& event)231void QtEventFeeder::dispatchKey(MirKeyEvent const& event)
173{232{
174 if (QGuiApplication::topLevelWindows().isEmpty())233 if (!mQtWindowSystem->hasTargetWindow())
175 return;234 return;
176235
177 QWindow *window = QGuiApplication::topLevelWindows().first();
178
179 ulong timestamp = event.event_time / 1000000;236 ulong timestamp = event.event_time / 1000000;
180 xkb_keysym_t xk_sym = static_cast<xkb_keysym_t>(event.key_code);237 xkb_keysym_t xk_sym = static_cast<xkb_keysym_t>(event.key_code);
181238
@@ -221,20 +278,33 @@
221 }278 }
222 }279 }
223280
224 QWindowSystemInterface::handleExtendedKeyEvent(window, timestamp, keyType, keyCode, modifiers, event.scan_code, event.key_code, event.modifiers, text);281 mQtWindowSystem->handleExtendedKeyEvent(timestamp, keyType, keyCode, modifiers,
282 event.scan_code, event.key_code, event.modifiers, text);
225}283}
226284
227void QtEventFeeder::dispatchMotion(MirMotionEvent const& event)285void QtEventFeeder::dispatchMotion(MirMotionEvent const& event)
228{286{
229 if (QGuiApplication::topLevelWindows().isEmpty())287 if (!mQtWindowSystem->hasTargetWindow())
230 return;288 return;
231289
232 QWindow *window = QGuiApplication::topLevelWindows().first();290 const int mirMotionAction = event.action & MirEventActionMask;
291
292 // Ignore the events that do not interest us (or that we currently don't support or know
293 // how to translate into Qt events)
294 if (mirMotionAction != mir_motion_action_move
295 && mirMotionAction != mir_motion_action_down
296 && mirMotionAction != mir_motion_action_up
297 && mirMotionAction != mir_motion_action_pointer_down
298 && mirMotionAction != mir_motion_action_pointer_up
299 && mirMotionAction != mir_motion_action_cancel) {
300 return;
301 }
302
233303
234 // FIXME(loicm) Max pressure is device specific. That one is for the Samsung Galaxy Nexus. That304 // FIXME(loicm) Max pressure is device specific. That one is for the Samsung Galaxy Nexus. That
235 // needs to be fixed as soon as the compat input lib adds query support.305 // needs to be fixed as soon as the compat input lib adds query support.
236 const float kMaxPressure = 1.28;306 const float kMaxPressure = 1.28;
237 const QRect kWindowGeometry = window->geometry();307 const QRect kWindowGeometry = mQtWindowSystem->targetWindowGeometry();
238 QList<QWindowSystemInterface::TouchPoint> touchPoints;308 QList<QWindowSystemInterface::TouchPoint> touchPoints;
239309
240 // TODO: Is it worth setting the Qt::TouchPointStationary ones? Currently they are left310 // TODO: Is it worth setting the Qt::TouchPointStationary ones? Currently they are left
@@ -257,7 +327,7 @@
257 touchPoints.append(touchPoint);327 touchPoints.append(touchPoint);
258 }328 }
259329
260 switch (event.action & MirEventActionMask) {330 switch (mirMotionAction) {
261 case mir_motion_action_move:331 case mir_motion_action_move:
262 // No extra work needed.332 // No extra work needed.
263 break;333 break;
@@ -291,13 +361,17 @@
291 case mir_motion_action_scroll:361 case mir_motion_action_scroll:
292 case mir_motion_action_hover_enter:362 case mir_motion_action_hover_enter:
293 case mir_motion_action_hover_exit:363 case mir_motion_action_hover_exit:
294 default:364 default:
295 qWarning() << "unhandled motion event action" << (int)(event.action & MirEventActionMask);365 // Should never reach this point. If so, it's a programming error.
366 qFatal("Trying to handle unsupported motion event action");
296 }367 }
297368
369 // Qt needs a happy, sane stream of touch events. So let's make sure we're not forwarding
370 // any insanity.
371 validateTouches(touchPoints);
372
298 // Touch event propagation.373 // Touch event propagation.
299 QWindowSystemInterface::handleTouchEvent(374 mQtWindowSystem->handleTouchEvent(
300 window,
301 event.event_time / 1000000, //scales down the nsec_t (int64) to fit a ulong, precision lost but time difference suitable375 event.event_time / 1000000, //scales down the nsec_t (int64) to fit a ulong, precision lost but time difference suitable
302 mTouchDevice,376 mTouchDevice,
303 touchPoints);377 touchPoints);
@@ -323,3 +397,86 @@
323 Q_UNUSED(device_id);397 Q_UNUSED(device_id);
324 Q_UNUSED(when);398 Q_UNUSED(when);
325}399}
400
401void QtEventFeeder::validateTouches(QList<QWindowSystemInterface::TouchPoint> &touchPoints)
402{
403 QSet<int> updatedTouches;
404
405 {
406 int i = 0;
407 while (i < touchPoints.count()) {
408 bool mustDiscardTouch = !validateTouch(touchPoints[i]);
409 if (mustDiscardTouch) {
410 touchPoints.removeAt(i);
411 } else {
412 updatedTouches.insert(touchPoints.at(i).id);
413 ++i;
414 }
415 }
416 }
417
418 // Release all unmentioned touches.
419 {
420 QHash<int, QWindowSystemInterface::TouchPoint>::iterator it = mActiveTouches.begin();
421 while (it != mActiveTouches.end()) {
422 if (!updatedTouches.contains(it.key())) {
423 qCWarning(QTMIR_MIR_INPUT)
424 << "There's a touch (id =" << it.key() << ") missing. Releasing it.";
425 it.value().state = Qt::TouchPointReleased;
426 touchPoints.append(it.value());
427 it = mActiveTouches.erase(it);
428 } else {
429 ++it;
430 }
431 }
432 }
433}
434
435bool QtEventFeeder::validateTouch(QWindowSystemInterface::TouchPoint &touchPoint)
436{
437 bool ok = true;
438
439 switch (touchPoint.state) {
440 case Qt::TouchPointPressed:
441 if (mActiveTouches.contains(touchPoint.id)) {
442 qCWarning(QTMIR_MIR_INPUT)
443 << "Would press an already existing touch (id =" << touchPoint.id
444 << "). Making it move instead.";
445 touchPoint.state = Qt::TouchPointMoved;
446 }
447 mActiveTouches[touchPoint.id] = touchPoint;
448 break;
449 case Qt::TouchPointMoved:
450 if (!mActiveTouches.contains(touchPoint.id)) {
451 qCWarning(QTMIR_MIR_INPUT)
452 << "Would move a touch that wasn't pressed before (id =" << touchPoint.id
453 << "). Making it press instead.";
454 touchPoint.state = Qt::TouchPointPressed;
455 }
456 mActiveTouches[touchPoint.id] = touchPoint;
457 break;
458 case Qt::TouchPointStationary:
459 if (!mActiveTouches.contains(touchPoint.id)) {
460 qCWarning(QTMIR_MIR_INPUT)
461 << "There's an stationary touch that wasn't pressed before (id =" << touchPoint.id
462 << "). Making it press instead.";
463 touchPoint.state = Qt::TouchPointPressed;
464 }
465 mActiveTouches[touchPoint.id] = touchPoint;
466 break;
467 case Qt::TouchPointReleased:
468 if (!mActiveTouches.contains(touchPoint.id)) {
469 qCWarning(QTMIR_MIR_INPUT)
470 << "Would release a touch that wasn't pressed before (id =" << touchPoint.id
471 << "). Ignoring it.";
472 ok = false;
473 } else {
474 mActiveTouches.remove(touchPoint.id);
475 }
476 break;
477 default:
478 qFatal("QtEventFeeder: invalid touch state");
479 }
480
481 return ok;
482}
326483
=== modified file 'src/platforms/mirserver/qteventfeeder.h'
--- src/platforms/mirserver/qteventfeeder.h 2014-07-18 20:23:35 +0000
+++ src/platforms/mirserver/qteventfeeder.h 2014-08-28 13:13:15 +0000
@@ -22,6 +22,8 @@
22#include <mir/input/input_dispatcher.h>22#include <mir/input/input_dispatcher.h>
23#include <mir/shell/input_targeter.h>23#include <mir/shell/input_targeter.h>
2424
25#include <qpa/qwindowsysteminterface.h>
26
25class QTouchDevice;27class QTouchDevice;
2628
27/*29/*
@@ -30,7 +32,29 @@
30class QtEventFeeder : public mir::input::InputDispatcher32class QtEventFeeder : public mir::input::InputDispatcher
31{33{
32public:34public:
33 QtEventFeeder();35 // Interface between QtEventFeeder and the actual QWindowSystemInterface functions
36 // and other related Qt methods and objects to enable replacing them with mocks in
37 // pure unit tests.
38 // TODO - Make it work with multimonitor scenarios
39 class QtWindowSystemInterface {
40 public:
41 virtual ~QtWindowSystemInterface() {}
42 virtual bool hasTargetWindow() = 0;
43 virtual QRect targetWindowGeometry() = 0;
44 virtual void registerTouchDevice(QTouchDevice *device) = 0;
45 virtual void handleExtendedKeyEvent(ulong timestamp, QEvent::Type type, int key,
46 Qt::KeyboardModifiers modifiers,
47 quint32 nativeScanCode, quint32 nativeVirtualKey,
48 quint32 nativeModifiers,
49 const QString& text = QString(), bool autorep = false,
50 ushort count = 1) = 0;
51 virtual void handleTouchEvent(ulong timestamp, QTouchDevice *device,
52 const QList<struct QWindowSystemInterface::TouchPoint> &points,
53 Qt::KeyboardModifiers mods = Qt::NoModifier) = 0;
54 };
55
56 QtEventFeeder(QtWindowSystemInterface *windowSystem = nullptr);
57 virtual ~QtEventFeeder();
3458
35 static const int MirEventActionMask;59 static const int MirEventActionMask;
36 static const int MirEventActionPointerIndexMask;60 static const int MirEventActionPointerIndexMask;
@@ -45,8 +69,14 @@
45private:69private:
46 void dispatchKey(MirKeyEvent const& event);70 void dispatchKey(MirKeyEvent const& event);
47 void dispatchMotion(MirMotionEvent const& event);71 void dispatchMotion(MirMotionEvent const& event);
72 void validateTouches(QList<QWindowSystemInterface::TouchPoint> &touchPoints);
73 bool validateTouch(QWindowSystemInterface::TouchPoint &touchPoint);
4874
49 QTouchDevice *mTouchDevice;75 QTouchDevice *mTouchDevice;
76 QtWindowSystemInterface *mQtWindowSystem;
77
78 // Maps the id of an active touch to its last known state
79 QHash<int, QWindowSystemInterface::TouchPoint> mActiveTouches;
50};80};
5181
52#endif // MIR_QT_EVENT_FEEDER_H82#endif // MIR_QT_EVENT_FEEDER_H
5383
=== added file 'tests/google-mock.pri'
--- tests/google-mock.pri 1970-01-01 00:00:00 +0000
+++ tests/google-mock.pri 2014-08-28 13:13:15 +0000
@@ -0,0 +1,24 @@
1GMOCK_SOURCES = /usr/src/gmock/src/gmock-all.cc \
2 /usr/src/gmock/src/gmock_main.cc \
3 /usr/src/gtest/src/gtest-all.cc
4
5QMAKE_EXTRA_COMPILERS += gmock_compiler
6gmock_compiler.input = GMOCK_SOURCES
7gmock_compiler.output = $${OUT_PWD}/${QMAKE_FILE_BASE}.o
8gmock_compiler.commands = g++ \
9 -I/usr/src/gtest \
10 -I/usr/src/gmock \
11 -c ${QMAKE_FILE_IN} \
12 -o ${QMAKE_FILE_OUT}
13gmock_compiler.CONFIG = no_link
14
15QMAKE_EXTRA_COMPILERS += gmock_linker
16gmock_linker.depends = $${OUT_PWD}/gmock-all.o \
17 $${OUT_PWD}/gmock_main.o \
18 $${OUT_PWD}/gtest-all.o
19gmock_linker.input = GMOCK_SOURCES
20gmock_linker.output = $${OUT_PWD}/libgmock.a
21gmock_linker.commands = ar -rv ${QMAKE_FILE_OUT} $${OUT_PWD}/gmock-all.o $${OUT_PWD}/gmock_main.o $${OUT_PWD}/gtest-all.o
22gmock_linker.CONFIG = combine explicit_dependencies no_link target_predeps
23
24LIBS += $${OUT_PWD}/libgmock.a
025
=== added directory 'tests/mirserver'
=== added directory 'tests/mirserver/QtEventFeeder'
=== added file 'tests/mirserver/QtEventFeeder/QtEventFeeder.pro'
--- tests/mirserver/QtEventFeeder/QtEventFeeder.pro 1970-01-01 00:00:00 +0000
+++ tests/mirserver/QtEventFeeder/QtEventFeeder.pro 2014-08-28 13:13:15 +0000
@@ -0,0 +1,16 @@
1include(../../test-includes.pri)
2
3TARGET = QtEventFeederTest
4
5QT += gui-private
6
7INCLUDEPATH += \
8 ../../../src/platforms/mirserver \
9 ../../../src/common
10
11SOURCES += \
12 qteventfeeder_test.cpp \
13 ../../../src/common/debughelpers.cpp
14
15LIBS += -Wl,-rpath,$${OUT_PWD}/../../../src/platforms/mirserver \
16 -L../../../src/platforms/mirserver -lqpa-mirserver
017
=== added file 'tests/mirserver/QtEventFeeder/mock_qtwindowsystem.h'
--- tests/mirserver/QtEventFeeder/mock_qtwindowsystem.h 1970-01-01 00:00:00 +0000
+++ tests/mirserver/QtEventFeeder/mock_qtwindowsystem.h 2014-08-28 13:13:15 +0000
@@ -0,0 +1,65 @@
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 *
16 */
17
18#ifndef MOCK_QTWINDOWSYSTEM_H
19#define MOCK_QTWINDOWSYSTEM_H
20
21#include <qteventfeeder.h>
22
23class MockQtWindowSystem : public QtEventFeeder::QtWindowSystemInterface {
24public:
25 MOCK_METHOD0(hasTargetWindow, bool());
26 MOCK_METHOD0(targetWindowGeometry, QRect());
27 MOCK_METHOD1(registerTouchDevice, void(QTouchDevice* device));
28 MOCK_METHOD10(handleExtendedKeyEvent, void(ulong timestamp, QEvent::Type type, int key,
29 Qt::KeyboardModifiers modifiers,
30 quint32 nativeScanCode, quint32 nativeVirtualKey,
31 quint32 nativeModifiers,
32 const QString& text, bool autorep,
33 ushort count));
34 MOCK_METHOD4(handleTouchEvent, void(ulong timestamp, QTouchDevice *device,
35 const QList<struct QWindowSystemInterface::TouchPoint> &points,
36 Qt::KeyboardModifiers mods));
37};
38
39namespace testing
40{
41
42MATCHER(IsPressed, std::string(negation ? "isn't" : "is") + " pressed")
43{
44 return arg.state == Qt::TouchPointPressed;
45}
46
47MATCHER(IsReleased, std::string(negation ? "isn't" : "is") + " released")
48{
49 return arg.state == Qt::TouchPointReleased;
50}
51
52MATCHER(StateIsMoved, "state " + std::string(negation ? "isn't" : "is") + " 'moved'")
53{
54 return arg.state == Qt::TouchPointMoved;
55}
56
57MATCHER_P(HasId, expectedId, "id " + std::string(negation ? "isn't " : "is ") + PrintToString(expectedId))
58{
59 return arg.id == expectedId;
60}
61
62} // namespace testing
63
64
65#endif // MOCK_QTWINDOWSYSTEM_H
066
=== added file 'tests/mirserver/QtEventFeeder/qteventfeeder_test.cpp'
--- tests/mirserver/QtEventFeeder/qteventfeeder_test.cpp 1970-01-01 00:00:00 +0000
+++ tests/mirserver/QtEventFeeder/qteventfeeder_test.cpp 2014-08-28 13:13:15 +0000
@@ -0,0 +1,224 @@
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 *
16 */
17
18#include <gmock/gmock.h>
19#include <gtest/gtest.h>
20
21#include <qteventfeeder.h>
22#include <debughelpers.h>
23
24#include <QGuiApplication>
25#include <QWindow>
26
27#include "mock_qtwindowsystem.h"
28
29using ::testing::_;
30using ::testing::AllOf;
31using ::testing::AnyNumber;
32using ::testing::Contains;
33using ::testing::AtLeast;
34using ::testing::InSequence;
35using ::testing::Mock;
36using ::testing::SizeIs;
37using ::testing::Return;
38
39// own gmock extensions
40using ::testing::IsPressed;
41using ::testing::IsReleased;
42using ::testing::HasId;
43using ::testing::StateIsMoved;
44
45void PrintTo(const struct QWindowSystemInterface::TouchPoint& touchPoint, ::std::ostream* os) {
46 *os << "TouchPoint("
47 << "id=" << touchPoint.id
48 << "," << touchPointStateToString(touchPoint.state)
49 << ")";
50}
51
52class QtEventFeederTest : public ::testing::Test {
53protected:
54 void SetUp() override;
55 void TearDown() override;
56
57 void setIrrelevantMockWindowSystemExpectations();
58
59 MockQtWindowSystem *mockWindowSystem;
60 QtEventFeeder *qtEventFeeder;
61};
62
63void QtEventFeederTest::SetUp()
64{
65 mockWindowSystem = new MockQtWindowSystem;
66
67 EXPECT_CALL(*mockWindowSystem, registerTouchDevice(_));
68
69 qtEventFeeder = new QtEventFeeder(mockWindowSystem);
70
71 ASSERT_TRUE(Mock::VerifyAndClearExpectations(mockWindowSystem));
72}
73
74void QtEventFeederTest::TearDown()
75{
76 // mockWindowSystem will be deleted by QtEventFeeder
77 delete qtEventFeeder;
78}
79
80void QtEventFeederTest::setIrrelevantMockWindowSystemExpectations()
81{
82 EXPECT_CALL(*mockWindowSystem, hasTargetWindow())
83 .Times(AnyNumber())
84 .WillRepeatedly(Return(true));
85 EXPECT_CALL(*mockWindowSystem, targetWindowGeometry())
86 .Times(AnyNumber())
87 .WillRepeatedly(Return(QRect(0,0,100,100)));
88}
89
90
91/*
92 Mir sends a MirEvent([touch(id=0,state=pressed)]. QtEventFeeder happily forwards that to Qt.
93
94 Then, Mir sends a MirEvent([touch(id=1,state=pressed)]). In MirEvents, every single active touch
95 point must be listed in the event even if it didn't change at all in the meantime. So that's a bug.
96 But instead of crashing or forwarding the bogus event stream down to Qt, QtEventFeeder should attempt
97 to fix the situation by synthesizing a touch[id=1,state=released] to be send along with the
98 touch(id=1,state=pressed) it got. So that Qt receives a correct touch event stream.
99 */
100TEST_F(QtEventFeederTest, GenerateMissingTouchEnd)
101{
102
103 setIrrelevantMockWindowSystemExpectations();
104
105 EXPECT_CALL(*mockWindowSystem, handleTouchEvent(_,_,AllOf(SizeIs(1),
106 Contains(AllOf(HasId(0),
107 IsPressed()))),_)).Times(1);
108
109 MirEvent mirEvent;
110 mirEvent.type = mir_event_type_motion;
111 mirEvent.motion.pointer_count = 1;
112 mirEvent.motion.pointer_coordinates[0].id = 0;
113 mirEvent.motion.pointer_coordinates[0].x = 10;
114 mirEvent.motion.pointer_coordinates[0].y = 10;
115 mirEvent.motion.pointer_coordinates[0].touch_major = 1;
116 mirEvent.motion.pointer_coordinates[0].touch_minor = 1;
117 mirEvent.motion.pointer_coordinates[0].pressure = 10;
118 mirEvent.motion.action = mir_motion_action_down;
119 mirEvent.motion.event_time = 123 * 1000000;
120
121 qtEventFeeder->dispatch(mirEvent);
122
123 ASSERT_TRUE(Mock::VerifyAndClearExpectations(mockWindowSystem));
124
125 setIrrelevantMockWindowSystemExpectations();
126
127 EXPECT_CALL(*mockWindowSystem, handleTouchEvent(_,_,AllOf(SizeIs(2),
128 Contains(AllOf(HasId(0),IsReleased())),
129 Contains(AllOf(HasId(1),IsPressed()))
130 ),_)).Times(1);
131
132 mirEvent.type = mir_event_type_motion;
133 mirEvent.motion.pointer_count = 1;
134 mirEvent.motion.pointer_coordinates[0].id = 1;
135 mirEvent.motion.pointer_coordinates[0].x = 20;
136 mirEvent.motion.pointer_coordinates[0].y = 20;
137 mirEvent.motion.pointer_coordinates[0].touch_major = 1;
138 mirEvent.motion.pointer_coordinates[0].touch_minor = 1;
139 mirEvent.motion.pointer_coordinates[0].pressure = 10;
140 mirEvent.motion.action = mir_motion_action_down;
141 mirEvent.motion.event_time = 125 * 1000000;
142
143 qtEventFeeder->dispatch(mirEvent);
144
145 ASSERT_TRUE(Mock::VerifyAndClearExpectations(mockWindowSystem));
146}
147
148TEST_F(QtEventFeederTest, PressSameTouchTwice)
149{
150 setIrrelevantMockWindowSystemExpectations();
151
152 EXPECT_CALL(*mockWindowSystem, handleTouchEvent(_,_,AllOf(SizeIs(1),
153 Contains(AllOf(HasId(0),
154 IsPressed()))),_)).Times(1);
155
156 MirEvent mirEvent;
157 mirEvent.type = mir_event_type_motion;
158 mirEvent.motion.pointer_count = 1;
159 mirEvent.motion.pointer_coordinates[0].id = 0;
160 mirEvent.motion.pointer_coordinates[0].x = 10;
161 mirEvent.motion.pointer_coordinates[0].y = 10;
162 mirEvent.motion.pointer_coordinates[0].touch_major = 1;
163 mirEvent.motion.pointer_coordinates[0].touch_minor = 1;
164 mirEvent.motion.pointer_coordinates[0].pressure = 10;
165 mirEvent.motion.action = mir_motion_action_down;
166 mirEvent.motion.event_time = 123 * 1000000;
167
168 qtEventFeeder->dispatch(mirEvent);
169
170 ASSERT_TRUE(Mock::VerifyAndClearExpectations(mockWindowSystem));
171
172 setIrrelevantMockWindowSystemExpectations();
173
174 EXPECT_CALL(*mockWindowSystem, handleTouchEvent(_,_,AllOf(SizeIs(1),
175 Contains(AllOf(HasId(0), StateIsMoved()))
176 ),_)).Times(1);
177
178 mirEvent.type = mir_event_type_motion;
179 mirEvent.motion.pointer_count = 1;
180 mirEvent.motion.pointer_coordinates[0].id = 0;
181 mirEvent.motion.pointer_coordinates[0].x = 20;
182 mirEvent.motion.pointer_coordinates[0].y = 20;
183 mirEvent.motion.pointer_coordinates[0].touch_major = 1;
184 mirEvent.motion.pointer_coordinates[0].touch_minor = 1;
185 mirEvent.motion.pointer_coordinates[0].pressure = 10;
186 mirEvent.motion.action = mir_motion_action_down;
187 mirEvent.motion.event_time = 125 * 1000000;
188
189 qtEventFeeder->dispatch(mirEvent);
190
191 ASSERT_TRUE(Mock::VerifyAndClearExpectations(mockWindowSystem));
192}
193
194TEST_F(QtEventFeederTest, IgnoreHovering)
195{
196 setIrrelevantMockWindowSystemExpectations();
197 EXPECT_CALL(*mockWindowSystem, handleTouchEvent(_,_,_,_)).Times(0);
198
199 MirEvent mirEvent;
200 mirEvent.type = mir_event_type_motion;
201 mirEvent.motion.pointer_count = 1;
202 mirEvent.motion.pointer_coordinates[0].id = 0;
203 mirEvent.motion.pointer_coordinates[0].x = 10;
204 mirEvent.motion.pointer_coordinates[0].y = 10;
205 mirEvent.motion.pointer_coordinates[0].touch_major = 1;
206 mirEvent.motion.pointer_coordinates[0].touch_minor = 1;
207 mirEvent.motion.pointer_coordinates[0].pressure = 10;
208 mirEvent.motion.action = mir_motion_action_hover_enter;
209 mirEvent.motion.event_time = 123 * 1000000;
210
211 qtEventFeeder->dispatch(mirEvent);
212
213 mirEvent.motion.pointer_coordinates[0].x = 20;
214 mirEvent.motion.pointer_coordinates[0].y = 20;
215 mirEvent.motion.action = mir_motion_action_hover_move;
216 mirEvent.motion.event_time = 125 * 1000000;
217
218 qtEventFeeder->dispatch(mirEvent);
219
220 mirEvent.motion.action = mir_motion_action_hover_exit;
221 mirEvent.motion.event_time = 127 * 1000000;
222
223 qtEventFeeder->dispatch(mirEvent);
224}
0225
=== added file 'tests/mirserver/mirserver.pro'
--- tests/mirserver/mirserver.pro 1970-01-01 00:00:00 +0000
+++ tests/mirserver/mirserver.pro 2014-08-28 13:13:15 +0000
@@ -0,0 +1,2 @@
1TEMPLATE = subdirs
2SUBDIRS = QtEventFeeder
03
=== modified file 'tests/modules/common/common.pri'
--- tests/modules/common/common.pri 2014-08-14 15:38:15 +0000
+++ tests/modules/common/common.pri 2014-08-28 13:13:15 +0000
@@ -1,8 +1,7 @@
1CONFIG += link_pkgconfig no_keywords # keywords clash with ProcessC++1CONFIG += no_keywords # keywords clash with ProcessC++
2PKGCONFIG += mirserver process-cpp ubuntu-app-launch-22PKGCONFIG += process-cpp ubuntu-app-launch-2
33
4QT += quick testlib4QT += quick
5QMAKE_CXXFLAGS = -std=c++11
65
7HEADERS += ../common/mock_application_controller.h \6HEADERS += ../common/mock_application_controller.h \
8 ../common/mock_desktop_file_reader.h \7 ../common/mock_desktop_file_reader.h \
@@ -19,31 +18,7 @@
19INCLUDEPATH += ../../../src/modules \18INCLUDEPATH += ../../../src/modules \
20 ../common19 ../common
2120
2221LIBS += \
23GMOCK_SOURCES = /usr/src/gmock/src/gmock-all.cc \
24 /usr/src/gmock/src/gmock_main.cc \
25 /usr/src/gtest/src/gtest-all.cc
26
27QMAKE_EXTRA_COMPILERS += gmock_compiler
28gmock_compiler.input = GMOCK_SOURCES
29gmock_compiler.output = $${OUT_PWD}/${QMAKE_FILE_BASE}.o
30gmock_compiler.commands = g++ \
31 -I/usr/src/gtest \
32 -I/usr/src/gmock \
33 -c ${QMAKE_FILE_IN} \
34 -o ${QMAKE_FILE_OUT}
35gmock_compiler.CONFIG = no_link
36
37QMAKE_EXTRA_COMPILERS += gmock_linker
38gmock_linker.depends = $${OUT_PWD}/gmock-all.o \
39 $${OUT_PWD}/gmock_main.o \
40 $${OUT_PWD}/gtest-all.o
41gmock_linker.input = GMOCK_SOURCES
42gmock_linker.output = $${OUT_PWD}/libgmock.a
43gmock_linker.commands = ar -rv ${QMAKE_FILE_OUT} $${OUT_PWD}/gmock-all.o $${OUT_PWD}/gmock_main.o $${OUT_PWD}/gtest-all.o
44gmock_linker.CONFIG = combine explicit_dependencies no_link target_predeps
45
46LIBS += $${OUT_PWD}/libgmock.a \
47 -Wl,-rpath,$${OUT_PWD}/../../../src/modules/Unity/Application \22 -Wl,-rpath,$${OUT_PWD}/../../../src/modules/Unity/Application \
48 -L$${OUT_PWD}/../../../src/modules/Unity/Application -lunityapplicationplugin \23 -L$${OUT_PWD}/../../../src/modules/Unity/Application -lunityapplicationplugin \
49 -Wl,-rpath,$${OUT_PWD}/../../../src/platforms/mirserver \24 -Wl,-rpath,$${OUT_PWD}/../../../src/platforms/mirserver \
5025
=== modified file 'tests/test-includes.pri'
--- tests/test-includes.pri 2014-07-07 19:33:56 +0000
+++ tests/test-includes.pri 2014-08-28 13:13:15 +0000
@@ -4,3 +4,12 @@
4# adds a 'make install' that installs the test cases, which we do not want.4# adds a 'make install' that installs the test cases, which we do not want.
5# Can configure it not to do that with 'no_testcase_installs'5# Can configure it not to do that with 'no_testcase_installs'
6CONFIG += testcase no_testcase_installs6CONFIG += testcase no_testcase_installs
7
8QMAKE_CXXFLAGS = -std=c++11
9
10QT += testlib
11
12CONFIG += link_pkgconfig
13PKGCONFIG += mirserver
14
15include(google-mock.pri)
716
=== modified file 'tests/tests.pro'
--- tests/tests.pro 2014-07-01 13:38:06 +0000
+++ tests/tests.pro 2014-08-28 13:13:15 +0000
@@ -1,2 +1,2 @@
1TEMPLATE = subdirs1TEMPLATE = subdirs
2SUBDIRS = modules2SUBDIRS = modules mirserver

Subscribers

People subscribed via source and target branches