Merge lp:~lukas-kde/unity8/cursorHiding into lp:unity8

Proposed by Lukáš Tinkl on 2015-12-01
Status: Superseded
Proposed branch: lp:~lukas-kde/unity8/cursorHiding
Merge into: lp:unity8
Prerequisite: lp:~dandrader/unity8/cursorThemes
Diff against target: 2228 lines (+1412/-128)
47 files modified
debian/unity8-private.install (+1/-0)
plugins/CMakeLists.txt (+1/-0)
plugins/Cursor/MousePointer.cpp (+4/-0)
plugins/Cursor/MousePointer.h (+1/-0)
plugins/UInput/CMakeLists.txt (+7/-0)
plugins/UInput/UInput.qmltypes (+44/-0)
plugins/UInput/plugin.cpp (+26/-0)
plugins/UInput/plugin.h (+32/-0)
plugins/UInput/qmldir (+3/-0)
plugins/UInput/uinput.cpp (+182/-0)
plugins/UInput/uinput.h (+61/-0)
plugins/Utils/CMakeLists.txt (+1/-1)
plugins/Utils/WindowInputMonitor.cpp (+19/-12)
plugins/Utils/WindowInputMonitor.h (+37/-20)
plugins/Utils/plugin.cpp (+2/-2)
qml/Components/InputMethod.qml (+1/-1)
qml/Components/VirtualTouchPad.qml (+138/-0)
qml/DisabledScreenNotice.qml (+73/-20)
qml/OrientedShell.qml (+28/-14)
qml/Shell.qml (+15/-6)
qml/Stages/AbstractStage.qml (+5/-1)
qml/Stages/DesktopStage.qml (+6/-1)
qml/Stages/PhoneStage.qml (+5/-1)
qml/Stages/TabletStage.qml (+5/-1)
tests/mocks/CMakeLists.txt (+1/-0)
tests/mocks/Cursor/Cursor.qml (+1/-0)
tests/mocks/UInput/CMakeLists.txt (+7/-0)
tests/mocks/UInput/mockuinput.cpp (+73/-0)
tests/mocks/UInput/mockuinput.h (+63/-0)
tests/mocks/UInput/plugin.cpp (+26/-0)
tests/mocks/UInput/plugin.h (+32/-0)
tests/mocks/UInput/qmldir (+2/-0)
tests/mocks/Unity/CMakeLists.txt (+1/-0)
tests/mocks/Unity/Screens/CMakeLists.txt (+14/-0)
tests/mocks/Unity/Screens/plugin.cpp (+29/-0)
tests/mocks/Unity/Screens/plugin.h (+25/-0)
tests/mocks/Unity/Screens/qmldir (+2/-0)
tests/mocks/Unity/Screens/screens.cpp (+71/-0)
tests/mocks/Unity/Screens/screens.h (+82/-0)
tests/mocks/Utils/WindowInputMonitor.qml (+3/-1)
tests/mocks/Utils/qmldir (+1/-1)
tests/plugins/Utils/CMakeLists.txt (+1/-1)
tests/plugins/Utils/WindowInputMonitorTest.cpp (+14/-14)
tests/qmltests/CMakeLists.txt (+1/-0)
tests/qmltests/Components/tst_VirtualTouchPad.qml (+143/-0)
tests/qmltests/tst_DisabledScreenNotice.qml (+10/-2)
tests/qmltests/tst_OrientedShell.qml (+113/-29)
To merge this branch: bzr merge lp:~lukas-kde/unity8/cursorHiding
Reviewer Review Type Date Requested Status
Daniel d'Andrada (community) 2015-12-01 Approve on 2015-12-03
PS Jenkins bot continuous-integration Needs Fixing on 2015-12-03
Review via email: mp+279198@code.launchpad.net

This proposal has been superseded by a proposal from 2016-01-28.

Commit Message

Hide/reveal the mouse pointer on touch/mouse events

Description of the Change

Hide the mouse pointer when a touch event is detected, reveal it again when the mouse is moved again.

This matches unity7 behavior in this regard, the mouse pointer is moved to the last known touch position.

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

Yes, lp:~dandrader/unity8/cursorThemes

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

Yes

 * Did you make sure that your branch does not contain spurious tags?

Yes

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

Yes

 * If you changed the UI, has there been a design review?

N/A, matches unity7 behavior

To post a comment you must log in.
PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:2078
http://jenkins.qa.ubuntu.com/job/unity8-ci/6860/
Executed test runs:
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-vivid-touch/5538/console
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-xenial-touch/275/console
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/unity-phablet-qmluitests-vivid/1571
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/unity8-qmluitest-xenial-amd64/274
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-vivid-amd64-ci/1466
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-vivid-i386-ci/1466
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-xenial-amd64-ci/273
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-xenial-i386-ci/272
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-vivid-touch/4335/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-vivid-armhf/5552
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-vivid-armhf/5552/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/25684
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-xenial-touch/95/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-xenial-armhf/274
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-xenial-armhf/274/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/25683

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/unity8-ci/6860/rebuild

review: Needs Fixing (continuous-integration)
Daniel d'Andrada (dandrader) wrote :

I wouldn't move the cursor because of touch interaction.

That's an habit of X11, because of its "low-level" mouse emulation out of touch events.

Daniel d'Andrada (dandrader) wrote :

If you plan to use HomeKeyWatcher for that stuff it should be renamed to something more generic, like WindowInputMonitor or something.

Lukáš Tinkl (lukas-kde) wrote :

> I wouldn't move the cursor because of touch interaction.
>
> That's an habit of X11, because of its "low-level" mouse emulation out of
> touch events.

Discussed this with mzanetti and I also compared it with other systems (unity7, Windows 10), it really feels natural this way. Of course, subjective feeling, we can ask design for opinion on this

Lukáš Tinkl (lukas-kde) wrote :

> If you plan to use HomeKeyWatcher for that stuff it should be renamed to
> something more generic, like WindowInputMonitor or something.

Yup, will do

lp:~lukas-kde/unity8/cursorHiding updated on 2015-12-02
2079. By Lukáš Tinkl on 2015-12-02

rename HomeKeyWatcher.cpp => WindowInputMonitor.cpp

Lukáš Tinkl (lukas-kde) wrote :

> If you plan to use HomeKeyWatcher for that stuff it should be renamed to
> something more generic, like WindowInputMonitor or something.

Done

PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:2079
http://jenkins.qa.ubuntu.com/job/unity8-ci/6869/
Executed test runs:
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-vivid-touch/5554
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-xenial-touch/284/console
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/unity-phablet-qmluitests-vivid/1580
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/unity8-qmluitest-xenial-amd64/283
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-vivid-amd64-ci/1475
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-vivid-i386-ci/1475
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-xenial-amd64-ci/282
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-xenial-i386-ci/281
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-vivid-touch/4350
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-vivid-armhf/5568
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-vivid-armhf/5568/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/25728
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-xenial-touch/102/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-xenial-armhf/283
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-xenial-armhf/283/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/25727

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/unity8-ci/6869/rebuild

review: Needs Fixing (continuous-integration)
Daniel d'Andrada (dandrader) wrote :

You also have to update/rephrase WindowInputMonitor's documentation and rename its activated() signal to something more specific since the class is not just about the home key anymore.

I would suggest some changes to those new signal names to get them closer to existing naming and concepts:

s/windowTouched/touchBegun
s/windowReleased/touchEnded

Also the class has "Window" in its name already, so no need to repeat that in the signal name.

------------

In WindowInputMonitor.cpp:

"""
if (touchEv && !touchEv->touchPoints().isEmpty()) {
"""

A touch event is malformed if it doesn't contain any touch point. So that should be an assertion if you care about checking it at all.

Furthermore, it would be inconsistent to emit a touchBegun() but not its touchEnded() counterpart.

review: Needs Fixing
Daniel d'Andrada (dandrader) wrote :

In Shell.qml

"""
// store the last known touch position
"""

Not a very accurate comment. You're not "storing" the touch position, you're moving the Cursor to it.

Daniel d'Andrada (dandrader) wrote :

In Shell.qml

"""
        function revealCursor() {
            if (touchDetected) {
                touchDetected = false;
                Mir.cursorName = "";
                shell.cursorVisible = true;
            }
        }
"""

There should be no need to touch Mir.cursorName.

review: Needs Fixing
Daniel d'Andrada (dandrader) wrote :

Can't you move the stuff in cursorPriv to Cursor.qml?

lp:~lukas-kde/unity8/cursorHiding updated on 2015-12-02
2080. By Lukáš Tinkl on 2015-12-02

rename signals, add some docu

2081. By Lukáš Tinkl on 2015-12-02

move the hide/reveal functions to Cursor.qml

Lukáš Tinkl (lukas-kde) wrote :

Comments/issues addressed

PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:2081
http://jenkins.qa.ubuntu.com/job/unity8-ci/6871/
Executed test runs:
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-vivid-touch/5564/console
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-xenial-touch/286/console
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/unity-phablet-qmluitests-vivid/1582
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/unity8-qmluitest-xenial-amd64/285
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-vivid-amd64-ci/1477
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-vivid-i386-ci/1477
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-xenial-amd64-ci/284
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-xenial-i386-ci/283
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-vivid-touch/4357/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-vivid-armhf/5578
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-vivid-armhf/5578/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/25745
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-xenial-touch/103/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-xenial-armhf/285
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-xenial-armhf/285/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/25744

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/unity8-ci/6871/rebuild

review: Needs Fixing (continuous-integration)
Daniel d'Andrada (dandrader) wrote :

I think there's quite a bit of redundant code there. I removed it and it works fine:
http://bazaar.launchpad.net/~dandrader/unity8/cursorHiding/revision/2080

In Shell.qml:

"""
        onTouchEnded: {
            // move the (hidden) cursor to the last known touch position
            cursor.x = pos.x;
            cursor.y = pos.y;
        }
"""

You can't simply assign as the touch position is in a different coordinate system. Cursor is in Shell coordinates whereas touch position is in root, OrientedShell, coordinates. You have to map it.

You can see it's broken is you try it on a Nexus 7 for instance.

review: Needs Fixing
lp:~lukas-kde/unity8/cursorHiding updated on 2015-12-03
2082. By Lukáš Tinkl on 2015-12-03

simplify code

2083. By Lukáš Tinkl on 2015-12-03

map the touch coords

2084. By Lukáš Tinkl on 2015-12-03

unbreak tests after the class renaming and new signal additions

Lukáš Tinkl (lukas-kde) wrote :

Comments addressed, also fixed the tests after the class renaming and new signal additions

lp:~lukas-kde/unity8/cursorHiding updated on 2015-12-03
2085. By Lukáš Tinkl on 2015-12-03

simplify, the item mapped from can be null

Daniel d'Andrada (dandrader) wrote :

In Shell.qml:

"""
var mappedCoords = mapFromItem(shell.parent, pos.x, pos.y);
"""

When mapping from the root coordinate system you just pass null, like this:

var mappedCoords = mapFromItem(null, pos.x, pos.y);

review: Needs Fixing
lp:~lukas-kde/unity8/cursorHiding updated on 2015-12-03
2086. By Lukáš Tinkl on 2015-12-03

map from null

2087. By Lukáš Tinkl on 2015-12-03

improve docu

2088. By Lukáš Tinkl on 2015-12-03

revert unrelated code changes

PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:2082
http://jenkins.qa.ubuntu.com/job/unity8-ci/6877/
Executed test runs:
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-vivid-touch/5579
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-xenial-touch/292/console
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/unity-phablet-qmluitests-vivid/1588
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/unity8-qmluitest-xenial-amd64/291
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-vivid-amd64-ci/1483
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-vivid-i386-ci/1483
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-xenial-amd64-ci/290
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-xenial-i386-ci/289
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-vivid-touch/4370
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-vivid-armhf/5593
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-vivid-armhf/5593/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/25783
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-xenial-touch/109/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-xenial-armhf/291
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-xenial-armhf/291/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/25784

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/unity8-ci/6877/rebuild

review: Needs Fixing (continuous-integration)
Daniel d'Andrada (dandrader) wrote :

Works fine and code looks ok. Let's wait for jenkins before top-approving.

review: Approve
PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:2088
http://jenkins.qa.ubuntu.com/job/unity8-ci/6879/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-vivid-touch/5585
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-xenial-touch/294/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity-phablet-qmluitests-vivid/1590
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/unity8-qmluitest-xenial-amd64/293
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-vivid-amd64-ci/1485
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-vivid-i386-ci/1485
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-xenial-amd64-ci/292
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-xenial-i386-ci/291
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-vivid-touch/4373
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-vivid-armhf/5599
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-vivid-armhf/5599/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/25789
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-xenial-touch/110/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-xenial-armhf/293
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-xenial-armhf/293/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/25790

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/unity8-ci/6879/rebuild

