From 79e255d19db16be3702ca195b24ce85fc1fcbaed Mon Sep 17 00:00:00 2001 From: cannoli-fruit Date: Sat, 1 Aug 2026 23:26:59 -0400 Subject: [PATCH] Init --- .gitignore | 2 + Makefile | 35 +++ README.md | 19 ++ bldit.lua | 64 +++++ src/device_select.c | 147 ++++++++++++ src/device_select.h | 4 + src/main.c | 559 ++++++++++++++++++++++++++++++++++++++++++++ src/mouse.c | 56 +++++ src/mouse.h | 2 + src/util.c | 26 +++ src/util.h | 1 + 11 files changed, 915 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 bldit.lua create mode 100644 src/device_select.c create mode 100644 src/device_select.h create mode 100644 src/main.c create mode 100644 src/mouse.c create mode 100644 src/mouse.h create mode 100644 src/util.c create mode 100644 src/util.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b5a1a07 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build/* +kvm diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d9f1546 --- /dev/null +++ b/Makefile @@ -0,0 +1,35 @@ +CC = cc +CFLAGS = -O2 -fPIE + +TARGET = kvm +LIBS = -ludev -lX11 -lXrandr -lXext -lXfixes -lXft +INCLUDES = -I/usr/include/freetype2 + +SRC_DIR = src +BUILD_DIR = build + +SRC = $(wildcard $(SRC_DIR)/*.c) +OBJ = $(SRC:$(SRC_DIR)/%.c=$(BUILD_DIR)/%.o) + +PREFIX = /usr/local +BINDIR = $(PREFIX)/bin + +all: $(TARGET) + +$(TARGET): $(OBJ) + $(CC) $(OBJ) -o $@ $(LIBS) + +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c + @mkdir -p $(BUILD_DIR) + $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ + +install: $(TARGET) + install -Dm755 $(TARGET) $(BINDIR)/$(TARGET) + +uninstall: + rm -f $(BINDIR)/$(TARGET) + +clean: + rm -rf $(BUILD_DIR) $(TARGET) + +.PHONY: all install uninstall clean diff --git a/README.md b/README.md new file mode 100644 index 0000000..37ce499 --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# KVM +Kernel virtual mouse +KVM is a tool that allows you to use mouse input without reaching over to the mouse, using the kernel's input routines to be portable across X and wayland. +This is still early stage, if you wanna send patches message me on xmpp at cody@youra.monster + +## Keybinds: +Exit: q / esc +Move mouse: vim keys +Click: W, E, R (left, middle, and right respectively) +Scroll: M,',' +Horizontal Scroll: '.','/' +Tree selector: X +Exit tree selector: P +Tree selector navigate: YUIDFGCVB (buttons are on screen you can figure it out) + +## Current problems: +Tree selector mode is X only +Code could probably be cleaned up +No config diff --git a/bldit.lua b/bldit.lua new file mode 100644 index 0000000..f853d3c --- /dev/null +++ b/bldit.lua @@ -0,0 +1,64 @@ +bldit_version = "1.1.3" +package_version = "1.1.3" + +dependencies = {} + +exec_name = "kvm" +libs = "-Wall -Wextra -ludev -lX11 -lXrandr -lXext -lXfixes -lXft" +includes = "-Iudev -ggdb -I/usr/include/freetype2" +cc = "gcc" + +execute = function(str) + print(str) + return os.execute(str) +end + +targets = { + default = { + build = function() + e,h,c = execute("rm -fr build") + if c ~= 0 and c ~= nil then + print("Build Error") + print("GIVEN UP") + return c + end + e,h,c = execute("mkdir build") + if c ~= 0 and c ~= nil then + print("Build Error") + print("GIVEN UP") + return c + end + cmd = [[find src -type f -name "*.c" -print]] + p = io.popen(cmd) + assert(p, "File detection error, GIVEN UP") + + for f in p:lines() do + base = f:match("([^/]+)%.c$") + obj = "build/"..base..".o" + ccom = cc.." -c -O3 '"..f.."' -o '"..obj.."' "..includes + e,h,c = execute(ccom) + if c ~= 0 and c ~= nil then + print("Compilation Error") + print("GIVEN UP") + return c + end + end + p:close() + e,h,c = execute(cc.." build/*.o "..libs.." -o "..exec_name) + if c ~= 0 and c ~= nil then + print("Compilation Error") + print("GIVEN UP") + return c + end + return 0 + end, + install = function() + e,h,c = execute("cp "..exec_name.." /bin") + return c or 0 + end, + uninstall = function() + e,h,c = execute("rm /bin"..exec_name) + return c or 0 + end, + } +} diff --git a/src/device_select.c b/src/device_select.c new file mode 100644 index 0000000..2523cd4 --- /dev/null +++ b/src/device_select.c @@ -0,0 +1,147 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int menuSelectDevice(struct udev *udev, + struct udev_list_entry *devices, char *fpath) { + struct udev_list_entry *entry; + int pipe_in[2]; + int pipe_out[2]; + pipe(pipe_in); + pipe(pipe_out); + + pid_t pid = fork(); + + if (pid == 0) { + dup2(pipe_in[0], STDIN_FILENO); + dup2(pipe_out[1], STDOUT_FILENO); + close(pipe_in[1]); + close(pipe_out[0]); + // TODO: read config for menu program + execlp("dmenu", "dmenu", "-p", "Keyboard:", NULL); + perror("execlp"); + exit(1); + } + + close(pipe_in[0]); + close(pipe_out[1]); + + udev_list_entry_foreach(entry, devices) { + const char *path = + udev_list_entry_get_name(entry); + struct udev_device *dev = + udev_device_new_from_syspath( + udev, + path + ); + const char *devnode = + udev_device_get_devnode(dev); + + if (devnode) { + const char *keyboard = udev_device_get_property_value( + dev,"ID_INPUT_KEYBOARD"); + const char *mouse = udev_device_get_property_value( + dev, "ID_INPUT_MOUSE"); + if (mouse) continue; + if (!keyboard) continue; + + const char *vendor = udev_device_get_property_value + (dev, "ID_VENDOR"); + const char *model = udev_device_get_property_value + (dev, "ID_MODEL"); + dprintf(pipe_in[1], "%s %s %s\n", devnode, vendor, model); + + } + udev_device_unref(dev); + } + close(pipe_in[1]); + FILE *f = fdopen(pipe_out[0], "r"); + char menures[256]; + fscanf(f, "%255s", menures); + close(pipe_out[0]); + printf("Selected %s\n", menures); + FILE *kbd_f = fopen(fpath, "w"); + fputs(menures, kbd_f); + fclose(kbd_f); + + waitpid(pid, NULL, 0); + udev_list_entry_foreach(entry, devices) { + const char *path = + udev_list_entry_get_name(entry); + struct udev_device *dev = + udev_device_new_from_syspath( + udev, + path + ); + const char *devnode = + udev_device_get_devnode(dev); + if (devnode && !strcmp(devnode, menures)) { + int fd = open(devnode, O_RDONLY); + udev_device_unref(dev); + return fd; + } + udev_device_unref(dev); + } + exit(1); +} + +int selectDevice(struct udev *udev, + struct udev_list_entry *devices, char *fpath) { + struct udev_list_entry *entry; + FILE *f = fopen(fpath, "r"); + char goalnode[4096]; + fgets(goalnode, 4096, f); + + uint devidx = 0; + udev_list_entry_foreach(entry, devices) { + const char *path = + udev_list_entry_get_name(entry); + struct udev_device *dev = + udev_device_new_from_syspath( + udev, + path + ); + const char *devnode = + udev_device_get_devnode(dev); + + if (devnode) { + const char *keyboard = udev_device_get_property_value( + dev,"ID_INPUT_KEYBOARD"); + const char *mouse = udev_device_get_property_value( + dev, "ID_INPUT_MOUSE"); + if (mouse) continue; + if (!keyboard) continue; + int fd = open(devnode, O_RDONLY); + + const char *path = udev_device_get_sysattr_value(dev, "ID_PATH"); + const char *name = udev_device_get_sysattr_value(dev, "name"); + const char *vendor = udev_device_get_property_value + (dev, "ID_VENDOR"); + const char *model = udev_device_get_property_value + (dev, "ID_MODEL"); + printf("[%d] Device: %s\n", ++devidx, devnode); + printf(" Name: %s\n", + name ? name : "unknown"); + printf(" Vendor: %s\n", + vendor ? vendor : "unknown"); + printf(" Model: %s\n", + model ? model : "unknown"); + printf(" Node: %s\n", + devnode ? devnode : "unknown"); + if (!strcmp(devnode, goalnode)) { + udev_device_unref(dev); + return fd; + } + + printf("\n"); + } + udev_device_unref(dev); + } + return menuSelectDevice(udev, devices, fpath); +} diff --git a/src/device_select.h b/src/device_select.h new file mode 100644 index 0000000..d83309f --- /dev/null +++ b/src/device_select.h @@ -0,0 +1,4 @@ +int menuSelectDevice(struct udev *udev, + struct udev_list_entry *devices, char *fpath); +int selectDevice(struct udev *udev, + struct udev_list_entry *devices, char *fpath); diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..3a24f63 --- /dev/null +++ b/src/main.c @@ -0,0 +1,559 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "util.h" +#include "device_select.h" +#include "mouse.h" +#include +#include +#include +#include +#include +#include +#include + +XRRMonitorInfo *findMonitor(Display *dpy, Window root, int fx, int fy) { + int nmonitors; + XRRMonitorInfo *monitors = + XRRGetMonitors(dpy, root, True, &nmonitors); + + if (!monitors || nmonitors <= 0) { + fprintf(stderr, "Could not find monitors\n"); + XCloseDisplay(dpy); + exit(1); + } + + XRRMonitorInfo *monitor = NULL; + + for (int i = 0; i < nmonitors; i++) { + int x1 = monitors[i].x; + int y1 = monitors[i].y; + int x2 = x1 + monitors[i].width; + int y2 = y1 + monitors[i].height; + + if (fx >= x1 && fx < x2 && fy >= y1 && fy < y2) { + monitor = &monitors[i]; + break; + } + } + + if (!monitor) + monitor = &monitors[0]; + return monitor; +} + +void renderGrid(int treeX, int treeY, int treeW, int treeH, + Display *dpy, int screen, + Pixmap treeMask, GC maskGC, + Window treeWin, GC gc, + XftDraw *xftDraw, XftColor textColor, XftFont *font, + XRRMonitorInfo *mon, + char *treekeynames) { + int fsize = treeH/4; + if (font) { + XftFontClose(dpy, font); + font = NULL; + } + char fontname[256]; + snprintf(fontname, sizeof(fontname), "Mono:size=%d", fsize); + printf("Loading %s\n", fontname); + font = XftFontOpenName( + dpy, + screen, + fontname + ); + if (!font) { + fprintf(stderr, "Failed to load font: %s\n", fontname); + exit(1); + } + int wx = treeW/3; + int wy = treeH/3; + XSetForeground(dpy, maskGC, 0); + XFillRectangle( + dpy, + treeMask, + maskGC, + 0, 0, + mon->width, mon->height + ); + XSetForeground(dpy, maskGC, 1); + for (int i = 0; i < 4; ++i) { + XDrawLine( + dpy, treeMask, maskGC, + treeX+i*wx, treeY+0, + treeX+i*wx, treeY+treeH + ); + XDrawLine( + dpy, treeMask, maskGC, + treeX+0, treeY+i*wy, + treeX+treeW, treeY+i*wy + ); + } + for (int j = 0; j < 3; ++j) { + for (int i = 0; i < 3; ++i) { + //const char *text = &treekeynames[i+j*3]; + XFillRectangle(dpy, treeMask, maskGC, + treeX+i*wx+wx/2, treeY+j*wy+(wy*3/4)-fsize, + fsize, fsize); // TODO: find character width + } + } + XShapeCombineMask( + dpy, + treeWin, + ShapeBounding, + 0, 0, + treeMask, + ShapeSet + ); + XSetForeground(dpy, gc, WhitePixel(dpy, screen)); + for (int i = 0; i < 4; ++i) { + XDrawLine( + dpy, treeWin, gc, + treeX+i*wx, treeY+0, + treeX+i*wx, treeY+treeH + ); + XDrawLine( + dpy, treeWin, gc, + treeX+0, treeY+i*wy, + treeX+treeW, treeY+i*wy + ); + } + for (int j = 0; j < 3; ++j) { + for (int i = 0; i < 3; ++i) { + const char *text = &treekeynames[i+j*3]; + XftDrawStringUtf8( + xftDraw, + &textColor, + font, + treeX+i*wx+wx/2, treeY+j*wy+(wy*3/4), + (const FcChar8 *)text, + 1 + ); + } + } + XFlush(dpy); +} + +int main(void) { + char config_dir[4096]; + const char *xdg = getenv("XDG_CONFIG_DIR"); + if (!xdg) { + const char *home = getenv("HOME"); + if (!home) { + printf("You don't have home or config..." + "what is wrong with your system\n"); + } + snprintf(config_dir, 4096, "%s/.config/", home); + } else { + snprintf(config_dir, 4096, "%s", xdg); + } + + char keyboard_path[4096]; + snprintf(keyboard_path, 4096, "%s/kvm/keyboard", config_dir); + if (access(keyboard_path, F_OK) != 0) { + makeFileAndParents(keyboard_path); + } + + struct udev *udev = udev_new(); + + if (!udev) { + printf("Failed to create udev\n"); + return 1; + } + + struct udev_enumerate *enumerate = + udev_enumerate_new(udev); + udev_enumerate_add_match_subsystem( + enumerate, + "input" + ); + udev_enumerate_scan_devices(enumerate); + + struct udev_list_entry *devices = + udev_enumerate_get_list_entry(enumerate); + int kbd_fd = selectDevice(udev, devices, keyboard_path); + int mouse_fd = create_virtual_mouse(); + usleep(100000); + + if (ioctl(kbd_fd, EVIOCGRAB, 1) < 0) { + perror("EVIOCGRAB grab"); + return 1; + } + printf("Keyboard grabbed\n"); + + struct input_event ev; + int dxn = 0; + int dxp = 0; + int dyn = 0; + int dyp = 0; + float speedmul = 1.0f; + + Display *dpy = XOpenDisplay(NULL); + int screen = DefaultScreen(dpy); + Window root = RootWindow(dpy, screen); + + Window focused; + int revert; + + XGetInputFocus(dpy, &focused, &revert); + int fx, fy; + Window child; + + if (!XTranslateCoordinates(dpy,focused, + root, 0, 0, &fx, &fy, &child)) { + fprintf(stderr, "Could not find focused window position\n"); + XCloseDisplay(dpy); + return 1; + } + + bool treemode = false; + +XSetWindowAttributes attrs = {0}; +attrs.override_redirect = True; + +XRRMonitorInfo *mon = findMonitor(dpy, root, fx, fy); + +//int treeX = mon->x; +//int treeY = mon->y; +int treeX = 0; +int treeY = 0; +int treeW = mon->width; +int treeH = mon->height; + +Window treeWin = XCreateWindow( + dpy, + root, + mon->x, mon->y, + mon->width, mon->height, + 0, + CopyFromParent, + InputOutput, + CopyFromParent, + CWOverrideRedirect, + &attrs +); + +/* + * ============================================================ + * Visible shape + * ============================================================ + */ + +Pixmap treeMask = XCreatePixmap( + dpy, + treeWin, + treeW, + treeH, + 1 +); + +GC maskGC = XCreateGC( + dpy, + treeMask, + 0, + NULL +); + +/* Start with everything invisible */ +XSetForeground(dpy, maskGC, 0); + +XFillRectangle( + dpy, + treeMask, + maskGC, + 0, 0, + treeW, + treeH +); + +GC gc = XCreateGC( + dpy, + treeWin, + 0, + NULL +); + XMapRaised(dpy, treeWin); + XShapeCombineMask( + dpy, + treeWin, + ShapeBounding, + 0, 0, + treeMask, + ShapeSet + ); + +XftDraw *xftDraw = XftDrawCreate( + dpy, + treeWin, + DefaultVisual(dpy, screen), + DefaultColormap(dpy, screen) +); + +XftFont *font = XftFontOpenName( + dpy, + screen, + "Mono:size=80" +); + +if (!font) { + fprintf(stderr, "Couldn't load font\n"); + return 1; +} + +XftColor textColor; + +if (!XftColorAllocName( + dpy, + DefaultVisual(dpy, screen), + DefaultColormap(dpy, screen), + "white", + &textColor)) +{ + fprintf(stderr, "Couldn't allocate text color\n"); + return 1; +} + XFlush(dpy); + + while (1) { + ssize_t n = read(kbd_fd, &ev, sizeof(ev)); + XEvent event; + while (XPending(dpy)) { + printf("Event\n"); + XNextEvent(dpy, &event); + } + if (n != sizeof(ev)) { + perror("read"); + exit(1); + } + + char treekeynames[9] = {'Y', 'U', 'I', 'D', 'F', 'G', 'C', 'V', 'B'}; + // These are characters but they don't match up right + // I.e KEY_I == 'm' + char treekeys[9] = {KEY_Y, KEY_U, KEY_I, KEY_D, KEY_F, KEY_G, KEY_C, KEY_V, KEY_B}; + + bool shouldsleep = true; + //movemouse(mouse_fd, ev); + if (ev.type == EV_KEY) { + if (ev.code == KEY_ESC || ev.code == KEY_Q) { + break; + } + if (ev.value == 1 && !treemode) { + if (ev.code == KEY_LEFTSHIFT) { + speedmul = 2.0f; + printf("start shift speed\n"); + } + if (ev.code == KEY_LEFTCTRL) { + speedmul = 0.5f; + printf("start control slow\n"); + } + if (ev.code == KEY_J) { + send_mouse_event(mouse_fd, EV_REL, REL_Y, speedmul*25); + send_mouse_event(mouse_fd, EV_SYN, SYN_REPORT, 0); + } + if (ev.code == KEY_K) { + send_mouse_event(mouse_fd, EV_REL, REL_Y, speedmul*-25); + send_mouse_event(mouse_fd, EV_SYN, SYN_REPORT, 0); + } + if (ev.code == KEY_L) { + send_mouse_event(mouse_fd, EV_REL, REL_X, speedmul*25); + send_mouse_event(mouse_fd, EV_SYN, SYN_REPORT, 0); + } + if (ev.code == KEY_H) { + send_mouse_event(mouse_fd, EV_REL, REL_X, speedmul*-25); + send_mouse_event(mouse_fd, EV_SYN, SYN_REPORT, 0); + } + if (ev.code == KEY_W) { + send_mouse_event(mouse_fd, EV_KEY, BTN_LEFT, 1); + send_mouse_event(mouse_fd, EV_SYN, SYN_REPORT, 0); + } + if (ev.code == KEY_E) { + send_mouse_event(mouse_fd, EV_KEY, BTN_MIDDLE, 1); + send_mouse_event(mouse_fd, EV_SYN, SYN_REPORT, 0); + } + if (ev.code == KEY_R) { + send_mouse_event(mouse_fd, EV_KEY, BTN_RIGHT, 1); + send_mouse_event(mouse_fd, EV_SYN, SYN_REPORT, 0); + } + if (ev.code == KEY_M) { + send_mouse_event(mouse_fd, EV_REL, REL_WHEEL, -speedmul); + send_mouse_event(mouse_fd, EV_SYN, SYN_REPORT, 0); + } + if (ev.code == KEY_COMMA) { + send_mouse_event(mouse_fd, EV_REL, REL_WHEEL, speedmul); + send_mouse_event(mouse_fd, EV_SYN, SYN_REPORT, 0); + } + if (ev.code == KEY_DOT) { + send_mouse_event(mouse_fd, EV_REL, REL_HWHEEL, -speedmul); + send_mouse_event(mouse_fd, EV_SYN, SYN_REPORT, 0); + } + if (ev.code == KEY_SLASH) { + send_mouse_event(mouse_fd, EV_REL, REL_HWHEEL, speedmul); + send_mouse_event(mouse_fd, EV_SYN, SYN_REPORT, 0); + } + if (ev.code == KEY_X) { + treemode = true; + renderGrid(treeX, treeY, treeW, treeH, dpy, screen, + treeMask, maskGC, treeWin, gc, xftDraw, textColor, + font, mon, treekeynames); + } + } + if (ev.value == 1 && treemode) { + for (int i = 0; i < 9; ++i) { + if (ev.code == treekeys[i]) { + int ix = i%3; + int iy = i/3; + int des_x = treeX + (treeW/3)*ix; + int des_y = treeY + (treeH/3)*iy; + + // This is X only + // If someone who understands wayland can send patches please do + XWarpPointer(dpy, root, root, 0, 0, 0, 0, + mon->x+des_x, mon->y+des_y); + treeX = des_x; + treeY = des_y; + treeW /= 3; + treeH /= 3; + XSetForeground(dpy, maskGC, 0); + XFillRectangle( + dpy, + treeMask, + maskGC, + 0, 0, + mon->width, mon->height + ); + XShapeCombineMask( + dpy, + treeWin, + ShapeBounding, + 0, 0, + treeMask, + ShapeSet + ); + + renderGrid(treeX, treeY, treeW, treeH, dpy, screen, + treeMask, maskGC, treeWin, gc, xftDraw, textColor, + font, mon, treekeynames); + } + } + if (ev.code == KEY_P) { + XSetForeground(dpy, maskGC, 0); + XFillRectangle( + dpy, + treeMask, + maskGC, + 0, 0, + mon->width, mon->height + ); + XShapeCombineMask( + dpy, + treeWin, + ShapeBounding, + 0, 0, + treeMask, + ShapeSet + ); + treeX = 0; + treeY = 0; + treeW = mon->width; + treeH = mon->height; + treemode = false; + } + } + if (ev.value == 2 && !treemode) { + int speed = 6; + if (ev.code == KEY_J) { + dyp = speed; + } + if (ev.code == KEY_K) { + dyn = speed; + } + if (ev.code == KEY_L) { + dxp = speed; + } + if (ev.code == KEY_H) { + dxn = speed; + } + if (ev.code == KEY_M) { + send_mouse_event(mouse_fd, EV_REL, REL_WHEEL, -speedmul); + send_mouse_event(mouse_fd, EV_SYN, SYN_REPORT, 0); + } + if (ev.code == KEY_COMMA) { + send_mouse_event(mouse_fd, EV_REL, REL_WHEEL, speedmul); + send_mouse_event(mouse_fd, EV_SYN, SYN_REPORT, 0); + } + if (ev.code == KEY_DOT) { + send_mouse_event(mouse_fd, EV_REL, REL_HWHEEL, -speedmul); + send_mouse_event(mouse_fd, EV_SYN, SYN_REPORT, 0); + } + if (ev.code == KEY_SLASH) { + send_mouse_event(mouse_fd, EV_REL, REL_HWHEEL, speedmul); + send_mouse_event(mouse_fd, EV_SYN, SYN_REPORT, 0); + } + shouldsleep = false; + } + if (ev.value == 0 && !treemode) { + if (ev.code == KEY_W) { + send_mouse_event(mouse_fd, EV_KEY, BTN_LEFT, 0); + send_mouse_event(mouse_fd, EV_SYN, SYN_REPORT, 0); + } + if (ev.code == KEY_E) { + send_mouse_event(mouse_fd, EV_KEY, BTN_MIDDLE, 0); + send_mouse_event(mouse_fd, EV_SYN, SYN_REPORT, 0); + } + if (ev.code == KEY_R) { + send_mouse_event(mouse_fd, EV_KEY, BTN_RIGHT, 0); + send_mouse_event(mouse_fd, EV_SYN, SYN_REPORT, 0); + } + if (ev.code == KEY_J) { + dyp = 0; + } + if (ev.code == KEY_K) { + dyn = 0; + } + if (ev.code == KEY_L) { + dxp = 0; + } + if (ev.code == KEY_H) { + dxn = 0; + } + if (ev.code == KEY_LEFTSHIFT) { + speedmul = 1.0f; + } + if (ev.code == KEY_LEFTCTRL) { + speedmul = 1.0f; + } + } + } + + int total_dx = dxp - dxn; + int total_dy = dyp - dyn; + + send_mouse_event(mouse_fd, EV_REL, REL_X, speedmul*total_dx); + send_mouse_event(mouse_fd, EV_SYN, SYN_REPORT, 0); + send_mouse_event(mouse_fd, EV_REL, REL_Y, speedmul*total_dy); + send_mouse_event(mouse_fd, EV_SYN, SYN_REPORT, 0); + + if (shouldsleep) usleep(1000000/60); + } + + // Give control back + if (ioctl(kbd_fd, EVIOCGRAB, 0) < 0) { + perror("EVIOCGRAB release"); + return 1; + } + + printf("Keyboard released\n"); + + udev_enumerate_unref(enumerate); + udev_unref(udev); + + return 0; +} \ No newline at end of file diff --git a/src/mouse.c b/src/mouse.c new file mode 100644 index 0000000..15de359 --- /dev/null +++ b/src/mouse.c @@ -0,0 +1,56 @@ +#include +#include +#include +#include +#include +#include +#include + + +void send_mouse_event(int fd, int type, int code, int value) { + struct input_event ev; + + memset(&ev, 0, sizeof(ev)); + + ev.type = type; + ev.code = code; + ev.value = value; + + write(fd, &ev, sizeof(ev)); +} + +int create_virtual_mouse() { + int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK); + if (fd < 0) { + perror("uinput"); + exit(1); + } + ioctl(fd, UI_SET_EVBIT, EV_REL); + ioctl(fd, UI_SET_RELBIT, REL_X); + ioctl(fd, UI_SET_RELBIT, REL_Y); + ioctl(fd, UI_SET_RELBIT, REL_WHEEL); + ioctl(fd, UI_SET_RELBIT, REL_HWHEEL); + ioctl(fd, UI_SET_EVBIT, EV_KEY); + ioctl(fd, UI_SET_KEYBIT, BTN_LEFT); + ioctl(fd, UI_SET_KEYBIT, BTN_RIGHT); + ioctl(fd, UI_SET_KEYBIT, BTN_MIDDLE); + ioctl(fd, UI_SET_EVBIT, EV_REL); + + struct uinput_setup setup; + + memset(&setup, 0, sizeof(setup)); + + setup.id.bustype = BUS_USB; + setup.id.vendor = 0x1234; + setup.id.product = 0x5678; + + strcpy(setup.name, "Virtual Keyboard Mouse"); + + ioctl(fd, UI_DEV_SETUP, &setup); + ioctl(fd, UI_DEV_CREATE); + + + printf("Virtual mouse created\n"); + + return fd; +} diff --git a/src/mouse.h b/src/mouse.h new file mode 100644 index 0000000..b60327d --- /dev/null +++ b/src/mouse.h @@ -0,0 +1,2 @@ +void send_mouse_event(int fd, int type, int code, int value); +int create_virtual_mouse(); diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..c85e9b1 --- /dev/null +++ b/src/util.c @@ -0,0 +1,26 @@ +#include +#include +#include +#include +#include + + +int makeFileAndParents(const char *path) { + char buf[4096]; + + strcpy(buf, path); + + for (char *p = buf + 1; *p; p++) { + if (*p == '/') { + *p = '\0'; + + if (mkdir(buf, 0755) == -1 && errno != EEXIST) + return -1; + + *p = '/'; + } + } + + return open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); +} + diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..e41899e --- /dev/null +++ b/src/util.h @@ -0,0 +1 @@ +int makeFileAndParents(const char *path);