├── .gitattributes ├── .gitignore ├── C++ ├── jni │ ├── Android.mk │ ├── Application.mk │ ├── TouchInput.hpp │ └── kmods.cpp └── libs │ ├── arm64-v8a │ └── touchtest │ └── armeabi-v7a │ └── touchtest ├── LICENSE ├── README.md ├── TouchInput.go ├── Uinput.go ├── UinputDefs.go ├── Utils.go ├── bin ├── TouchTest └── TouchTest64 ├── build.sh ├── go.mod ├── go.sum └── kmods.go /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /C++/jni/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | 5 | LOCAL_MODULE := touchtest 6 | 7 | LOCAL_SRC_FILES := kmods.cpp 8 | LOCAL_CFLAGS += -O0 9 | 10 | LOCAL_LDLIBS += -llog 11 | 12 | include $(BUILD_EXECUTABLE) -------------------------------------------------------------------------------- /C++/jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_ABI := arm64-v8a armeabi-v7a 2 | APP_PLATFORM := android-21 3 | APP_STL := c++_static 4 | APP_OPTIM := release 5 | APP_CPPFLAGS += -fexceptions -std=c++14 -------------------------------------------------------------------------------- /C++/jni/TouchInput.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TOUCHINPUT_H 2 | #define TOUCHINPUT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | ///Go like Channel Implementation 20 | template 21 | class Channel { 22 | private: 23 | std::list queue; 24 | std::mutex m; 25 | std::condition_variable cv; 26 | bool closed; 27 | public: 28 | Channel() : closed(false) {} 29 | 30 | void close() { 31 | std::unique_lock lock(m); 32 | closed = true; 33 | cv.notify_all(); 34 | } 35 | 36 | bool is_closed() { 37 | std::unique_lock lock(m); 38 | return closed; 39 | } 40 | 41 | void put(const item &i) { 42 | std::unique_lock lock(m); 43 | if (closed) 44 | throw std::logic_error("put to closed channel"); 45 | queue.push_back(i); 46 | cv.notify_one(); 47 | } 48 | 49 | bool get(item &out, bool wait = true) { 50 | std::unique_lock lock(m); 51 | if (wait) 52 | cv.wait(lock, [&]() { return closed || !queue.empty(); }); 53 | if (queue.empty()) 54 | return false; 55 | out = queue.front(); 56 | queue.pop_front(); 57 | return true; 58 | } 59 | }; 60 | 61 | namespace TouchInput { 62 | struct input_absinfo absX; 63 | struct input_absinfo absY; 64 | struct input_absinfo absSlot; 65 | const char letterBytes[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 66 | 67 | char* randString(int n) { 68 | char* b = (char*)malloc(n + 1); // +1 for the null-terminator 69 | if (b == NULL) { 70 | perror("Failed to allocate memory"); 71 | exit(1); 72 | } 73 | 74 | for (int i = 0; i < n; i++) { 75 | b[i] = letterBytes[rand() % (sizeof(letterBytes) - 1)]; 76 | } 77 | b[n] = '\0'; // Null-terminate the string 78 | 79 | return b; 80 | } 81 | 82 | int randNum(int n) { 83 | // Generate a random number from 0 to n - 1. 84 | return rand() % n; 85 | } 86 | 87 | ///Find Touch Device and Create new UInput 88 | int createUInput(int ifd) { 89 | int ufd; 90 | const char *uinput = "/dev/uinput"; 91 | 92 | //Get Touch Screen Size 93 | ioctl(ifd, EVIOCGABS(ABS_MT_SLOT), &absSlot); 94 | ioctl(ifd, EVIOCGABS(ABS_MT_POSITION_X), &absX); 95 | ioctl(ifd, EVIOCGABS(ABS_MT_POSITION_Y), &absY); 96 | 97 | //Open UInput 98 | ufd = open(uinput, O_WRONLY | O_NONBLOCK); 99 | if (ufd < 0) { 100 | perror("Unable to open uinput"); 101 | } 102 | 103 | //Setup Touch Value 104 | ioctl(ufd, UI_SET_EVBIT, EV_KEY); 105 | ioctl(ufd, UI_SET_KEYBIT, BTN_TOUCH); 106 | 107 | //Setup Touch Params 108 | ioctl(ufd, UI_SET_EVBIT, EV_ABS); 109 | ioctl(ufd, UI_SET_ABSBIT, ABS_MT_POSITION_X); 110 | ioctl(ufd, UI_SET_ABSBIT, ABS_MT_POSITION_Y); 111 | ioctl(ufd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID); 112 | ioctl(ufd, UI_SET_PROPBIT, INPUT_PROP_DIRECT); 113 | 114 | //Setup User Device 115 | struct uinput_user_dev uidev; 116 | memset(&uidev, 0, sizeof(uidev)); 117 | uidev.id.bustype = 0x1C; //BUS_SPI 118 | uidev.id.vendor = randNum(0x2000); 119 | uidev.id.product = randNum(0x2000); 120 | uidev.id.version = randNum(0x200); 121 | 122 | char* devName = randString(7); 123 | strncpy(uidev.name, devName, UINPUT_MAX_NAME_SIZE); 124 | free(devName); 125 | 126 | uidev.absmin[ABS_MT_POSITION_X] = absX.minimum; 127 | uidev.absmax[ABS_MT_POSITION_X] = absX.maximum; 128 | uidev.absmin[ABS_MT_POSITION_Y] = absY.minimum; 129 | uidev.absmax[ABS_MT_POSITION_Y] = absY.maximum; 130 | uidev.absmin[ABS_MT_TRACKING_ID] = 0; 131 | uidev.absmax[ABS_MT_TRACKING_ID] = absSlot.maximum; 132 | 133 | //Write to Input Sub-System 134 | write(ufd, &uidev, sizeof(uidev)); 135 | 136 | //Declare Input Device 137 | ioctl(ufd, UI_DEV_CREATE); 138 | 139 | //Stop Primary Touch Device 140 | ioctl(ifd, EVIOCGRAB, 1); 141 | 142 | return ufd; 143 | } 144 | 145 | ///Determine if a event has specified Abs Key. 146 | bool HasSpecificAbs(int device_fd, unsigned int key) { 147 | size_t nchar = ABS_MAX / 8 + 1; 148 | unsigned char bits[nchar]; 149 | ioctl(device_fd, EVIOCGBIT(EV_ABS, sizeof(bits)), &bits); 150 | return bits[key / 8] & (1 << (key % 8)); 151 | } 152 | 153 | ///Determine if a event has specified Input Prop. 154 | bool HasSpecificProp(int device_fd, unsigned int prop) { 155 | size_t nchar = INPUT_PROP_MAX / 8 + 1; 156 | unsigned char bits[nchar]; 157 | ioctl(device_fd, EVIOCGPROP(sizeof(bits)), &bits); 158 | return bits[prop / 8] & (1 << (prop % 8)); 159 | } 160 | 161 | ///Determine if a path exist and is a character input device. 162 | int isCharDevice(const char* path) { 163 | struct stat st; 164 | if (stat(path, &st) == -1) { 165 | return 0; // Not a character device or does not exist 166 | } 167 | 168 | if (S_ISCHR(st.st_mode)) { 169 | return 1; // Is a character device 170 | } 171 | 172 | return 0; // Not a character device 173 | } 174 | 175 | ///Find Current Input Device 176 | int getTouchDevice() { 177 | int fd = -1; 178 | struct dirent *entry; 179 | const char *input_path = "/dev/input"; 180 | 181 | DIR *dir = opendir(input_path); 182 | if(!dir) { 183 | return -1; 184 | } 185 | 186 | while((entry = readdir(dir))) { 187 | if (!strstr(entry->d_name, "event")) 188 | continue; 189 | 190 | char devname[PATH_MAX]; 191 | snprintf(devname, sizeof(devname), "%s/%s", input_path, entry->d_name); 192 | 193 | if(!isCharDevice(devname)){ 194 | continue; 195 | } 196 | 197 | fd = open(devname, O_RDONLY); 198 | if(fd < 0) { 199 | continue; 200 | } 201 | 202 | if(!HasSpecificAbs(fd, ABS_MT_SLOT)) { 203 | continue; 204 | } 205 | 206 | if(!HasSpecificAbs(fd, ABS_MT_TRACKING_ID)){ 207 | continue; 208 | } 209 | 210 | if(!HasSpecificAbs(fd, ABS_MT_POSITION_X)) { 211 | continue; 212 | } 213 | 214 | if(!HasSpecificProp(fd, INPUT_PROP_DIRECT)) { 215 | continue; 216 | } 217 | 218 | break; 219 | } 220 | 221 | closedir(dir); 222 | 223 | return fd; 224 | } 225 | 226 | ///Write Input Event to Specified Fd 227 | void writeEvent(int ifd, int type, int code, int value) { 228 | struct input_event inputEvent = {}; 229 | 230 | inputEvent.type = type; 231 | inputEvent.code = code; 232 | inputEvent.value = value; 233 | 234 | write(ifd, &inputEvent, sizeof(input_event)); 235 | } 236 | 237 | ///----------Fake Touch Input-----------/// 238 | ///Touch Contact Struct 239 | struct TouchContact { 240 | int posX = -1; 241 | int posY = -1; 242 | bool enabled = false; 243 | }; 244 | 245 | #define FAKE_CONTACT 9 246 | int touchXMin; 247 | int touchXMax; 248 | int touchYMin; 249 | int touchYMax; 250 | int maxContacts; 251 | int displayWidth; 252 | int displayHeight; 253 | int touchDeviceFd = -1; 254 | int uInputTouchFd = -1; 255 | int swipeInterval = 10; 256 | int swipeFrameTime = 15; 257 | bool isBtnDown = false; 258 | bool touchSend = false; 259 | bool touchStart = false; 260 | std::mutex touchSynMtx; 261 | Channel touchChannel; 262 | struct TouchContact *contacts = NULL; 263 | 264 | ///Reading Touch Inputs 265 | void eventReaderThread(int fd) { 266 | int currSlot = 0; 267 | bool hasSyn = false; 268 | struct input_event evt; 269 | 270 | //Check TouchChannel 271 | if(touchChannel.is_closed()){ 272 | return; 273 | } 274 | 275 | //printf("-------------------------------------\n"); 276 | 277 | while (read(fd, &evt, sizeof(evt))) { 278 | //Contact Data Sync 279 | touchSynMtx.lock(); 280 | 281 | switch (evt.type) { 282 | case EV_SYN: 283 | if(evt.code == SYN_REPORT){ 284 | hasSyn = true; 285 | //printf("SYN_REPORT\n"); 286 | } 287 | break; 288 | case EV_KEY: 289 | if (evt.code == BTN_TOUCH) { 290 | //printf("BTN_TOUCH: %s\n", (evt.value == 1) ? "DOWN" : "UP"); 291 | } 292 | break; 293 | case EV_ABS: 294 | switch (evt.code) { 295 | case ABS_MT_SLOT: 296 | currSlot = evt.value; 297 | //printf("ABS_MT_SLOT: %d\n", evt.value); 298 | break; 299 | case ABS_MT_TRACKING_ID: 300 | contacts[currSlot].enabled = evt.value != -1; 301 | //printf("ABS_MT_TRACKING_ID: %d | Slot: %d\n", evt.value, currSlot); 302 | break; 303 | case ABS_MT_POSITION_X: 304 | contacts[currSlot].posX = evt.value; 305 | //printf("ABS_MT_POSITION_X: %d | Slot: %d\n", evt.value, currSlot); 306 | break; 307 | case ABS_MT_POSITION_Y: 308 | contacts[currSlot].posY = evt.value; 309 | //printf("ABS_MT_POSITION_Y: %d | Slot: %d\n", evt.value, currSlot); 310 | break; 311 | } 312 | break; 313 | } 314 | 315 | touchSynMtx.unlock(); 316 | 317 | if (hasSyn) { 318 | touchChannel.put(true); 319 | hasSyn = false; 320 | //printf("-------------------------------------\n"); 321 | } 322 | } 323 | } 324 | 325 | ///Writing Touch Inputs 326 | void eventWriterThread(int ufd){ 327 | while(!touchChannel.is_closed()){ 328 | bool sync; 329 | while(touchChannel.get(sync) && sync){ 330 | //Contact Data Sync 331 | touchSynMtx.lock(); 332 | 333 | int nextSlot = 0; 334 | 335 | for (int i = 0; i < maxContacts; i++) { 336 | if(contacts[i].enabled && contacts[i].posX > 0 && contacts[i].posY > 0){ 337 | writeEvent(ufd, EV_ABS, ABS_MT_POSITION_X, contacts[i].posX); 338 | writeEvent(ufd, EV_ABS, ABS_MT_POSITION_Y, contacts[i].posY); 339 | writeEvent(ufd, EV_ABS, ABS_MT_TRACKING_ID, i); 340 | writeEvent(ufd, EV_SYN, SYN_MT_REPORT, 0x0); 341 | 342 | nextSlot++; 343 | } 344 | } 345 | 346 | if(nextSlot == 0 && isBtnDown){ //Button Up 347 | isBtnDown = false; 348 | writeEvent(ufd, EV_SYN, SYN_MT_REPORT, 0x0); 349 | writeEvent(ufd, EV_KEY, BTN_TOUCH, 0x0); 350 | } else if(nextSlot == 1 && !isBtnDown){ //Button Down 351 | isBtnDown = true; 352 | writeEvent(ufd, EV_KEY, BTN_TOUCH, 0x1); 353 | } 354 | 355 | writeEvent(ufd, EV_SYN, SYN_REPORT, 0x0); 356 | 357 | touchSynMtx.unlock(); 358 | } 359 | } 360 | } 361 | 362 | void sendTouchMove(int x, int y) { 363 | if(!touchStart){ 364 | return; 365 | } 366 | 367 | if(!touchSend){ 368 | touchSend = true; 369 | } 370 | 371 | //Contact Data Sync 372 | touchSynMtx.lock(); 373 | 374 | contacts[FAKE_CONTACT].posX = (x * touchXMax / displayWidth) + touchXMin; 375 | contacts[FAKE_CONTACT].posY = (y * touchYMax / displayHeight) + touchYMin; 376 | contacts[FAKE_CONTACT].enabled = true; 377 | 378 | touchSynMtx.unlock(); 379 | 380 | touchChannel.put(true); 381 | } 382 | 383 | void sendTouchUp() { 384 | if(!touchStart || !touchSend){ 385 | return; 386 | } 387 | 388 | touchSend = false; 389 | 390 | //Contact Data Sync 391 | touchSynMtx.lock(); 392 | 393 | contacts[FAKE_CONTACT].posX = -1; 394 | contacts[FAKE_CONTACT].posY = -1; 395 | contacts[FAKE_CONTACT].enabled = false; 396 | 397 | touchSynMtx.unlock(); 398 | 399 | touchChannel.put(true); 400 | } 401 | 402 | void touchInputStart(int width, int height) { 403 | if(!touchStart){ 404 | touchDeviceFd = getTouchDevice(); 405 | 406 | if(touchDeviceFd < 0){ 407 | perror("Unable to find touch device\n"); 408 | return; 409 | } else { 410 | uInputTouchFd = createUInput(touchDeviceFd); 411 | 412 | if(uInputTouchFd < 0){ 413 | perror("Unable to create virtual touch device\n"); 414 | return; 415 | } 416 | 417 | //Init Things 418 | displayWidth = width; 419 | displayHeight = height; 420 | 421 | touchXMin = absX.minimum; 422 | touchXMax = absX.maximum - absX.minimum + 1; 423 | touchYMin = absY.minimum; 424 | touchYMax = absY.maximum - absY.minimum + 1; 425 | 426 | //Set Default Values in Touch Contacts Array 427 | maxContacts = absSlot.maximum + 1; 428 | contacts = (struct TouchContact *)malloc((maxContacts) * sizeof(struct TouchContact)); 429 | for (int i = 0; i < maxContacts; i++) { 430 | contacts[i].posX = -1; 431 | contacts[i].posY = -1; 432 | contacts[i].enabled = false; 433 | } 434 | 435 | std::thread(eventReaderThread, touchDeviceFd).detach(); 436 | std::thread(eventWriterThread, uInputTouchFd).detach(); 437 | 438 | touchStart = true; 439 | } 440 | } 441 | } 442 | 443 | void touchInputStop() { 444 | if(touchStart && touchDeviceFd > -1 && uInputTouchFd > -1){ 445 | touchChannel.close(); 446 | 447 | close(touchDeviceFd); 448 | close(uInputTouchFd); 449 | free(contacts); 450 | 451 | touchDeviceFd = -1; 452 | uInputTouchFd = -1; 453 | contacts = NULL; 454 | 455 | touchStart = false; 456 | } 457 | } 458 | 459 | ///Touch Test Code 460 | void genMovePoints(int StartX, int StartY, int EndX, int EndY){ 461 | int minPointCount = 2; 462 | int maxMoveDistance = 10; 463 | 464 | float dX = EndX - StartX; 465 | float dY = EndY - StartY; 466 | 467 | int xCount = abs((int)dX / maxMoveDistance); 468 | int yCount = abs((int)dY / maxMoveDistance); 469 | 470 | int count = (xCount > yCount) ? xCount : yCount; 471 | count = (count > minPointCount) ? count : minPointCount; 472 | 473 | for(int i=0; i < count; i++){ 474 | sendTouchMove((int)(StartX + (dX / count) * i), (int)(StartY + (dY / count) * i)); 475 | std::this_thread::sleep_for(std::chrono::milliseconds(swipeInterval)); 476 | } 477 | } 478 | 479 | void Swipe(int StartX, int StartY, int EndX, int EndY) { 480 | sendTouchMove(StartX, StartY); 481 | std::this_thread::sleep_for(std::chrono::milliseconds(swipeInterval)); 482 | 483 | genMovePoints(StartX, StartY, EndX, EndY); 484 | 485 | sendTouchMove(EndX, EndY); 486 | std::this_thread::sleep_for(std::chrono::milliseconds(swipeInterval)); 487 | 488 | sendTouchUp(); 489 | std::this_thread::sleep_for(std::chrono::milliseconds(swipeFrameTime)); 490 | } 491 | 492 | void Test() { 493 | int x = 746; 494 | int y = 1064; 495 | int nx = 400; 496 | int ny = 1408; 497 | 498 | //Using Common Display Resolution, 2340x1080 499 | touchInputStart(1080, 2340);//Start TouchInput 500 | 501 | std::this_thread::sleep_for(std::chrono::milliseconds(3000)); 502 | 503 | Swipe(x, y, x, ny); 504 | 505 | std::this_thread::sleep_for(std::chrono::milliseconds(3000)); 506 | 507 | Swipe(nx, y, x, ny); 508 | 509 | std::this_thread::sleep_for(std::chrono::milliseconds(3000)); 510 | 511 | Swipe(x, ny, x, y); 512 | 513 | std::this_thread::sleep_for(std::chrono::milliseconds(3000)); 514 | 515 | Swipe(x, ny, nx, y); 516 | 517 | std::this_thread::sleep_for(std::chrono::milliseconds(3000)); 518 | 519 | while(1){ 520 | } 521 | } 522 | } 523 | 524 | #endif //TOUCHINPUT_H -------------------------------------------------------------------------------- /C++/jni/kmods.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | int main(int argc, char *argv[]) { 6 | TouchInput::Test(); 7 | return 0; 8 | } -------------------------------------------------------------------------------- /C++/libs/arm64-v8a/touchtest: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kp7742/TouchSimulation/e904cf1faa6be0e9b8b2849b8aeacda12034174c/C++/libs/arm64-v8a/touchtest -------------------------------------------------------------------------------- /C++/libs/armeabi-v7a/touchtest: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kp7742/TouchSimulation/e904cf1faa6be0e9b8b2849b8aeacda12034174c/C++/libs/armeabi-v7a/touchtest -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Kuldip Patel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Touch Simulation 2 | Touch Simulation is program to simulate Touch Input in android devices using Virtual Display with UInput interface of Android(Linux) kernel. 3 | 4 | There are 2 variants made in Golang and C++. 5 | 6 | ## Features 7 | - Generate random data for uinput device. 8 | - Bridges Type-B device to Type-A device. 9 | - Simulate Original Touch Screen data. 10 | - Support 1 Touch Simulation point. 11 | - Test Program to check simulation. 12 | 13 | ## Notes 14 | - Not every device support directly, Modification may need. 15 | - Need either root access or adb shell. 16 | 17 | ## How to Build Go variant 18 | - Clone this repo. 19 | - Install Android NDK and Go Binaries, if not already. 20 | - Open bash in project directory and Execute build.sh script. 21 | - Output will be in bin directory. 22 | - Precompiled Binaries: [HERE](https://github.com/kp7742/TouchSimulation/tree/main/bin/) 23 | 24 | ## How to Build C++ variant 25 | - Clone this repo. 26 | - Install Android NDK, if not already. 27 | - Open Shell/CMD in C++ directory. 28 | - Drag ndk-build from NDK in Shell or CMD and then Execute. 29 | - Output will be in libs directory. 30 | - Precompiled Binaries: [HERE](https://github.com/kp7742/TouchSimulation/tree/main/C++/libs/) 31 | 32 | ## Sources 33 | - [Linux Multi Touch Protocol](https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt) 34 | - [Android Touch Devices](https://source.android.com/devices/input/touch-devices) 35 | - [Linux UInput](https://www.kernel.org/doc/html/v4.12/input/uinput.html) 36 | 37 | ## Credits 38 | - [uinput](https://github.com/bendahl/uinput): UInput Wrappers 39 | - [go-evdev](https://github.com/holoplot/go-evdev): InputEvent Definitions 40 | - [golang-evdev](https://github.com/dddpaul/golang-evdev): IOCTL Definitions 41 | - [Golang-evdev](https://github.com/gvalkov/golang-evdev): EVDEV Implementation 42 | 43 | ## Technlogy Communication 44 | > Email: patel.kuldip91@gmail.com 45 | -------------------------------------------------------------------------------- /TouchInput.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "fmt" 7 | "os" 8 | "syscall" 9 | "time" 10 | "unsafe" 11 | 12 | "github.com/lunixbochs/struc" 13 | ) 14 | 15 | type TypeMode int 16 | 17 | const ( 18 | TYPEA TypeMode = iota 19 | TYPEARND 20 | TYPEB 21 | ) 22 | 23 | const ( 24 | fakeContact = 9 25 | ) 26 | 27 | var ( 28 | currMode TypeMode 29 | 30 | touchSend = false 31 | touchStart = false 32 | 33 | displayWidth int32 34 | displayHeight int32 35 | 36 | fakeTouchMajor int32 = -1 37 | fakeTouchMinor int32 = -1 38 | fakeWidthMajor int32 = -1 39 | fakeWidthMinor int32 = -1 40 | fakeOrientation int32 = -1 41 | fakePressure int32 = -1 42 | 43 | syncChannel chan bool 44 | stopChannel chan bool 45 | 46 | touchDevice *InputDevice 47 | uInputTouch *InputDevice 48 | 49 | touchContactsA []TouchContactA 50 | touchContactsB []TouchContactB 51 | ) 52 | 53 | ///----------Touch Contacts-----------/// 54 | 55 | // TouchContact Touch Contact Struct 56 | type TouchContactA struct { 57 | PosX int32 58 | PosY int32 59 | Active bool 60 | } 61 | 62 | // TouchContact Touch Contact Struct 63 | type TouchContactB struct { 64 | TouchMajor int32 65 | TouchMinor int32 66 | WidthMajor int32 67 | WidthMinor int32 68 | Orientation int32 69 | PositionX int32 70 | PositionY int32 71 | TrackingId int32 72 | Pressure int32 73 | 74 | Active bool 75 | TUpdate bool 76 | TMAUpdate bool 77 | TMIUpdate bool 78 | WMAUpdate bool 79 | WMIUpdate bool 80 | OriUpdate bool 81 | PosXUpdate bool 82 | PosYUpdate bool 83 | TrackUpdate bool 84 | PressUpdate bool 85 | } 86 | 87 | ///----------Touch Management Interface-----------/// 88 | 89 | // Read Input Event from Input Device 90 | func readInputEvent(f *os.File) (InputEvent, error) { 91 | event := InputEvent{} 92 | buffer := make([]byte, unsafe.Sizeof(InputEvent{})) 93 | 94 | _, err := f.Read(buffer) 95 | if err != nil { 96 | return event, err 97 | } 98 | 99 | err = binary.Read(bytes.NewBuffer(buffer), binary.LittleEndian, &event) 100 | if err != nil { 101 | return event, err 102 | } 103 | 104 | return event, nil 105 | } 106 | 107 | func inputEventToBytes(event InputEvent) []byte { 108 | var buf bytes.Buffer 109 | _ = struc.PackWithOptions(&buf, &event, &struc.Options{Order: binary.LittleEndian}) 110 | return buf.Bytes() 111 | } 112 | 113 | // Write Input Event to Specified Fd 114 | func writeEvent(f *os.File, Type, Code uint16, Value int32) { 115 | _, _ = f.Write(inputEventToBytes(InputEvent{ 116 | Time: syscall.Timeval{ 117 | Sec: 0, 118 | Usec: 0, 119 | }, 120 | Type: Type, 121 | Code: Code, 122 | Value: Value, 123 | })) 124 | } 125 | 126 | // Reading Touch Inputs from TypeA event 127 | func eventReaderA() { 128 | var currSlot int32 = 0 129 | 130 | fmt.Printf("-------------------------------------\n") 131 | 132 | for { 133 | select { 134 | case <-stopChannel: 135 | return 136 | default: 137 | } 138 | 139 | inputEvent, err := readInputEvent(touchDevice.File) 140 | if err != nil { 141 | fmt.Printf("input read error\n") 142 | break 143 | } 144 | 145 | hasSyn := false 146 | 147 | switch inputEvent.Type { 148 | case evSyn: 149 | if inputEvent.Code == synReport { 150 | hasSyn = true 151 | fmt.Printf("SYN_REPORT\n") 152 | } 153 | break 154 | case evKey: 155 | if inputEvent.Code == btnTouch { 156 | touchType := "UP" 157 | if inputEvent.Value == 1 { 158 | touchType = "DOWN" 159 | } 160 | fmt.Printf("BTN_TOUCH: %s\n", touchType) 161 | } 162 | break 163 | case evAbs: 164 | switch inputEvent.Code { 165 | case absMtSlot: 166 | currSlot = inputEvent.Value 167 | fmt.Printf("ABS_MT_SLOT: %d\n", inputEvent.Value) 168 | break 169 | case absMtTrackingId: 170 | touchContactsA[currSlot].Active = inputEvent.Value != -1 171 | fmt.Printf("ABS_MT_TRACKING_ID: %d | Slot: %d\n", inputEvent.Value, currSlot) 172 | break 173 | case absMtPositionX: 174 | touchContactsA[currSlot].PosX = inputEvent.Value 175 | fmt.Printf("ABS_MT_POSITION_X: %d | Slot: %d\n", inputEvent.Value, currSlot) 176 | break 177 | case absMtPositionY: 178 | touchContactsA[currSlot].PosY = inputEvent.Value 179 | fmt.Printf("ABS_MT_POSITION_Y: %d | Slot: %d\n", inputEvent.Value, currSlot) 180 | break 181 | } 182 | break 183 | } 184 | 185 | if hasSyn { 186 | syncChannel <- true 187 | fmt.Printf("-------------------------------------\n") 188 | } 189 | } 190 | } 191 | 192 | // Writing Touch Inputs to TypeA event 193 | func eventDispatcherA() { 194 | var isBtnDown bool = false 195 | 196 | for { 197 | select { 198 | case <-stopChannel: 199 | return 200 | default: 201 | } 202 | 203 | select { 204 | case <-syncChannel: 205 | { 206 | nextSlot := 0 207 | 208 | for idx, contact := range touchContactsA { 209 | if contact.Active && contact.PosX > 0 && contact.PosY > 0 { 210 | writeEvent(uInputTouch.File, evAbs, absMtPositionX, contact.PosX) 211 | writeEvent(uInputTouch.File, evAbs, absMtPositionY, contact.PosY) 212 | writeEvent(uInputTouch.File, evAbs, absMtTrackingId, int32(idx)) 213 | writeEvent(uInputTouch.File, evSyn, synMtReport, 0) 214 | 215 | nextSlot++ 216 | } 217 | } 218 | 219 | if nextSlot == 0 && isBtnDown { //Button Up 220 | isBtnDown = false 221 | writeEvent(uInputTouch.File, evSyn, synMtReport, 0) 222 | writeEvent(uInputTouch.File, evKey, btnTouch, 0) 223 | } else if nextSlot > 0 && !isBtnDown { //Button Down 224 | isBtnDown = true 225 | writeEvent(uInputTouch.File, evKey, btnTouch, 1) 226 | } 227 | 228 | writeEvent(uInputTouch.File, evSyn, synReport, 0) 229 | } 230 | default: 231 | } 232 | } 233 | } 234 | 235 | // Reading Touch Inputs from TypeB event 236 | func eventReaderB() { 237 | var currSlot int32 = 0 238 | 239 | fmt.Printf("-------------------------------------\n") 240 | 241 | for { 242 | select { 243 | case <-stopChannel: 244 | return 245 | default: 246 | } 247 | 248 | inputEvent, err := readInputEvent(touchDevice.File) 249 | if err != nil { 250 | fmt.Printf("input read error\n") 251 | break 252 | } 253 | 254 | hasSyn := false 255 | 256 | switch inputEvent.Type { 257 | case evSyn: 258 | if inputEvent.Code == synReport { 259 | hasSyn = true 260 | fmt.Printf("SYN_REPORT\n") 261 | } 262 | break 263 | case evKey: 264 | if inputEvent.Code == btnTouch { 265 | touchType := "UP" 266 | if inputEvent.Value == 1 { 267 | touchType = "DOWN" 268 | } 269 | fmt.Printf("BTN_TOUCH: %s\n", touchType) 270 | } 271 | break 272 | case evAbs: 273 | switch inputEvent.Code { 274 | case absMtSlot: 275 | currSlot = inputEvent.Value 276 | fmt.Printf("ABS_MT_SLOT: %d\n", inputEvent.Value) 277 | break 278 | case absMtTouchMajor: 279 | // The length of the major axis of the contact. The length should be given in surface units. 280 | // If the surface has an X times Y resolution, the largest possible value of ABS_MT_TOUCH_MAJOR is sqrt(X^2 + Y^2), the diagonal 281 | if touchContactsB[currSlot].Active { 282 | touchContactsB[currSlot].TUpdate = true 283 | touchContactsB[currSlot].TMAUpdate = true 284 | touchContactsB[currSlot].TouchMajor = inputEvent.Value 285 | } 286 | fmt.Printf("ABS_MT_TOUCH_MAJOR: %d | Slot: %d\n", inputEvent.Value, currSlot) 287 | break 288 | case absMtTouchMinor: 289 | // The length, in surface units, of the minor axis of the contact. If the contact is circular, this event can be omitted 290 | if touchContactsB[currSlot].Active { 291 | touchContactsB[currSlot].TUpdate = true 292 | touchContactsB[currSlot].TMIUpdate = true 293 | touchContactsB[currSlot].TouchMinor = inputEvent.Value 294 | } 295 | fmt.Printf("ABS_MT_TOUCH_MINOR: %d | Slot: %d\n", inputEvent.Value, currSlot) 296 | break 297 | case absMtWidthMajor: 298 | // The length, in surface units, of the major axis of the approaching tool. This should be understood as the size of the tool itself. 299 | // The orientation of the contact and the approaching tool are assumed to be the same 300 | if touchContactsB[currSlot].Active { 301 | touchContactsB[currSlot].TUpdate = true 302 | touchContactsB[currSlot].WMAUpdate = true 303 | touchContactsB[currSlot].WidthMajor = inputEvent.Value 304 | } 305 | fmt.Printf("ABS_MT_WIDTH_MAJOR: %d | Slot: %d\n", inputEvent.Value, currSlot) 306 | break 307 | case absMtWidthMinor: 308 | // The length, in surface units, of the minor axis of the approaching tool. Omit if circular [4]. 309 | // The above four values can be used to derive additional information about the contact. 310 | // The ratio ABS_MT_TOUCH_MAJOR / ABS_MT_WIDTH_MAJOR approximates the notion of pressure. 311 | // The fingers of the hand and the palm all have different characteristic widths. 312 | if touchContactsB[currSlot].Active { 313 | touchContactsB[currSlot].TUpdate = true 314 | touchContactsB[currSlot].WMIUpdate = true 315 | touchContactsB[currSlot].WidthMinor = inputEvent.Value 316 | } 317 | fmt.Printf("ABS_MT_WIDTH_MINOR: %d | Slot: %d\n", inputEvent.Value, currSlot) 318 | break 319 | case absMtOrientation: 320 | // The orientation of the touching ellipse. The value should describe a signed quarter of a revolution clockwise around the touch center. 321 | // The signed value range is arbitrary, but zero should be returned for an ellipse aligned with the Y axis (north) of the surface, 322 | // a negative value when the ellipse is turned to the left, and a positive value when the ellipse is turned to the right. 323 | // When aligned with the X axis in the positive direction, the range max should be returned; when aligned with the X axis in the negative direction, 324 | // the range -max should be returned. 325 | // Touch ellipsis are symmetrical by default. For devices capable of true 360 degree orientation, the reported orientation must exceed the range max 326 | // to indicate more than a quarter of a revolution. For an upside-down finger, range max * 2 should be returned. 327 | // Orientation can be omitted if the touch area is circular, or if the information is not available in the kernel driver. 328 | // Partial orientation support is possible if the device can distinguish between the two axis, but not (uniquely) any values in between. 329 | // In such cases, the range of ABS_MT_ORIENTATION should be [0, 1] [4]. 330 | if touchContactsB[currSlot].Active { 331 | touchContactsB[currSlot].TUpdate = true 332 | touchContactsB[currSlot].OriUpdate = true 333 | touchContactsB[currSlot].Orientation = inputEvent.Value 334 | } 335 | fmt.Printf("ABS_MT_ORIENTATION: %d | Slot: %d\n", inputEvent.Value, currSlot) 336 | break 337 | case absMtPositionX: 338 | // The surface X coordinate of the center of the touching ellipse. 339 | if touchContactsB[currSlot].Active { 340 | touchContactsB[currSlot].TUpdate = true 341 | touchContactsB[currSlot].PosXUpdate = true 342 | touchContactsB[currSlot].PositionX = inputEvent.Value 343 | } 344 | fmt.Printf("ABS_MT_POSITION_X: %d | Slot: %d\n", inputEvent.Value, currSlot) 345 | break 346 | case absMtPositionY: 347 | // The surface Y coordinate of the center of the touching ellipse. 348 | if touchContactsB[currSlot].Active { 349 | touchContactsB[currSlot].TUpdate = true 350 | touchContactsB[currSlot].PosYUpdate = true 351 | touchContactsB[currSlot].PositionY = inputEvent.Value 352 | } 353 | fmt.Printf("ABS_MT_POSITION_Y: %d | Slot: %d\n", inputEvent.Value, currSlot) 354 | break 355 | case absMtToolType: 356 | // The type of approaching tool. A lot of kernel drivers cannot distinguish between different tool types, such as a finger or a pen. 357 | // In such cases, the event should be omitted. 358 | // The protocol currently supports MT_TOOL_FINGER, MT_TOOL_PEN, and MT_TOOL_PALM [2]. For type B devices, this event is handled by input core; 359 | // drivers should instead use input_mt_report_slot_state(). A contact’s ABS_MT_TOOL_TYPE may change over time while still touching the device, 360 | // because the firmware may not be able to determine which tool is being used when it first appears. 361 | fmt.Printf("ABS_MT_TOOL_TYPE: %d | Slot: %d\n", inputEvent.Value, currSlot) 362 | break 363 | case absMtBlobId: 364 | // The BLOB_ID groups several packets together into one arbitrarily shaped contact. The sequence of points forms a polygon which defines the shape of the contact. 365 | // This is a low-level anonymous grouping for type A devices, and should not be confused with the high-level trackingID [5]. 366 | // Most type A devices do not have blob capability, so drivers can safely omit this event. 367 | fmt.Printf("ABS_MT_BLOB_ID: %d | Slot: %d\n", inputEvent.Value, currSlot) 368 | break 369 | case absMtTrackingId: 370 | // The TRACKING_ID identifies an initiated contact throughout its life cycle [5]. 371 | // The value range of the TRACKING_ID should be large enough to ensure unique identification of a contact maintained over an extended period of time. 372 | // For type B devices, this event is handled by input core; drivers should instead use input_mt_report_slot_state(). 373 | touchContactsB[currSlot].TUpdate = true 374 | touchContactsB[currSlot].TrackUpdate = true 375 | touchContactsB[currSlot].TrackingId = inputEvent.Value 376 | touchContactsB[currSlot].Active = inputEvent.Value != -1 377 | fmt.Printf("ABS_MT_TRACKING_ID: %d | Slot: %d\n", inputEvent.Value, currSlot) 378 | break 379 | case absMtPressure: 380 | // The pressure, in arbitrary units, on the contact area. May be used instead of TOUCH and WIDTH for pressure-based devices 381 | // or any device with a spatial signal intensity distribution. 382 | if touchContactsB[currSlot].Active { 383 | touchContactsB[currSlot].TUpdate = true 384 | touchContactsB[currSlot].PressUpdate = true 385 | touchContactsB[currSlot].Pressure = inputEvent.Value 386 | } 387 | fmt.Printf("ABS_MT_PRESSURE: %d | Slot: %d\n", inputEvent.Value, currSlot) 388 | break 389 | case absMtDistance: 390 | // The distance, in surface units, between the contact and the surface. Zero distance means the contact is touching the surface. 391 | // A positive number means the contact is hovering above the surface. 392 | fmt.Printf("ABS_MT_DISTANCE: %d | Slot: %d\n", inputEvent.Value, currSlot) 393 | break 394 | case absMtToolX: 395 | // The surface X coordinate of the center of the approaching tool. Omit if the device cannot distinguish between the intended touch point and the tool itself. 396 | fmt.Printf("ABS_MT_TOOL_X: %d | Slot: %d\n", inputEvent.Value, currSlot) 397 | break 398 | case absMtToolY: 399 | // The surface Y coordinate of the center of the approaching tool. Omit if the device cannot distinguish between the intended touch point and the tool itself. 400 | // The four position values can be used to separate the position of the touch from the position of the tool. 401 | // If both positions are present, the major tool axis points towards the touch point [1]. Otherwise, the tool axes are aligned with the touch axes. 402 | fmt.Printf("ABS_MT_TOOL_Y: %d | Slot: %d\n", inputEvent.Value, currSlot) 403 | break 404 | } 405 | break 406 | } 407 | 408 | if hasSyn { 409 | syncChannel <- true 410 | fmt.Printf("-------------------------------------\n") 411 | } 412 | } 413 | } 414 | 415 | // Writing Touch Inputs to TypeB device 416 | func eventDispatcherB() { 417 | var isBtnDown bool = false 418 | 419 | for { 420 | select { 421 | case <-stopChannel: 422 | return 423 | default: 424 | } 425 | 426 | select { 427 | case <-syncChannel: 428 | { 429 | activeSlots := 0 430 | 431 | for idx, contact := range touchContactsB { 432 | if contact.Active { 433 | activeSlots++ 434 | 435 | writeEvent(uInputTouch.File, evAbs, absMtSlot, int32(idx)) 436 | 437 | if contact.TUpdate { 438 | if contact.TrackUpdate { 439 | writeEvent(uInputTouch.File, evAbs, absMtTrackingId, contact.TrackingId) 440 | touchContactsB[idx].TrackUpdate = false 441 | } 442 | 443 | if contact.PosXUpdate { 444 | writeEvent(uInputTouch.File, evAbs, absMtPositionX, contact.PositionX) 445 | touchContactsB[idx].PosXUpdate = false 446 | } 447 | 448 | if contact.PosYUpdate { 449 | writeEvent(uInputTouch.File, evAbs, absMtPositionY, contact.PositionY) 450 | touchContactsB[idx].PosYUpdate = false 451 | } 452 | 453 | if contact.TMAUpdate { 454 | writeEvent(uInputTouch.File, evAbs, absMtTouchMajor, contact.TouchMajor) 455 | touchContactsB[idx].TMAUpdate = false 456 | } 457 | 458 | if contact.TMIUpdate { 459 | writeEvent(uInputTouch.File, evAbs, absMtTouchMinor, contact.TouchMinor) 460 | touchContactsB[idx].TMIUpdate = false 461 | } 462 | 463 | if contact.WMAUpdate { 464 | writeEvent(uInputTouch.File, evAbs, absMtWidthMajor, contact.WidthMajor) 465 | touchContactsB[idx].WMAUpdate = false 466 | } 467 | 468 | if contact.WMIUpdate { 469 | writeEvent(uInputTouch.File, evAbs, absMtWidthMinor, contact.WidthMinor) 470 | touchContactsB[idx].WMIUpdate = false 471 | } 472 | 473 | if contact.PressUpdate { 474 | writeEvent(uInputTouch.File, evAbs, absMtPressure, contact.Pressure) 475 | touchContactsB[idx].PressUpdate = false 476 | } 477 | 478 | if contact.OriUpdate { 479 | writeEvent(uInputTouch.File, evAbs, absMtOrientation, contact.Orientation) 480 | touchContactsB[idx].OriUpdate = false 481 | } 482 | 483 | touchContactsB[idx].TUpdate = false 484 | } 485 | } else if !contact.Active && contact.TrackUpdate { 486 | writeEvent(uInputTouch.File, evAbs, absMtSlot, int32(idx)) 487 | writeEvent(uInputTouch.File, evAbs, absMtTrackingId, -1) 488 | if touchDevice.hasPressure { 489 | writeEvent(uInputTouch.File, evAbs, absMtPressure, 0) 490 | } 491 | if touchDevice.hasOrientation { 492 | writeEvent(uInputTouch.File, evAbs, absMtOrientation, 0) 493 | } 494 | touchContactsB[idx].TrackUpdate = false 495 | touchContactsB[idx].TUpdate = false 496 | } 497 | } 498 | 499 | if activeSlots == 0 && isBtnDown { //Button Up 500 | isBtnDown = false 501 | writeEvent(uInputTouch.File, evKey, btnTouch, 0) 502 | } else if activeSlots > 0 && !isBtnDown { //Button Down 503 | isBtnDown = true // Button down state change here 504 | writeEvent(uInputTouch.File, evKey, btnTouch, 1) 505 | } 506 | 507 | writeEvent(uInputTouch.File, evSyn, synReport, 0) 508 | } 509 | default: 510 | } 511 | } 512 | } 513 | 514 | func touchInputSetup(mode TypeMode, width, height int32) bool { 515 | tDevs, err := getInputDevices() 516 | if err != nil { 517 | return false 518 | } 519 | 520 | if len(tDevs) < 1 { 521 | return false 522 | } 523 | return touchInputStart(mode, width, height, tDevs[0]) 524 | } 525 | 526 | func touchInputStart(mode TypeMode, width, height int32, inDev *InputDevice) bool { 527 | if !touchStart { 528 | currMode = mode 529 | 530 | //Init Things 531 | touchDevice = inDev 532 | displayWidth = width 533 | displayHeight = height 534 | 535 | syncChannel = make(chan bool) 536 | stopChannel = make(chan bool) 537 | 538 | if mode == TYPEA || mode == TYPEARND { 539 | //Setup TypeA UInput Touch Device 540 | if mode == TYPEARND { 541 | tsDev, err := newTypeADevRandom(inDev) 542 | if err != nil { 543 | return false 544 | } 545 | uInputTouch = tsDev 546 | } else { 547 | tsDev, err := newTypeADevSame(inDev) 548 | if err != nil { 549 | return false 550 | } 551 | uInputTouch = tsDev 552 | } 553 | 554 | //Set Default Values in Touch Contacts Array 555 | touchContactsA = make([]TouchContactA, touchDevice.Slots) 556 | for idx := range touchContactsA { 557 | touchContactsA[idx].PosX = -1 558 | touchContactsA[idx].PosY = -1 559 | touchContactsA[idx].Active = false 560 | } 561 | 562 | //Start Threads 563 | go eventReaderA() 564 | go eventDispatcherA() 565 | } else { 566 | //Setup TypeB UInput Touch Device 567 | tsDev, err := newTypeBDevSame(inDev) 568 | if err != nil { 569 | return false 570 | } 571 | uInputTouch = tsDev 572 | 573 | if touchDevice.hasTouchMajor { 574 | fakeTouchMajor = int32(float32(touchDevice.AbsInfos[absMtTouchMajor].Maximum) * 0.14) 575 | } 576 | if touchDevice.hasTouchMinor { 577 | fakeTouchMinor = int32(float32(touchDevice.AbsInfos[absMtTouchMinor].Maximum) * 0.10) 578 | } 579 | if touchDevice.hasWidthMajor { 580 | fakeWidthMajor = int32(float32(touchDevice.AbsInfos[absMtWidthMajor].Maximum) * 0.14) 581 | } 582 | if touchDevice.hasWidthMinor { 583 | fakeWidthMinor = int32(float32(touchDevice.AbsInfos[absMtWidthMinor].Maximum) * 0.10) 584 | } 585 | if touchDevice.hasOrientation { 586 | fakeOrientation = int32(float32(touchDevice.AbsInfos[absMtOrientation].Maximum) * 0.28) 587 | } 588 | if touchDevice.hasPressure { 589 | fakePressure = int32(float32(touchDevice.AbsInfos[absMtPressure].Maximum) * 0.35) 590 | } 591 | 592 | //Set Default Values in Touch Contacts Array 593 | touchContactsB = make([]TouchContactB, touchDevice.Slots) 594 | for idx := range touchContactsB { 595 | touchContactsB[idx].TouchMajor = -1 596 | touchContactsB[idx].TouchMinor = -1 597 | touchContactsB[idx].WidthMajor = -1 598 | touchContactsB[idx].WidthMinor = -1 599 | touchContactsB[idx].Orientation = -1 600 | touchContactsB[idx].PositionX = -1 601 | touchContactsB[idx].PositionY = -1 602 | touchContactsB[idx].TrackingId = -1 603 | touchContactsB[idx].Pressure = -1 604 | 605 | touchContactsB[idx].Active = false 606 | touchContactsB[idx].TUpdate = false 607 | touchContactsB[idx].TMAUpdate = false 608 | touchContactsB[idx].TMIUpdate = false 609 | touchContactsB[idx].WMAUpdate = false 610 | touchContactsB[idx].WMIUpdate = false 611 | touchContactsB[idx].OriUpdate = false 612 | touchContactsB[idx].PosXUpdate = false 613 | touchContactsB[idx].PosYUpdate = false 614 | touchContactsB[idx].TrackUpdate = false 615 | touchContactsB[idx].PressUpdate = false 616 | } 617 | 618 | //Start Threads 619 | go eventReaderB() 620 | go eventDispatcherB() 621 | } 622 | 623 | touchStart = true 624 | } 625 | return true 626 | } 627 | 628 | func touchInputStop() { 629 | if touchStart && touchDevice != nil { 630 | stopChannel <- true 631 | 632 | if uInputTouch != nil { 633 | _ = releaseDevice(uInputTouch.File) 634 | _ = uInputTouch.File.Close() 635 | } 636 | _ = touchDevice.Release() 637 | 638 | uInputTouch = nil 639 | touchDevice = nil 640 | 641 | touchStart = false 642 | } 643 | } 644 | 645 | ///----------Fake Touch Input-----------/// 646 | 647 | func sendTouchMove(x, y int32) { 648 | if !touchStart { 649 | return 650 | } 651 | 652 | if !touchSend { 653 | touchSend = true 654 | } 655 | 656 | x = (x * touchDevice.TouchXMax / displayWidth) + touchDevice.TouchXMin 657 | y = (y * touchDevice.TouchYMax / displayHeight) + touchDevice.TouchYMin 658 | 659 | if currMode == TYPEA { 660 | touchContactsA[fakeContact].PosX = x 661 | touchContactsA[fakeContact].PosY = y 662 | touchContactsA[fakeContact].Active = true 663 | } else { 664 | if touchDevice.hasTouchMajor { 665 | touchContactsB[fakeContact].TouchMajor = fakeTouchMajor 666 | touchContactsB[fakeContact].TMAUpdate = true 667 | } 668 | if touchDevice.hasTouchMinor { 669 | touchContactsB[fakeContact].TouchMinor = fakeTouchMinor 670 | touchContactsB[fakeContact].TMIUpdate = true 671 | } 672 | if touchDevice.hasWidthMajor { 673 | touchContactsB[fakeContact].WidthMajor = fakeWidthMajor 674 | touchContactsB[fakeContact].WMAUpdate = true 675 | } 676 | if touchDevice.hasWidthMinor { 677 | touchContactsB[fakeContact].WidthMinor = fakeWidthMinor 678 | touchContactsB[fakeContact].WMIUpdate = true 679 | } 680 | if touchDevice.hasOrientation { 681 | touchContactsB[fakeContact].Orientation = fakeOrientation 682 | touchContactsB[fakeContact].OriUpdate = true 683 | } 684 | if touchDevice.hasPressure { 685 | touchContactsB[fakeContact].Pressure = fakePressure 686 | touchContactsB[fakeContact].PressUpdate = true 687 | } 688 | if touchContactsB[fakeContact].TrackingId < 0 { 689 | touchContactsB[fakeContact].TrackingId = touchDevice.AbsInfos[absMtTrackingId].Maximum - 2 690 | touchContactsB[fakeContact].TrackUpdate = true 691 | } 692 | 693 | touchContactsB[fakeContact].PositionX = x 694 | touchContactsB[fakeContact].PositionY = y 695 | touchContactsB[fakeContact].PosXUpdate = true 696 | touchContactsB[fakeContact].PosYUpdate = true 697 | 698 | touchContactsB[fakeContact].Active = true 699 | touchContactsB[fakeContact].TUpdate = true 700 | } 701 | 702 | syncChannel <- true 703 | 704 | time.Sleep(15 * time.Millisecond) 705 | } 706 | 707 | func sendTouchUp() { 708 | if !touchStart || !touchSend { 709 | return 710 | } 711 | 712 | touchSend = false 713 | 714 | if currMode == TYPEA { 715 | touchContactsA[fakeContact].PosX = -1 716 | touchContactsA[fakeContact].PosY = -1 717 | touchContactsA[fakeContact].Active = false 718 | } else { 719 | if touchDevice.hasTouchMajor { 720 | touchContactsB[fakeContact].TouchMajor = -1 721 | } 722 | if touchDevice.hasTouchMinor { 723 | touchContactsB[fakeContact].TouchMinor = -1 724 | } 725 | if touchDevice.hasWidthMajor { 726 | touchContactsB[fakeContact].WidthMajor = -1 727 | } 728 | if touchDevice.hasWidthMinor { 729 | touchContactsB[fakeContact].WidthMinor = -1 730 | } 731 | if touchDevice.hasOrientation { 732 | touchContactsB[fakeContact].Orientation = 0 733 | touchContactsB[fakeContact].OriUpdate = true 734 | } 735 | if touchDevice.hasPressure { 736 | touchContactsB[fakeContact].Pressure = 0 737 | touchContactsB[fakeContact].PressUpdate = true 738 | } 739 | 740 | touchContactsB[fakeContact].TrackingId = -1 741 | touchContactsB[fakeContact].PositionX = -1 742 | touchContactsB[fakeContact].PositionY = -1 743 | touchContactsB[fakeContact].Active = false 744 | touchContactsB[fakeContact].TUpdate = true 745 | touchContactsB[fakeContact].TrackUpdate = true 746 | } 747 | 748 | syncChannel <- true 749 | 750 | time.Sleep(15 * time.Millisecond) 751 | } 752 | -------------------------------------------------------------------------------- /Uinput.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "errors" 7 | "os" 8 | "path/filepath" 9 | "syscall" 10 | "time" 11 | "unsafe" 12 | 13 | "github.com/lunixbochs/struc" 14 | ) 15 | 16 | // InputDevice A Linux input device 17 | type InputDevice struct { 18 | Name string 19 | Path string 20 | Slots int32 21 | Version int32 22 | TouchXMin int32 23 | TouchXMax int32 24 | TouchYMin int32 25 | TouchYMax int32 26 | Grabed bool 27 | hasTouchMajor bool 28 | hasTouchMinor bool 29 | hasWidthMajor bool 30 | hasWidthMinor bool 31 | hasOrientation bool 32 | hasPressure bool 33 | Dbits *[evCnt / 8]byte 34 | AbsBits *[absCnt / 8]byte 35 | KeyBits *[keyCnt / 8]byte 36 | PropBits *[inputPropCnt / 8]byte 37 | AbsInfos map[int]AbsInfo 38 | IID InputID 39 | File *os.File 40 | } 41 | 42 | // Grab the input device exclusively. 43 | func (dev *InputDevice) Grab() error { 44 | dev.Grabed = true 45 | return ioctl(dev.File.Fd(), EVIOCGRAB(), uintptr(1)) 46 | } 47 | 48 | // Release a grabbed input device. 49 | func (dev *InputDevice) Release() error { 50 | dev.Grabed = false 51 | return ioctl(dev.File.Fd(), EVIOCGRAB(), uintptr(0)) 52 | } 53 | 54 | // Determine if input device has specified Abs Key. 55 | func (dev *InputDevice) hasAbs(key int) bool { 56 | return dev.AbsBits[key/8]&(1< 0 { 182 | return ids, nil 183 | } else { 184 | return nil, errors.New("devices are not found") 185 | } 186 | } 187 | 188 | // Determine if a path exist and is a character input device. 189 | func isCharDevice(path string) bool { 190 | fi, err := os.Stat(path) 191 | 192 | if os.IsNotExist(err) { 193 | return false 194 | } 195 | 196 | return fi.Mode()&os.ModeCharDevice != 0 197 | } 198 | 199 | // Determine if a evbits has specified Event Type. 200 | func hasSpecificType(evbits *[96]byte, key int) bool { 201 | return evbits[key/8]&(1<