on xenial help sign in
replace MouseArea with MultiPointTouchArea

This implements full support for using it like a touchpad with up to two fingers. Supported things are: * moving cursor * clicking * two-finger scrolling * two-finger right-clicking * quick double tap and hold to enter drag mode

commitdeee36b4841bca514e2e6ff3ae9b19f39e1376da authorMichael Zanetti <michael.zanetti@canonical.com> dateFri 13 Nov 2015, 18:34 (11 years ago) parent9034dd1
3 files changed+187−39
plugins/UInput/uinput.cpp+33−9 plugins/UInput/uinput.h+12−3 qml/DisabledScreenNotice.qml+142−27
plugins/UInput/uinput.cpp+33−9
@@ -40,6 +40,8 @@ void UInput::createMouse()
4040 ioctl(m_uinput.handle(), UI_SET_EVBIT, EV_REL);
4141 ioctl(m_uinput.handle(), UI_SET_RELBIT, REL_X);
4242 ioctl(m_uinput.handle(), UI_SET_RELBIT, REL_Y);
43+ ioctl(m_uinput.handle(), UI_SET_RELBIT, REL_HWHEEL);
44+ ioctl(m_uinput.handle(), UI_SET_RELBIT, REL_WHEEL);
4345
4446 ioctl(m_uinput.handle(), UI_SET_EVBIT, EV_KEY);
4547 ioctl(m_uinput.handle(), UI_SET_KEYBIT, BTN_MOUSE);
@@ -107,36 +109,58 @@ void UInput::moveMouse(int dx, int dy)
107109 write(m_uinput.handle(), &event, sizeof(event));
108110}
109111
110−void UInput::pressMouse(Qt::MouseButton button)
112+void UInput::pressMouse(Button button)
111113{
112114 injectMouse(button, 1);
113115}
114116
115−void UInput::releaseMouse(Qt::MouseButton button)
117+void UInput::releaseMouse(Button button)
116118{
117119 injectMouse(button, 0);
118120}
119121
120−void UInput::injectMouse(Qt::MouseButton button, int down)
122+void UInput::scrollMouse(int dh, int dv)
123+{
124+ struct input_event event;
125+ memset(&event, 0, sizeof(event));
126+ gettimeofday(&event.time, 0);
127+ event.type = EV_REL;
128+ event.code = REL_HWHEEL;
129+ event.value = dh;
130+ write(m_uinput.handle(), &event, sizeof(event));
131+
132+ event.code = REL_WHEEL;
133+ event.value = dv;
134+ write(m_uinput.handle(), &event, sizeof(event));
135+
136+ event.type = EV_SYN;
137+ event.code = SYN_REPORT;
138+ event.value = 0;
139+ write(m_uinput.handle(), &event, sizeof(event));
140+}
141+
142+void UInput::injectMouse(Button button, int down)
121143{
122144 struct input_event event;
123145 memset(&event, 0, sizeof(event));
124146 gettimeofday(&event.time, 0);
125147 event.type = EV_KEY;
126148 switch (button) {
127− case Qt::LeftButton:
149+ case ButtonLeft:
128150 event.code = BTN_LEFT;
129151 break;
130− case Qt::RightButton:
152+ case ButtonRight:
131153 event.code = BTN_RIGHT;
132154 break;
133− case Qt::MiddleButton:
155+ case ButtonMiddle:
134156 event.code = BTN_MIDDLE;
135157 break;
136− default:
137− qWarning() << "Unsupported mouse button.";
138− return;
139158 }
140159 event.value = down;
141160 write(m_uinput.handle(), &event, sizeof(event));
161+
162+ event.type = EV_SYN;
163+ event.code = SYN_REPORT;
164+ event.value = 0;
165+ write(m_uinput.handle(), &event, sizeof(event));
142166}
plugins/UInput/uinput.h+12−3
@@ -10,7 +10,15 @@
1010class UInput : public QObject
1111{
1212 Q_OBJECT
13+ Q_ENUMS(Button)
14+
1315public:
16+ enum Button {
17+ ButtonLeft,
18+ ButtonRight,
19+ ButtonMiddle
20+ };
21+
1422 explicit UInput(QObject *parent = 0);
1523 ~UInput();
1624
@@ -18,11 +26,12 @@ public:
1826 Q_INVOKABLE void removeMouse();
1927
2028 Q_INVOKABLE void moveMouse(int dx, int dy);
21− Q_INVOKABLE void pressMouse(Qt::MouseButton button);
22− Q_INVOKABLE void releaseMouse(Qt::MouseButton button);
29+ Q_INVOKABLE void pressMouse(Button button);
30+ Q_INVOKABLE void releaseMouse(Button button);
31+ Q_INVOKABLE void scrollMouse(int dh, int dv);
2332
2433private:
25− void injectMouse(Qt::MouseButton button, int down);
34+ void injectMouse(Button button, int down);
2635
2736private:
2837 QFile m_uinput;
qml/DisabledScreenNotice.qml+142−27
@@ -15,8 +15,10 @@
1515 */
1616
1717import QtQuick 2.4
18+import QtQuick.Layouts 1.1
1819import Ubuntu.Components 1.3
1920import UInput 0.1
21+import Cursor 1.0
2022import "Components"
2123
2224Image {
@@ -29,45 +31,158 @@ Image {
2931
3032 source: wallpaperResolver.background
3133
32− UbuntuShape {
33− anchors.fill: virtualTouchPad
34− backgroundColor: "black"
35− opacity: 0.4
36− }
37−
3834 UInput {
3935 id: uinput
4036 Component.onCompleted: createMouse();
4137 Component.onDestruction: removeMouse();
4238 }
4339
44− MouseArea {
45− id: virtualTouchPad
40+ ColumnLayout {
4641 anchors.fill: parent
4742 anchors.margins: units.gu(2)
43+ spacing: units.gu(2)
44+
45+ Label {
46+ id: text
47+ Layout.fillWidth: true
48+ text: i18n.tr("Your device is now connected to an external display.")
49+ color: "white"
50+ horizontalAlignment: Text.AlignHCenter
51+ verticalAlignment: Text.AlignVCenter
52+ fontSize: "x-large"
53+ wrapMode: Text.Wrap
54+ }
55+
56+ UbuntuShape {
57+ Layout.fillWidth: true
58+ Layout.fillHeight: true
59+ backgroundColor: "black"
60+ opacity: 0.4
61+
62+ Image {
63+ anchors.centerIn: parent
64+ height: units.gu(2)
65+ width: units.gu(2)
66+ source: "image://cursor/default/left_ptr"
67+ }
68+
69+ MultiPointTouchArea {
70+ anchors.fill: parent
71+
72+ property int clickThreshold: units.gu(1.5)
73+ property bool isClick: false
74+
75+ onPressed: {
76+ if (point1.pressed && !point2.pressed && clickTimer.running) {
77+ clickTimer.stop();
78+ uinput.pressMouse(UInput.ButtonLeft)
79+ }
80+ isClick = true;
81+ }
4882
49− property int oldX
50− property int oldY
83+ onUpdated: {
84+ switch (touchPoints.length) {
85+ case 1:
86+ moveMouse(touchPoints);
87+ return;
88+ case 2:
89+ scroll(touchPoints);
90+ return;
91+ }
92+ }
5193
52− onPressed: {
53− oldX = mouseX;
54− oldY = mouseY;
94+ onReleased: {
95+ var tp = touchPoints[0]
96+ if (isClick) {
97+ clickTimer.scheduleClick(point1.pressed ? UInput.ButtonRight : UInput.ButtonLeft)
98+ }
99+ isClick = false;
100+ }
101+
102+ Timer {
103+ id: clickTimer
104+ repeat: false
105+ interval: 100
106+ property int button: UInput.ButtonLeft
107+ onTriggered: {
108+ uinput.pressMouse(button);
109+ uinput.releaseMouse(button);
110+ }
111+ function scheduleClick(button) {
112+ clickTimer.button = button;
113+ clickTimer.start();
114+ }
115+ }
116+
117+ function moveMouse(touchPoints) {
118+ var tp = touchPoints[0];
119+ if (isClick &&
120+ (Math.abs(tp.x - tp.startX) > clickThreshold ||
121+ Math.abs(tp.y - tp.startY) > clickThreshold)) {
122+ isClick = false;
123+ }
124+
125+ uinput.moveMouse(tp.x - tp.previousX, tp.y - tp.previousY);
126+ }
127+
128+ function scroll(touchPoints) {
129+ var dh = 0;
130+ var dv = 0;
131+ var tp = touchPoints[0];
132+ if (isClick &&
133+ (Math.abs(tp.x - tp.startX) > clickThreshold ||
134+ Math.abs(tp.y - tp.startY) > clickThreshold)) {
135+ isClick = false;
136+ }
137+ dh += tp.x - tp.previousX;
138+ dv += tp.y - tp.previousY;
139+
140+ tp = touchPoints[1];
141+ if (isClick &&
142+ (Math.abs(tp.x - tp.startX) > clickThreshold ||
143+ Math.abs(tp.y - tp.startY) > clickThreshold)) {
144+ isClick = false;
145+ }
146+ dh += tp.x - tp.previousX;
147+ dv += tp.y - tp.previousY;
148+
149+ uinput.scrollMouse(dh, dv);
150+ }
151+
152+ touchPoints: [
153+ TouchPoint {
154+ id: point1
155+ },
156+ TouchPoint {
157+ id: point2
158+ }
159+ ]
160+ }
55161 }
56162
57− onPositionChanged: {
58− uinput.moveMouse(mouseX - oldX, mouseY - oldY)
163+ RowLayout {
164+ Layout.fillWidth: true
165+ spacing: units.gu(2)
166+ Button {
167+ Layout.fillWidth: true
168+ onPressedChanged: {
169+ if (pressed) {
170+ uinput.pressMouse(UInput.ButtonLeft)
171+ } else {
172+ uinput.releaseMouse(UInput.ButtonLeft)
173+ }
174+ }
175+ }
176+ Button {
177+ Layout.fillWidth: true
178+ onPressedChanged: {
179+ if (pressed) {
180+ uinput.pressMouse(UInput.ButtonRight)
181+ } else {
182+ uinput.releaseMouse(UInput.ButtonRight)
183+ }
184+ }
185+ }
59186 }
60187 }
61−
62−// Label {
63−// id: text
64−// anchors.centerIn: parent
65−// width: parent.width / 2
66−// text: i18n.tr("Your device is now connected to an external display.")
67−// color: "white"
68−// horizontalAlignment: Text.AlignHCenter
69−// verticalAlignment: Text.AlignVCenter
70−// fontSize: "x-large"
71−// wrapMode: Text.Wrap
72−// }
73188}