Skip to content

Commit

Permalink
feat: Implement touchpad relative input mode
Browse files Browse the repository at this point in the history
  • Loading branch information
Xtr126 committed Nov 20, 2023
1 parent e6ccf6b commit 4cd7684
Show file tree
Hide file tree
Showing 8 changed files with 328 additions and 99 deletions.
14 changes: 14 additions & 0 deletions app/Android.mk
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,20 @@ include $(BUILD_SHARED_LIBRARY)

include $(CLEAR_VARS)

LOCAL_MODULE := evdev_common
LOCAL_SRC_FILES := $(JNI_SRC_PATH)/evdev_common.cpp
include $(BUILD_SHARED_LIBRARY)

include $(CLEAR_VARS)

LOCAL_MODULE := touchpad_direct
LOCAL_SRC_FILES := $(JNI_SRC_PATH)/touchpad_direct.cpp
LOCAL_SHARED_LIBRARIES := evdev_common
include $(BUILD_SHARED_LIBRARY)

include $(CLEAR_VARS)

LOCAL_MODULE := touchpad_relative
LOCAL_SRC_FILES := $(JNI_SRC_PATH)/touchpad_relative.cpp
LOCAL_SHARED_LIBRARIES := evdev_common
include $(BUILD_SHARED_LIBRARY)
116 changes: 116 additions & 0 deletions app/src/main/cpp/evdev_common.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#include "mouse_cursor.h"
#include <thread>
#include <jni.h>
#include <vector>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/poll.h>
#include <linux/uinput.h>
#include <linux/input.h>
#include <linux/input-event-codes.h>
#include <cstdlib>
#include "evdev_common.h"

using std::string;

std::vector<string> ListInputDevices() {
const string input_directory = "/dev/input";
std::vector<string> filenames;
struct DIR * directory = opendir(input_directory.c_str());

struct dirent *entry;
while ((entry = readdir(directory))) {
if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
filenames.push_back(input_directory + "/" + entry->d_name);
}
}
return filenames;
}

std::vector<int> scanTouchpadDevices() {
std::vector<string> evdevNames = ListInputDevices();
std::vector<int> touchpadDeviceFds;

printf("I: Searching for touchpad devices...\n");
for (auto & evdev : evdevNames) {
int device_fd = open(evdev.c_str(), O_RDWR);
if (device_fd < 0) {
perror("opening device");
}

// Get device name
char dev_name[24];
if(!ioctl(device_fd, EVIOCGNAME(sizeof(dev_name) - 1), &dev_name)) {
perror("get device name");
close(device_fd);
continue;
}

if(!HasSpecificAbs(device_fd, ABS_X) || !HasSpecificAbs(device_fd, ABS_Y)) {
printf("%s does not have ABS_X or ABS_Y events\n", dev_name);
printf("%s: Not a touch device\n", dev_name);
close(device_fd);
continue;
}

if (!HasInputProp(device_fd, INPUT_PROP_POINTER)) {
printf("%s does not have INPUT_PROP_POINTER\n", dev_name);
printf("%s: Not a touchpad device\n", dev_name);
close(device_fd);
continue;
}


// Check for virtual devices
if (x_virtual_tablet == dev_name || x_virtual_mouse == dev_name) {
printf("skipping %s\n", dev_name);
close(device_fd);
continue;
}
else if (x_virtual_touch == dev_name) {
touchpadDeviceFds.clear();
return touchpadDeviceFds;
}

printf(" adding touchpad device...\n", evdev.c_str());
ioctl(device_fd, EVIOCGRAB, (void *)1);

touchpadDeviceFds.push_back(device_fd);
}
return touchpadDeviceFds;
}

bool HasSpecificAbs(int device_fd, unsigned int abs) {
unsigned long nchar = KEY_MAX / 8 + 1;
unsigned char bits[nchar];
// Get the bit fields of available abs events.
ioctl(device_fd, EVIOCGBIT(EV_ABS, sizeof(bits)), &bits);
return bits[abs/8] & (1 << (abs % 8));
}

