commit d916bd3eda61598c474e02fa05bd3ec15f843f0b Author: bʰedoh₂ swé Date: Mon Jul 8 10:50:35 2024 +0500 Initial commit. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7af89a7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +poormansgamepad diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..50bb52d --- /dev/null +++ b/LICENSE @@ -0,0 +1,5 @@ +Copyright (C) 2024 by bedohswe bedohswe@firemail.cc + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8bc2ec0 --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +CFLAGS ?= + +SRCS=src/main.c src/gamepad.c src/panic.c + +all: poormansgamepad + +poormansgamepad: $(SRCS) + $(CC) $^ -O2 -Wall -Wpedantic $(CFLAGS) -o poormansgamepad + +clean: + $(RM) poormansgamepad diff --git a/README.md b/README.md new file mode 100644 index 0000000..4e3cf84 --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +# Emulate a gamepad with a mouse + +# Usage + +`poormansgamepad devicepath scale_axis scale_wheel` +`devicepath` - Path to mouse device (/dev/input/eventX) +`scale_axis` - Sensitivity for the virtual thumbstick +`scale_wheel` - Sensitivity for the virtual trigger diff --git a/src/gamepad.c b/src/gamepad.c new file mode 100644 index 0000000..c934c59 --- /dev/null +++ b/src/gamepad.c @@ -0,0 +1,93 @@ +#include +#include +#include +#include +#include + +#include "panic.h" + +void emit(int uinput, int type, int code, int val){ + struct input_event ie; + + ie.type = type; + ie.code = code; + ie.value = val; + ie.time.tv_sec = 0; + ie.time.tv_usec = 0; + + write(uinput, &ie, sizeof(ie)); +} + +void destroygamepad(int uinput); + +void sendkey(int uinput, int code, int val) { + emit(uinput, EV_KEY, code, val); + emit(uinput, EV_SYN, SYN_REPORT, 0); +} + +void sendaxis(int uinput, int code, int val) { + emit(uinput, EV_ABS, code, val); + emit(uinput, EV_SYN, SYN_REPORT, 0); +} + +int setupaxis(int uinput, int code) { + int er; + er = ioctl(uinput, UI_SET_ABSBIT, code); + if (er < 0) + return er; + + struct uinput_abs_setup axis; + axis.code = code; + axis.absinfo.minimum = 1024; + axis.absinfo.maximum = 1024; + axis.absinfo.fuzz = 0; + axis.absinfo.flat = 0; + axis.absinfo.resolution = 1; + axis.absinfo.value = 0; + + return ioctl(uinput, UI_ABS_SETUP, &axis); +} + +int setupgamepad() { + struct uinput_setup usetup; + + int uinput = open("/dev/uinput", O_WRONLY | O_NONBLOCK); + if (!uinput) + panic("Unable to open /dev/uinput."); + + if (ioctl(uinput, UI_SET_EVBIT, EV_KEY) < 0) + panic("Unable to setup EV_KEY."); + if (ioctl(uinput, UI_SET_EVBIT, EV_ABS) < 0) + panic("Unable to setup EV_ABS."); + if (ioctl(uinput, UI_SET_KEYBIT, BTN_SOUTH) < 0) + panic("Unable to setup BTN_SOUTH."); + if (ioctl(uinput, UI_SET_KEYBIT, BTN_TL2) < 0) + panic("Unable to setup BTN_TL2."); + if (ioctl(uinput, UI_SET_KEYBIT, BTN_TR2) < 0) + panic("Unable to setup BTN_TR2."); + + if (setupaxis(uinput, ABS_X) < 0) + panic("Unable to setup ABS_X.");; + if (setupaxis(uinput, ABS_Y) < 0) + panic("Unable to setup ABS_Y.");; + if (setupaxis(uinput, ABS_HAT1X) < 0) + panic("Unable to setup ABS_HAT1X.");; + + memset(&usetup, 0, sizeof(usetup)); + usetup.id.bustype = BUS_USB; + usetup.id.vendor = 0; + usetup.id.product = 0; + strcpy(usetup.name, "Poor man's gamepad"); + + ioctl(uinput, UI_DEV_SETUP, &usetup); + if (ioctl(uinput, UI_DEV_CREATE) < 0) + panic("Unable to create device.");; + + return uinput; +} + +void destroygamepad(int uinput) { + sleep(1); + ioctl(uinput, UI_DEV_DESTROY); + close(uinput); +} diff --git a/src/gamepad.h b/src/gamepad.h new file mode 100644 index 0000000..dcde443 --- /dev/null +++ b/src/gamepad.h @@ -0,0 +1,3 @@ +void sendkey(int uinput, int code, int val); +void sendaxis(int uinput, int code, int val); +int setupgamepad(); diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..c5b4dfd --- /dev/null +++ b/src/main.c @@ -0,0 +1,92 @@ +#include +#include +#include + +#include +#include +#include + +#include "gamepad.h" +#include "panic.h" + + +void waitmouse(int mouse, struct input_event* ie) { + while (1) { + read(mouse, ie, sizeof(struct input_event)); + if (ie->type != EV_SYN) + return; + } +} + +int clamp(int num) { + // Bruh + if (num > 33000) + return 33000; + if (num < -33000) + return -33000; + return num; +} + +int cut(int num, int amount) { + if (num > -amount && num < amount) + return 0; + return num; +} + +int drop(int num, int amount) { + if (num > 0) + return num - amount; + if (num < 0) + return num + amount; + return 0; +} + +int main(int argc, char* argv[]) { + if (argc == 0) { + fprintf(stderr, "????\n"); + return 255; + } + if (argc != 5) { + printf("Usage: %s devicepath scale_axis scale_wheel deadzone\n", argv[0]); + return 1; + } + struct input_event ie; + int axis_x = 0; + int axis_y = 0; + int axis_w = 0; + int scale_axis = strtol(argv[2], NULL, 10); + int scale_wheel = strtol(argv[3], NULL, 10); + int deadzone = strtol(argv[3], NULL, 10); + int drop_axis = 0; + int drop_wheel = 0; + + char* device = argv[1]; + int mouse = open(device, 0); + if (!mouse) + panic("Unable to open device."); + + int gamepad = setupgamepad(); + + while (1) { + waitmouse(mouse, &ie); + if (ie.type == EV_REL && ie.code == REL_X) + axis_x = drop(axis_x + ie.value * scale_axis, drop_axis); + if (ie.type == EV_REL && ie.code == REL_Y) + axis_y = drop(axis_y + ie.value * scale_axis, drop_axis); + if (ie.type == EV_REL && ie.code == REL_WHEEL) + axis_w = drop(axis_w + ie.value * scale_wheel, drop_wheel); + axis_x = clamp(axis_x); + axis_y = clamp(axis_y); + axis_w = clamp(axis_w); + sendaxis(gamepad, ABS_X, cut(axis_x, deadzone)); + sendaxis(gamepad, ABS_Y, cut(axis_y, deadzone)); + sendaxis(gamepad, ABS_HAT1X, axis_w); + if (ie.type == EV_KEY && ie.code == BTN_LEFT) + sendkey(gamepad, BTN_TL2, ie.value); + if (ie.type == EV_KEY && ie.code == BTN_MIDDLE) + sendkey(gamepad, BTN_SOUTH, ie.value); + if (ie.type == EV_KEY && ie.code == BTN_RIGHT) + sendkey(gamepad, BTN_TR2, ie.value); + } + return 0; +} diff --git a/src/panic.c b/src/panic.c new file mode 100644 index 0000000..20f8e8e --- /dev/null +++ b/src/panic.c @@ -0,0 +1,7 @@ +#include +#include + +void panic(char* msg) { + fprintf(stderr, "Fatal error occured.\n%s\n", msg); + exit(1); +} diff --git a/src/panic.h b/src/panic.h new file mode 100644 index 0000000..46e77e0 --- /dev/null +++ b/src/panic.h @@ -0,0 +1 @@ +void panic(char*);