Merge lp:~aacid/unity-2d/unity-2d_pointer_barrier into lp:unity-2d

Proposed by Albert Astals Cid
Status: Merged
Approved by: Gerry Boland
Approved revision: 974
Merged at revision: 963
Proposed branch: lp:~aacid/unity-2d/unity-2d_pointer_barrier
Merge into: lp:unity-2d
Diff against target: 1524 lines (+1187/-52)
22 files modified
CMakeLists.txt (+4/-0)
data/com.canonical.Unity2d.gschema.xml (+38/-0)
debian/control (+1/-0)
libunity-2d-private/CMakeLists.txt (+1/-0)
libunity-2d-private/Unity2d/plugin.cpp (+4/-0)
libunity-2d-private/src/CMakeLists.txt (+5/-0)
libunity-2d-private/src/decayedvalue.cpp (+65/-0)
libunity-2d-private/src/decayedvalue.h (+45/-0)
libunity-2d-private/src/pointerbarrier.cpp (+361/-0)
libunity-2d-private/src/pointerbarrier.h (+154/-0)
libunity-2d-private/src/pointerbarriermanager.cpp (+84/-0)
libunity-2d-private/src/pointerbarriermanager.h (+44/-0)
libunity-2d-private/tests/CMakeLists.txt (+4/-0)
libunity-2d-private/tests/pointerbarriertest.cpp (+312/-0)
shell/Shell.qml (+11/-18)
shell/common/visibilityBehaviors/AutoHideBehavior.qml (+6/-12)
shell/common/visibilityBehaviors/IntelliHideBehavior.qml (+5/-5)
shell/launcher/Launcher.qml (+23/-1)
shell/launcher/LauncherLoader.qml (+3/-13)
tests/launcher/autohide_show_tests.rb (+6/-0)
tests/launcher/autohide_show_tests_common.rb (+5/-3)
tests/launcher/autohide_show_tests_rtl.rb (+6/-0)
To merge this branch: bzr merge lp:~aacid/unity-2d/unity-2d_pointer_barrier
Reviewer Review Type Date Requested Status
Gerry Boland (community) Approve
Review via email: mp+94983@code.launchpad.net

Description of the change

Implementation of Pointer barriers and use in the launcher autohide behaviour

To post a comment you must log in.
Revision history for this message
Albert Astals Cid (aacid) wrote :

There is the issue that it crashes when run inside of VirtualBox, it crashes because the XFixesBarrierNotifyEvent->xany.display is set to 0 (happens only inside the VirtualBox VM)

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

Setup: 2 monitors, and shell in RTL mode (so launcher is on right of monitor 1, which borders monitor 2)

Steps to repro:
1. On screen 1 I push mouse against barrier, so launcher reveals
2. I push a bit harder so mouse passes through barrier to screen 2

Bug: Launcher stays open.

I also notice you drop the barrier while the launcher is open. Then for the above bug, there's no barrier stopping the mouse passing from screen 2 to screen 1.

Yes this bug is a bit "multi-monitor" :)

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

Removing intellihide, you'll need to remove reference to it from com.canonical.Unity2d.gschema.xml. Also will we need a way to migrate existing intellihide settings to autohide/fixed?

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

Checking with Unity, I noticed 3 differences:
1. barrier extends along whole edge of screen, including panel
2. pushing the bit of the barrier that meets the panel does *not* reveal launcher
3. as you push launcher barrier, a small shadow appears which indicates you should push harder to get launcher to open.

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