review: Needs Fixing (continuous-integration)
Daniel d'Andrada (dandrader) wrote :

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

* Did CI run pass? If not, please explain why.
Yes (apart from the usual xenial AP failure)

* Did you make sure that the branch does not contain spurious tags?
Yes

review: Approve
lp:~lukas-kde/unity8/cursorHiding updated on 2016-01-28
2089. By Lukáš Tinkl on 2015-12-16

remove conflicting empty line

2090. By Lukáš Tinkl on 2015-12-16

+s

2091. By Lukáš Tinkl on 2016-01-15

merge trunk

2092. By Lukáš Tinkl on 2016-01-28

merge lp:~mzanetti/unity8/inputmethod2

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/unity8-private.install'
2--- debian/unity8-private.install 2015-09-29 13:45:05 +0000
3+++ debian/unity8-private.install 2016-01-28 18:26:00 +0000
4@@ -12,6 +12,7 @@
5 usr/lib/*/unity8/qml/SessionBroadcast
6 usr/lib/*/unity8/qml/Ubuntu
7 usr/lib/*/unity8/qml/Unity
8+usr/lib/*/unity8/qml/UInput
9 usr/lib/*/unity8/qml/Utils
10 usr/lib/*/unity8/qml/Wizard
11 usr/share/accountsservice/interfaces
12
13=== modified file 'plugins/CMakeLists.txt'
14--- plugins/CMakeLists.txt 2015-09-29 13:45:05 +0000
15+++ plugins/CMakeLists.txt 2016-01-28 18:26:00 +0000
16@@ -22,6 +22,7 @@
17 add_subdirectory(SessionBroadcast)
18 add_subdirectory(ScreenGrabber)
19 add_subdirectory(Ubuntu)
20+add_subdirectory(UInput)
21 add_subdirectory(Unity)
22 add_subdirectory(Utils)
23 add_subdirectory(Wizard)
24
25=== modified file 'plugins/Cursor/MousePointer.cpp'
26--- plugins/Cursor/MousePointer.cpp 2015-11-30 17:38:20 +0000
27+++ plugins/Cursor/MousePointer.cpp 2016-01-28 18:26:00 +0000
28@@ -42,6 +42,10 @@
29 return;
30 }
31
32+ if (!movement.isNull()) {
33+ Q_EMIT mouseMoved();
34+ }
35+
36 qreal newX = x() + movement.x();
37 if (newX < 0) {
38 Q_EMIT pushedLeftBoundary(qAbs(newX), buttons);
39
40=== modified file 'plugins/Cursor/MousePointer.h'
41--- plugins/Cursor/MousePointer.h 2015-11-26 13:51:24 +0000
42+++ plugins/Cursor/MousePointer.h 2016-01-28 18:26:00 +0000
43@@ -48,6 +48,7 @@
44 Q_SIGNALS:
45 void pushedLeftBoundary(qreal amount, Qt::MouseButtons buttons);
46 void pushedRightBoundary(qreal amount, Qt::MouseButtons buttons);
47+ void mouseMoved();
48
49 protected:
50 void itemChange(ItemChange change, const ItemChangeData &value) override;
51
52=== added directory 'plugins/UInput'
53=== added file 'plugins/UInput/CMakeLists.txt'
54--- plugins/UInput/CMakeLists.txt 1970-01-01 00:00:00 +0000
55+++ plugins/UInput/CMakeLists.txt 2016-01-28 18:26:00 +0000
56@@ -0,0 +1,7 @@
57+add_library(UInput-qml MODULE
58+ plugin.cpp
59+ uinput.cpp
60+)
61+
62+qt5_use_modules(UInput-qml Qml)
63+add_unity8_plugin(UInput 0.1 UInput TARGETS UInput-qml)
64
65=== added file 'plugins/UInput/UInput.qmltypes'
66--- plugins/UInput/UInput.qmltypes 1970-01-01 00:00:00 +0000
67+++ plugins/UInput/UInput.qmltypes 2016-01-28 18:26:00 +0000
68@@ -0,0 +1,44 @@
69+import QtQuick.tooling 1.1
70+
71+// This file describes the plugin-supplied types contained in the library.
72+// It is used for QML tooling purposes only.
73+//
74+// This file was auto-generated by:
75+// 'qmlplugindump -notrelocatable UInput 0.1 plugins'
76+
77+Module {
78+ Component {
79+ name: "UInput"
80+ prototype: "QObject"
81+ exports: ["UInput/UInput 0.1"]
82+ exportMetaObjectRevisions: [0]
83+ Enum {
84+ name: "Button"
85+ values: {
86+ "ButtonLeft": 0,
87+ "ButtonRight": 1,
88+ "ButtonMiddle": 2
89+ }
90+ }
91+ Method { name: "createMouse" }
92+ Method { name: "removeMouse" }
93+ Method {
94+ name: "moveMouse"
95+ Parameter { name: "dx"; type: "int" }
96+ Parameter { name: "dy"; type: "int" }
97+ }
98+ Method {
99+ name: "pressMouse"
100+ Parameter { name: "button"; type: "Button" }
101+ }
102+ Method {
103+ name: "releaseMouse"
104+ Parameter { name: "button"; type: "Button" }
105+ }
106+ Method {
107+ name: "scrollMouse"
108+ Parameter { name: "dh"; type: "int" }
109+ Parameter { name: "dv"; type: "int" }
110+ }
111+ }
112+}
113
114=== added file 'plugins/UInput/plugin.cpp'
115--- plugins/UInput/plugin.cpp 1970-01-01 00:00:00 +0000
116+++ plugins/UInput/plugin.cpp 2016-01-28 18:26:00 +0000
117@@ -0,0 +1,26 @@
118+/*
119+ * Copyright (C) 2015 Canonical Ltd.
120+ *
121+ * This program is free software: you can redistribute it and/or modify it
122+ * under the terms of the GNU General Public License version 3, as published
123+ * by the Free Software Foundation.
124+ *
125+ * This program is distributed in the hope that it will be useful, but
126+ * WITHOUT ANY WARRANTY; without even the implied warranties of
127+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
128+ * PURPOSE. See the GNU General Public License for more details.
129+ *
130+ * You should have received a copy of the GNU General Public License along
131+ * with this program. If not, see <http://www.gnu.org/licenses/>.
132+ */
133+
134+#include "plugin.h"
135+#include "uinput.h"
136+
137+#include <QtQml/qqml.h>
138+
139+void UInputPlugin::registerTypes(const char *uri)
140+{
141+ Q_ASSERT(uri == QLatin1String("UInput"));
142+ qmlRegisterType<UInput>(uri, 0, 1, "UInput");
143+}
144
145=== added file 'plugins/UInput/plugin.h'
146--- plugins/UInput/plugin.h 1970-01-01 00:00:00 +0000
147+++ plugins/UInput/plugin.h 2016-01-28 18:26:00 +0000
148@@ -0,0 +1,32 @@
149+/*
150+ * Copyright (C) 2015 Canonical, Ltd.
151+ *
152+ * This program is free software; you can redistribute it and/or modify
153+ * it under the terms of the GNU General Public License as published by
154+ * the Free Software Foundation; version 3.
155+ *
156+ * This program is distributed in the hope that it will be useful,
157+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
158+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
159+ * GNU General Public License for more details.
160+ *
161+ * You should have received a copy of the GNU General Public License
162+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
163+ */
164+
165+#ifndef UINPUT_PLUGIN_H
166+#define UINPUT_PLUGIN_H
167+
168+#include <QtQml/QQmlEngine>
169+#include <QtQml/QQmlExtensionPlugin>
170+
171+class UInputPlugin : public QQmlExtensionPlugin
172+{
173+ Q_OBJECT
174+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
175+
176+public:
177+ void registerTypes(const char *uri) override;
178+};
179+
180+#endif
181
182=== added file 'plugins/UInput/qmldir'
183--- plugins/UInput/qmldir 1970-01-01 00:00:00 +0000
184+++ plugins/UInput/qmldir 2016-01-28 18:26:00 +0000
185@@ -0,0 +1,3 @@
186+module UInput
187+plugin UInput-qml
188+typeinfo UInput.qmltypes
189
190=== added file 'plugins/UInput/uinput.cpp'
191--- plugins/UInput/uinput.cpp 1970-01-01 00:00:00 +0000
192+++ plugins/UInput/uinput.cpp 2016-01-28 18:26:00 +0000
193@@ -0,0 +1,182 @@
194+/*
195+ * Copyright (C) 2015 Canonical Ltd.
196+ *
197+ * This program is free software: you can redistribute it and/or modify it
198+ * under the terms of the GNU General Public License version 3, as published
199+ * by the Free Software Foundation.
200+ *
201+ * This program is distributed in the hope that it will be useful, but
202+ * WITHOUT ANY WARRANTY; without even the implied warranties of
203+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
204+ * PURPOSE. See the GNU General Public License for more details.
205+ *
206+ * You should have received a copy of the GNU General Public License along
207+ * with this program. If not, see <http://www.gnu.org/licenses/>.
208+ */
209+
210+#include "uinput.h"
211+
212+#include <QFile>
213+#include <QDebug>
214+#include <QDateTime>
215+
216+#include <unistd.h>
217+#include <time.h>
218+
219+UInput::UInput(QObject *parent) :
220+ QObject(parent)
221+{
222+ m_devName = QByteArrayLiteral("unity8-simulated-mouse");
223+ m_uinput.setFileName(QStringLiteral("/dev/uinput"));
224+
225+ memset(&m_uinput_mouse_dev, 0, sizeof(m_uinput_mouse_dev));
226+ m_uinput_mouse_dev.id.bustype = BUS_USB;
227+ m_uinput_mouse_dev.id.version = 1;
228+ strncpy(m_uinput_mouse_dev.name, m_devName.constData(), m_devName.length());
229+}
230+
231+UInput::~UInput()
232+{
233+ if (m_mouseCreated) {
234+ removeMouse();
235+ }
236+}
237+
238+void UInput::createMouse()
239+{
240+ if (m_mouseCreated) {
241+ qDebug() << "Already have a virtual device. Not creating another one.";
242+ return;
243+ }
244+
245+ if (!m_uinput.isOpen() && !m_uinput.open(QFile::WriteOnly)) {
246+ return;
247+ }
248+
249+ ioctl(m_uinput.handle(), UI_SET_EVBIT, EV_REL);
250+ ioctl(m_uinput.handle(), UI_SET_RELBIT, REL_X);
251+ ioctl(m_uinput.handle(), UI_SET_RELBIT, REL_Y);
252+ ioctl(m_uinput.handle(), UI_SET_RELBIT, REL_HWHEEL);
253+ ioctl(m_uinput.handle(), UI_SET_RELBIT, REL_WHEEL);
254+
255+ ioctl(m_uinput.handle(), UI_SET_EVBIT, EV_KEY);
256+ ioctl(m_uinput.handle(), UI_SET_KEYBIT, BTN_MOUSE);
257+ ioctl(m_uinput.handle(), UI_SET_KEYBIT, BTN_LEFT);
258+ ioctl(m_uinput.handle(), UI_SET_KEYBIT, BTN_MIDDLE);
259+ ioctl(m_uinput.handle(), UI_SET_KEYBIT, BTN_RIGHT);
260+ ioctl(m_uinput.handle(), UI_SET_KEYBIT, BTN_FORWARD);
261+ ioctl(m_uinput.handle(), UI_SET_KEYBIT, BTN_BACK);
262+
263+ ioctl(m_uinput.handle(), UI_SET_EVBIT, EV_SYN);
264+
265+ int len = write(m_uinput.handle(), &m_uinput_mouse_dev, sizeof(m_uinput_mouse_dev));
266+ if (len <= 0) {
267+ qWarning() << "Failed to write to uinput. Cannot create virtual uinput mouse.";
268+ return;
269+ }
270+
271+ int err = ioctl(m_uinput.handle(), UI_DEV_CREATE);
272+ if (err != 0) {
273+ qWarning() << "Cannot create virtual uinput device. Create ioctl failed:" << err;
274+ return;
275+ }
276+ m_mouseCreated = true;
277+ qDebug() << "Virtual uinput mouse device created.";
278+}
279+
280+void UInput::removeMouse()
281+{
282+ if (!m_mouseCreated) {
283+ return;
284+ }
285+
286+ if (!m_uinput.isOpen() && !m_uinput.open(QFile::WriteOnly)) {
287+ qWarning() << "cannot open uinput... ";
288+ return;
289+ }
290+
291+ int err = ioctl(m_uinput.handle(), UI_DEV_DESTROY);
292+ if (err != 0) {
293+ qWarning() << "Failed to destroy virtual uinput device. Destroy ioctl failed:" << err;
294+ } else {
295+ qDebug() << "Virtual uinput mouse device removed.";
296+ }
297+ m_uinput.close();
298+ m_mouseCreated = false;
299+}
300+
301+void UInput::moveMouse(int dx, int dy)
302+{
303+ struct input_event event;
304+ memset(&event, 0, sizeof(event));
305+ clock_gettime(CLOCK_MONOTONIC, (timespec*)&event.time);
306+ event.type = EV_REL;
307+ event.code = REL_X;
308+ event.value = dx;
309+ write(m_uinput.handle(), &event, sizeof(event));
310+
311+ event.code = REL_Y;
312+ event.value = dy;
313+ write(m_uinput.handle(), &event, sizeof(event));
314+
315+ event.type = EV_SYN;
316+ event.code = SYN_REPORT;
317+ event.value = 0;
318+ write(m_uinput.handle(), &event, sizeof(event));
319+}
320+
321+void UInput::pressMouse(Button button)
322+{
323+ injectMouse(button, 1);
324+}
325+
326+void UInput::releaseMouse(Button button)
327+{
328+ injectMouse(button, 0);
329+}
330+
331+void UInput::scrollMouse(int dh, int dv)
332+{
333+ struct input_event event;
334+ memset(&event, 0, sizeof(event));
335+ clock_gettime(CLOCK_MONOTONIC, (timespec*)&event.time);
336+ event.type = EV_REL;
337+ event.code = REL_HWHEEL;
338+ event.value = dh;
339+ write(m_uinput.handle(), &event, sizeof(event));
340+
341+ event.code = REL_WHEEL;
342+ event.value = dv;
343+ write(m_uinput.handle(), &event, sizeof(event));
344+
345+ event.type = EV_SYN;
346+ event.code = SYN_REPORT;
347+ event.value = 0;
348+ write(m_uinput.handle(), &event, sizeof(event));
349+}
350+
351+void UInput::injectMouse(Button button, int down)
352+{
353+ struct input_event event;
354+ memset(&event, 0, sizeof(event));
355+ clock_gettime(CLOCK_MONOTONIC, (timespec*)&event.time);
356+ event.type = EV_KEY;
357+ switch (button) {
358+ case ButtonLeft:
359+ event.code = BTN_LEFT;
360+ break;
361+ case ButtonRight:
362+ event.code = BTN_RIGHT;
363+ break;
364+ case ButtonMiddle:
365+ event.code = BTN_MIDDLE;
366+ break;
367+ }
368+ event.value = down;
369+ write(m_uinput.handle(), &event, sizeof(event));
370+
371+ event.type = EV_SYN;
372+ event.code = SYN_REPORT;
373+ event.value = 0;
374+ write(m_uinput.handle(), &event, sizeof(event));
375+}
376
377=== added file 'plugins/UInput/uinput.h'
378--- plugins/UInput/uinput.h 1970-01-01 00:00:00 +0000
379+++ plugins/UInput/uinput.h 2016-01-28 18:26:00 +0000
380@@ -0,0 +1,61 @@
381+/*
382+ * Copyright (C) 2015 Canonical Ltd.
383+ *
384+ * This program is free software: you can redistribute it and/or modify it
385+ * under the terms of the GNU General Public License version 3, as published
386+ * by the Free Software Foundation.
387+ *
388+ * This program is distributed in the hope that it will be useful, but
389+ * WITHOUT ANY WARRANTY; without even the implied warranties of
390+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
391+ * PURPOSE. See the GNU General Public License for more details.
392+ *
393+ * You should have received a copy of the GNU General Public License along
394+ * with this program. If not, see <http://www.gnu.org/licenses/>.
395+ */
396+
397+
398+#ifndef UINPUT_H
399+#define UINPUT_H
400+
401+#include <QObject>
402+#include <QFile>
403+
404+#include <linux/uinput.h>
405+
406+
407+class UInput : public QObject
408+{
409+ Q_OBJECT
410+ Q_ENUMS(Button)
411+
412+public:
413+ enum Button {
414+ ButtonLeft,
415+ ButtonRight,
416+ ButtonMiddle
417+ };
418+
419+ explicit UInput(QObject *parent = nullptr);
420+ ~UInput();
421+
422+ Q_INVOKABLE void createMouse();
423+ Q_INVOKABLE void removeMouse();
424+
425+ Q_INVOKABLE void moveMouse(int dx, int dy);
426+ Q_INVOKABLE void pressMouse(Button button);
427+ Q_INVOKABLE void releaseMouse(Button button);
428+ Q_INVOKABLE void scrollMouse(int dh, int dv);
429+
430+private:
431+ void injectMouse(Button button, int down);
432+
433+private:
434+ QFile m_uinput;
435+ uinput_user_dev m_uinput_mouse_dev;
436+ QByteArray m_devName;
437+
438+ bool m_mouseCreated = false;
439+};
440+
441+#endif // UINPUT_H
442
443=== modified file 'plugins/Utils/CMakeLists.txt'
444--- plugins/Utils/CMakeLists.txt 2015-10-28 10:32:47 +0000
445+++ plugins/Utils/CMakeLists.txt 2016-01-28 18:26:00 +0000
446@@ -13,7 +13,7 @@
447 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
448 applicationsfiltermodel.cpp
449 constants.cpp
450- HomeKeyWatcher.cpp
451+ WindowInputMonitor.cpp
452 inputwatcher.cpp
453 qlimitproxymodelqml.cpp
454 unitysortfilterproxymodelqml.cpp
455
456=== renamed file 'plugins/Utils/HomeKeyWatcher.cpp' => 'plugins/Utils/WindowInputMonitor.cpp'
457--- plugins/Utils/HomeKeyWatcher.cpp 2015-04-23 12:49:15 +0000
458+++ plugins/Utils/WindowInputMonitor.cpp 2016-01-28 18:26:00 +0000
459@@ -14,18 +14,18 @@
460 * along with this program. If not, see <http://www.gnu.org/licenses/>.
461 */
462
463-#include "HomeKeyWatcher.h"
464+#include "WindowInputMonitor.h"
465
466 #include <QQuickWindow>
467
468 using namespace UnityUtil;
469
470-HomeKeyWatcher::HomeKeyWatcher(QQuickItem *parent)
471- : HomeKeyWatcher(new Timer, new ElapsedTimer, parent)
472+WindowInputMonitor::WindowInputMonitor(QQuickItem *parent)
473+ : WindowInputMonitor(new Timer, new ElapsedTimer, parent)
474 {
475 }
476
477-HomeKeyWatcher::HomeKeyWatcher(UnityUtil::AbstractTimer *timer,
478+WindowInputMonitor::WindowInputMonitor(UnityUtil::AbstractTimer *timer,
479 UnityUtil::AbstractElapsedTimer *elapsedTimer,
480 QQuickItem *parent)
481 : QQuickItem(parent)
482@@ -37,20 +37,20 @@
483 m_windowLastTouchedTimer->start();
484
485 connect(this, &QQuickItem::windowChanged,
486- this, &HomeKeyWatcher::setupFilterOnWindow);
487+ this, &WindowInputMonitor::setupFilterOnWindow);
488
489 connect(m_activationTimer, &UnityUtil::AbstractTimer::timeout,
490- this, &HomeKeyWatcher::emitActivatedIfNoTouchesAround);
491+ this, &WindowInputMonitor::emitActivatedIfNoTouchesAround);
492 m_activationTimer->setInterval(msecsWithoutTouches);
493 }
494
495-HomeKeyWatcher::~HomeKeyWatcher()
496+WindowInputMonitor::~WindowInputMonitor()
497 {
498 delete m_windowLastTouchedTimer;
499 delete m_activationTimer;
500 }
501
502-bool HomeKeyWatcher::eventFilter(QObject *watched, QEvent *event)
503+bool WindowInputMonitor::eventFilter(QObject *watched, QEvent *event)
504 {
505 Q_ASSERT(!m_filteredWindow.isNull());
506 Q_ASSERT(watched == static_cast<QObject*>(m_filteredWindow.data()));
507@@ -62,7 +62,7 @@
508 return false;
509 }
510
511-void HomeKeyWatcher::update(QEvent *event)
512+void WindowInputMonitor::update(QEvent *event)
513 {
514 if (event->type() == QEvent::KeyPress) {
515 QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
516@@ -86,15 +86,22 @@
517
518 m_activationTimer->stop();
519 m_windowBeingTouched = true;
520+ Q_EMIT touchBegun();
521
522 } else if (event->type() == QEvent::TouchEnd) {
523
524 m_windowBeingTouched = false;
525 m_windowLastTouchedTimer->start();
526+
527+ QTouchEvent * touchEv = static_cast<QTouchEvent *>(event);
528+ if (touchEv && !touchEv->touchPoints().isEmpty()) {
529+ const QPointF pos = touchEv->touchPoints().last().screenPos();
530+ Q_EMIT touchEnded(pos);
531+ }
532 }
533 }
534
535-void HomeKeyWatcher::setupFilterOnWindow(QQuickWindow *window)
536+void WindowInputMonitor::setupFilterOnWindow(QQuickWindow *window)
537 {
538 if (!m_filteredWindow.isNull()) {
539 m_filteredWindow->removeEventFilter(this);
540@@ -107,10 +114,10 @@
541 }
542 }
543
544-void HomeKeyWatcher::emitActivatedIfNoTouchesAround()
545+void WindowInputMonitor::emitActivatedIfNoTouchesAround()
546 {
547 if (!m_homeKeyPressed && !m_windowBeingTouched &&
548 (m_windowLastTouchedTimer->elapsed() > msecsWithoutTouches)) {
549- Q_EMIT activated();
550+ Q_EMIT homeKeyActivated();
551 }
552 }
553
554=== renamed file 'plugins/Utils/HomeKeyWatcher.h' => 'plugins/Utils/WindowInputMonitor.h'
555--- plugins/Utils/HomeKeyWatcher.h 2015-04-30 09:31:51 +0000
556+++ plugins/Utils/WindowInputMonitor.h 2016-01-28 18:26:00 +0000
557@@ -14,8 +14,8 @@
558 * along with this program. If not, see <http://www.gnu.org/licenses/>.
559 */
560
561-#ifndef UNITY_HOMEKEYWATCHER_H
562-#define UNITY_HOMEKEYWATCHER_H
563+#ifndef UNITY_WINDOWINPUTMONITOR_H
564+#define UNITY_WINDOWINPUTMONITOR_H
565
566 #include <QQuickItem>
567 #include <QPointer>
568@@ -23,31 +23,26 @@
569 #include "Timer.h"
570 #include "ElapsedTimer.h"
571
572-/*
573- Signals when the home key seems to be have been intentionally tapped.
574-
575- It only says the home key has been activated if it has been tapped in isolation,
576- that is, without being accompanied by touches on the screen. Home key taps that
577- happen along with (or immediately after, or immediately before) touches on the
578- screen are considered to have happened unintentionally and are thus ignored.
579-
580- Rationale being that it's easy to accidentally hit the home key while performing
581- a swipe from a screen edge, for instance. That's particularly the case when the
582- home key is a capacitive key.
583+/**
584+ * Monitors input events received by the window holding this item and the Home (Win aka Super_L)
585+ * key presses.
586+ *
587+ * Additionally, this class monitors for generic touch events on the screen, to
588+ * help with hiding/revealing the mouse pointer.
589 */
590-class HomeKeyWatcher : public QQuickItem
591+class WindowInputMonitor : public QQuickItem
592 {
593 Q_OBJECT
594 public:
595
596- HomeKeyWatcher(QQuickItem *parent = 0);
597+ WindowInputMonitor(QQuickItem *parent = 0);
598
599 // for testing
600- HomeKeyWatcher(UnityUtil::AbstractTimer *timer,
601+ WindowInputMonitor(UnityUtil::AbstractTimer *timer,
602 UnityUtil::AbstractElapsedTimer *elapsedTimer,
603 QQuickItem *parent = 0);
604
605- virtual ~HomeKeyWatcher();
606+ virtual ~WindowInputMonitor();
607
608 bool eventFilter(QObject *watched, QEvent *event) override;
609
610@@ -56,8 +51,30 @@
611 const qint64 msecsWithoutTouches = 150;
612
613 Q_SIGNALS:
614- // Emitted when the home key has been intentionally tapped
615- void activated();
616+ /**
617+ * Emitted when the home key has been intentionally tapped
618+ *
619+ * It only says the home key has been activated if it has been tapped in isolation,
620+ * that is, without being accompanied by touches on the screen. Home key taps that
621+ * happen along with (or immediately after, or immediately before) touches on the
622+ * screen are considered to have happened unintentionally and are thus ignored.
623+ *
624+ * Rationale being that it's easy to accidentally hit the home key while performing
625+ * a swipe from a screen edge, for instance. That's particularly the case when the
626+ * home key is a capacitive key.
627+ */
628+ void homeKeyActivated();
629+
630+ /**
631+ * Emitted when a touch begin event is detected
632+ */
633+ void touchBegun();
634+
635+ /**
636+ * Emitted when a touch end event is detected
637+ * @param pos the position in screen coordinates
638+ */
639+ void touchEnded(const QPointF &pos);
640
641 private Q_SLOTS:
642 void setupFilterOnWindow(QQuickWindow *window);
643@@ -71,4 +88,4 @@
644 UnityUtil::AbstractTimer *m_activationTimer;
645 };
646
647-#endif // UNITY_HOMEKEYWATCHER_H
648+#endif // UNITY_WINDOWINPUTMONITOR_H
649
650=== modified file 'plugins/Utils/plugin.cpp'
651--- plugins/Utils/plugin.cpp 2015-10-26 16:47:52 +0000
652+++ plugins/Utils/plugin.cpp 2016-01-28 18:26:00 +0000
653@@ -25,7 +25,7 @@
654 // local
655 #include "activefocuslogger.h"
656 #include "easingcurve.h"
657-#include "HomeKeyWatcher.h"
658+#include "WindowInputMonitor.h"
659 #include "inputwatcher.h"
660 #include "qlimitproxymodelqml.h"
661 #include "unitysortfilterproxymodelqml.h"
662@@ -54,7 +54,7 @@
663 void UtilsPlugin::registerTypes(const char *uri)
664 {
665 Q_ASSERT(uri == QLatin1String("Utils"));
666- qmlRegisterType<HomeKeyWatcher>(uri, 0, 1, "HomeKeyWatcher");
667+ qmlRegisterType<WindowInputMonitor>(uri, 0, 1, "WindowInputMonitor");
668 qmlRegisterType<QAbstractItemModel>();
669 qmlRegisterType<QLimitProxyModelQML>(uri, 0, 1, "LimitProxyModel");
670 qmlRegisterType<UnitySortFilterProxyModelQML>(uri, 0, 1, "UnitySortFilterProxyModel");
671
672=== modified file 'qml/Components/InputMethod.qml'
673--- qml/Components/InputMethod.qml 2015-09-02 07:42:27 +0000
674+++ qml/Components/InputMethod.qml 2016-01-28 18:26:00 +0000
675@@ -59,7 +59,7 @@
676 }
677
678 state: {
679- if (surfaceItem.surface && surfaceItem.surfaceState != Mir.MinimizedState) {
680+ if (surfaceItem.surface && surfaceItem.surfaceState != Mir.MinimizedState && root.enabled) {
681 return "shown";
682 } else {
683 return "hidden";
684
685=== added file 'qml/Components/VirtualTouchPad.qml'
686--- qml/Components/VirtualTouchPad.qml 1970-01-01 00:00:00 +0000
687+++ qml/Components/VirtualTouchPad.qml 2016-01-28 18:26:00 +0000
688@@ -0,0 +1,138 @@
689+/*
690+ * Copyright (C) 2015 Canonical, Ltd.
691+ *
692+ * This program is free software; you can redistribute it and/or modify
693+ * it under the terms of the GNU General Public License as published by
694+ * the Free Software Foundation; version 3.
695+ *
696+ * This program is distributed in the hope that it will be useful,
697+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
698+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
699+ * GNU General Public License for more details.
700+ *
701+ * You should have received a copy of the GNU General Public License
702+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
703+ */
704+
705+import QtQuick 2.4
706+import Ubuntu.Components 1.3
707+import UInput 0.1
708+
709+Item {
710+ property var uinput: UInput {
711+ Component.onCompleted: createMouse();
712+ Component.onDestruction: removeMouse();
713+ }
714+
715+ readonly property bool pressed: point1.pressed || point2.pressed
716+
717+ MultiPointTouchArea {
718+ objectName: "touchPadArea"
719+ anchors.fill: parent
720+
721+ // FIXME: Once we have Qt DPR support, this should be Qt.styleHints.startDragDistance
722+ readonly property int clickThreshold: units.gu(1.5)
723+ property bool isClick: false
724+ property bool isDoubleClick: false
725+ property bool isDrag: false
726+
727+ onPressed: {
728+ // If double-tapping *really* fast, it could happen that we end up having only point2 pressed
729+ // Make sure we check for both combos, only point1 or only point2
730+ if (((point1.pressed && !point2.pressed) || (!point1.pressed && point2.pressed))
731+ && clickTimer.running) {
732+ clickTimer.stop();
733+ uinput.pressMouse(UInput.ButtonLeft)
734+ isDoubleClick = true;
735+ }
736+ isClick = true;
737+ }
738+
739+ onUpdated: {
740+ switch (touchPoints.length) {
741+ case 1:
742+ moveMouse(touchPoints);
743+ return;
744+ case 2:
745+ scroll(touchPoints);
746+ return;
747+ }
748+ }
749+
750+ onReleased: {
751+ if (isDoubleClick || isDrag) {
752+ uinput.releaseMouse(UInput.ButtonLeft)
753+ isDoubleClick = false;
754+ }
755+ if (isClick) {
756+ clickTimer.scheduleClick(point1.pressed ? UInput.ButtonRight : UInput.ButtonLeft)
757+ }
758+ isClick = false;
759+ isDrag = false;
760+ }
761+
762+ Timer {
763+ id: clickTimer
764+ repeat: false
765+ interval: 200
766+ property int button: UInput.ButtonLeft
767+ onTriggered: {
768+ uinput.pressMouse(button);
769+ uinput.releaseMouse(button);
770+ }
771+ function scheduleClick(button) {
772+ clickTimer.button = button;
773+ clickTimer.start();
774+ }
775+ }
776+
777+ function moveMouse(touchPoints) {
778+ var tp = touchPoints[0];
779+ if (isClick &&
780+ (Math.abs(tp.x - tp.startX) > clickThreshold ||
781+ Math.abs(tp.y - tp.startY) > clickThreshold)) {
782+ isClick = false;
783+ isDrag = true;
784+ }
785+
786+ uinput.moveMouse(tp.x - tp.previousX, tp.y - tp.previousY);
787+ }
788+
789+ function scroll(touchPoints) {
790+ var dh = 0;
791+ var dv = 0;
792+ var tp = touchPoints[0];
793+ if (isClick &&
794+ (Math.abs(tp.x - tp.startX) > clickThreshold ||
795+ Math.abs(tp.y - tp.startY) > clickThreshold)) {
796+ isClick = false;
797+ }
798+ dh += tp.x - tp.previousX;
799+ dv += tp.y - tp.previousY;
800+
801+ tp = touchPoints[1];
802+ if (isClick &&
803+ (Math.abs(tp.x - tp.startX) > clickThreshold ||
804+ Math.abs(tp.y - tp.startY) > clickThreshold)) {
805+ isClick = false;
806+ }
807+ dh += tp.x - tp.previousX;
808+ dv += tp.y - tp.previousY;
809+
810+ // As we added up the movement of the two fingers, let's divide it again by 2
811+ dh /= 2;
812+ dv /= 2;
813+
814+ uinput.scrollMouse(dh, dv);
815+ }
816+
817+ touchPoints: [
818+ TouchPoint {
819+ id: point1
820+ },
821+ TouchPoint {
822+ id: point2
823+ }
824+ ]
825+ }
826+}
827
828=== modified file 'qml/DisabledScreenNotice.qml'
829--- qml/DisabledScreenNotice.qml 2015-10-05 16:04:47 +0000
830+++ qml/DisabledScreenNotice.qml 2016-01-28 18:26:00 +0000
831@@ -1,5 +1,5 @@
832 /*
833- * Copyright (C) 2015 Canonical, Ltd.
834+ * Copyright (C) 2016 Canonical, Ltd.
835 *
836 * This program is free software; you can redistribute it and/or modify
837 * it under the terms of the GNU General Public License as published by
838@@ -15,35 +15,88 @@
839 */
840
841 import QtQuick 2.4
842+import QtQuick.Layouts 1.1
843 import Ubuntu.Components 1.3
844+import QtQuick.Window 2.2
845 import "Components"
846
847-Image {
848+Item {
849 id: root
850
851+ property bool infoNoteDisplayed: true
852+
853 WallpaperResolver {
854 width: root.width
855 id: wallpaperResolver
856 }
857
858- source: wallpaperResolver.background
859-
860- UbuntuShape {
861- anchors.fill: text
862- anchors.margins: -units.gu(2)
863- backgroundColor: "black"
864- opacity: 0.4
865- }
866-
867- Label {
868- id: text
869+ Item {
870+ id: contentContainer
871 anchors.centerIn: parent
872- width: parent.width / 2
873- text: i18n.tr("Your device is now connected to an external display.")
874- color: "white"
875- horizontalAlignment: Text.AlignHCenter
876- verticalAlignment: Text.AlignVCenter
877- fontSize: "x-large"
878- wrapMode: Text.Wrap
879+ height: rotation == 90 || rotation == 270 ? parent.width : parent.height
880+ width: rotation == 90 || rotation == 270 ? parent.height : parent.width
881+
882+ rotation: {
883+ switch (Screen.orientation) {
884+ case Qt.PortraitOrientation:
885+ return 0;
886+ case Qt.LandscapeOrientation:
887+ return 270;
888+ case Qt.InvertedPortraitOrientation:
889+ return 180;
890+ case Qt.InvertedLandscapeOrientation:
891+ return 90;
892+ }
893+ }
894+ transformOrigin: Item.Center
895+
896+ VirtualTouchPad {
897+ anchors.fill: parent
898+
899+ onPressedChanged: {
900+ if (pressed && infoNoteDisplayed) {
901+ infoNoteDisplayed = false;
902+ }
903+ }
904+ }
905+
906+ Image {
907+ anchors.fill: parent
908+ source: wallpaperResolver.background
909+ }
910+
911+ Item {
912+ objectName: "infoNoticeArea"
913+ anchors.fill: parent
914+ opacity: infoNoteDisplayed ? 1 : 0
915+ visible: opacity > 0
916+ Behavior on opacity {
917+ UbuntuNumberAnimation { }
918+ }
919+
920+ Rectangle {
921+ anchors.fill: parent
922+ color: "black"
923+ opacity: 0.4
924+ }
925+
926+ Label {
927+ id: text
928+ anchors.centerIn: parent
929+ width: parent.width - units.gu(8)
930+ text: i18n.tr("Your device is now connected to an external display. Use this screen as a touch pad to interact with the mouse.")
931+ color: "white"
932+ horizontalAlignment: Text.AlignHCenter
933+ verticalAlignment: Text.AlignVCenter
934+ fontSize: "x-large"
935+ wrapMode: Text.Wrap
936+ }
937+ }
938+
939+ InputMethod {
940+ id: inputMethod
941+ objectName: "inputMethod"
942+ anchors.fill: parent
943+ }
944 }
945 }
946
947=== modified file 'qml/OrientedShell.qml'
948--- qml/OrientedShell.qml 2015-12-01 13:56:54 +0000
949+++ qml/OrientedShell.qml 2016-01-28 18:26:00 +0000
950@@ -18,6 +18,7 @@
951 import QtQuick.Window 2.2
952 import Unity.InputInfo 0.1
953 import Unity.Session 0.1
954+import Unity.Screens 0.1
955 import GSettings 1.0
956 import "Components"
957 import "Rotation"
958@@ -78,6 +79,15 @@
959 deviceFilter: InputInfo.Keyboard
960 }
961
962+ InputDeviceModel {
963+ id: touchScreensModel
964+ deviceFilter: InputInfo.TouchScreen
965+ }
966+
967+ Screens {
968+ id: screens
969+ }
970+
971 property int orientation
972 onPhysicalOrientationChanged: {
973 if (!orientationLocked) {
974@@ -97,8 +107,7 @@
975 }
976 // We need to manually update this on startup as the binding
977 // below doesn't seem to have any effect at that stage
978- oskSettings.stayHidden = keyboardsModel.count > 0
979- oskSettings.disableHeight = shell.usageScenario == "desktop"
980+ oskSettings.disableHeight = !shell.oskEnabled || shell.usageScenario == "desktop"
981 }
982
983 // we must rotate to a supported orientation regardless of shell's preference
984@@ -106,17 +115,10 @@
985 (shell.orientation & supportedOrientations) === 0 ? true
986 : shell.orientationChangesEnabled
987
988-
989- Binding {
990- target: oskSettings
991- property: "stayHidden"
992- value: keyboardsModel.count > 0
993- }
994-
995 Binding {
996 target: oskSettings
997 property: "disableHeight"
998- value: shell.usageScenario == "desktop"
999+ value: !shell.oskEnabled || shell.usageScenario == "desktop"
1000 }
1001
1002 readonly property int supportedOrientations: shell.supportedOrientations
1003@@ -189,6 +191,11 @@
1004 nativeHeight: root.height
1005 mode: applicationArguments.mode
1006 hasMouse: miceModel.count + touchPadModel.count > 0
1007+ // TODO: Factor in if the current screen is a touch screen and if the user wants to
1008+ // have multiple keyboards around. For now we only enable one keyboard at a time
1009+ // thus hiding it here if there is a physical one around or if we have a second
1010+ // screen (the virtual touchpad & osk on the phone) attached.
1011+ oskEnabled: keyboardsModel.count === 0 && screens.count === 1
1012
1013 // TODO: Factor in the connected input devices (eg: physical keyboard, mouse, touchscreen),
1014 // what's the output device (eg: big TV, desktop monitor, phone display), etc.
1015@@ -202,11 +209,18 @@
1016 return "tablet";
1017 }
1018 } else { // automatic
1019- if (miceModel.count + touchPadModel.count > 0) {
1020- return "desktop";
1021- } else {
1022- return deviceConfiguration.category;
1023+ var longEdgeWidth = Math.max(root.width, root.height)
1024+ if (longEdgeWidth > units.gu(120)) {
1025+ if (keyboardsModel.count + miceModel.count + touchPadModel.count > 0) {
1026+ return "desktop";
1027+ }
1028+ } else if (longEdgeWidth > units.gu(90)){
1029+ if (miceModel.count + touchPadModel.count > 0) {
1030+ return "desktop";
1031+ }
1032 }
1033+
1034+ return deviceConfiguration.category;
1035 }
1036 }
1037
1038
1039=== modified file 'qml/Shell.qml'
1040--- qml/Shell.qml 2015-11-30 11:55:40 +0000
1041+++ qml/Shell.qml 2016-01-28 18:26:00 +0000
1042@@ -1,5 +1,5 @@
1043 /*
1044- * Copyright (C) 2013-2015 Canonical, Ltd.
1045+ * Copyright (C) 2013-2016 Canonical, Ltd.
1046 *
1047 * This program is free software; you can redistribute it and/or modify
1048 * it under the terms of the GNU General Public License as published by
1049@@ -56,7 +56,7 @@
1050 property bool beingResized
1051 property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
1052 property string mode: "full-greeter"
1053- property bool cursorVisible: false
1054+ property alias oskEnabled: inputMethod.enabled
1055 function updateFocusedAppOrientation() {
1056 applicationsDisplayLoader.item.updateFocusedAppOrientation();
1057 }
1058@@ -84,8 +84,8 @@
1059 return Qt.PrimaryOrientation;
1060 } else if (greeter && greeter.shown) {
1061 return Qt.PrimaryOrientation;
1062- } else if (mainApp) {
1063- return shell.orientations.map(mainApp.supportedOrientations);
1064+ } else if (applicationsDisplayLoader.item) {
1065+ return shell.orientations.map(applicationsDisplayLoader.item.supportedOrientations);
1066 } else {
1067 // we just don't care
1068 return Qt.PortraitOrientation
1069@@ -176,8 +176,15 @@
1070 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, currentEventTimestamp);
1071 }
1072
1073- HomeKeyWatcher {
1074- onActivated: { launcher.fadeOut(); shell.showHome(); }
1075+ WindowInputMonitor {
1076+ onHomeKeyActivated: { launcher.fadeOut(); shell.showHome(); }
1077+ onTouchBegun: { cursor.opacity = 0; }
1078+ onTouchEnded: {
1079+ // move the (hidden) cursor to the last known touch position
1080+ var mappedCoords = mapFromItem(null, pos.x, pos.y);
1081+ cursor.x = mappedCoords.x;
1082+ cursor.y = mappedCoords.y;
1083+ }
1084 }
1085
1086 Item {
1087@@ -676,6 +683,8 @@
1088 applicationsDisplayLoader.item.pushRightEdge(amount);
1089 }
1090 }
1091+
1092+ onMouseMoved: { cursor.opacity = 1; }
1093 }
1094
1095 Rectangle {
1096
1097=== modified file 'qml/Stages/AbstractStage.qml'
1098--- qml/Stages/AbstractStage.qml 2015-10-27 21:29:56 +0000
1099+++ qml/Stages/AbstractStage.qml 2016-01-28 18:26:00 +0000
1100@@ -1,5 +1,5 @@
1101 /*
1102- * Copyright (C) 2015 Canonical, Ltd.
1103+ * Copyright (C) 2015-2016 Canonical, Ltd.
1104 *
1105 * This program is free software; you can redistribute it and/or modify
1106 * it under the terms of the GNU General Public License as published by
1107@@ -44,6 +44,10 @@
1108 property var mainApp: null
1109 property int mainAppWindowOrientationAngle
1110 property bool orientationChangesEnabled
1111+ property int supportedOrientations: Qt.PortraitOrientation
1112+ | Qt.LandscapeOrientation
1113+ | Qt.InvertedPortraitOrientation
1114+ | Qt.InvertedLandscapeOrientation
1115
1116 // Shared code for use in stage implementations
1117 GSettings {
1118
1119=== modified file 'qml/Stages/DesktopStage.qml'
1120--- qml/Stages/DesktopStage.qml 2016-01-08 13:29:03 +0000
1121+++ qml/Stages/DesktopStage.qml 2016-01-28 18:26:00 +0000
1122@@ -1,5 +1,5 @@
1123 /*
1124- * Copyright (C) 2014-2015 Canonical, Ltd.
1125+ * Copyright (C) 2014-2016 Canonical, Ltd.
1126 *
1127 * This program is free software; you can redistribute it and/or modify
1128 * it under the terms of the GNU General Public License as published by
1129@@ -40,6 +40,11 @@
1130 ? ApplicationManager.findApplication(ApplicationManager.focusedApplicationId)
1131 : null
1132
1133+ // application windows never rotate independently
1134+ mainAppWindowOrientationAngle: shellOrientationAngle
1135+
1136+ orientationChangesEnabled: true
1137+
1138 Connections {
1139 target: ApplicationManager
1140 onApplicationAdded: {
1141
1142=== modified file 'qml/Stages/PhoneStage.qml'
1143--- qml/Stages/PhoneStage.qml 2015-12-03 18:10:39 +0000
1144+++ qml/Stages/PhoneStage.qml 2016-01-28 18:26:00 +0000
1145@@ -1,5 +1,5 @@
1146 /*
1147- * Copyright (C) 2014-2015 Canonical, Ltd.
1148+ * Copyright (C) 2014-2016 Canonical, Ltd.
1149 *
1150 * This program is free software; you can redistribute it and/or modify
1151 * it under the terms of the GNU General Public License as published by
1152@@ -83,6 +83,10 @@
1153 && !(priv.focusedAppDelegate && priv.focusedAppDelegate.xBehavior.running)
1154 && spreadView.phase === 0
1155
1156+ supportedOrientations: mainApp ? mainApp.supportedOrientations
1157+ : (Qt.PortraitOrientation | Qt.LandscapeOrientation
1158+ | Qt.InvertedPortraitOrientation | Qt.InvertedLandscapeOrientation)
1159+
1160 // How far left the stage has been dragged
1161 readonly property real dragProgress: spreadRepeater.count > 0 ? -spreadRepeater.itemAt(0).xTranslate : 0
1162
1163
1164=== modified file 'qml/Stages/TabletStage.qml'
1165--- qml/Stages/TabletStage.qml 2015-12-03 18:10:39 +0000
1166+++ qml/Stages/TabletStage.qml 2016-01-28 18:26:00 +0000
1167@@ -1,5 +1,5 @@
1168 /*
1169- * Copyright (C) 2014-2015 Canonical, Ltd.
1170+ * Copyright (C) 2014-2016 Canonical, Ltd.
1171 *
1172 * This program is free software; you can redistribute it and/or modify
1173 * it under the terms of the GNU General Public License as published by
1174@@ -72,6 +72,10 @@
1175
1176 orientationChangesEnabled: priv.mainAppOrientationChangesEnabled
1177
1178+ supportedOrientations: mainApp ? mainApp.supportedOrientations
1179+ : (Qt.PortraitOrientation | Qt.LandscapeOrientation
1180+ | Qt.InvertedPortraitOrientation | Qt.InvertedLandscapeOrientation)
1181+
1182 onWidthChanged: {
1183 spreadView.selectedIndex = -1;
1184 spreadView.phase = 0;
1185
1186=== modified file 'tests/mocks/CMakeLists.txt'
1187--- tests/mocks/CMakeLists.txt 2015-09-29 13:45:05 +0000
1188+++ tests/mocks/CMakeLists.txt 2016-01-28 18:26:00 +0000
1189@@ -43,6 +43,7 @@
1190 add_subdirectory(QtMultimedia)
1191 add_subdirectory(Wizard)
1192 add_subdirectory(Utils)
1193+add_subdirectory(UInput)
1194
1195 install(
1196 DIRECTORY data
1197
1198=== modified file 'tests/mocks/Cursor/Cursor.qml'
1199--- tests/mocks/Cursor/Cursor.qml 2015-11-24 17:44:18 +0000
1200+++ tests/mocks/Cursor/Cursor.qml 2016-01-28 18:26:00 +0000
1201@@ -19,4 +19,5 @@
1202 Item {
1203 signal pushedLeftBoundary(real amount, int buttons)
1204 signal pushedRightBoundary(real amount, int buttons)
1205+ signal mouseMoved()
1206 }
1207
1208=== added directory 'tests/mocks/UInput'
1209=== added file 'tests/mocks/UInput/CMakeLists.txt'
1210--- tests/mocks/UInput/CMakeLists.txt 1970-01-01 00:00:00 +0000
1211+++ tests/mocks/UInput/CMakeLists.txt 2016-01-28 18:26:00 +0000
1212@@ -0,0 +1,7 @@
1213+add_library(FakeUInput-qml MODULE
1214+ plugin.cpp
1215+ mockuinput.cpp
1216+)
1217+
1218+qt5_use_modules(FakeUInput-qml Qml)
1219+add_unity8_mock(UInput 0.1 UInput TARGETS FakeUInput-qml)
1220
1221=== added file 'tests/mocks/UInput/mockuinput.cpp'
1222--- tests/mocks/UInput/mockuinput.cpp 1970-01-01 00:00:00 +0000
1223+++ tests/mocks/UInput/mockuinput.cpp 2016-01-28 18:26:00 +0000
1224@@ -0,0 +1,73 @@
1225+/*
1226+ * Copyright (C) 2015 Canonical Ltd.
1227+ *
1228+ * This program is free software: you can redistribute it and/or modify it
1229+ * under the terms of the GNU General Public License version 3, as published
1230+ * by the Free Software Foundation.
1231+ *
1232+ * This program is distributed in the hope that it will be useful, but
1233+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1234+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1235+ * PURPOSE. See the GNU General Public License for more details.
1236+ *
1237+ * You should have received a copy of the GNU General Public License along
1238+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1239+ */
1240+
1241+
1242+#include "mockuinput.h"
1243+
1244+#include <QFile>
1245+#include <QDebug>
1246+#include <QDateTime>
1247+
1248+#include <unistd.h>
1249+
1250+MockUInput::MockUInput(QObject *parent) :
1251+ QObject(parent)
1252+{
1253+}
1254+
1255+MockUInput::~MockUInput()
1256+{
1257+}
1258+
1259+void MockUInput::createMouse()
1260+{
1261+ if (m_mouseCreated) {
1262+ qDebug() << "Already have a virtual device. Not creating another one.";
1263+ return;
1264+ }
1265+ m_mouseCreated = true;
1266+ Q_EMIT mouseCreated();
1267+}
1268+
1269+void MockUInput::removeMouse()
1270+{
1271+ if (!m_mouseCreated) {
1272+ return;
1273+ }
1274+ qDebug() << "Virtual uinput mouse device removed.";
1275+ m_mouseCreated = false;
1276+ Q_EMIT mouseRemoved();
1277+}
1278+
1279+void MockUInput::moveMouse(int dx, int dy)
1280+{
1281+ Q_EMIT mouseMoved(dx, dy);
1282+}
1283+
1284+void MockUInput::pressMouse(Button button)
1285+{
1286+ Q_EMIT mousePressed(button);
1287+}
1288+
1289+void MockUInput::releaseMouse(Button button)
1290+{
1291+ Q_EMIT mouseReleased(button);
1292+}
1293+
1294+void MockUInput::scrollMouse(int dh, int dv)
1295+{
1296+ Q_EMIT mouseScrolled(dh, dv);
1297+}
1298
1299=== added file 'tests/mocks/UInput/mockuinput.h'
1300--- tests/mocks/UInput/mockuinput.h 1970-01-01 00:00:00 +0000
1301+++ tests/mocks/UInput/mockuinput.h 2016-01-28 18:26:00 +0000
1302@@ -0,0 +1,63 @@
1303+/*
1304+ * Copyright (C) 2015 Canonical Ltd.
1305+ *
1306+ * This program is free software: you can redistribute it and/or modify it
1307+ * under the terms of the GNU General Public License version 3, as published
1308+ * by the Free Software Foundation.
1309+ *
1310+ * This program is distributed in the hope that it will be useful, but
1311+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1312+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1313+ * PURPOSE. See the GNU General Public License for more details.
1314+ *
1315+ * You should have received a copy of the GNU General Public License along
1316+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1317+ */
1318+
1319+
1320+#ifndef MOCKUINPUT_H
1321+#define MOCKUINPUT_H
1322+
1323+#include <QObject>
1324+#include <QFile>
1325+
1326+#include <linux/uinput.h>
1327+
1328+
1329+class MockUInput : public QObject
1330+{
1331+ Q_OBJECT
1332+ Q_ENUMS(Button)
1333+
1334+public:
1335+ enum Button {
1336+ ButtonLeft,
1337+ ButtonRight,
1338+ ButtonMiddle
1339+ };
1340+
1341+ explicit MockUInput(QObject *parent = nullptr);
1342+ ~MockUInput();
1343+
1344+ Q_INVOKABLE void createMouse();
1345+ Q_INVOKABLE void removeMouse();
1346+
1347+ Q_INVOKABLE void moveMouse(int dx, int dy);
1348+ Q_INVOKABLE void pressMouse(Button button);
1349+ Q_INVOKABLE void releaseMouse(Button button);
1350+ Q_INVOKABLE void scrollMouse(int dh, int dv);
1351+
1352+Q_SIGNALS:
1353+ // for testing
1354+ void mouseCreated();
1355+ void mouseRemoved();
1356+ void mouseMoved(int dx, int dy);
1357+ void mousePressed(Button button);
1358+ void mouseReleased(Button button);
1359+ void mouseScrolled(int dh, int dv);
1360+
1361+private:
1362+ bool m_mouseCreated = false;
1363+};
1364+
1365+#endif // MOCKUINPUT_H
1366
1367=== added file 'tests/mocks/UInput/plugin.cpp'
1368--- tests/mocks/UInput/plugin.cpp 1970-01-01 00:00:00 +0000
1369+++ tests/mocks/UInput/plugin.cpp 2016-01-28 18:26:00 +0000
1370@@ -0,0 +1,26 @@
1371+/*
1372+ * Copyright (C) 2015 Canonical Ltd.
1373+ *
1374+ * This program is free software: you can redistribute it and/or modify it
1375+ * under the terms of the GNU General Public License version 3, as published
1376+ * by the Free Software Foundation.
1377+ *
1378+ * This program is distributed in the hope that it will be useful, but
1379+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1380+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1381+ * PURPOSE. See the GNU General Public License for more details.
1382+ *
1383+ * You should have received a copy of the GNU General Public License along
1384+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1385+ */
1386+
1387+#include "plugin.h"
1388+#include "mockuinput.h"
1389+
1390+#include <QtQml/qqml.h>
1391+
1392+void MockUInputPlugin::registerTypes(const char *uri)
1393+{
1394+ Q_ASSERT(uri == QLatin1String("UInput"));
1395+ qmlRegisterType<MockUInput>(uri, 0, 1, "UInput");
1396+}
1397
1398=== added file 'tests/mocks/UInput/plugin.h'
1399--- tests/mocks/UInput/plugin.h 1970-01-01 00:00:00 +0000
1400+++ tests/mocks/UInput/plugin.h 2016-01-28 18:26:00 +0000
1401@@ -0,0 +1,32 @@
1402+/*
1403+ * Copyright (C) 2015 Canonical, Ltd.
1404+ *
1405+ * This program is free software; you can redistribute it and/or modify
1406+ * it under the terms of the GNU General Public License as published by
1407+ * the Free Software Foundation; version 3.
1408+ *
1409+ * This program is distributed in the hope that it will be useful,
1410+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1411+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1412+ * GNU General Public License for more details.
1413+ *
1414+ * You should have received a copy of the GNU General Public License
1415+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1416+ */
1417+
1418+#ifndef MOCKUINPUT_PLUGIN_H
1419+#define MOCKUINPUT_PLUGIN_H
1420+
1421+#include <QtQml/QQmlEngine>
1422+#include <QtQml/QQmlExtensionPlugin>
1423+
1424+class MockUInputPlugin : public QQmlExtensionPlugin
1425+{
1426+ Q_OBJECT
1427+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
1428+
1429+public:
1430+ void registerTypes(const char *uri) override;
1431+};
1432+
1433+#endif
1434
1435=== added file 'tests/mocks/UInput/qmldir'
1436--- tests/mocks/UInput/qmldir 1970-01-01 00:00:00 +0000
1437+++ tests/mocks/UInput/qmldir 2016-01-28 18:26:00 +0000
1438@@ -0,0 +1,2 @@
1439+module UInput
1440+plugin FakeUInput-qml
1441
1442=== modified file 'tests/mocks/Unity/CMakeLists.txt'
1443--- tests/mocks/Unity/CMakeLists.txt 2015-11-30 09:24:35 +0000
1444+++ tests/mocks/Unity/CMakeLists.txt 2016-01-28 18:26:00 +0000
1445@@ -5,6 +5,7 @@
1446 add_subdirectory(Launcher)
1447 add_subdirectory(Notifications)
1448 add_subdirectory(DashCommunicator)
1449+add_subdirectory(Screens)
1450
1451 pkg_search_module(GOBJECT gobject-2.0 REQUIRED)
1452 pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=9)
1453
1454=== added directory 'tests/mocks/Unity/Screens'
1455=== added file 'tests/mocks/Unity/Screens/CMakeLists.txt'
1456--- tests/mocks/Unity/Screens/CMakeLists.txt 1970-01-01 00:00:00 +0000
1457+++ tests/mocks/Unity/Screens/CMakeLists.txt 2016-01-28 18:26:00 +0000
1458@@ -0,0 +1,14 @@
1459+include_directories(
1460+ ${CMAKE_CURRENT_SOURCE_DIR}
1461+)
1462+
1463+set(MockScreens_SOURCES
1464+ plugin.cpp
1465+ screens.cpp
1466+)
1467+
1468+add_library(MockScreensPlugin MODULE ${MockScreens_SOURCES})
1469+
1470+qt5_use_modules(MockScreensPlugin Gui Qml)
1471+
1472+add_unity8_mock(Unity.Screens 0.1 Unity/Screens TARGETS MockScreensPlugin)
1473
1474=== added file 'tests/mocks/Unity/Screens/plugin.cpp'
1475--- tests/mocks/Unity/Screens/plugin.cpp 1970-01-01 00:00:00 +0000
1476+++ tests/mocks/Unity/Screens/plugin.cpp 2016-01-28 18:26:00 +0000
1477@@ -0,0 +1,29 @@
1478+/*
1479+ * Copyright (C) 2015 Canonical, Ltd.
1480+ *
1481+ * This program is free software: you can redistribute it and/or modify it under
1482+ * the terms of the GNU Lesser General Public License version 3, as published by
1483+ * the Free Software Foundation.
1484+ *
1485+ * This program is distributed in the hope that it will be useful, but WITHOUT
1486+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1487+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1488+ * Lesser General Public License for more details.
1489+ *
1490+ * You should have received a copy of the GNU Lesser General Public License
1491+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1492+ */
1493+
1494+#include "plugin.h"
1495+#include "screens.h"
1496+
1497+#include <QScreen>
1498+
1499+void UnityScreensPlugin::registerTypes(const char* uri)
1500+{
1501+ Q_ASSERT(QLatin1String(uri) == QLatin1String("Unity.Screens"));
1502+
1503+ qRegisterMetaType<QScreen*>("QScreen*");
1504+
1505+ qmlRegisterType<Screens>(uri, 0, 1, "Screens");
1506+}
1507
1508=== added file 'tests/mocks/Unity/Screens/plugin.h'
1509--- tests/mocks/Unity/Screens/plugin.h 1970-01-01 00:00:00 +0000
1510+++ tests/mocks/Unity/Screens/plugin.h 2016-01-28 18:26:00 +0000
1511@@ -0,0 +1,25 @@
1512+/*
1513+ * Copyright (C) 2015 Canonical, Ltd.
1514+ *
1515+ * This program is free software: you can redistribute it and/or modify it under
1516+ * the terms of the GNU Lesser General Public License version 3, as published by
1517+ * the Free Software Foundation.
1518+ *
1519+ * This program is distributed in the hope that it will be useful, but WITHOUT
1520+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1521+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1522+ * Lesser General Public License for more details.
1523+ *
1524+ * You should have received a copy of the GNU Lesser General Public License
1525+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1526+ */
1527+
1528+#include <QQmlExtensionPlugin>
1529+#include <QtQml/qqml.h>
1530+
1531+class UnityScreensPlugin : public QQmlExtensionPlugin {
1532+ Q_OBJECT
1533+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface/1.0")
1534+public:
1535+ virtual void registerTypes(const char* uri);
1536+};
1537
1538=== added file 'tests/mocks/Unity/Screens/qmldir'
1539--- tests/mocks/Unity/Screens/qmldir 1970-01-01 00:00:00 +0000
1540+++ tests/mocks/Unity/Screens/qmldir 2016-01-28 18:26:00 +0000
1541@@ -0,0 +1,2 @@
1542+module Unity.Screens
1543+plugin MockScreensPlugin
1544
1545=== added file 'tests/mocks/Unity/Screens/screens.cpp'
1546--- tests/mocks/Unity/Screens/screens.cpp 1970-01-01 00:00:00 +0000
1547+++ tests/mocks/Unity/Screens/screens.cpp 2016-01-28 18:26:00 +0000
1548@@ -0,0 +1,71 @@
1549+/*
1550+ * Copyright (C) 2015 Canonical, Ltd.
1551+ *
1552+ * This program is free software: you can redistribute it and/or modify it under
1553+ * the terms of the GNU Lesser General Public License version 3, as published by
1554+ * the Free Software Foundation.
1555+ *
1556+ * This program is distributed in the hope that it will be useful, but WITHOUT
1557+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1558+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1559+ * Lesser General Public License for more details.
1560+ *
1561+ * You should have received a copy of the GNU Lesser General Public License
1562+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1563+ */
1564+
1565+#include "screens.h"
1566+
1567+// Qt
1568+#include <QGuiApplication>
1569+#include <QScreen>
1570+#include <QDebug>
1571+
1572+Q_DECLARE_METATYPE(QScreen*)
1573+
1574+Screens::Screens(QObject *parent) :
1575+ QAbstractListModel(parent)
1576+{
1577+ // start with one screen attached
1578+ m_screenList.append(new Screen());
1579+}
1580+
1581+Screens::~Screens()
1582+{
1583+ qDeleteAll(m_screenList);
1584+ m_screenList.clear();
1585+}
1586+
1587+QHash<int, QByteArray> Screens::roleNames() const
1588+{
1589+ QHash<int, QByteArray> roles;
1590+ roles[ScreenRole] = "screen";
1591+ roles[OutputTypeRole] = "outputType";
1592+ return roles;
1593+}
1594+
1595+QVariant Screens::data(const QModelIndex &index, int role) const
1596+{
1597+ if (!index.isValid() || index.row() >= m_screenList.size()) {
1598+ return QVariant();
1599+ }
1600+
1601+ switch(role) {
1602+ case ScreenRole:
1603+ return QVariant::fromValue(m_screenList.at(index.row())->qScreen);
1604+ case OutputTypeRole:
1605+ return m_screenList.at(index.row())->outputTypes;
1606+ }
1607+
1608+ return QVariant();
1609+}
1610+
1611+int Screens::rowCount(const QModelIndex &) const
1612+{
1613+ return count();
1614+}
1615+
1616+int Screens::count() const
1617+{
1618+ return m_screenList.size();
1619+}
1620
1621=== added file 'tests/mocks/Unity/Screens/screens.h'
1622--- tests/mocks/Unity/Screens/screens.h 1970-01-01 00:00:00 +0000
1623+++ tests/mocks/Unity/Screens/screens.h 2016-01-28 18:26:00 +0000
1624@@ -0,0 +1,82 @@
1625+/*
1626+ * Copyright (C) 2015 Canonical, Ltd.
1627+ *
1628+ * This program is free software: you can redistribute it and/or modify it under
1629+ * the terms of the GNU Lesser General Public License version 3, as published by
1630+ * the Free Software Foundation.
1631+ *
1632+ * This program is distributed in the hope that it will be useful, but WITHOUT
1633+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1634+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1635+ * Lesser General Public License for more details.
1636+ *
1637+ * You should have received a copy of the GNU Lesser General Public License
1638+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1639+ */
1640+
1641+#ifndef SCREENS_H
1642+#define SCREENS_H
1643+
1644+#include <QAbstractListModel>
1645+#include <QScreen>
1646+
1647+class Screen;
1648+
1649+class Screens : public QAbstractListModel
1650+{
1651+ Q_OBJECT
1652+ Q_ENUMS(OutputTypes)
1653+
1654+ Q_PROPERTY(int count READ count NOTIFY countChanged)
1655+
1656+public:
1657+ enum ItemRoles {
1658+ ScreenRole = Qt::UserRole + 1,
1659+ OutputTypeRole
1660+ };
1661+
1662+ enum OutputTypes {
1663+ Unknown,
1664+ VGA,
1665+ DVII,
1666+ DVID,
1667+ DVIA,
1668+ Composite,
1669+ SVideo,
1670+ LVDS,
1671+ Component,
1672+ NinePinDIN,
1673+ DisplayPort,
1674+ HDMIA,
1675+ HDMIB,
1676+ TV,
1677+ EDP
1678+ };
1679+
1680+ explicit Screens(QObject *parent = 0);
1681+ virtual ~Screens() noexcept;
1682+
1683+ /* QAbstractItemModel */
1684+ QHash<int, QByteArray> roleNames() const override;
1685+ QVariant data(const QModelIndex &index, int role = ScreenRole) const override;
1686+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
1687+
1688+ int count() const;
1689+
1690+Q_SIGNALS:
1691+ void countChanged();
1692+ void screenAdded(QScreen *screen);
1693+ void screenRemoved(QScreen *screen);
1694+
1695+private:
1696+ QList<Screen *> m_screenList;
1697+};
1698+
1699+class Screen
1700+{
1701+public:
1702+ Screens::OutputTypes outputTypes = Screens::Unknown;
1703+ QScreen *qScreen = nullptr;
1704+};
1705+
1706+#endif // SCREENS_H
1707
1708=== renamed file 'tests/mocks/Utils/HomeKeyWatcher.qml' => 'tests/mocks/Utils/WindowInputMonitor.qml'
1709--- tests/mocks/Utils/HomeKeyWatcher.qml 2015-07-15 15:07:19 +0000
1710+++ tests/mocks/Utils/WindowInputMonitor.qml 2016-01-28 18:26:00 +0000
1711@@ -17,5 +17,7 @@
1712 import QtQuick 2.4
1713
1714 QtObject {
1715- signal activated()
1716+ signal homeKeyActivated()
1717+ signal touchBegun()
1718+ signal touchEnded(point pos)
1719 }
1720
1721=== modified file 'tests/mocks/Utils/qmldir'
1722--- tests/mocks/Utils/qmldir 2015-11-24 17:44:18 +0000
1723+++ tests/mocks/Utils/qmldir 2016-01-28 18:26:00 +0000
1724@@ -2,5 +2,5 @@
1725 plugin FakeUtils-qml
1726 typeinfo Utils.qmltypes
1727 Style 0.1 Style.js
1728-HomeKeyWatcher 0.1 HomeKeyWatcher.qml
1729+WindowInputMonitor 0.1 WindowInputMonitor.qml
1730 singleton EdgeBarrierSettings 0.1 EdgeBarrierSettings.qml
1731
1732=== modified file 'tests/plugins/Utils/CMakeLists.txt'
1733--- tests/plugins/Utils/CMakeLists.txt 2015-07-15 11:37:23 +0000
1734+++ tests/plugins/Utils/CMakeLists.txt 2016-01-28 18:26:00 +0000
1735@@ -7,7 +7,7 @@
1736 foreach(util_test
1737 QLimitProxyModel
1738 UnitySortFilterProxyModel
1739- HomeKeyWatcher
1740+ WindowInputMonitor
1741 )
1742 add_executable(${util_test}TestExec ${util_test}Test.cpp ModelTest.cpp)
1743 qt5_use_modules(${util_test}TestExec Test Core Qml)
1744
1745=== renamed file 'tests/plugins/Utils/HomeKeyWatcherTest.cpp' => 'tests/plugins/Utils/WindowInputMonitorTest.cpp'
1746--- tests/plugins/Utils/HomeKeyWatcherTest.cpp 2015-08-19 14:24:07 +0000
1747+++ tests/plugins/Utils/WindowInputMonitorTest.cpp 2016-01-28 18:26:00 +0000
1748@@ -14,7 +14,7 @@
1749 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1750 */
1751
1752-#include "HomeKeyWatcher.h"
1753+#include "WindowInputMonitor.h"
1754
1755 #include <QTest>
1756 #include <QSignalSpy>
1757@@ -71,7 +71,7 @@
1758
1759 using namespace UnityUtil;
1760
1761-class HomeKeyWatcherTest : public QObject {
1762+class WindowInputMonitorTest : public QObject {
1763 Q_OBJECT
1764 private Q_SLOTS:
1765 void init(); // called right before each and every test function is executed
1766@@ -88,25 +88,25 @@
1767 FakeTimerFactory *m_fakeTimerFactory;
1768 };
1769
1770-void HomeKeyWatcherTest::init()
1771+void WindowInputMonitorTest::init()
1772 {
1773 m_fakeTimerFactory = new FakeTimerFactory;
1774 }
1775
1776-void HomeKeyWatcherTest::cleanup()
1777+void WindowInputMonitorTest::cleanup()
1778 {
1779 delete m_fakeTimerFactory;
1780 m_fakeTimerFactory = nullptr;
1781 }
1782
1783
1784-void HomeKeyWatcherTest::passTime(qint64 timeSpanMs)
1785+void WindowInputMonitorTest::passTime(qint64 timeSpanMs)
1786 {
1787 qint64 finalTime = FakeElapsedTimer::msecsSinceEpoch + timeSpanMs;
1788 m_fakeTimerFactory->updateTime(finalTime);
1789 }
1790
1791-void HomeKeyWatcherTest::touchTapTouch_data()
1792+void WindowInputMonitorTest::touchTapTouch_data()
1793 {
1794 QTest::addColumn<int>("silenceBeforeTap");
1795 QTest::addColumn<int>("tapDuration");
1796@@ -121,14 +121,14 @@
1797 QTest::newRow("isolated long press") << 1000 << 200 << 1000 << 0;
1798 }
1799
1800-void HomeKeyWatcherTest::touchTapTouch()
1801+void WindowInputMonitorTest::touchTapTouch()
1802 {
1803 QFETCH(int, silenceBeforeTap);
1804 QFETCH(int, tapDuration);
1805 QFETCH(int, silenceAfterTap);
1806 QFETCH(int, expectedActivatedCount);
1807- HomeKeyWatcher homeKeyWatcher(m_fakeTimerFactory->create(), new FakeElapsedTimer);
1808- QSignalSpy activatedSpy(&homeKeyWatcher, &HomeKeyWatcher::activated);
1809+ WindowInputMonitor homeKeyWatcher(m_fakeTimerFactory->create(), new FakeElapsedTimer);
1810+ QSignalSpy activatedSpy(&homeKeyWatcher, &WindowInputMonitor::homeKeyActivated);
1811 QVERIFY(activatedSpy.isValid());
1812
1813 {
1814@@ -186,10 +186,10 @@
1815 QCOMPARE(activatedSpy.count(), expectedActivatedCount);
1816 }
1817
1818-void HomeKeyWatcherTest::tapWhileTouching()
1819+void WindowInputMonitorTest::tapWhileTouching()
1820 {
1821- HomeKeyWatcher homeKeyWatcher(m_fakeTimerFactory->create(), new FakeElapsedTimer);
1822- QSignalSpy activatedSpy(&homeKeyWatcher, &HomeKeyWatcher::activated);
1823+ WindowInputMonitor homeKeyWatcher(m_fakeTimerFactory->create(), new FakeElapsedTimer);
1824+ QSignalSpy activatedSpy(&homeKeyWatcher, &WindowInputMonitor::homeKeyActivated);
1825 QVERIFY(activatedSpy.isValid());
1826
1827 {
1828@@ -318,6 +318,6 @@
1829 return fakeTimer;
1830 }
1831
1832-QTEST_GUILESS_MAIN(HomeKeyWatcherTest)
1833+QTEST_GUILESS_MAIN(WindowInputMonitorTest)
1834
1835-#include "HomeKeyWatcherTest.moc"
1836+#include "WindowInputMonitorTest.moc"
1837
1838=== modified file 'tests/qmltests/CMakeLists.txt'
1839--- tests/qmltests/CMakeLists.txt 2016-01-11 17:37:50 +0000
1840+++ tests/qmltests/CMakeLists.txt 2016-01-28 18:26:00 +0000
1841@@ -17,6 +17,7 @@
1842 add_unity8_qmltest(Components ResponsiveGridView)
1843 add_unity8_qmltest(Components ResponsiveVerticalJournal)
1844 add_unity8_qmltest(Components Showable)
1845+add_unity8_qmltest(Components VirtualTouchPad)
1846 add_unity8_qmltest(Components WallpaperResolver)
1847 add_unity8_qmltest(Components ZoomableImage)
1848 add_unity8_qmltest(Dash Dash)
1849
1850=== added file 'tests/qmltests/Components/tst_VirtualTouchPad.qml'
1851--- tests/qmltests/Components/tst_VirtualTouchPad.qml 1970-01-01 00:00:00 +0000
1852+++ tests/qmltests/Components/tst_VirtualTouchPad.qml 2016-01-28 18:26:00 +0000
1853@@ -0,0 +1,143 @@
1854+/*
1855+ * Copyright (C) 2015 Canonical, Ltd.
1856+ *
1857+ * This program is free software; you can redistribute it and/or modify
1858+ * it under the terms of the GNU General Public License as published by
1859+ * the Free Software Foundation; version 3.
1860+ *
1861+ * This program is distributed in the hope that it will be useful,
1862+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1863+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1864+ * GNU General Public License for more details.
1865+ *
1866+ * You should have received a copy of the GNU General Public License
1867+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1868+ */
1869+
1870+import QtQuick 2.4
1871+import QtTest 1.0
1872+import Unity.Test 0.1
1873+import UInput 0.1
1874+import "../../../qml/Components"
1875+
1876+
1877+Item {
1878+ id: root
1879+ width: units.gu(70)
1880+ height: units.gu(70)
1881+
1882+ VirtualTouchPad {
1883+ id: touchScreenPad
1884+ anchors.fill: parent
1885+ }
1886+
1887+ SignalSpy {
1888+ id: mouseEventSpy1
1889+ target: touchScreenPad.uinput
1890+ }
1891+ SignalSpy {
1892+ id: mouseEventSpy2
1893+ target: touchScreenPad.uinput
1894+ }
1895+
1896+ UnityTestCase {
1897+ id: testCase
1898+ name: "VirtualTouchPad"
1899+ when: windowShown
1900+
1901+ function init() {
1902+ mouseEventSpy1.clear();
1903+ mouseEventSpy2.clear();
1904+ }
1905+
1906+ function test_click() {
1907+ mouseEventSpy1.signalName = "mousePressed"
1908+ mouseEventSpy2.signalName = "mouseReleased"
1909+
1910+ var touchPadArea = findChild(touchScreenPad, "touchPadArea");
1911+
1912+ tap(touchPadArea)
1913+ tryCompare(mouseEventSpy1, "count", 1)
1914+ tryCompare(mouseEventSpy2, "count", 1)
1915+ }
1916+
1917+ function test_doubleClick() {
1918+ mouseEventSpy1.signalName = "mousePressed"
1919+ mouseEventSpy2.signalName = "mouseReleased"
1920+
1921+ var touchPadArea = findChild(touchScreenPad, "touchPadArea");
1922+
1923+ tap(touchPadArea)
1924+ tap(touchPadArea)
1925+
1926+ tryCompare(mouseEventSpy1, "count", 2)
1927+ tryCompare(mouseEventSpy2, "count", 2)
1928+ }
1929+
1930+ function test_move() {
1931+ mouseEventSpy1.signalName = "mouseMoved"
1932+
1933+ var touchPadArea = findChild(touchScreenPad, "touchPadArea");
1934+
1935+ var moveDiff = units.gu(2);
1936+ var moveSteps = 5;
1937+
1938+ touchFlick(touchPadArea, units.gu(1), units.gu(1), units.gu(1) + moveDiff, units.gu(1) + moveDiff, true, true, 10, moveSteps)
1939+
1940+ tryCompare(mouseEventSpy1, "count", moveSteps)
1941+ var movedX = 0;
1942+ var movedY = 0
1943+ for (var i = 0; i < 5; i++) {
1944+ movedX += mouseEventSpy1.signalArguments[i][0]
1945+ movedY += mouseEventSpy1.signalArguments[i][1]
1946+ }
1947+ compare(movedX, moveDiff)
1948+ compare(movedY, moveDiff)
1949+ }
1950+
1951+ function test_doubleTapAndHoldToDrag() {
1952+ mouseEventSpy1.signalName = "mousePressed"
1953+ mouseEventSpy2.signalName = "mouseReleased"
1954+
1955+ var touchPadArea = findChild(touchScreenPad, "touchPadArea");
1956+
1957+ mouseClick(touchPadArea)
1958+ mousePress(touchPadArea)
1959+ mouseMove(touchPadArea, touchPadArea.width / 4, touchPadArea.height / 4)
1960+
1961+ tryCompare(mouseEventSpy1, "count", 1)
1962+ tryCompare(mouseEventSpy2, "count", 0)
1963+ mouseRelease(touchPadArea)
1964+ tryCompare(mouseEventSpy2, "count", 1)
1965+ }
1966+
1967+ function test_twoFingerScroll() {
1968+ var touchPadArea = findChild(touchScreenPad, "touchPadArea");
1969+
1970+ mouseEventSpy1.signalName = "mouseScrolled"
1971+
1972+ var startX = touchPadArea.width / 2;
1973+ var startY = touchPadArea.height / 2;
1974+
1975+ var startX1 = startX - units.gu(1);
1976+ var startX2 = startX + units.gu(1);
1977+
1978+ var event = touchEvent(touchPadArea)
1979+ event.press(0, startX1, startY)
1980+ event.press(1, startX2, startY);
1981+ event.commit()
1982+
1983+ for (var i = 0; i < 10; i++) {
1984+ event.move(0, startX1, startY + units.gu(i))
1985+ event.move(1, startX2, startY + units.gu(i));
1986+ event.commit();
1987+
1988+ tryCompare(mouseEventSpy1, "count", i + 1);
1989+ }
1990+
1991+ event.release(0, startX1, startY + units.gu(11))
1992+ event.release(1, startX2, startY + units.gu(11));
1993+ event.commit();
1994+ }
1995+ }
1996+}
1997
1998=== modified file 'tests/qmltests/tst_DisabledScreenNotice.qml'
1999--- tests/qmltests/tst_DisabledScreenNotice.qml 2015-10-05 16:00:00 +0000
2000+++ tests/qmltests/tst_DisabledScreenNotice.qml 2016-01-28 18:26:00 +0000
2001@@ -19,13 +19,13 @@
2002 import Unity.Test 0.1
2003 import "../../qml"
2004
2005-
2006 Item {
2007 id: root
2008- width: units.gu(70)
2009+ width: units.gu(40)
2010 height: units.gu(70)
2011
2012 DisabledScreenNotice {
2013+ id: touchScreenPad
2014 anchors.fill: parent
2015 }
2016
2017@@ -33,5 +33,13 @@
2018 id: testCase
2019 name: "DisabledScreenNotice"
2020 when: windowShown
2021+
2022+ function test_mouseAreaHidesOnFirstTap() {
2023+ var noticeArea = findChild(touchScreenPad, "infoNoticeArea")
2024+ print("noticeArea", noticeArea.visible)
2025+ compare(noticeArea.visible, true)
2026+ tap(root)
2027+ tryCompare(noticeArea, "visible", false)
2028+ }
2029 }
2030 }
2031
2032=== modified file 'tests/qmltests/tst_OrientedShell.qml'
2033--- tests/qmltests/tst_OrientedShell.qml 2015-12-01 12:17:24 +0000
2034+++ tests/qmltests/tst_OrientedShell.qml 2016-01-28 18:26:00 +0000
2035@@ -1,5 +1,5 @@
2036 /*
2037- * Copyright (C) 2015 Canonical, Ltd.
2038+ * Copyright (C) 2015-2016 Canonical, Ltd.
2039 *
2040 * This program is free software; you can redistribute it and/or modify
2041 * it under the terms of the GNU General Public License as published by
2042@@ -292,6 +292,9 @@
2043 activeFocusOnPress: false
2044 text: "Usage Mode"
2045 model: ["Staged", "Windowed", "Automatic"]
2046+ function selectStaged() {selectedIndex = 0;}
2047+ function selectWindowed() {selectedIndex = 1;}
2048+ function selectAutomatic() {selectedIndex = 2;}
2049 }
2050 MouseTouchEmulationCheckbox {
2051 checked: true
2052@@ -393,7 +396,7 @@
2053 activeFocusOnPress: false
2054 property string prevDevName: "mako"
2055 onClicked: {
2056- usageModeSelector.selectedIndex = 2; // "Automatic"
2057+ usageModeSelector.selectAutomatic();
2058
2059 if (applicationArguments.deviceName === "desktop") {
2060 applicationArguments.deviceName = prevDevName;
2061@@ -457,6 +460,8 @@
2062 signalSpy.signalName = "";
2063
2064 LightDM.Greeter.authenticate(""); // reset greeter
2065+
2066+ usageModeSelector.selectStaged();
2067 }
2068
2069 function cleanup() {
2070@@ -996,37 +1001,58 @@
2071 tryCompare(shell, "transformRotationAngle", 0);
2072 }
2073
2074- function test_attachRemoveInputDevices() {
2075- usageModeSelector.selectedIndex = 2;
2076+ function test_attachRemoveInputDevices_data() {
2077+ return [
2078+ { tag: "small screen, no devices", screenWidth: units.gu(50), mouse: false, kbd: false, expectedMode: "phone", oskExpected: true },
2079+ { tag: "medium screen, no devices", screenWidth: units.gu(100), mouse: false, kbd: false, expectedMode: "phone", oskExpected: true },
2080+ { tag: "big screen, no devices", screenWidth: units.gu(200), mouse: false, kbd: false, expectedMode: "phone", oskExpected: true },
2081+ { tag: "small screen, mouse", screenWidth: units.gu(50), mouse: true, kbd: false, expectedMode: "phone", oskExpected: true },
2082+ { tag: "medium screen, mouse", screenWidth: units.gu(100), mouse: true, kbd: false, expectedMode: "desktop", oskExpected: true },
2083+ { tag: "big screen, mouse", screenWidth: units.gu(200), mouse: true, kbd: false, expectedMode: "desktop", oskExpected: true },
2084+ { tag: "small screen, kbd", screenWidth: units.gu(50), mouse: false, kbd: true, expectedMode: "phone", oskExpected: false },
2085+ { tag: "medium screen, kbd", screenWidth: units.gu(100), mouse: false, kbd: true, expectedMode: "phone", oskExpected: false },
2086+ { tag: "big screen, kbd", screenWidth: units.gu(200), mouse: false, kbd: true, expectedMode: "desktop", oskExpected: false },
2087+ { tag: "small screen, mouse & kbd", screenWidth: units.gu(50), mouse: true, kbd: true, expectedMode: "phone", oskExpected: false },
2088+ { tag: "medium screen, mouse & kbd", screenWidth: units.gu(100), mouse: true, kbd: true, expectedMode: "desktop", oskExpected: false },
2089+ { tag: "big screen, mouse & kbd", screenWidth: units.gu(200), mouse: true, kbd: true, expectedMode: "desktop", oskExpected: false },
2090+ ]
2091+ }
2092+
2093+ function test_attachRemoveInputDevices(data) {
2094+ usageModeSelector.selectAutomatic();
2095 tryCompare(mockUnity8Settings, "usageMode", "Automatic")
2096
2097 loadShell("mako")
2098 var shell = findChild(orientedShell, "shell");
2099-
2100- tryCompare(shell, "usageScenario", "phone");
2101- tryCompare(mockOskSettings, "stayHidden", false);
2102-
2103- MockInputDeviceBackend.addMockDevice("/kbd0", InputInfo.Keyboard);
2104- tryCompare(shell, "usageScenario", "phone");
2105- tryCompare(mockOskSettings, "stayHidden", true);
2106-
2107- MockInputDeviceBackend.addMockDevice("/mouse0", InputInfo.Mouse);
2108- tryCompare(shell, "usageScenario", "desktop");
2109- tryCompare(mockOskSettings, "stayHidden", true);
2110-
2111- MockInputDeviceBackend.removeDevice("/kbd0");
2112- tryCompare(shell, "usageScenario", "desktop");
2113- tryCompare(mockOskSettings, "stayHidden", false);
2114-
2115- MockInputDeviceBackend.removeDevice("/mouse0");
2116- tryCompare(shell, "usageScenario", "phone");
2117- tryCompare(mockOskSettings, "stayHidden", false);
2118-
2119- MockInputDeviceBackend.addMockDevice("/touchpad0", InputInfo.TouchPad);
2120- tryCompare(shell, "usageScenario", "desktop");
2121-
2122- MockInputDeviceBackend.removeDevice("/touchpad0");
2123- tryCompare(shell, "usageScenario", "phone");
2124+ var inputMethod = findChild(shell, "inputMethod");
2125+
2126+ var oldWidth = orientedShellLoader.width;
2127+ orientedShellLoader.width = data.screenWidth;
2128+
2129+ tryCompare(shell, "usageScenario", "phone");
2130+ tryCompare(inputMethod, "enabled", true);
2131+ tryCompare(mockOskSettings, "disableHeight", false);
2132+
2133+ if (data.kbd) {
2134+ MockInputDeviceBackend.addMockDevice("/kbd0", InputInfo.Keyboard);
2135+ }
2136+ if (data.mouse) {
2137+ MockInputDeviceBackend.addMockDevice("/mouse0", InputInfo.Mouse);
2138+ }
2139+
2140+ tryCompare(shell, "usageScenario", data.expectedMode);
2141+ tryCompare(inputMethod, "enabled", data.oskExpected);
2142+ tryCompare(mockOskSettings, "disableHeight", data.expectedMode == "desktop" || data.kbd);
2143+
2144+ if (data.kbd) {
2145+ MockInputDeviceBackend.removeDevice("/kbd0");
2146+ }
2147+ if (data.mouse) {
2148+ MockInputDeviceBackend.removeDevice("/mouse0");
2149+ }
2150+
2151+ // Restore width
2152+ orientedShellLoader.width = oldWidth;
2153 }
2154
2155 /*
2156@@ -1140,6 +1166,61 @@
2157 tryCompare(shell, "transformRotationAngle" , 0);
2158 }
2159
2160+ /*
2161+ Regression test for https://launchpad.net/bugs/1515977
2162+
2163+ Preconditions:
2164+ UI in Desktop mode and landscape
2165+
2166+ Steps:
2167+ - Launch a portrait-only application
2168+
2169+ Expected outcome:
2170+ - Shell stays in landscape
2171+
2172+ Buggy outcome:
2173+ - Shell would rotate to portrait as the newly-focused app doesn't support landscape
2174+ */
2175+ function test_portraitOnlyAppInLandscapeDesktop_data() {
2176+ return [
2177+ {tag: "mako", deviceName: "mako"},
2178+ {tag: "manta", deviceName: "manta"},
2179+ {tag: "flo", deviceName: "flo"}
2180+ ];
2181+ }
2182+ function test_portraitOnlyAppInLandscapeDesktop(data) {
2183+ loadShell(data.deviceName);
2184+
2185+ ////
2186+ // setup preconditions (put shell in Desktop mode and landscape)
2187+
2188+ usageModeSelector.selectWindowed();
2189+
2190+ orientedShell.physicalOrientation = orientedShell.orientations.landscape;
2191+ waitUntilShellIsInOrientation(orientedShell.orientations.landscape);
2192+ waitForRotationAnimationsToFinish();
2193+
2194+ ////
2195+ // Launch a portrait-only application
2196+
2197+ var dialerApp = ApplicationManager.startApplication("dialer-app");
2198+ verify(dialerApp);
2199+
2200+ // ensure the mock dialer-app is as we expect
2201+ compare(dialerApp.rotatesWindowContents, false);
2202+ compare(dialerApp.supportedOrientations, Qt.PortraitOrientation | Qt.InvertedPortraitOrientation);
2203+
2204+ waitUntilAppSurfaceShowsUp("dialer-app");
2205+ waitUntilAppWindowCanRotate("dialer-app");
2206+ verify(isAppSurfaceFocused("dialer-app"));
2207+
2208+ ////
2209+ // check outcome (shell should stay in landscape)
2210+
2211+ waitForRotationAnimationsToFinish();
2212+ compare(shell.orientation, orientedShell.orientations.landscape);
2213+ }
2214+
2215 // angle - rotation angle in degrees clockwise, relative to the primary orientation.
2216 function rotateTo(angle) {
2217 switch (angle) {
2218@@ -1158,7 +1239,10 @@
2219 default:
2220 verify(false);
2221 }
2222+ waitForRotationAnimationsToFinish();
2223+ }
2224
2225+ function waitForRotationAnimationsToFinish() {
2226 var rotationStates = findInvisibleChild(orientedShell, "rotationStates");
2227 verify(rotationStates.d);
2228 verify(rotationStates.d.stateUpdateTimer);

Subscribers

People subscribed via source and target branches