Init
This commit is contained in:
commit
79e255d19d
11 changed files with 915 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
build/*
|
||||||
|
kvm
|
||||||
35
Makefile
Normal file
35
Makefile
Normal 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
19
README.md
Normal 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
64
bldit.lua
Normal 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
147
src/device_select.c
Normal 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
4
src/device_select.h
Normal 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
559
src/main.c
Normal 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
56
src/mouse.c
Normal 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
2
src/mouse.h
Normal 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
26
src/util.c
Normal 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
1
src/util.h
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
int makeFileAndParents(const char *path);
|
||||||
Loading…
Add table
Add a link
Reference in a new issue