Also in RTL multi-monitor setup described above (https://code.launchpad.net/~aacid/unity-2d/unity-2d_pointer_barrier/+merge/94983/comments/205455)

If I push the left edge of screen 2 (which borders the launcher on screen 1), it makes the launcher reveal.

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

Bug: for hide-mode: 0, no barrier in effect at all
hide-mode 1,2: while launcher visible, no barrier in effect at all

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

Some minor code style comments (possibly my code to start with!):

In PointerBarrierWrapper::createBarrier(), please add braces around statements like:

+ if (!m_enabled)
+ return;

Can you add a debug comment explaining that barrier must be horizontal/vertical here:
+ if ((m_p1.x() != m_p2.x()) && (m_p1.y() != m_p2.y()))
+ return;

In PointerBarrierWrapper::decay(), your indentation is wrong, 2 spaces instead of the usual 4.

You should add yourself as an author to pointerbarrier.h

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

Functionally this is perfect, nice!

Code-wise, I can find no problems with the math anywhere. There is a little more going on that I had expected, but I can see no way to avoid everything you've done. So again, nice job!

Just some names I think could be clearer:
- "breakp1" could simply be "p1" and so on.
- "trigger" more understandable with "triggerZone" maybe?
- DecayedValue::add maybe clearer with DecayedValue::addAndCheckExceedingTarget since it returns an important bool.

I also want to see more tests of this barrier. Could you write a couple of unit tests, which run under a fake X server (xvfb) - as all tests in libunity-2d-private/tests do right now. We'd need to verify that the barrier works inside xvfb first however.

You can use XTest to move the mouse around. The source code of xdotool is a nice place to see it being used.

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

Works great, and the simplification helps too. Test is good, I'm approving.
Thank you Albert!

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

Holding off approval until FFe obtained

973. By Albert Astals Cid

Merge lp:unity-2d

974. By Albert Astals Cid

Fix docu

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

FFe obtained, can now merge when tarmac instance happy again.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2012-03-01 17:42:03 +0000
+++ CMakeLists.txt 2012-03-05 14:20:25 +0000
@@ -45,6 +45,10 @@
45pkg_check_modules(PANGO REQUIRED pango)45pkg_check_modules(PANGO REQUIRED pango)
46pkg_check_modules(DCONFQT REQUIRED dconf-qt)46pkg_check_modules(DCONFQT REQUIRED dconf-qt)
4747
48# X11_XTest_FOUND is defined by find_package(X11 REQUIRED)
49if (NOT X11_XTest_FOUND)
50 message(SEND_ERROR "Xtest library not found")
51endif (NOT X11_XTest_FOUND)
4852
49# GSettings schemas53# GSettings schemas
50pkg_check_modules(GLIB REQUIRED glib-2.0)54pkg_check_modules(GLIB REQUIRED glib-2.0)
5155
=== modified file 'data/com.canonical.Unity2d.gschema.xml'
--- data/com.canonical.Unity2d.gschema.xml 2012-02-13 14:59:11 +0000
+++ data/com.canonical.Unity2d.gschema.xml 2012-03-05 14:20:25 +0000
@@ -31,6 +31,44 @@
31 2: intellihide; same as auto hide but the launcher will disappear if a window is placed on top of it31 2: intellihide; same as auto hide but the launcher will disappear if a window is placed on top of it
32 </description>32 </description>
33 </key>33 </key>
34 <key name="edge-responsiveness" type="d">
35 <default>2</default>
36 <summary>Responsiveness of the Launcher</summary>
37 <description>How quickly the Launcher will reveal when you push the pointer against the
38 monitor edge
39 </description>
40 </key>
41 <key name="edge-decayrate" type="i">
42 <default>1500</default>
43 <summary>Decay rate of mouse pressure against barrier</summary>
44 <description>The pressures against the monitor edge are continually added up, and when the sum
45 hits a certain threshold the launcher reveals. To prevent a series of distinct gentle pushes
46 of the pointer against the barrier from causing the launcher to reveal, we substract this
47 parameter - the decay rate - at a steady rate
48 </description>
49 </key>
50 <key name="edge-reveal-pressure" type="i">
51 <default>2000</default>
52 <summary>Launcher reveal edge pressure</summary>
53 <description>The minimum pressure to press the pointer against the monitor edge to
54 cause the launcher to reveal
55 </description>
56 </key>
57 <key name="edge-stop-velocity" type="i">
58 <default>6500</default>
59 <summary>Barrier edge stop velocity</summary>
60 <description>The minimum velocity the pointer needs to travel to pass through the barrier without
61 any resistance. Only relevant for multi-monitor setups
62 </description>
63 </key>
64 <key name="edge-overcome-pressure" type="i">
65 <default>2000</default>
66 <summary>Barrier edge overcome pressure
67 </summary>
68 <description>Minimum pressure the pointer needs to exert on the barrier for the barrier to drop.
69 Only relevant for multi-monitor setups
70 </description>
71 </key>
34 </schema>72 </schema>
35 <schema path="/com/canonical/unity-2d/panel/" id="com.canonical.Unity2d.Panel" gettext-domain="unity-2d">73 <schema path="/com/canonical/unity-2d/panel/" id="com.canonical.Unity2d.Panel" gettext-domain="unity-2d">
36 <key type="as" name="applets">74 <key type="as" name="applets">
3775
=== modified file 'debian/control'
--- debian/control 2012-02-17 13:14:40 +0000
+++ debian/control 2012-03-05 14:20:25 +0000
@@ -25,6 +25,7 @@
25 libunity-core-5.0-dev (>= 5.2.0),25 libunity-core-5.0-dev (>= 5.2.0),
26 libnux-2.0-dev (>= 2.4),26 libnux-2.0-dev (>= 2.4),
27 libxi-dev,27 libxi-dev,
28 libxtst-dev,
28Standards-Version: 3.9.229Standards-Version: 3.9.2
29Vcs-Bzr: https://code.launchpad.net/~unity-2d-team/unity-2d/trunk30Vcs-Bzr: https://code.launchpad.net/~unity-2d-team/unity-2d/trunk
3031
3132
=== modified file 'libunity-2d-private/CMakeLists.txt'
--- libunity-2d-private/CMakeLists.txt 2011-12-08 19:00:51 +0000
+++ libunity-2d-private/CMakeLists.txt 2012-03-05 14:20:25 +0000
@@ -11,6 +11,7 @@
11pkg_check_modules(DEE REQUIRED dee-1.0)11pkg_check_modules(DEE REQUIRED dee-1.0)
12pkg_check_modules(XINPUT REQUIRED xi)12pkg_check_modules(XINPUT REQUIRED xi)
13pkg_check_modules(GEIS REQUIRED libutouch-geis)13pkg_check_modules(GEIS REQUIRED libutouch-geis)
14pkg_check_modules(XFIXES REQUIRED xfixes)
1415
15set(libunity-2d-private_SOVERSION 0)16set(libunity-2d-private_SOVERSION 0)
16set(libunity-2d-private_VERSION ${libunity-2d-private_SOVERSION}.0.0)17set(libunity-2d-private_VERSION ${libunity-2d-private_SOVERSION}.0.0)
1718
=== modified file 'libunity-2d-private/Unity2d/plugin.cpp'
--- libunity-2d-private/Unity2d/plugin.cpp 2012-02-28 12:30:17 +0000
+++ libunity-2d-private/Unity2d/plugin.cpp 2012-03-05 14:20:25 +0000
@@ -80,6 +80,8 @@
80#include "unity2dpanel.h"80#include "unity2dpanel.h"
81#include "strutmanager.h"81#include "strutmanager.h"
8282
83#include "pointerbarrier.h"
84
83#include <QtDeclarative/qdeclarative.h>85#include <QtDeclarative/qdeclarative.h>
84#include <QDeclarativeEngine>86#include <QDeclarativeEngine>
85#include <QDeclarativeContext>87#include <QDeclarativeContext>
@@ -183,6 +185,8 @@
183185
184 qmlRegisterType<Unity2dPanel>(uri, 0, 1, "Unity2dPanel");186 qmlRegisterType<Unity2dPanel>(uri, 0, 1, "Unity2dPanel");
185 qmlRegisterType<StrutManager>(uri, 0, 1, "StrutManager");187 qmlRegisterType<StrutManager>(uri, 0, 1, "StrutManager");
188
189 qmlRegisterType<PointerBarrierWrapper>(uri, 0, 1, "PointerBarrier");
186}190}
187191
188void Unity2dPlugin::initializeEngine(QDeclarativeEngine *engine, const char *uri)192void Unity2dPlugin::initializeEngine(QDeclarativeEngine *engine, const char *uri)
189193
=== modified file 'libunity-2d-private/src/CMakeLists.txt'
--- libunity-2d-private/src/CMakeLists.txt 2012-02-28 12:30:17 +0000
+++ libunity-2d-private/src/CMakeLists.txt 2012-03-05 14:20:25 +0000
@@ -78,6 +78,9 @@
78 inputshaperectangle.cpp78 inputshaperectangle.cpp
79 inputshapemask.cpp79 inputshapemask.cpp
80 strutmanager.cpp80 strutmanager.cpp
81 pointerbarrier.cpp
82 pointerbarriermanager.cpp
83 decayedvalue.cpp
81 )84 )
8285
83# Build86# Build
@@ -106,6 +109,7 @@
106 ${DEE_INCLUDE_DIRS}109 ${DEE_INCLUDE_DIRS}
107 ${XINPUT_INCLUDE_DIRS}110 ${XINPUT_INCLUDE_DIRS}
108 ${GEIS_INCLUDE_DIRS}111 ${GEIS_INCLUDE_DIRS}
112 ${XFIXES_INCLUDE_DIRS}
109 )113 )
110114
111add_library(${LIB_NAME} SHARED ${libunity-2d-private_SRCS} listmodelwrapper.h)115add_library(${LIB_NAME} SHARED ${libunity-2d-private_SRCS} listmodelwrapper.h)
@@ -142,6 +146,7 @@
142 ${DEE_LDFLAGS}146 ${DEE_LDFLAGS}
143 ${XINPUT_LDFLAGS}147 ${XINPUT_LDFLAGS}
144 ${GEIS_LDFLAGS}148 ${GEIS_LDFLAGS}
149 ${XFIXES_LDFLAGS}
145 )150 )
146151
147# Install152# Install
148153
=== added file 'libunity-2d-private/src/decayedvalue.cpp'
--- libunity-2d-private/src/decayedvalue.cpp 1970-01-01 00:00:00 +0000
+++ libunity-2d-private/src/decayedvalue.cpp 2012-03-05 14:20:25 +0000
@@ -0,0 +1,65 @@
1/*
2 * Copyright (C) 2012 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "decayedvalue.h"
18
19DecayedValue::DecayedValue()
20 : m_value(0)
21 , m_target(0)
22 , m_decayRate(0)
23{
24 m_valueDecayTimer.setInterval(10);
25 connect(&m_valueDecayTimer, SIGNAL(timeout()), this, SLOT(decay()));
26}
27
28bool DecayedValue::addAndCheckExceedingTarget(int i)
29{
30 m_value += i;
31 if (!m_valueDecayTimer.isActive()) {
32 m_valueDecayTimer.start();
33 }
34 if (m_value > m_target) {
35 m_value = 0;
36 m_valueDecayTimer.stop();
37 return true;
38 } else {
39 return false;
40 }
41}
42
43void DecayedValue::setDecayRate(int decayRate)
44{
45 m_decayRate = decayRate;
46}
47
48void DecayedValue::setTarget(int target)
49{
50 m_target = target;
51}
52
53void DecayedValue::decay() {
54 const int partial_decay = m_decayRate / 100;
55
56 m_value -= partial_decay;
57
58 if (m_value <= 0)
59 {
60 m_value = 0;
61 m_valueDecayTimer.stop();
62 }
63}
64
65#include "decayedvalue.moc"
0\ No newline at end of file66\ No newline at end of file
167
=== added file 'libunity-2d-private/src/decayedvalue.h'
--- libunity-2d-private/src/decayedvalue.h 1970-01-01 00:00:00 +0000
+++ libunity-2d-private/src/decayedvalue.h 2012-03-05 14:20:25 +0000
@@ -0,0 +1,45 @@
1/*
2 * Copyright (C) 2012 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef DECAYEDVALUE_H
18#define DECAYEDVALUE_H
19
20#include <QObject>
21
22#include <QTimer>
23
24class DecayedValue : public QObject
25{
26 Q_OBJECT
27public:
28 DecayedValue();
29
30 bool addAndCheckExceedingTarget(int i);
31
32 void setDecayRate(int decayRate);
33 void setTarget(int target);
34
35private Q_SLOTS:
36 void decay();
37
38private:
39 int m_value;
40 int m_target;
41 int m_decayRate;
42 QTimer m_valueDecayTimer;
43};
44
45#endif // DECAYEDVALUE_H
046
=== added file 'libunity-2d-private/src/pointerbarrier.cpp'
--- libunity-2d-private/src/pointerbarrier.cpp 1970-01-01 00:00:00 +0000
+++ libunity-2d-private/src/pointerbarrier.cpp 2012-03-05 14:20:25 +0000
@@ -0,0 +1,361 @@
1/*
2 * Copyright (C) 2012 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17// Qt
18#include <QDebug>
19#include <QTimer>
20#include <QX11Info>
21
22// libunity-2d
23#include "pointerbarriermanager.h"
24
25// Self
26#include "pointerbarrier.h"
27
28PointerBarrierWrapper::PointerBarrierWrapper(QObject *parent)
29 : QObject(parent)
30 , m_barrier(0)
31 , m_triggerDirection(TriggerFromAnywhere)
32 , m_triggerZoneEnabled(false)
33 , m_threshold(-1)
34 , m_maxVelocityMultiplier(-1)
35 , m_decayRate(-1)
36 , m_triggerPressure(-1)
37 , m_breakPressure(-1)
38 , m_smoothingTimer(new QTimer(this))
39 , m_lastEventX(0)
40 , m_lastEventY(0)
41 , m_lastEventId(0)
42 , m_smoothingCount(0)
43 , m_smoothingAccumulator(0)
44{
45 m_smoothingTimer->setSingleShot(true);
46 m_smoothingTimer->setInterval(75);
47 connect(m_smoothingTimer, SIGNAL(timeout()), this, SLOT(smoother()));
48
49 PointerBarrierManager::instance()->addBarrier(this);
50}
51
52PointerBarrierWrapper::~PointerBarrierWrapper()
53{
54 PointerBarrierManager::instance()->removeBarrier(this);
55 destroyBarrier();
56}
57
58QPointF PointerBarrierWrapper::p1() const
59{
60 return m_p1;
61}
62
63void PointerBarrierWrapper::setP1(const QPointF& p)
64{
65 if (p != m_p1) {
66 if (m_barrier != 0) {
67 destroyBarrier();
68 }
69
70 m_p1 = p;
71 Q_EMIT p1Changed(p);
72
73 createBarrier();
74 }
75}
76
77QPointF PointerBarrierWrapper::p2() const
78{
79 return m_p2;
80}
81
82void PointerBarrierWrapper::setP2(const QPointF& p)
83{
84 if (p != m_p2) {
85 if (m_barrier != 0) {
86 destroyBarrier();
87 }
88
89 m_p2 = p;
90 Q_EMIT p2Changed(p);
91
92 createBarrier();
93 }
94}
95
96QPointF PointerBarrierWrapper::triggerZoneP1() const
97{
98 return m_triggerZoneP1;
99}
100
101void PointerBarrierWrapper::setTriggerZoneP1(const QPointF& p)
102{
103 if (p != m_triggerZoneP1) {
104 m_triggerZoneP1 = p;
105 Q_EMIT triggerZoneP1Changed(p);
106
107 handleTriggerZoneChanged();
108 }
109}
110
111QPointF PointerBarrierWrapper::triggerZoneP2() const
112{
113 return m_triggerZoneP2;
114}
115
116void PointerBarrierWrapper::setTriggerZoneP2(const QPointF& p)
117{
118 if (p != m_triggerZoneP2) {
119 m_triggerZoneP2 = p;
120 Q_EMIT triggerZoneP2Changed(p);
121
122 handleTriggerZoneChanged();
123 }
124}
125
126PointerBarrierWrapper::TriggerDirection PointerBarrierWrapper::triggerDirection() const
127{
128 return m_triggerDirection;
129}
130
131void PointerBarrierWrapper::setTriggerDirection(TriggerDirection direction)
132{
133 if (direction != m_triggerDirection) {
134 m_triggerDirection = direction;
135 Q_EMIT triggerDirectionChanged(direction);
136 }
137}
138
139bool PointerBarrierWrapper::triggerZoneEnabled() const
140{
141 return m_triggerZoneEnabled;
142}
143
144void PointerBarrierWrapper::setTriggerZoneEnabled(bool enabled)
145{
146 if (m_triggerZoneEnabled != enabled) {
147 m_triggerZoneEnabled = enabled;
148 Q_EMIT triggerZoneEnabledChanged(enabled);
149
150 handleTriggerZoneChanged();
151 }
152}
153
154void PointerBarrierWrapper::createBarrier()
155{
156 if (m_threshold < 0) {
157 return;
158 }
159
160 if (!isPointAlignmentCorrect()) {
161 return;
162 }
163
164 Display *display = QX11Info::display();
165
166 m_barrier = XFixesCreatePointerBarrierVelocity(display,
167 DefaultRootWindow(display),
168 m_p1.x(), m_p1.y(),
169 m_p2.x(), m_p2.y(),
170 0,
171 m_threshold,
172 0,
173 NULL);
174 Q_ASSERT(m_barrier != 0);
175}
176
177void PointerBarrierWrapper::destroyBarrier()
178{
179 if (m_barrier != 0) {
180 XFixesDestroyPointerBarrier(QX11Info::display(), m_barrier);
181 m_barrier = 0;
182 }
183}
184
185void PointerBarrierWrapper::doProcess(XFixesBarrierNotifyEvent *notifyEvent)
186{
187 m_lastEventX = notifyEvent->x;
188 m_lastEventY = notifyEvent->y;
189 m_lastEventId = notifyEvent->event_id;
190 m_smoothingAccumulator += notifyEvent->velocity;
191 m_smoothingCount++;
192
193 /* Gathers events for m_smoothingTimer->interval() miliseconds, then takes average */
194 if (!m_smoothingTimer->isActive()) {
195 m_smoothingTimer->start();
196 }
197}
198
199int PointerBarrierWrapper::threshold() const
200{
201 return m_threshold;
202}
203
204void PointerBarrierWrapper::setThreshold(int threshold)
205{
206 if (m_threshold != threshold) {
207 m_threshold = threshold;
208 destroyBarrier();
209 createBarrier();
210 Q_EMIT thresholdChanged(threshold);
211 }
212}
213
214qreal PointerBarrierWrapper::maxVelocityMultiplier() const
215{
216 return m_maxVelocityMultiplier;
217}
218
219void PointerBarrierWrapper::setMaxVelocityMultiplier(qreal maxVelocityMultiplier)
220{
221 if (maxVelocityMultiplier != m_maxVelocityMultiplier) {
222 m_maxVelocityMultiplier = maxVelocityMultiplier;
223 Q_EMIT maxVelocityMultiplierChanged(maxVelocityMultiplier);
224
225 updateRealDecayTargetPressures();
226 }
227}
228
229int PointerBarrierWrapper::decayRate() const
230{
231 return m_decayRate;
232}
233
234void PointerBarrierWrapper::setDecayRate(int decayRate)
235{
236 if (decayRate != m_decayRate) {
237 m_decayRate = decayRate;
238 Q_EMIT decayRateChanged(decayRate);
239
240 updateRealDecayTargetPressures();
241 }
242}
243
244int PointerBarrierWrapper::triggerPressure() const
245{
246 return m_triggerPressure;
247}
248
249void PointerBarrierWrapper::setTriggerPressure(int pressure)
250{
251 if (m_triggerPressure != pressure) {
252 m_triggerPressure = pressure;
253 Q_EMIT triggerPressureChanged(pressure);
254
255 updateRealDecayTargetPressures();
256 }
257}
258
259int PointerBarrierWrapper::breakPressure() const
260{
261 return m_breakPressure;
262}
263
264void PointerBarrierWrapper::setBreakPressure(int breakPressure)
265{
266 if (m_breakPressure != breakPressure) {
267 m_breakPressure = breakPressure;
268 Q_EMIT breakPressureChanged(breakPressure);
269
270 updateRealDecayTargetPressures();
271 }
272}
273
274PointerBarrier PointerBarrierWrapper::barrier() const
275{
276 return m_barrier;
277}
278
279void PointerBarrierWrapper::smoother()
280{
281 if (m_maxVelocityMultiplier < 0 || m_decayRate < 0 || m_breakPressure < 0) {
282 qWarning() << "PointerBarrierWrapper::smoother: maxVelocityMultiplier, decayRate or breakPressure not set";
283 return;
284 }
285
286 if (m_smoothingCount <= 0) {
287 return;
288 }
289 const int velocity = qMin<qreal>(600 * m_maxVelocityMultiplier, m_smoothingAccumulator / m_smoothingCount);
290
291 bool againstTrigger = false;
292 if (m_triggerZoneEnabled && m_triggerZoneP1.x() == m_triggerZoneP2.x() && m_triggerZoneP1.y() <= m_lastEventY && m_triggerZoneP2.y() >= m_lastEventY) {
293 againstTrigger = m_triggerDirection == TriggerFromAnywhere ||
294 (m_triggerDirection == TriggerFromRight && m_lastEventX >= m_triggerZoneP1.x()) ||
295 (m_triggerDirection == TriggerFromLeft && m_lastEventX < m_triggerZoneP1.x());
296 }
297 if (m_triggerZoneEnabled && m_triggerZoneP1.y() == m_triggerZoneP2.y() && m_triggerZoneP1.x() <= m_lastEventX && m_triggerZoneP2.x() >= m_lastEventX) {
298 againstTrigger = m_triggerDirection == TriggerFromAnywhere ||
299 (m_triggerDirection == TriggerFromTop && m_lastEventY >= m_triggerZoneP1.y()) ||
300 (m_triggerDirection == TriggerFromBottom && m_lastEventY < m_triggerZoneP1.y());
301 }
302 if (againstTrigger) {
303 if (m_triggerValue.addAndCheckExceedingTarget(velocity)) {
304 Q_EMIT triggered();
305 }
306 } else {
307 if (m_breakValue.addAndCheckExceedingTarget(velocity)) {
308 Display *display = QX11Info::display();
309 XFixesBarrierReleasePointer (display, m_barrier, m_lastEventId);
310
311 Q_EMIT broken();
312 }
313 }
314
315 m_smoothingAccumulator = 0;
316 m_smoothingCount = 0;
317}
318
319void PointerBarrierWrapper::updateRealDecayTargetPressures()
320{
321 // make the effect half as strong as specified as other values shouldn't scale
322 // as quickly as the max velocity multiplier
323 const float responsiveness_mult = ((m_maxVelocityMultiplier - 1) * .025) + 1;
324 const int realDecayRate = m_decayRate * responsiveness_mult;
325 m_triggerValue.setDecayRate(realDecayRate);
326 m_breakValue.setDecayRate(realDecayRate);
327 m_triggerValue.setTarget(m_triggerPressure * responsiveness_mult);
328 m_breakValue.setTarget(m_breakPressure * responsiveness_mult);
329}
330
331void PointerBarrierWrapper::handleTriggerZoneChanged()
332{
333 // Make sure barrier point alignment is still valid
334 if (!isPointAlignmentCorrect()) {
335 destroyBarrier();
336 }
337 // If there is no barrier try to create one now
338 if (m_barrier == 0) {
339 createBarrier();
340 }
341}
342
343bool PointerBarrierWrapper::isPointAlignmentCorrect() const
344{
345 bool alignmentCorrect = false;
346
347 // Outer points can't be the same
348 if (m_p1 != m_p2) {
349 // The two points need to be aligned either vertically or horizontally
350 if (m_p1.x() == m_p2.x()) {
351 alignmentCorrect = !m_triggerZoneEnabled || (m_triggerZoneP1.x() == m_p1.x() && m_triggerZoneP2.x() == m_p1.x());
352 } else if (m_p1.y() == m_p2.y()) {
353 alignmentCorrect = !m_triggerZoneEnabled || (m_triggerZoneP1.y() == m_p1.y() && m_triggerZoneP2.y() == m_p1.y());
354 }
355 }
356
357 return alignmentCorrect;
358
359}
360
361#include <pointerbarrier.moc>
0362
=== added file 'libunity-2d-private/src/pointerbarrier.h'
--- libunity-2d-private/src/pointerbarrier.h 1970-01-01 00:00:00 +0000
+++ libunity-2d-private/src/pointerbarrier.h 2012-03-05 14:20:25 +0000
@@ -0,0 +1,154 @@
1/*
2 * Copyright (C) 2012 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef POINTERBARRIER_H
18#define POINTERBARRIER_H
19
20#include <QObject>
21#include <QPointF>
22
23// X11
24#include <X11/extensions/Xfixes.h>
25
26#include "decayedvalue.h"
27
28struct PointerBarrierWrapperPrivate;
29
30class PointerBarrierWrapper : public QObject
31{
32 Q_OBJECT
33 Q_PROPERTY(QPointF p1 READ p1 WRITE setP1 NOTIFY p1Changed)
34 Q_PROPERTY(QPointF p2 READ p2 WRITE setP2 NOTIFY p2Changed)
35 Q_PROPERTY(QPointF triggerZoneP1 READ triggerZoneP1 WRITE setTriggerZoneP1 NOTIFY triggerZoneP1Changed)
36 Q_PROPERTY(QPointF triggerZoneP2 READ triggerZoneP2 WRITE setTriggerZoneP2 NOTIFY triggerZoneP2Changed)
37 Q_PROPERTY(TriggerDirection triggerDirection READ triggerDirection WRITE setTriggerDirection NOTIFY triggerDirectionChanged)
38 Q_PROPERTY(bool triggerZoneEnabled READ triggerZoneEnabled WRITE setTriggerZoneEnabled NOTIFY triggerZoneEnabledChanged)
39 Q_PROPERTY(int threshold READ threshold WRITE setThreshold NOTIFY thresholdChanged)
40 Q_PROPERTY(int maxVelocityMultiplier READ maxVelocityMultiplier WRITE setMaxVelocityMultiplier NOTIFY maxVelocityMultiplierChanged)
41 Q_PROPERTY(int decayRate READ decayRate WRITE setDecayRate NOTIFY decayRateChanged)
42 Q_PROPERTY(int triggerPressure READ triggerPressure WRITE setTriggerPressure NOTIFY triggerPressureChanged)
43 Q_PROPERTY(int breakPressure READ breakPressure WRITE setBreakPressure NOTIFY breakPressureChanged)
44
45friend class PointerBarrierManager;
46
47public:
48 enum TriggerDirection {
49 TriggerFromAnywhere,
50 TriggerFromRight,
51 TriggerFromLeft,
52 TriggerFromTop,
53 TriggerFromBottom
54 };
55 Q_ENUMS(TriggerDirection)
56
57 PointerBarrierWrapper(QObject* parent = 0);
58 ~PointerBarrierWrapper();
59
60 QPointF p1() const;
61 void setP1(const QPointF &p);
62
63 QPointF p2() const;
64 void setP2(const QPointF &p);
65
66 QPointF triggerZoneP1() const;
67 void setTriggerZoneP1(const QPointF &p);
68
69 QPointF triggerZoneP2() const;
70 void setTriggerZoneP2(const QPointF &p);
71
72 TriggerDirection triggerDirection() const;
73 void setTriggerDirection(TriggerDirection direction);
74
75 bool triggerZoneEnabled() const;
76 void setTriggerZoneEnabled(bool enabled);
77
78 int threshold() const;
79 void setThreshold(int threshold);
80
81 qreal maxVelocityMultiplier() const;
82 void setMaxVelocityMultiplier(qreal maxVelocityMultiplier);
83
84 int decayRate() const;
85 void setDecayRate(int decayRate);
86
87 int triggerPressure() const;
88 void setTriggerPressure(int pressure);
89
90 int breakPressure() const;
91 void setBreakPressure(int pressure);
92
93 PointerBarrier barrier() const;
94
95Q_SIGNALS:
96 void p1Changed(const QPointF &p1);
97 void p2Changed(const QPointF &p2);
98 void triggerZoneP1Changed(const QPointF &p1);
99 void triggerZoneP2Changed(const QPointF &p2);
100 void triggerDirectionChanged(TriggerDirection direction);
101 void triggerZoneEnabledChanged(bool changed);
102 void thresholdChanged(int threshold);
103 void maxVelocityMultiplierChanged(qreal maxVelocityMultiplier);
104 void decayRateChanged(int decayRate);
105 void triggerPressureChanged(int breakPressure);
106 void breakPressureChanged(int breakPressure);
107
108 void triggered();
109 void broken();
110
111private Q_SLOTS:
112 void smoother();
113
114private:
115 Q_DISABLE_COPY(PointerBarrierWrapper);
116
117 void createBarrier();
118 void destroyBarrier();
119
120 void doProcess(XFixesBarrierNotifyEvent *event);
121
122 void updateRealDecayTargetPressures();
123
124 void handleTriggerZoneChanged();
125
126 bool isPointAlignmentCorrect() const;
127
128 PointerBarrier m_barrier;
129
130 QPointF m_p1;
131 QPointF m_p2;
132 QPointF m_triggerZoneP1;
133 QPointF m_triggerZoneP2;
134 TriggerDirection m_triggerDirection;
135 bool m_triggerZoneEnabled;
136 int m_threshold;
137 qreal m_maxVelocityMultiplier;
138 int m_decayRate;
139 int m_triggerPressure;
140 int m_breakPressure;
141
142 QTimer *m_smoothingTimer;
143
144 int m_lastEventX;
145 int m_lastEventY;
146 int m_lastEventId;
147 int m_smoothingCount;
148 int m_smoothingAccumulator;
149
150 DecayedValue m_triggerValue;
151 DecayedValue m_breakValue;
152};
153
154#endif // POINTERBARRIER_H
0155
=== added file 'libunity-2d-private/src/pointerbarriermanager.cpp'
--- libunity-2d-private/src/pointerbarriermanager.cpp 1970-01-01 00:00:00 +0000
+++ libunity-2d-private/src/pointerbarriermanager.cpp 2012-03-05 14:20:25 +0000
@@ -0,0 +1,84 @@
1/*
2 * Copyright (C) 2012 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17// Special ordering to bypass evil X11 include
18#include <QDebug>
19
20// Self
21#include "pointerbarriermanager.h"
22
23// libunity-2d
24#include "pointerbarrier.h"
25
26// Qt
27#include <QX11Info>
28
29// X
30#include <X11/extensions/Xfixes.h>
31
32PointerBarrierManager *PointerBarrierManager::instance()
33{
34 static PointerBarrierManager *bpm = NULL;
35 if (bpm == NULL) bpm = new PointerBarrierManager();
36 return bpm;
37}
38
39PointerBarrierManager::PointerBarrierManager()
40{
41 Display *display = QX11Info::display();
42
43 XFixesQueryExtension(display, &m_eventBase, &m_errorBase);
44
45 Unity2dApplication* application = Unity2dApplication::instance();
46 if (application == NULL) {
47 /* This can happen for example when using qmlviewer to run the launcher */
48 qWarning() << "The application is not an Unity2dApplication."
49 "Barriers will not be monitored.";
50 } else {
51 application->installX11EventFilter(this);
52 }
53
54 /* Enables barrier detection events - only call once!! */
55 XFixesSelectBarrierInput(display, DefaultRootWindow(display), 0xdeadbeef);
56}
57
58void PointerBarrierManager::addBarrier(PointerBarrierWrapper *barrier)
59{
60 m_barriers += barrier;
61}
62
63void PointerBarrierManager::removeBarrier(PointerBarrierWrapper *barrier)
64{
65 m_barriers -= barrier;
66}
67
68bool PointerBarrierManager::x11EventFilter(XEvent* event)
69{
70 if (event->type - m_eventBase == XFixesBarrierNotify) {
71 XFixesBarrierNotifyEvent *notifyEvent = (XFixesBarrierNotifyEvent *)event;
72
73 if (notifyEvent->subtype == XFixesBarrierHitNotify) {
74 Q_FOREACH (PointerBarrierWrapper *barrier, m_barriers) {
75 if (barrier->barrier() == notifyEvent->barrier) {
76 barrier->doProcess(notifyEvent);
77 return true;
78 }
79 }
80 }
81 }
82 return false;
83
84}
085
=== added file 'libunity-2d-private/src/pointerbarriermanager.h'
--- libunity-2d-private/src/pointerbarriermanager.h 1970-01-01 00:00:00 +0000
+++ libunity-2d-private/src/pointerbarriermanager.h 2012-03-05 14:20:25 +0000
@@ -0,0 +1,44 @@
1/*
2 * Copyright (C) 2012 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef POINTERBARRIERMANAGER_H
18#define POINTERBARRIERMANAGER_H
19
20#include "unity2dapplication.h"
21
22class PointerBarrierWrapper;
23
24class PointerBarrierManager : protected AbstractX11EventFilter
25{
26public:
27 static PointerBarrierManager *instance();
28
29 void addBarrier(PointerBarrierWrapper *barrier);
30 void removeBarrier(PointerBarrierWrapper *barrier);
31
32protected:
33 bool x11EventFilter(XEvent* event);
34
35private:
36 Q_DISABLE_COPY(PointerBarrierManager);
37
38 PointerBarrierManager();
39 QSet<PointerBarrierWrapper*> m_barriers;
40 int m_eventBase;
41 int m_errorBase;
42};
43
44#endif // POINTERBARRIERMANAGER_H
045
=== modified file 'libunity-2d-private/tests/CMakeLists.txt'
--- libunity-2d-private/tests/CMakeLists.txt 2012-01-24 08:38:24 +0000
+++ libunity-2d-private/tests/CMakeLists.txt 2012-03-05 14:20:25 +0000
@@ -8,6 +8,7 @@
8 ${CMAKE_CURRENT_BINARY_DIR}8 ${CMAKE_CURRENT_BINARY_DIR}
9 ${GLIB_INCLUDE_DIRS}9 ${GLIB_INCLUDE_DIRS}
10 ${QT_QTTEST_INCLUDE_DIR}10 ${QT_QTTEST_INCLUDE_DIR}
11 ${X11_XTest_INCLUDE_PATH}
11 )12 )
1213
13set(LIBUNITY_2D_TEST_DIR ${libunity-2d-private_BINARY_DIR}/tests)14set(LIBUNITY_2D_TEST_DIR ${libunity-2d-private_BINARY_DIR}/tests)
@@ -37,7 +38,10 @@
37 listaggregatormodeltest38 listaggregatormodeltest
38 qsortfilterproxymodeltest39 qsortfilterproxymodeltest
39 focuspathtest40 focuspathtest
41 pointerbarriertest
40 )42 )
43
44target_link_libraries(pointerbarriertest ${X11_XTest_LIB})
41 45
42# unity2dtrtest - FIXME46# unity2dtrtest - FIXME
43#add_test(NAME unity2dtrtest_check47#add_test(NAME unity2dtrtest_check
4448
=== added file 'libunity-2d-private/tests/pointerbarriertest.cpp'
--- libunity-2d-private/tests/pointerbarriertest.cpp 1970-01-01 00:00:00 +0000
+++ libunity-2d-private/tests/pointerbarriertest.cpp 2012-03-05 14:20:25 +0000
@@ -0,0 +1,312 @@
1/*
2 * This file is part of unity-2d
3 *
4 * Copyright 2010 Canonical Ltd.
5 *
6 * Authors:
7 * - Aurélien Gâteau <aurelien.gateau@canonical.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 3.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22// Local
23#include <unitytestmacro.h>
24#include <pointerbarrier.h>
25
26// Qt
27#include <QApplication>
28#include <QSignalSpy>
29#include <QX11Info>
30#include <QtTestGui>
31
32#include <X11/extensions/XTest.h>
33
34class DisableTriggerZoneOnTriggerHelper : public QObject
35{
36 Q_OBJECT
37public:
38 DisableTriggerZoneOnTriggerHelper(PointerBarrierWrapper *barrier)
39 {
40 m_barrier = barrier;
41 connect(barrier, SIGNAL(triggered()), this, SLOT(disable()));
42 }
43
44public Q_SLOTS:
45 void disable()
46 {
47 m_barrier->setTriggerZoneEnabled(false);
48 }
49
50private:
51 PointerBarrierWrapper *m_barrier;
52};
53
54class PointerBarrierTest : public QObject
55{
56 Q_OBJECT
57private Q_SLOTS:
58 void testBreak()
59 {
60 Display *display = QX11Info::display();
61 PointerBarrierWrapper barrier;
62
63 QSignalSpy brokenSpy(&barrier, SIGNAL(broken()));
64 QSignalSpy triggeredSpy(&barrier, SIGNAL(triggered()));
65
66 XTestFakeMotionEvent(display, -1, 50, 50, 0);
67 QCOMPARE(QCursor::pos(), QPoint(50, 50));
68
69 barrier.setP1(QPointF(100, 0));
70 barrier.setP2(QPointF(100, 100));
71 barrier.setThreshold(6500);
72 barrier.setMaxVelocityMultiplier(2);
73 barrier.setDecayRate(1500);
74 barrier.setBreakPressure(2000);
75
76 XTestFakeRelativeMotionEvent(display, 300, 0, 0);
77 // We are stopped by the barrier and instead in 350, 50 we are in 99, 50
78 QCOMPARE(QCursor::pos(), QPoint(99, 50));
79
80 QCOMPARE(brokenSpy.count(), 0);
81 QCOMPARE(triggeredSpy.count(), 0);
82
83 for (int i = 0; i < 10; ++i) {
84 XTestFakeRelativeMotionEvent(display, 100, 0, 0);
85 QTest::qWait(100);
86 }
87 // We have broken the barrier and are somewhere else
88 QVERIFY(QCursor::pos() != QPoint(99, 50));
89 QCOMPARE(brokenSpy.count(), 1);
90 QCOMPARE(triggeredSpy.count(), 0);
91 }
92
93 void testStopArea()
94 {
95 Display *display = QX11Info::display();
96 PointerBarrierWrapper barrier;
97
98 QSignalSpy brokenSpy(&barrier, SIGNAL(broken()));
99 QSignalSpy triggeredSpy(&barrier, SIGNAL(triggered()));
100
101 XTestFakeMotionEvent(display, -1, 50, 150, 0);
102 QCOMPARE(QCursor::pos(), QPoint(50, 150));
103
104 barrier.setP1(QPointF(100, 0));
105 barrier.setP2(QPointF(100, 100));
106 barrier.setThreshold(6500);
107 barrier.setMaxVelocityMultiplier(2);
108 barrier.setDecayRate(1500);
109 barrier.setBreakPressure(2000);
110
111 XTestFakeRelativeMotionEvent(display, 300, 0, 0);
112 // We are not stopped by the barrier because it's above us
113 // and are in 350, 150
114 QCOMPARE(QCursor::pos(), QPoint(350, 150));
115
116 QCOMPARE(brokenSpy.count(), 0);
117 QCOMPARE(triggeredSpy.count(), 0);
118 }
119
120 void testTrigger()
121 {
122 Display *display = QX11Info::display();
123 PointerBarrierWrapper barrier;
124
125 QSignalSpy brokenSpy(&barrier, SIGNAL(broken()));
126 QSignalSpy triggeredSpy(&barrier, SIGNAL(triggered()));
127
128 XTestFakeMotionEvent(display, -1, 50, 50, 0);
129 QCOMPARE(QCursor::pos(), QPoint(50, 50));
130
131 barrier.setP1(QPointF(100, 0));
132 barrier.setP2(QPointF(100, 100));
133 barrier.setTriggerZoneP1(QPointF(100, 0));
134 barrier.setTriggerZoneP2(QPointF(100, 100));
135 barrier.setTriggerZoneEnabled(true);
136 barrier.setThreshold(6500);
137 barrier.setMaxVelocityMultiplier(2);
138 barrier.setDecayRate(1500);
139 barrier.setTriggerPressure(2000);
140 barrier.setBreakPressure(2000);
141
142 XTestFakeRelativeMotionEvent(display, 300, 0, 0);
143 // We are stopped by the barrier and instead in 350, 50 we are in 99, 50
144 QCOMPARE(QCursor::pos(), QPoint(99, 50));
145
146 QCOMPARE(brokenSpy.count(), 0);
147 QCOMPARE(triggeredSpy.count(), 0);
148
149 for (int i = 0; i < 10; ++i) {
150 XTestFakeRelativeMotionEvent(display, 100, 0, 0);
151 QTest::qWait(100);
152 }
153 // We have triggered the barrier and are still there
154 QCOMPARE(QCursor::pos(), QPoint(99, 50));
155 QCOMPARE(brokenSpy.count(), 0);
156 QVERIFY(triggeredSpy.count() >= 1);
157 }
158
159 void testTriggerAndBreak()
160 {
161 Display *display = QX11Info::display();
162 PointerBarrierWrapper barrier;
163
164 QSignalSpy brokenSpy(&barrier, SIGNAL(broken()));
165 QSignalSpy triggeredSpy(&barrier, SIGNAL(triggered()));
166
167 XTestFakeMotionEvent(display, -1, 50, 50, 0);
168 QCOMPARE(QCursor::pos(), QPoint(50, 50));
169
170 barrier.setP1(QPointF(100, 0));
171 barrier.setP2(QPointF(100, 100));
172 barrier.setTriggerZoneP1(QPointF(100, 0));
173 barrier.setTriggerZoneP2(QPointF(100, 100));
174 barrier.setTriggerZoneEnabled(true);
175 barrier.setThreshold(6500);
176 barrier.setMaxVelocityMultiplier(2);
177 barrier.setDecayRate(1500);
178 barrier.setTriggerPressure(2000);
179 barrier.setBreakPressure(2000);
180
181 DisableTriggerZoneOnTriggerHelper helper(&barrier);
182
183 XTestFakeRelativeMotionEvent(display, 300, 0, 0);
184 // We are stopped by the barrier and instead in 350, 50 we are in 99, 50
185 QCOMPARE(QCursor::pos(), QPoint(99, 50));
186
187 QCOMPARE(brokenSpy.count(), 0);
188 QCOMPARE(triggeredSpy.count(), 0);
189
190 for (int i = 0; i < 10; ++i) {
191 XTestFakeRelativeMotionEvent(display, 100, 0, 0);
192 QTest::qWait(100);
193 }
194 // We have triggered and broken the barrier and somewhere else
195 QVERIFY(QCursor::pos() != QPoint(99, 50));
196 QCOMPARE(brokenSpy.count(), 1);
197 QCOMPARE(triggeredSpy.count(), 1);
198 }
199
200 void testTriggerWithoutAndBreak()
201 {
202 Display *display = QX11Info::display();
203 PointerBarrierWrapper barrier;
204
205 QSignalSpy brokenSpy(&barrier, SIGNAL(broken()));
206 QSignalSpy triggeredSpy(&barrier, SIGNAL(triggered()));
207
208 XTestFakeMotionEvent(display, -1, 50, 50, 0);
209 QCOMPARE(QCursor::pos(), QPoint(50, 50));
210
211 barrier.setP1(QPointF(100, 0));
212 barrier.setP2(QPointF(100, 100));
213 barrier.setTriggerZoneP1(QPointF(100, 0));
214 barrier.setTriggerZoneP2(QPointF(100, 100));
215 barrier.setTriggerZoneEnabled(true);
216 barrier.setThreshold(6500);
217 barrier.setMaxVelocityMultiplier(2);
218 barrier.setDecayRate(1500);
219 barrier.setTriggerPressure(2000);
220 barrier.setBreakPressure(2000);
221
222 DisableTriggerZoneOnTriggerHelper helper(&barrier);
223
224 XTestFakeRelativeMotionEvent(display, 300, 0, 0);
225 // We are stopped by the barrier and instead in 350, 50 we are in 99, 50
226 QCOMPARE(QCursor::pos(), QPoint(99, 50));
227
228 QCOMPARE(brokenSpy.count(), 0);
229 QCOMPARE(triggeredSpy.count(), 0);
230
231 for (int i = 0; i < 10 && barrier.triggerZoneEnabled(); ++i) {
232 XTestFakeRelativeMotionEvent(display, 100, 0, 0);
233 QTest::qWait(100);
234 }
235 // We have triggered the barrier
236 QCOMPARE(QCursor::pos(), QPoint(99, 50));
237 QCOMPARE(brokenSpy.count(), 0);
238 QCOMPARE(triggeredSpy.count(), 1);
239
240 // We can push a bit more without breaking the barrier
241 for (int i = 0; i < 2; ++i) {
242 XTestFakeRelativeMotionEvent(display, 100, 0, 0);
243 QTest::qWait(100);
244 }
245 QCOMPARE(QCursor::pos(), QPoint(99, 50));
246 QCOMPARE(brokenSpy.count(), 0);
247 QCOMPARE(triggeredSpy.count(), 1);
248 }
249
250 void testTriggerAndBreakZones()
251 {
252 Display *display = QX11Info::display();
253 PointerBarrierWrapper barrier;
254
255 QSignalSpy brokenSpy(&barrier, SIGNAL(broken()));
256 QSignalSpy triggeredSpy(&barrier, SIGNAL(triggered()));
257
258 XTestFakeMotionEvent(display, -1, 50, 25, 0);
259 QCOMPARE(QCursor::pos(), QPoint(50, 25));
260
261 barrier.setP1(QPointF(100, 0));
262 barrier.setP2(QPointF(100, 200));
263 barrier.setTriggerZoneP1(QPointF(100, 50));
264 barrier.setTriggerZoneP2(QPointF(100, 150));
265 barrier.setTriggerZoneEnabled(true);
266 barrier.setThreshold(6500);
267 barrier.setMaxVelocityMultiplier(2);
268 barrier.setDecayRate(1500);
269 barrier.setTriggerPressure(2000);
270 barrier.setBreakPressure(2000);
271
272 DisableTriggerZoneOnTriggerHelper helper(&barrier);
273
274 XTestFakeRelativeMotionEvent(display, 300, 0, 0);
275 // We are stopped by the barrier and instead in 350, 25 we are in 99, 25
276 QCOMPARE(QCursor::pos(), QPoint(99, 25));
277
278 QCOMPARE(brokenSpy.count(), 0);
279 QCOMPARE(triggeredSpy.count(), 0);
280
281 for (int i = 0; i < 10; ++i) {
282 XTestFakeRelativeMotionEvent(display, 100, 0, 0);
283 QTest::qWait(100);
284 }
285
286 // We are above the trigger zone so we have only broke the barrier
287 QVERIFY(QCursor::pos() != QPoint(99, 25));
288 QCOMPARE(brokenSpy.count(), 1);
289 QCOMPARE(triggeredSpy.count(), 0);
290
291 // Go back
292 XTestFakeMotionEvent(display, -1, 50, 100, 0);
293
294 XTestFakeRelativeMotionEvent(display, 300, 0, 0);
295 // We are stopped by the barrier and instead in 350, 100 we are in 99, 100
296 QCOMPARE(QCursor::pos(), QPoint(99, 100));
297
298 for (int i = 0; i < 10; ++i) {
299 XTestFakeRelativeMotionEvent(display, 100, 0, 0);
300 QTest::qWait(100);
301 }
302
303 // We are in the trigger zone so we have triggered and broken the barrier
304 QVERIFY(QCursor::pos() != QPoint(99, 100));
305 QCOMPARE(brokenSpy.count(), 2);
306 QCOMPARE(triggeredSpy.count(), 1);
307 }
308};
309
310UAPP_TEST_MAIN(PointerBarrierTest)
311
312#include "pointerbarriertest.moc"
0313
=== modified file 'shell/Shell.qml'
--- shell/Shell.qml 2012-02-28 12:00:14 +0000
+++ shell/Shell.qml 2012-03-05 14:20:25 +0000
@@ -93,6 +93,13 @@
93 if (dashLoader.status == Loader.Ready) dashLoader.item.deactivateAllLenses()93 if (dashLoader.status == Loader.Ready) dashLoader.item.deactivateAllLenses()
94 }94 }
95 }95 }
96 onGlobalPositionChanged: {
97 var x = declarativeView.globalPosition.x + (Utils.isLeftToRight() ? 0 : shell.width)
98 launcherLoader.item.barrierP1 = Qt.point(x, 0)
99 launcherLoader.item.barrierP2 = Qt.point(x, declarativeView.screen.geometry.height)
100 launcherLoader.item.barrierTriggerZoneP1 = Qt.point(x, declarativeView.globalPosition.y)
101 launcherLoader.item.barrierTriggerZoneP2 = Qt.point(x, declarativeView.globalPosition.y + launcherLoader.height)
102 }
96 }103 }
97104
98 SpreadMonitor {105 SpreadMonitor {
@@ -219,24 +226,10 @@
219 Binding {226 Binding {
220 target: launcherInputShape227 target: launcherInputShape
221 property: "rectangle"228 property: "rectangle"
222 value: {229 value: Qt.rect(launcherLoader.x,
223 // FIXME: this results in a 1px wide white rectangle on the launcher edge, we should switch230 launcherLoader.y,
224 // to cpp-based edge detection, and later XFixes barriers to get rid of that completely231 launcherLoader.width,
225 var somewhatShown = Utils.isLeftToRight() ? -launcherLoader.x < launcherLoader.width : launcherLoader.x < shell.width232 launcherLoader.height)
226 if (somewhatShown) {
227 return Qt.rect(launcherLoader.x,
228 launcherLoader.y,
229 launcherLoader.width,
230 launcherLoader.height)
231 } else {
232 // The outerEdgeMouseArea is one pixel bigger on each side so use it
233 // when the launcher is hidden to have that extra pixel in the border
234 return Qt.rect(launcherLoader.x + launcherLoader.outerEdgeMouseArea.x,
235 launcherLoader.y,
236 launcherLoader.outerEdgeMouseArea.width,
237 launcherLoader.height)
238 }
239 }
240 when: !launcherLoaderXAnimation.running233 when: !launcherLoaderXAnimation.running
241 }234 }
242235
243236
=== modified file 'shell/common/visibilityBehaviors/AutoHideBehavior.qml'
--- shell/common/visibilityBehaviors/AutoHideBehavior.qml 2012-03-02 13:48:01 +0000
+++ shell/common/visibilityBehaviors/AutoHideBehavior.qml 2012-03-05 14:20:25 +0000
@@ -18,13 +18,14 @@
1818
19import QtQuick 1.019import QtQuick 1.0
2020
21// Shows the target when it has the focus or when you move the21// Shows the target when it has the focus or when you trigger the pointer barrier
22// mouse for 500 msec to the edge of the target22// in the edge of the target
23// Hides the target when none of the above conditions are met23// Hides the target when none of the above conditions are met
24// and you have not had the mouse over it during more than 1000 msec24// and you have not had the mouse over it during more than 1000 msec
25// To use this Behavior your target needs to provide two properties25// To use this Behavior your target needs to provide one properties
26// - containsMouse: Defines if the mouse is inside the target26// - containsMouse: Defines if the mouse is inside the target
27// - outerEdgeContainsMouse: Defines if the mouse is in the edge of the target27// and one signal
28// - barrierTriggered: Defines when the pointer barrier has been triggered
2829
29BaseBehavior {30BaseBehavior {
30 id: autoHide31 id: autoHide
@@ -51,12 +52,6 @@
51 onTriggered: shownRegardlessOfFocus = false52 onTriggered: shownRegardlessOfFocus = false
52 }53 }
5354
54 Timer {
55 id: edgeHitTimer
56 interval: 500
57 onTriggered: shownRegardlessOfFocus = true
58 }
59
60 Connections {55 Connections {
61 target: (autoHide.target !== undefined) ? autoHide.target : null56 target: (autoHide.target !== undefined) ? autoHide.target : null
62 onContainsMouseChanged: {57 onContainsMouseChanged: {
@@ -68,7 +63,6 @@
6863
69 Connections {64 Connections {
70 target: autoHide.target !== undefined ? autoHide.target : null65 target: autoHide.target !== undefined ? autoHide.target : null
71 onOuterEdgeContainsMouseChanged: edgeHitTimer.running = target.outerEdgeContainsMouse66 onBarrierTriggered: shownRegardlessOfFocus = true
72 ignoreUnknownSignals: true
73 }67 }
74}68}
7569
=== modified file 'shell/common/visibilityBehaviors/IntelliHideBehavior.qml'
--- shell/common/visibilityBehaviors/IntelliHideBehavior.qml 2012-03-02 13:48:01 +0000
+++ shell/common/visibilityBehaviors/IntelliHideBehavior.qml 2012-03-05 14:20:25 +0000
@@ -20,14 +20,14 @@
20import Unity2d 1.020import Unity2d 1.0
21import "../utils.js" as Utils21import "../utils.js" as Utils
2222
23// Shows the target when it has the focus or when you move the23// Shows the target when it has the focus or when you trigger the pointer barrier
24// mouse for 500 msec to the edge of the target or there are no 24// or there are no windows that intersect with the target
25// windows that intersect with the target
26// Hides the target when none of the above conditions are met25// Hides the target when none of the above conditions are met
27// and you have not had the mouse over it during more than 1000 msec26// and you have not had the mouse over it during more than 1000 msec
28// To use this Behavior your target needs to provide two properties27// To use this Behavior your target needs to provide one properties
29// - containsMouse: Defines if the mouse is inside the target28// - containsMouse: Defines if the mouse is inside the target
30// - outerEdgeContainsMouse: Defines if the mouse is in the edge of the target29// and one signal
30// - barrierTriggered: Defines when the pointer barrier has been triggered
3131
32AutoHideBehavior {32AutoHideBehavior {
33 id: intellihide33 id: intellihide
3434
=== modified file 'shell/launcher/Launcher.qml'
--- shell/launcher/Launcher.qml 2012-02-27 10:50:43 +0000
+++ shell/launcher/Launcher.qml 2012-03-05 14:20:25 +0000
@@ -25,11 +25,33 @@
25 id: launcher25 id: launcher
26 Accessible.name: "launcher"26 Accessible.name: "launcher"
2727
28 property bool outerEdgeContainsMouse28 signal barrierTriggered
29
29 property bool shown30 property bool shown
30 property bool showMenus: true31 property bool showMenus: true
3132
32 property bool containsMouse: declarativeView.monitoredAreaContainsMouse33 property bool containsMouse: declarativeView.monitoredAreaContainsMouse
34 property variant barrierP1
35 property variant barrierP2
36 property variant barrierTriggerZoneP1
37 property variant barrierTriggerZoneP2
38
39 PointerBarrier {
40 id: barrier
41 triggerDirection: Utils.isLeftToRight() ? PointerBarrier.TriggerFromRight : PointerBarrier.TriggerFromLeft
42 triggerZoneEnabled: !shown
43 p1: barrierP1
44 p2: barrierP2
45 triggerZoneP1: barrierTriggerZoneP1
46 triggerZoneP2: barrierTriggerZoneP2
47 threshold: launcher2dConfiguration.edgeStopVelocity
48 maxVelocityMultiplier: launcher2dConfiguration.edgeResponsiveness
49 decayRate: launcher2dConfiguration.edgeDecayrate
50 triggerPressure: launcher2dConfiguration.edgeRevealPressure
51 breakPressure: launcher2dConfiguration.edgeOvercomePressure
52
53 onTriggered: launcher.barrierTriggered()
54 }
3355
34 function hideMenu() {56 function hideMenu() {
35 if (main.visibleMenu !== undefined) {57 if (main.visibleMenu !== undefined) {
3658
=== modified file 'shell/launcher/LauncherLoader.qml'
--- shell/launcher/LauncherLoader.qml 2012-02-10 11:17:36 +0000
+++ shell/launcher/LauncherLoader.qml 2012-03-05 14:20:25 +0000
@@ -26,7 +26,6 @@
26 source: "Launcher.qml"26 source: "Launcher.qml"
27 property variant visibilityController: visibilityController27 property variant visibilityController: visibilityController
28 onLoaded: item.focus = true28 onLoaded: item.focus = true
29 property alias outerEdgeMouseArea: outerEdge
3029
31 VisibilityController {30 VisibilityController {
32 id: visibilityController31 id: visibilityController
@@ -79,16 +78,7 @@
7978
80 Binding {79 Binding {
81 target: launcherLoader.item80 target: launcherLoader.item
82 property: "outerEdgeContainsMouse"81 property: "shown"
83 value: outerEdge.containsMouse && outerEdge.enabled82 value: visibilityController.shown
84 }83 }
85
86 MouseArea {
87 id: outerEdge
88 anchors.fill: parent
89 anchors.margins: -1
90 hoverEnabled: !visibilityController.shown
91 enabled: !visibilityController.shown
92 }
93
94}84}
9585
=== modified file 'tests/launcher/autohide_show_tests.rb'
--- tests/launcher/autohide_show_tests.rb 2012-02-20 14:11:16 +0000
+++ tests/launcher/autohide_show_tests.rb 2012-03-05 14:20:25 +0000
@@ -55,6 +55,12 @@
55 XDo::Mouse.move(0, 200, 0, true)55 XDo::Mouse.move(0, 200, 0, true)
56 end56 end
5757
58 def mouse_push_screen_edge
59 (1..100).each do |i|
60 $SUT.execute_shell_command 'xdotool mousemove_relative -- -10 0'
61 end
62 end
63
58 def move_mouse_to_launcher_inner_border()64 def move_mouse_to_launcher_inner_border()
59 XDo::Mouse.move(LAUNCHER_WIDTH-1,200)65 XDo::Mouse.move(LAUNCHER_WIDTH-1,200)
60 end66 end
6167
=== modified file 'tests/launcher/autohide_show_tests_common.rb'
--- tests/launcher/autohide_show_tests_common.rb 2012-02-20 16:44:00 +0000
+++ tests/launcher/autohide_show_tests_common.rb 2012-03-05 14:20:25 +0000
@@ -114,7 +114,7 @@
114# * Open application in position overlapping Launcher114# * Open application in position overlapping Launcher
115# * Verify Launcher hiding115# * Verify Launcher hiding
116# * Move mouse to left of screen to reveal Launcher116# * Move mouse to left of screen to reveal Launcher
117# * Verify Launcher shows but not immediately117# * Verify Launcher shows only if we push the barrier
118# * Move mouse to the right, but still over the Launcher118# * Move mouse to the right, but still over the Launcher
119# * Verify Launcher still showing119# * Verify Launcher still showing
120# * Move mouse further right to not overlap Launcher120# * Move mouse further right to not overlap Launcher
@@ -128,8 +128,8 @@
128 verify_launcher_hidden(TIMEOUT, 'Launcher visible with window in the way, should be hidden')128 verify_launcher_hidden(TIMEOUT, 'Launcher visible with window in the way, should be hidden')
129129
130 move_mouse_to_screen_edge()130 move_mouse_to_screen_edge()
131 sleep 0.4131 verify_launcher_hidden(0, 'Launcher should not be visible immediately without pushing the edge')
132 verify_launcher_hidden(0, 'Launcher should not be visible immediately after mouse moves to the edge, has to wait 0.5 seconds to show')132 mouse_push_screen_edge()
133 verify_launcher_visible(TIMEOUT, 'Launcher hiding when mouse at edge of screen')133 verify_launcher_visible(TIMEOUT, 'Launcher hiding when mouse at edge of screen')
134134
135 move_mouse_to_launcher_inner_border()135 move_mouse_to_launcher_inner_border()
@@ -422,6 +422,7 @@
422 if !tiles.empty?422 if !tiles.empty?
423 tile = tiles[0]423 tile = tiles[0]
424 move_mouse_to_screen_edge()424 move_mouse_to_screen_edge()
425 mouse_push_screen_edge()
425 verify_launcher_visible(TIMEOUT, 'Launcher hiding when mouse at edge of screen, should be visible')426 verify_launcher_visible(TIMEOUT, 'Launcher hiding when mouse at edge of screen, should be visible')
426 tile.move_mouse()427 tile.move_mouse()
427 XDo::Mouse.click(nil, nil, :right)428 XDo::Mouse.click(nil, nil, :right)
@@ -457,6 +458,7 @@
457458
458 bfb = @app.LauncherList( :name => 'main' ).LauncherList( :isBfb => true );459 bfb = @app.LauncherList( :name => 'main' ).LauncherList( :isBfb => true );
459 move_mouse_to_screen_edge()460 move_mouse_to_screen_edge()
461 mouse_push_screen_edge()
460 verify_launcher_visible(TIMEOUT, 'Launcher hiding when mouse at edge of screen, should be visible')462 verify_launcher_visible(TIMEOUT, 'Launcher hiding when mouse at edge of screen, should be visible')
461 bfb.move_mouse()463 bfb.move_mouse()
462 bfb.tap()464 bfb.tap()
463465
=== modified file 'tests/launcher/autohide_show_tests_rtl.rb'
--- tests/launcher/autohide_show_tests_rtl.rb 2012-02-20 14:11:16 +0000
+++ tests/launcher/autohide_show_tests_rtl.rb 2012-03-05 14:20:25 +0000
@@ -56,6 +56,12 @@
56 XDo::Mouse.move(XDo::XWindow.display_geometry[0], 200, 0, true)56 XDo::Mouse.move(XDo::XWindow.display_geometry[0], 200, 0, true)
57 end57 end
5858
59 def mouse_push_screen_edge
60 (1..100).each do |i|
61 $SUT.execute_shell_command 'xdotool mousemove_relative 10 0'
62 end
63 end
64
59 def move_mouse_to_launcher_inner_border()65 def move_mouse_to_launcher_inner_border()
60 XDo::Mouse.move(XDo::XWindow.display_geometry[0] - LAUNCHER_WIDTH, 200)66 XDo::Mouse.move(XDo::XWindow.display_geometry[0] - LAUNCHER_WIDTH, 200)
61 end67 end

Subscribers

People subscribed via source and target branches