on xenial help sign in
add a UInput plugin to fake a touchpad
commit9034dd18f174985d870c7b1e429de465e21b8654 authorMichael Zanetti <michael.zanetti@canonical.com> dateWed 11 Nov 2015, 17:03 (11 years ago) parent78bdee5
8 files changed+282−12
plugins/CMakeLists.txt+1 plugins/UInput/CMakeLists.txt+7 plugins/UInput/plugin.cpp+26 plugins/UInput/plugin.h+32 plugins/UInput/qmldir+3 plugins/UInput/uinput.cpp+142 plugins/UInput/uinput.h+35 qml/DisabledScreenNotice.qml+36−12
plugins/CMakeLists.txt+1
@@ -22,6 +22,7 @@ add_subdirectory(Powerd)
2222add_subdirectory(SessionBroadcast)
2323add_subdirectory(ScreenGrabber)
2424add_subdirectory(Ubuntu)
25+add_subdirectory(UInput)
2526add_subdirectory(Unity)
2627add_subdirectory(Utils)
2728add_subdirectory(Wizard)
plugins/UInput/CMakeLists.txtnew file+7
@@ -0,0 +1,7 @@
1+add_library(UInput-qml MODULE
2+ plugin.cpp
3+ uinput.cpp
4+)
5+
6+qt5_use_modules(UInput-qml Qml)
7+add_unity8_plugin(UInput 0.1 UInput TARGETS UInput-qml)
plugins/UInput/plugin.cppnew file+26
@@ -0,0 +1,26 @@
1+/*
2+ * Copyright (C) 2015 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU General Public License version 3, as published
6+ * by the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but
9+ * WITHOUT ANY WARRANTY; without even the implied warranties of
10+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11+ * PURPOSE. See the GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License along
14+ * with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "plugin.h"
18+#include "uinput.h"
19+
20+#include <QtQml/qqml.h>
21+
22+void UInputPlugin::registerTypes(const char *uri)
23+{
24+ Q_ASSERT(uri == QLatin1String("UInput"));
25+ qmlRegisterType<UInput>(uri, 0, 1, "UInput");
26+}
plugins/UInput/plugin.hnew file+32
@@ -0,0 +1,32 @@
1+/*
2+ * Copyright (C) 2015 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 UINPUT_PLUGIN_H
18+#define UINPUT_PLUGIN_H
19+
20+#include <QtQml/QQmlEngine>
21+#include <QtQml/QQmlExtensionPlugin>
22+
23+class UInputPlugin : public QQmlExtensionPlugin
24+{
25+ Q_OBJECT
26+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
27+
28+public:
29+ void registerTypes(const char *uri) override;
30+};
31+
32+#endif
plugins/UInput/qmldirnew file+3
@@ -0,0 +1,3 @@
1+module UInput
2+plugin UInput-qml
3+typeinfo UInput.qmltypes
plugins/UInput/uinput.cppnew file+142
@@ -0,0 +1,142 @@
1+#include "uinput.h"
2+
3+#include <QFile>
4+#include <QDebug>
5+#include <QDateTime>
6+
7+#include <unistd.h>
8+
9+UInput::UInput(QObject *parent) :
10+ QObject(parent)
11+{
12+ m_devName = QStringLiteral("unity8-simulated-mouse");
13+ m_uinput.setFileName("/dev/uinput");
14+
15+ memset(&m_uinput_mouse_dev, 0, sizeof(m_uinput_mouse_dev));
16+ m_uinput_mouse_dev.id.bustype = BUS_USB;
17+ m_uinput_mouse_dev.id.version = 1;
18+ strncpy(m_uinput_mouse_dev.name, m_devName.toLocal8Bit().data(), m_devName.length());
19+
20+}
21+
22+UInput::~UInput()
23+{
24+ if (m_mouseCreated) {
25+ removeMouse();
26+ }
27+}
28+
29+void UInput::createMouse()
30+{
31+ if (m_mouseCreated) {
32+ qDebug() << "Already have a virtual device. Not creating another one.";
33+ return;
34+ }
35+
36+ if (!m_uinput.isOpen() && !m_uinput.open(QFile::WriteOnly)) {
37+ return;
38+ }
39+
40+ ioctl(m_uinput.handle(), UI_SET_EVBIT, EV_REL);
41+ ioctl(m_uinput.handle(), UI_SET_RELBIT, REL_X);
42+ ioctl(m_uinput.handle(), UI_SET_RELBIT, REL_Y);
43+
44+ ioctl(m_uinput.handle(), UI_SET_EVBIT, EV_KEY);
45+ ioctl(m_uinput.handle(), UI_SET_KEYBIT, BTN_MOUSE);
46+ ioctl(m_uinput.handle(), UI_SET_KEYBIT, BTN_LEFT);
47+ ioctl(m_uinput.handle(), UI_SET_KEYBIT, BTN_MIDDLE);
48+ ioctl(m_uinput.handle(), UI_SET_KEYBIT, BTN_RIGHT);
49+ ioctl(m_uinput.handle(), UI_SET_KEYBIT, BTN_FORWARD);
50+ ioctl(m_uinput.handle(), UI_SET_KEYBIT, BTN_BACK);
51+
52+ ioctl(m_uinput.handle(), UI_SET_EVBIT, EV_SYN);
53+
54+ int len = write(m_uinput.handle(), &m_uinput_mouse_dev, sizeof(m_uinput_mouse_dev));
55+ if (len <= 0) {
56+ qWarning() << "Failed to write to uinput. Cannot create virtual uinput mouse.";
57+ return;
58+ }
59+
60+ int err = ioctl(m_uinput.handle(), UI_DEV_CREATE);
61+ if (err != 0) {
62+ qWarning() << "Cannot create virtual uinput device. Create ioctl failed:" << err;
63+ return;
64+ }
65+ m_mouseCreated = true;
66+ qDebug() << "Virtual uinput mouse device created.";
67+}
68+
69+void UInput::removeMouse()
70+{
71+ if (!m_mouseCreated) {
72+ return;
73+ }
74+
75+ if (!m_uinput.isOpen() && !m_uinput.open(QFile::WriteOnly)) {
76+ qDebug() << "cannot open uinput... ";
77+ return;
78+ }
79+
80+ int err = ioctl(m_uinput.handle(), UI_DEV_DESTROY);
81+ if (err != 0) {
82+ qWarning() << "Failed to destroy virtual uinput device. Destroy ioctl failed:" << err;
83+ } else {
84+ qDebug() << "Virtual uinput mouse device removed.";
85+ }
86+ m_uinput.close();
87+ m_mouseCreated = false;
88+}
89+
90+void UInput::moveMouse(int dx, int dy)
91+{
92+ struct input_event event;
93+ memset(&event, 0, sizeof(event));
94+ gettimeofday(&event.time, 0);
95+ event.type = EV_REL;
96+ event.code = REL_X;
97+ event.value = dx;
98+ write(m_uinput.handle(), &event, sizeof(event));
99+
100+ event.code = REL_Y;
101+ event.value = dy;
102+ write(m_uinput.handle(), &event, sizeof(event));
103+
104+ event.type = EV_SYN;
105+ event.code = SYN_REPORT;
106+ event.value = 0;
107+ write(m_uinput.handle(), &event, sizeof(event));
108+}
109+
110+void UInput::pressMouse(Qt::MouseButton button)
111+{
112+ injectMouse(button, 1);
113+}
114+
115+void UInput::releaseMouse(Qt::MouseButton button)
116+{
117+ injectMouse(button, 0);
118+}
119+
120+void UInput::injectMouse(Qt::MouseButton button, int down)
121+{
122+ struct input_event event;
123+ memset(&event, 0, sizeof(event));
124+ gettimeofday(&event.time, 0);
125+ event.type = EV_KEY;
126+ switch (button) {
127+ case Qt::LeftButton:
128+ event.code = BTN_LEFT;
129+ break;
130+ case Qt::RightButton:
131+ event.code = BTN_RIGHT;
132+ break;
133+ case Qt::MiddleButton:
134+ event.code = BTN_MIDDLE;
135+ break;
136+ default:
137+ qWarning() << "Unsupported mouse button.";
138+ return;
139+ }
140+ event.value = down;
141+ write(m_uinput.handle(), &event, sizeof(event));
142+}
plugins/UInput/uinput.hnew file+35
@@ -0,0 +1,35 @@
1+#ifndef UINPUT_H
2+#define UINPUT_H
3+
4+#include <QObject>
5+#include <QFile>
6+
7+#include <linux/uinput.h>
8+
9+
10+class UInput : public QObject
11+{
12+ Q_OBJECT
13+public:
14+ explicit UInput(QObject *parent = 0);
15+ ~UInput();
16+
17+ Q_INVOKABLE void createMouse();
18+ Q_INVOKABLE void removeMouse();
19+
20+ Q_INVOKABLE void moveMouse(int dx, int dy);
21+ Q_INVOKABLE void pressMouse(Qt::MouseButton button);
22+ Q_INVOKABLE void releaseMouse(Qt::MouseButton button);
23+
24+private:
25+ void injectMouse(Qt::MouseButton button, int down);
26+
27+private:
28+ QFile m_uinput;
29+ uinput_user_dev m_uinput_mouse_dev;
30+ QString m_devName;
31+
32+ bool m_mouseCreated = false;
33+};
34+
35+#endif // UINPUT_H
qml/DisabledScreenNotice.qml+36−12
@@ -16,6 +16,7 @@
1616
1717import QtQuick 2.4
1818import Ubuntu.Components 1.3
19+import UInput 0.1
1920import "Components"
2021
2122Image {
@@ -29,21 +30,44 @@ Image {
2930 source: wallpaperResolver.background
3031
3132 UbuntuShape {
32− anchors.fill: text
33− anchors.margins: -units.gu(2)
33+ anchors.fill: virtualTouchPad
3434 backgroundColor: "black"
3535 opacity: 0.4
3636 }
3737
38− Label {
39− id: text
40− anchors.centerIn: parent
41− width: parent.width / 2
42− text: i18n.tr("Your device is now connected to an external display.")
43− color: "white"
44− horizontalAlignment: Text.AlignHCenter
45− verticalAlignment: Text.AlignVCenter
46− fontSize: "x-large"
47− wrapMode: Text.Wrap
38+ UInput {
39+ id: uinput
40+ Component.onCompleted: createMouse();
41+ Component.onDestruction: removeMouse();
4842 }
43+
44+ MouseArea {
45+ id: virtualTouchPad
46+ anchors.fill: parent
47+ anchors.margins: units.gu(2)
48+
49+ property int oldX
50+ property int oldY
51+
52+ onPressed: {
53+ oldX = mouseX;
54+ oldY = mouseY;
55+ }
56+
57+ onPositionChanged: {
58+ uinput.moveMouse(mouseX - oldX, mouseY - oldY)
59+ }
60+ }
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+// }
4973}