1 2 3 4 5 6 | 受不了xxxx恶心人的行为,遂搬迁至博客园。 始发: 2016 - 12 - 28 17 : 38 : 57 版本信息: Linux: 3.10 Android: 4.4 |
uinput is a linux kernel module that allows to handle the input subsystem from user land. It can be used to create and to handle input devices from an application. It creates a character device in /dev/input directory. The device is a virtual interface, it doesn't belong to a physical device.
In this document, we will see how to create a such input device and how it can be used.
1. Creating an input device
Once the uinput module is installed (via modprobe or insmod), a character device is created, named as /dev/input/uinput (or /dev/uinput on some systems). This device represents the interface between the application and the kernel input subsystem.
To use uinput, we need to open the character device in write-only and non-blocking mode:
1 2 3 4 5 6 7 8 9 10 11 | #include <linux/input.h> #include <linux/uinput.h> ... int fd; fd = open( "/dev/input/uinput" , O_WRONLY | O_NONBLOCK); if (fd < 0) { ... exit (EXIT_FAILURE); } |
Now the device is opened, we will configure our input device. First, we need to inform the input subsystem which types of input event we want to use. Types of events are defined in /usr/include/linux/input.h:
1 2 3 4 5 | ... #define EV_KEY 0x01 #define EV_REL 0x02 #define EV_ABS 0x03 ... |
- EV_KEY type represents key press and release events,
- EV_REL type represents relative axis events (such as mouse movements),
- EV_ABS type represents absolute axis events (such as touchscreen movements),
- …
The ioctl request UI_SET_EVBIT applied on the uinput file descriptor is used to enable a type of event. The two following lines enable key press/release and synchronization events.
1 2 3 | ret = ioctl(fd, UI_SET_EVBIT, EV_KEY); ... ret = ioctl(fd, UI_SET_EVBIT, EV_SYN); |
When enabling EV_KEY events, we need to describe which keycodes are allowed to be sent via the input subsystem.
As linux/input.h defines the 'd' key as KEY_D, we can enable the keycode representing the 'd' key by using:
1 2 | ret = ioctl(fd, UI_SET_KEYBIT, KEY_D); ... |
Now some basic features have been enabled, we need to finish the configuration by using the struct uinput_user_dev from linux/uinput.h. This structure is defined as:
1 2 3 4 5 6 7 8 9 10 11 | #define UINPUT_MAX_NAME_SIZE 80 struct uinput_user_dev { char name[UINPUT_MAX_NAME_SIZE]; struct input_id id; int ff_effects_max; int absmax[ABS_MAX + 1]; int absmin[ABS_MAX + 1]; int absfuzz[ABS_MAX + 1]; int absflat[ABS_MAX + 1]; }; |
The most important fields are:
- name is the given name to the input device we will create,
- id is a linux internal structure that describes the device bustype, vendor id, product id and version,
- absmin and absmax are integer array that defines mininal and maximal values for an absolute axis (i.e absmin[ABS_X] = 0, absmax[ABS_X] = 1024 for the X axis on a touchscreen device).
Now, we can fill this structure with appropriate values:
1 2 3 4 5 6 7 8 9 | struct uinput_user_dev uidev; memset (&uidev, 0, sizeof (uidev)); snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "uinput-sample" ); uidev.id.bustype = BUS_USB; uidev.id.vendor = 0x1234; uidev.id.product = 0xfedc; uidev.id.version = 1; |
Then, we write this structure in the uinput file descriptor.
1 | ret = write(fd, &uidev, sizeof (uidev)); |
Last step is to request the creation of the device via the UI_DEV_CREATE ioctl request on the file descriptor:
1 | ret = ioctl(fd, UI_DEV_CREATE); |
Now, the file descriptor fd represents the end-point file descriptor of the new input device.
2. Injecting events in the input subsystem
The following block code injects a key press event in the input subsystem. The input_event structure contains 3 important fields:
type: is an event type (EV_KEY,EV_ABS,EV_REL, ...),
code: could be either a key code when using EV_KEY, or an axis for EV_ABS and EV_REL,
value: may be 1 (press) or 0 (release) for EV_KEY, or any values for others (positive integer for EV_ABS, signed integer for EV_REL, etc…).
To inject a press event on the 'd' key:
1 2 3 4 5 6 7 8 9 | struct input_event ev; memset (&ev, 0, sizeof (ev)); ev.type = EV_KEY; ev.code = KEY_D; ev.value = 1; ret = write(fd, &ev, sizeof (ev)); |
3. Destroying an input device
1 | ret = ioctl(fd, UI_DEV_DESTROY); |
4. Handling absolute axis events
If we want to inject absolute events, we first need to activate EV_ABS event and the desired axes support with ioctl requests. The following ioctl requests enable X and Y absolute axes:
1 2 3 4 5 6 | ret = ioctl(fd, UI_SET_EVBIT, EV_ABS); ... ret = ioctl(fd, UI_SET_ABSBIT, ABS_X); ... ret = ioctl(fd, UI_SET_ABSBIT, ABS_Y); |
Then we need to defined a range of values for each axis with absmin and absmax fields from the uinput_user_dev structure:
1 2 | uidev.absmin[ABS_X] = 0; uidev.absmax[ABS_X] = 1023; |
Event injection follows the same method as for any other events.
1 2 3 4 5 6 7 8 9 10 11 12 | struct input_event ev[2]; memset (ev, 0, sizeof (ev)); ev[0].type = EV_ABS; ev[0].code = ABS_X; ev[0].value = 1023; ev[1].type = EV_ABS; ev[1].code = ABS_Y; ev[1].value = 767; ret = write(fd, ev, sizeof (ev)); |
5. Sample code
以下是模拟键盘的一个示例及其使用测试,在Android平台测试:
First compile code under Android source,
Then push your bin file to Android system,
Third, open an App that contains an EditText, such as Google browser and run your bin file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 | #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <fcntl.h> #include <paths.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <linux/input.h> #include <linux/uinput.h> #include <signal.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <sys/stat.h> #include <sys/time.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/time.h> #include <sys/ioctl.h> #include <sys/types.h> #include <linux/types.h> #include <termios.h> #include <time.h> #include <pthread.h> #include <hardware_legacy/power.h> #define LOG_TAG "UINPUT" #include <utils/Log.h> //all Android LOG macros are defined here. #define DEBUG 1 #ifdef DEBUG #define LOG(lvl, f,...) do{ \ LOG_PRI(ANDROID_LOG_ERROR,LOG_TAG, "%d:%s(): " f, __LINE__, __FUNCTION__, ##__VA_ARGS__);\ } while (0) #else #define LOG(lvl,f,...) do{}while(0) #endif #define SHIFT (1 << 16) /* * Convert from character to KEY_CODE */ static int keyboard_map( const char key) { int event = -1; switch (key) { case '1' : event = KEY_1; break ; case '2' : event = KEY_2; break ; case '3' : event = KEY_3; break ; case '4' : event = KEY_4; break ; case '5' : event = KEY_5; break ; case '6' : event = KEY_6; break ; case '7' : event = KEY_7; break ; case '8' : event = KEY_8; break ; case '9' : event = KEY_9; break ; case '0' : event = KEY_0; break ; case 'a' : event = KEY_A; break ; case 'b' : event = KEY_B; break ; case 'c' : event = KEY_C; break ; case 'd' : event = KEY_D; break ; case 'e' : event = KEY_E; break ; case 'f' : event = KEY_F; break ; case 'g' : event = KEY_G; break ; case 'h' : event = KEY_H; break ; case 'i' : event = KEY_I; break ; case 'j' : event = KEY_J; break ; case 'k' : event = KEY_K; break ; case 'l' : event = KEY_L; break ; case 'm' : event = KEY_M; break ; case 'n' : event = KEY_N; break ; case 'o' : event = KEY_O; break ; case 'p' : event = KEY_P; break ; case 'q' : event = KEY_Q; break ; case 'r' : event = KEY_R; break ; case 's' : event = KEY_S; break ; case 't' : event = KEY_T; break ; case 'u' : event = KEY_U; break ; case 'v' : event = KEY_V; break ; case 'w' : event = KEY_W; break ; case 'x' : event = KEY_X; break ; case 'y' : event = KEY_Y; break ; case 'z' : event = KEY_Z; break ; case 'A' : event = KEY_A; event |= SHIFT; break ; case 'B' : event = KEY_B; event |= SHIFT; break ; case 'C' : event = KEY_C; event |= SHIFT; break ; case 'D' : event = KEY_D; event |= SHIFT; break ; case 'E' : event = KEY_E; event |= SHIFT; break ; case 'F' : event = KEY_F; event |= SHIFT; break ; case 'G' : event = KEY_G; event |= SHIFT; break ; case 'H' : event = KEY_H; event |= SHIFT; break ; case 'I' : event = KEY_I; event |= SHIFT; break ; case 'J' : event = KEY_J; event |= SHIFT; break ; case 'K' : event = KEY_K; event |= SHIFT; break ; case 'L' : event = KEY_L; event |= SHIFT; break ; case 'M' : event = KEY_M; event |= SHIFT; break ; case 'N' : event = KEY_N; event |= SHIFT; break ; case 'O' : event = KEY_O; event |= SHIFT; break ; case 'P' : event = KEY_P; event |= SHIFT; break ; case 'Q' : event = KEY_Q; event |= SHIFT; break ; case 'R' : event = KEY_R; event |= SHIFT; break ; case 'S' : event = KEY_S; event |= SHIFT; break ; case 'T' : event = KEY_T; event |= SHIFT; break ; case 'U' : event = KEY_U; event |= SHIFT; break ; case 'V' : event = KEY_V; event |= SHIFT; break ; case 'W' : event = KEY_W; event |= SHIFT; break ; case 'X' : event = KEY_X; event |= SHIFT; break ; case 'Y' : event = KEY_Y; event |= SHIFT; break ; case 'Z' : event = KEY_Z; event |= SHIFT; break ; case '!' : event = KEY_1; event |= SHIFT; break ; case '@' : event = KEY_2; event |= SHIFT; break ; case '#' : event = KEY_3; event |= SHIFT; break ; case '$' : event = KEY_4; event |= SHIFT; break ; case '%' : event = KEY_5; event |= SHIFT; break ; case '^' : event = KEY_6; event |= SHIFT; break ; case '&' : event = KEY_7; event |= SHIFT; break ; case '*' : event = KEY_8; event |= SHIFT; break ; case '(' : event = KEY_9; event |= SHIFT; break ; case ')' : event = KEY_0; event |= SHIFT; break ; case '-' : event = KEY_MINUS; break ; case '_' : event = KEY_MINUS; event |= SHIFT; break ; case '=' : event = KEY_EQUAL; break ; case '+' : event = KEY_EQUAL; event |= SHIFT; break ; case ';' : event = KEY_SEMICOLON; break ; case ':' : event = KEY_SEMICOLON; event |= SHIFT; break ; case ',' : event = KEY_COMMA; break ; case '<' : event = KEY_COMMA; event |= SHIFT; break ; case '.' : event = KEY_DOT; break ; case '>' : event = KEY_DOT; event |= SHIFT; break ; case '/' : event = KEY_SLASH; break ; case '?' : event = KEY_SLASH; event |= SHIFT; break ; case '[' : event = KEY_LEFTBRACE; break ; case '{' : event = KEY_LEFTBRACE; event |= SHIFT; break ; case ']' : event = KEY_RIGHTBRACE; break ; case '}' : event = KEY_RIGHTBRACE; event |= SHIFT; break ; case '\\' : event = KEY_BACKSLASH; break ; case '|' : event = KEY_BACKSLASH; event |= SHIFT; break ; case '\'' : event = KEY_APOSTROPHE; break ; case '"' : event = KEY_APOSTROPHE; event |= SHIFT; break ; case '`' : event = KEY_GRAVE; break ; case '~' : event = KEY_BACKSLASH; event |= SHIFT; break ; default : event = -1; break ; } return event; } int keyboard_init( void ) { int fd; struct uinput_user_dev uidev; //struct input_event ev; int i; fd = open( "/dev/uinput" , O_WRONLY | O_NONBLOCK); if (fd <= 0) { LOG(0, "error: open" ); return -1; } else { LOG(0, "open /dev/uinput fd = %d\n" , fd); } memset (&uidev, 0, sizeof (uidev)); snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "HID Fake Device" ); uidev.id.bustype = BUS_USB; uidev.id.vendor = 0x1; uidev.id.product = 0x1; uidev.id.version = 1; // config uinput device Keyboard ioctl(fd, UI_SET_EVBIT, EV_KEY); for (i = 0; i < 128; i++) { ioctl(fd, UI_SET_KEYBIT, i); usleep(10000); } if (write(fd, &uidev, sizeof (uidev)) < 0) { LOG(0, "error: write" ); return -1; } if (ioctl(fd, UI_DEV_CREATE) < 0) { LOG(0, "error: ioctl" ); return -1; } return fd; } int keyboard_release( int fd) { if (fd <= 0) return -1; if (ioctl(fd, UI_DEV_DESTROY) < 0) LOG(0, "error: ioctl" ); close(fd); return 0; } int keyboard_write( int fd, const char key) { struct input_event event; const int kval = keyboard_map(key); const unsigned short key_code = (unsigned short ) kval; if (fd <= 0) { LOG(0, "open fd <= 0 error!\n" ); return -1; } if (kval <= 0) { LOG(0, "keyboard_write error!\n" ); return -1; } // shift down if ((kval & SHIFT) == SHIFT) { memset (&event, 0, sizeof (event)); gettimeofday(&event. time , 0); event.type = EV_KEY; event.value = 1; event.code = KEY_LEFTSHIFT; if (write(fd, &event, sizeof (event)) < 0) return -1; } // key down memset (&event, 0, sizeof (event)); gettimeofday(&event. time , 0); event.type = EV_KEY; event.value = 1; event.code = key_code; if (write(fd, &event, sizeof (event)) < 0) return -1; // key up memset (&event, 0, sizeof (event)); gettimeofday(&event. time , 0); event.type = EV_KEY; event.value = 0; event.code = key_code; if (write(fd, &event, sizeof (event)) < 0) return -1; // shift down if ((kval & SHIFT) == SHIFT) { memset (&event, 0, sizeof (event)); gettimeofday(&event. time , 0); event.type = EV_KEY; event.value = 0; event.code = KEY_LEFTSHIFT; if (write(fd, &event, sizeof (event)) < 0) return -1; } // sync event.type = EV_SYN; event.value = 0; event.code = SYN_REPORT; if (write(fd, &event, sizeof (event)) < 0) return -1; return 0; } int main( int argc, char **argv) { int fd = -1; int i = -1; const char *buf = "!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~" ; fd = keyboard_init(); if (fd <= 0) { LOG(0, "error: keyboard_init()" ); return -1; } for (i = 0; i < strlen (buf); i++) { keyboard_write(fd, buf[i]); usleep(10000); } keyboard_release(fd); return 0; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!