bool HasSpecificKey(int device_fd, unsigned int key) {
unsigned long nchar = KEY_MAX / 8 + 1;
unsigned char bits[nchar];
// Get the bit fields of available keys.
ioctl(device_fd, EVIOCGBIT(EV_KEY, sizeof(bits)), &bits);
return bits[key/8] & (1 << (key % 8));
}

bool HasInputProp(int device_fd, unsigned int input_prop) {
unsigned long nchar = INPUT_PROP_MAX / 8 + 1;
unsigned char bits[nchar];
// Get the bit fields of available keys.
ioctl(device_fd, EVIOCGPROP(sizeof(bits)), &bits);
return bits[input_prop/8] & (1 << (input_prop % 8));
}

bool HasEventType(int device_fd, unsigned int type) {
unsigned long evbit = 0;
// Get the bit field of available event types.
ioctl(device_fd, EVIOCGBIT(0, sizeof(evbit)), &evbit);
return evbit & (1 << type);
}
16 changes: 16 additions & 0 deletions app/src/main/cpp/evdev_common.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using std::string;

const char *x_virtual_touch = "x-virtual-touch";
const char *x_virtual_mouse = "x-virtual-mouse";

std::vector<string> ListInputDevices();

std::vector<int> scanTouchpadDevices();

bool HasSpecificAbs(int device_fd, unsigned int abs);

bool HasSpecificKey(int device_fd, unsigned int key);

bool HasInputProp(int device_fd, unsigned int input_prop);

bool HasEventType(int device_fd, unsigned int type);
104 changes: 15 additions & 89 deletions app/src/main/cpp/touchpad_direct.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
#include <vector>
#include <thread>
#include "mouse_cursor.h"
#include "evdev_common.h"

using std::string;

struct input_event ie {};
const char *device_name = "x-virtual-touch";

std::atomic<bool> running = false;

Expand All @@ -28,28 +28,6 @@ std::thread looper;
std::vector<pollfd> poll_fds;
std::vector<int> uinput_fds;

std::vector<string> ListInputDevices() {
const string input_directory = "/dev/input";
std::vector<string> filenames;
DIR* directory = opendir(input_directory.c_str());

struct dirent *entry;
while ((entry = readdir(directory))) {
if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
filenames.push_back(input_directory + "/" + entry->d_name);
}
}
return filenames;
}

bool HasSpecificAbs(int device_fd, unsigned int abs) {
size_t nchar = KEY_MAX/8 + 1;
unsigned char bits[nchar];
// Get the bit fields of available abs events.
ioctl(device_fd, EVIOCGBIT(EV_ABS, sizeof(bits)), &bits);
return bits[abs/8] & (1 << (abs % 8));
}

