Lomiri
uinput.cpp
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 "uinput.h"
18 
19 #include <QFile>
20 #include <QDebug>
21 #include <QDateTime>
22 
23 #include <unistd.h>
24 #include <time.h>
25 
26 UInput::UInput(QObject *parent) :
27  QObject(parent)
28 {
29  m_devName = QByteArrayLiteral("lomiri-simulated-mouse");
30  m_uinput.setFileName(QStringLiteral("/dev/uinput"));
31 
32  memset(&m_uinput_mouse_dev, 0, sizeof(m_uinput_mouse_dev));
33  m_uinput_mouse_dev.id.bustype = BUS_USB;
34  m_uinput_mouse_dev.id.version = 1;
35  strncpy(m_uinput_mouse_dev.name, m_devName.constData(), m_devName.length());
36 }
37 
38 UInput::~UInput()
39 {
40  if (m_mouseCreated) {
41  removeMouse();
42  }
43 }
44 
45 void UInput::createMouse()
46 {
47  if (m_mouseCreated) {
48  qDebug() << "Already have a virtual device. Not creating another one.";
49  return;
50  }
51 
52  if (!m_uinput.isOpen() && !m_uinput.open(QFile::WriteOnly)) {
53  return;
54  }
55 
56  ioctl(m_uinput.handle(), UI_SET_EVBIT, EV_REL);
57  ioctl(m_uinput.handle(), UI_SET_RELBIT, REL_X);
58  ioctl(m_uinput.handle(), UI_SET_RELBIT, REL_Y);
59  ioctl(m_uinput.handle(), UI_SET_RELBIT, REL_HWHEEL);
60  ioctl(m_uinput.handle(), UI_SET_RELBIT, REL_WHEEL);
61 
62  ioctl(m_uinput.handle(), UI_SET_EVBIT, EV_KEY);
63  ioctl(m_uinput.handle(), UI_SET_KEYBIT, BTN_MOUSE);
64  ioctl(m_uinput.handle(), UI_SET_KEYBIT, BTN_LEFT);
65  ioctl(m_uinput.handle(), UI_SET_KEYBIT, BTN_MIDDLE);
66  ioctl(m_uinput.handle(), UI_SET_KEYBIT, BTN_RIGHT);
67  ioctl(m_uinput.handle(), UI_SET_KEYBIT, BTN_FORWARD);
68  ioctl(m_uinput.handle(), UI_SET_KEYBIT, BTN_BACK);
69 
70  ioctl(m_uinput.handle(), UI_SET_EVBIT, EV_SYN);
71 
72  int len = write(m_uinput.handle(), &m_uinput_mouse_dev, sizeof(m_uinput_mouse_dev));
73  if (len <= 0) {
74  qWarning() << "Failed to write to uinput. Cannot create virtual uinput mouse.";
75  return;
76  }
77 
78  int err = ioctl(m_uinput.handle(), UI_DEV_CREATE);
79  if (err != 0) {
80  qWarning() << "Cannot create virtual uinput device. Create ioctl failed:" << err;
81  return;
82  }
83  m_mouseCreated = true;
84  qDebug() << "Virtual uinput mouse device created.";
85 }
86 
87 void UInput::removeMouse()
88 {
89  if (!m_mouseCreated) {
90  return;
91  }
92 
93  if (!m_uinput.isOpen() && !m_uinput.open(QFile::WriteOnly)) {
94  qWarning() << "cannot open uinput... ";
95  return;
96  }
97 
98  int err = ioctl(m_uinput.handle(), UI_DEV_DESTROY);
99  if (err != 0) {
100  qWarning() << "Failed to destroy virtual uinput device. Destroy ioctl failed:" << err;
101  } else {
102  qDebug() << "Virtual uinput mouse device removed.";
103  }
104  m_uinput.close();
105  m_mouseCreated = false;
106 }
107 
108 void UInput::moveMouse(int dx, int dy)
109 {
110  struct input_event event;
111  memset(&event, 0, sizeof(event));
112  clock_gettime(CLOCK_MONOTONIC, (timespec*)&event.time);
113  event.type = EV_REL;
114  event.code = REL_X;
115  event.value = dx;
116  write(m_uinput.handle(), &event, sizeof(event));
117 
118  event.code = REL_Y;
119  event.value = dy;
120  write(m_uinput.handle(), &event, sizeof(event));
121 
122  event.type = EV_SYN;
123  event.code = SYN_REPORT;
124  event.value = 0;
125  write(m_uinput.handle(), &event, sizeof(event));
126 }
127 
128 void UInput::pressMouse(Button button)
129 {
130  injectMouse(button, 1);
131 }
132 
133 void UInput::releaseMouse(Button button)
134 {
135  injectMouse(button, 0);
136 }
137 
138 void UInput::scrollMouse(int dh, int dv)
139 {
140  struct input_event event;
141  memset(&event, 0, sizeof(event));
142  clock_gettime(CLOCK_MONOTONIC, (timespec*)&event.time);
143  event.type = EV_REL;
144  event.code = REL_HWHEEL;
145  event.value = dh;
146  write(m_uinput.handle(), &event, sizeof(event));
147 
148  event.code = REL_WHEEL;
149  event.value = dv;
150  write(m_uinput.handle(), &event, sizeof(event));
151 
152  event.type = EV_SYN;
153  event.code = SYN_REPORT;
154  event.value = 0;
155  write(m_uinput.handle(), &event, sizeof(event));
156 }
157 
158 void UInput::injectMouse(Button button, int down)
159 {
160  struct input_event event;
161  memset(&event, 0, sizeof(event));
162  clock_gettime(CLOCK_MONOTONIC, (timespec*)&event.time);
163  event.type = EV_KEY;
164  switch (button) {
165  case ButtonLeft:
166  event.code = BTN_LEFT;
167  break;
168  case ButtonRight:
169  event.code = BTN_RIGHT;
170  break;
171  case ButtonMiddle:
172  event.code = BTN_MIDDLE;
173  break;
174  }
175  event.value = down;
176  write(m_uinput.handle(), &event, sizeof(event));
177 
178  event.type = EV_SYN;
179  event.code = SYN_REPORT;
180  event.value = 0;
181  write(m_uinput.handle(), &event, sizeof(event));
182 }