summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordavidovski <david@sendula.com>2023-02-11 02:40:24 +0000
committerdavidovski <david@sendula.com>2023-02-11 02:40:24 +0000
commitec05f00c6c0681e10f06f6e16d9f9ea33acb4d8e (patch)
tree999173a016cf59ebd5000aa1957b8d15e0b3f0a9
Initial commit
-rw-r--r--Makefile6
-rw-r--r--cursorpen.c150
-rwxr-xr-xcursorpen.py98
3 files changed, 254 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..53e4d23
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,6 @@
+CC ?= gcc
+PROG = cursorpen
+FLAGS = -lX11 -lXtst -lXi -lpthread -ludev -levdev
+
+${PROG}: ${PROG}.o
+ ${CC} ${PROG}.c -o ${PROG} ${FLAGS}
diff --git a/cursorpen.c b/cursorpen.c
new file mode 100644
index 0000000..f3bda5a
--- /dev/null
+++ b/cursorpen.c
@@ -0,0 +1,150 @@
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/input.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <libevdev-1.0/libevdev/libevdev.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/XTest.h>
+
+#define SCALE 1.35
+
+struct abs_range {
+ int x_min;
+ int x_max;
+ int x_range;
+ int y_min;
+ int y_max;
+ int y_range;
+};
+
+int click_mouse(Display *dpy, struct input_event *ev) {
+ int btn;
+
+ if (ev->code == BTN_LEFT) {
+ btn = 1;
+ } else if (ev->code == BTN_RIGHT) {
+ btn = 2;
+ } else {
+ return 1;
+ }
+
+ if (ev->value < 2) {
+ printf("Click with value %d button: %d\n", ev->value, btn);
+ XTestFakeButtonEvent(dpy, btn, ev->value, CurrentTime);
+ }
+}
+
+int move_mouse(Display *dpy, float posx, float posy, int screenw, int screenh) {
+ float x, y;
+ //x = (screenw * posx) * SCALE;
+ //y = (screenh * posy) * SCALE;
+ x = ((screenw * posx) - (screenw*0.3)) * SCALE;
+ y = ((screenh * posy) - (screenh*0.1)) * SCALE;
+
+ Window root = DefaultRootWindow(dpy);
+ XWarpPointer(dpy, None, root, 0, 0, 0, 0, x, y);
+ XFlush(dpy);
+}
+
+int handle_event(struct input_event *ev, Display *dpy, struct abs_range abs_range, float *posx, float *posy, int sw, int sh) {
+ if (ev->type == EV_ABS) {
+ if (ev->code == ABS_X) {
+ *posx = (float) (ev->value - abs_range.x_min) / abs_range.x_range;
+ } else if (ev->code == ABS_Y) {
+ *posy = (float) (ev->value - abs_range.y_min) / abs_range.y_range;
+ }
+ } else if (ev->type == EV_KEY) {
+ click_mouse(dpy, ev);
+ }
+ move_mouse(dpy, *posx, *posy, sw, sh);
+
+ return 0;
+}
+
+
+Display *get_resolution(int *width, int *height) {
+ Display *dpy; int snum;
+ if(!(dpy = XOpenDisplay(0)))
+ fprintf(stderr, "cannot open display '%s'", XDisplayName(0));
+ snum = DefaultScreen(dpy);
+ *width = DisplayWidth(dpy, snum);
+ *height = DisplayHeight(dpy, snum);
+ return dpy;
+}
+
+
+XDevice *get_trackpad(Display *dpy) {
+ XDeviceInfo *devices;
+ XDevice *device;
+
+ Atom touchpad_type = 0;
+ touchpad_type = XInternAtom(dpy, XI_TOUCHPAD, True);
+
+ int ndevices = 10;
+ devices = XListInputDevices(dpy, &ndevices);
+
+ while (ndevices--) {
+ if (devices[ndevices].type == touchpad_type) {
+ device = XOpenDevice(dpy, devices[ndevices].id);
+ if (!device) {
+ fprintf(stderr, "Failed to open device '%s'.\n", devices[ndevices].name);
+ break;
+ }
+ printf("Opened device '%s'.\n", devices[ndevices].name);
+ }
+ }
+
+ XFreeDeviceList(devices);
+ return device;
+}
+
+int main() {
+ Display *dpy;
+ XDevice *device;
+ struct libevdev *dev;
+ struct input_event ev;
+ struct abs_range abs_range;
+
+ float posx, posy;
+ int sw, sh;
+
+ int fd, rc;
+
+ fd = open("/dev/input/event17", O_RDONLY|O_NONBLOCK);
+ if (fd < 0)
+ fprintf(stderr, "error: %d %s\n", errno, strerror(errno));
+ rc = libevdev_new_from_fd(fd, &dev);
+ if (rc < 0)
+ fprintf(stderr, "error: %d %s\n", -rc, strerror(-rc));
+
+ abs_range.x_min = libevdev_get_abs_minimum(dev, ABS_X);
+ abs_range.x_max = libevdev_get_abs_maximum(dev, ABS_X);
+ abs_range.y_min = libevdev_get_abs_minimum(dev, ABS_Y);
+ abs_range.y_max = libevdev_get_abs_maximum(dev, ABS_Y);
+
+ abs_range.x_range = abs_range.x_max - abs_range.x_min;
+ abs_range.y_range = abs_range.y_max - abs_range.y_min;
+
+ dpy = get_resolution(&sw, &sh);
+
+ device = get_trackpad(dpy);
+
+ do {
+ struct input_event ev;
+ rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL|LIBEVDEV_READ_FLAG_BLOCKING, &ev);
+ if (rc == LIBEVDEV_READ_STATUS_SYNC) {
+ //while (rc == LIBEVDEV_READ_STATUS_SYNC) {
+ //handle_event(&ev, dpy, abs_range, &posx, &posy, sw, sh);
+ //rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev);
+ //}
+ } else if (rc == LIBEVDEV_READ_STATUS_SUCCESS) {
+ handle_event(&ev, dpy, abs_range, &posx, &posy, sw, sh);
+ }
+ } while (rc == LIBEVDEV_READ_STATUS_SYNC || rc == LIBEVDEV_READ_STATUS_SUCCESS || rc == -EAGAIN);
+;
+ return 0;
+}
diff --git a/cursorpen.py b/cursorpen.py
new file mode 100755
index 0000000..fb9d3bb
--- /dev/null
+++ b/cursorpen.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python
+
+import sys
+from pynput.mouse import Button, Controller
+from screeninfo import get_monitors
+import subprocess as sp
+import evdev
+import contextlib
+import pyudev
+import screeninfo
+
+scale = 1.5
+
+@contextlib.contextmanager
+def lock_pointer(devname):
+ sp.call(['xinput', 'disable', devname])
+ try:
+ yield
+ finally:
+ sp.call(['xinput', 'enable', devname])
+
+def get_touchpad(udev):
+ for device in get_touchpads(udev):
+ dev_name = get_device_name(device).strip('"')
+ print('Using touchpad:', dev_name, file=sys.stderr)
+ try:
+ return evdev.InputDevice(device.device_node), dev_name
+ except PermissionError:
+ permission_error()
+ return None, None
+
+def get_touchpads(udev):
+ for device in udev.list_devices(ID_INPUT_TOUCHPAD='1'):
+ if device.device_node is not None and device.device_node.rpartition('/')[2].startswith('event'):
+ yield device
+
+
+def get_device_name(dev):
+ while dev is not None:
+ name = dev.properties.get('NAME')
+ if name:
+ return name
+ else:
+ dev = next(dev.ancestors, None)
+
+
+
+def move_mouse(posx, posy):
+ x = (monitor.width * posx - monitor.width*0.4) * scale
+ y = (monitor.height * posy - monitor.height*0.1) * scale
+ #x = (monitor.width * posx)
+ #y = (monitor.height * posy)
+ mouse.position = (x, y)
+
+def loop(touchpad):
+ x_absinfo = touchpad.absinfo(evdev.ecodes.ABS_X)
+ y_absinfo = touchpad.absinfo(evdev.ecodes.ABS_Y)
+ val_range = (x_absinfo.max - x_absinfo.min, y_absinfo.max - y_absinfo.min)
+
+ posx, posy = 0, 0
+ while True:
+ event = touchpad.read_one()
+ if event:
+ #print(posx, posy)
+ if event.type == evdev.ecodes.EV_ABS:
+ if event.code == evdev.ecodes.ABS_X:
+ posx = (event.value - x_absinfo.min) / val_range[0]
+ if event.code == evdev.ecodes.ABS_Y:
+ posy = (event.value - y_absinfo.min) / val_range[1]
+ move_mouse(posx, posy)
+
+ if event.type == evdev.ecodes.EV_KEY:
+ if event.code == evdev.ecodes.BTN_TOUCH and event.value == 0:
+ mouse.click(Button.left, 1)
+ if event.code == evdev.ecodes.BTN_LEFT:
+ if event.value:
+ mouse.press(Button.left)
+ else:
+ mouse.release(Button.left)
+ if event.code == evdev.ecodes.BTN_RIGHT:
+ if event.value:
+ mouse.press(Button.right)
+ else:
+ mouse.release(Button.right)
+
+
+
+mouse = Controller()
+monitor = get_monitors()[0]
+udev = pyudev.Context()
+touchpad, devname = get_touchpad(udev)
+print(touchpad.path)
+if touchpad is None:
+ print('No touchpad found', file=sys.stderr)
+ exit(1)
+
+with lock_pointer(devname):
+ loop(touchpad)