void SetAbsInfoFrom(int device_fd, int uinput_fd) {
for(int abs_i = ABS_X; abs_i <= ABS_MAX; abs_i++) {
if(HasSpecificAbs(device_fd, abs_i)) {
Expand All @@ -65,22 +43,6 @@ void SetAbsInfoFrom(int device_fd, int uinput_fd) {
}
}

bool HasSpecificKey(int device_fd, unsigned int key) {
size_t nchar = KEY_MAX/8 + 1;
unsigned char bits[nchar];
// Get the bit fields of available keys.
ioctl(device_fd, EVIOCGBIT(EV_KEY, sizeof(bits)), &bits);
return bits[key/8] & (1 << (key % 8));
}

bool HasInputProp(int device_fd, unsigned int input_prop) {
size_t nchar = INPUT_PROP_MAX/8 + 1;
unsigned char bits[nchar];
// Get the bit fields of available keys.
ioctl(device_fd, EVIOCGPROP(sizeof(bits)), &bits);
return bits[input_prop/8] & (1 << (input_prop % 8));
}

void SetKeyBits(int device_fd, int uinput_fd) {
for(int key_i = BTN_MOUSE; key_i <= KEY_MAX; key_i++) {
if (HasSpecificKey(device_fd, key_i)) {
Expand All @@ -92,13 +54,6 @@ void SetKeyBits(int device_fd, int uinput_fd) {
}
}

bool HasEventType(int device_fd, unsigned int type) {
unsigned long evbit = 0;
// Get the bit field of available event types.
ioctl(device_fd, EVIOCGBIT(0, sizeof(evbit)), &evbit);
return evbit & (1 << type);
}


void SetEventTypeBits(int device_fd, int uinput_fd) {
for(int ev_i = EV_SYN; ev_i <= EV_MAX; ev_i++) {
Expand All @@ -121,7 +76,7 @@ int SetupUinputDevice(int device_fd) {

memset(&uinputSetup, 0, sizeof(uinputSetup));

strncpy(uinputSetup.name, device_name, strlen(device_name));
strncpy(uinputSetup.name, x_virtual_touch, strlen(x_virtual_touch));
uinputSetup.id.version = 1;
uinputSetup.id.bustype = BUS_VIRTUAL;
ioctl(uinput_fd, UI_DEV_SETUP, &uinputSetup);
Expand Down Expand Up @@ -153,50 +108,20 @@ JNIEXPORT void JNICALL
Java_xtr_keymapper_server_InputService_startTouchpadDirect(JNIEnv *env, jobject thiz) {
running = true;

std::vector<string> evdevNames = ListInputDevices();

poll_fds.clear();
uinput_fds.clear();

printf("I: Searching for touchpad devices...\n");
for (auto & evdev : evdevNames) {
int device_fd = open(evdev.c_str(), O_RDWR);
if (device_fd < 0) {
perror("opening device");
}

// Print device name
char dev_name[24];
if(ioctl(device_fd, EVIOCGNAME(sizeof(dev_name) - 1), &dev_name)) {
printf("device: %s\n", dev_name);
}

if (HasInputProp(device_fd, INPUT_PROP_POINTER)) {
printf("has INPUT_PROP_POINTER", dev_name);
} else {
close(device_fd);
continue;
}

// Ignore virtual tablet
if (strcmp(x_virtual_tablet, dev_name) == 0)
continue;

if (strcmp(device_name, dev_name) == 0)
return;

if(!HasSpecificAbs(device_fd, ABS_X) || !HasSpecificAbs(device_fd, ABS_Y)) {
continue;
}

printf(" adding touchpad device...\n", evdev.c_str());
std::vector<int> touchpadDeviceFds = scanTouchpadDevices();

ioctl(device_fd, EVIOCGRAB, (void *)1);

poll_fds.push_back(pollfd{device_fd, POLLIN, 0});
uinput_fds.push_back(SetupUinputDevice(device_fd));
for (auto & evdev : touchpadDeviceFds) {
poll_fds.push_back(pollfd{evdev, POLLIN, 0});
uinput_fds.push_back(SetupUinputDevice(evdev));
}
if (poll_fds.empty()) return;

if (poll_fds.empty()) {
printf("I: No touchpad devices found\n");
return;
}

looper = std::thread(start);
}
Expand All @@ -205,9 +130,10 @@ extern "C"
JNIEXPORT void JNICALL
Java_xtr_keymapper_server_InputService_stopTouchpadDirect(JNIEnv *env, jobject thiz) {
running = false;
for (auto & poll_fd : poll_fds) {
write(poll_fd.fd, &ie, sizeof(struct input_event));
}
for (auto & poll_fd : poll_fds) {
struct input_event ie {0, 0, EV_SYN, SYN_REPORT, 0};
write(poll_fd.fd, &ie, sizeof(struct input_event));
}
for (size_t i = 0; i < poll_fds.size(); i++) {
ioctl(uinput_fds[i], UI_DEV_DESTROY);
close(uinput_fds[i]);
Expand Down
Loading

0 comments on commit 4cd7684

Please sign in to comment.