This commit is contained in:
cannoli-fruit 2026-08-01 23:26:59 -04:00
commit 79e255d19d
11 changed files with 915 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
build/*
kvm

35
Makefile Normal file
View file

@ -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

19
README.md Normal file
View file

@ -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

64
bldit.lua Normal file
View file

@ -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,
}
}

147
src/device_select.c Normal file
View file

@ -0,0 +1,147 @@
#include <stdbool.h>
#include <libudev.h>
#include <linux/input.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
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);
}

4
src/device_select.h Normal file
View file

@ -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);

559
src/main.c Normal file
View file

@ -0,0 +1,559 @@
#include <stdio.h>
#include <libudev.h>
#include <stdbool.h>
#include <unistd.h>
#include <fcntl.h>
#include <linux/input.h>
#include <linux/uinput.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <time.h>
#include "util.h"
#include "device_select.h"
#include "mouse.h"
#include <X11/Xlib.h>
#include <X11/extensions/shape.h>
#include <X11/extensions/Xrandr.h>
#include <X11/extensions/Xfixes.h>
#include <string.h>
#include <X11/Xft/Xft.h>
#include <fontconfig/fontconfig.h>
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;
}

56
src/mouse.c Normal file
View file

@ -0,0 +1,56 @@
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <linux/uinput.h>
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;
}

2
src/mouse.h Normal file
View file

@ -0,0 +1,2 @@
void send_mouse_event(int fd, int type, int code, int value);
int create_virtual_mouse();

26
src/util.c Normal file
View file

@ -0,0 +1,26 @@
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
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);
}

1
src/util.h Normal file
View file

@ -0,0 +1 @@
int makeFileAndParents(const char *path);