commit 4834c1965624a415829253a14bd2d7f105df160c Author: Zahkc Date: Thu Nov 20 12:05:06 2025 +0000 Add mouse2joy.c diff --git a/mouse2joy.c b/mouse2joy.c new file mode 100644 index 0000000..528a53c --- /dev/null +++ b/mouse2joy.c @@ -0,0 +1,245 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define msleep(ms) usleep((ms)*1000) + +double clamp(double d, double min, double max) { + const double t = d < min ? min : d; + return t > max ? max : t; +} + +static volatile unsigned char keepRunning = 1; + +// handle Ctrl+C +static void sigintHandler(int x) { + printf("Interrupted1\n"); + keepRunning = 0; +} + +// enable and configure an absolute "position" analog channel +static void setup_abs(int fd2, unsigned chan, int min, int max) { + if (ioctl(fd2, UI_SET_ABSBIT, chan)) + perror("UI_SET_ABSBIT"); + + struct uinput_abs_setup s = { + .code = chan, + .absinfo = {.minimum = min, .maximum = max}, + }; + + if (ioctl(fd2, UI_ABS_SETUP, &s)) + perror("UI_ABS_SETUP"); +} + +int main() { + // register CTRL+C + signal(SIGINT, &sigintHandler); + + // Initialise Evdev + struct libevdev *dev = NULL; + int fd1; + int rc = 1; + int grabbed = 0; + + fd1 = open("/dev/input/event7", O_RDONLY | O_NONBLOCK); + rc = libevdev_new_from_fd(fd1, &dev); + if (rc < 0) { + fprintf(stderr, "Failed to init libevdev (%s)\n", strerror(-rc)); + exit(1); + } + printf("Input device name: \"%s\"\n", libevdev_get_name(dev)); + printf("Input device ID: bus %#x vendor %#x product %#x\n", + libevdev_get_id_bustype(dev), libevdev_get_id_vendor(dev), + libevdev_get_id_product(dev)); + if (!libevdev_has_event_type(dev, EV_REL) || + !libevdev_has_event_code(dev, EV_KEY, BTN_LEFT)) { + printf("This device does not look like a mouse\n"); + exit(1); + } + + //rc = ioctl(fd1, EVIOCGRAB, (void *)1); + //grabbed = 1; + + if (rc) { + printf("Failed to grab mouse\n"); + exit(1); + } + + // Initialise Uinput + int fd2 = open("/dev/uinput", O_WRONLY | O_NONBLOCK); + + if (fd2 < 0) { + perror("open /dev/uinput"); + return 1; + } + + ioctl(fd2, UI_SET_EVBIT, EV_KEY); // enable button/key handling + + ioctl(fd2, UI_SET_KEYBIT, BTN_SOUTH); + ioctl(fd2, UI_SET_KEYBIT, BTN_EAST); + ioctl(fd2, UI_SET_KEYBIT, BTN_NORTH); + ioctl(fd2, UI_SET_KEYBIT, BTN_WEST); + ioctl(fd2, UI_SET_KEYBIT, BTN_Z); + ioctl(fd2, UI_SET_KEYBIT, BTN_TL); + ioctl(fd2, UI_SET_KEYBIT, BTN_TR); + ioctl(fd2, UI_SET_KEYBIT, BTN_TL2); + ioctl(fd2, UI_SET_KEYBIT, BTN_TR2); + ioctl(fd2, UI_SET_KEYBIT, BTN_SELECT); + ioctl(fd2, UI_SET_KEYBIT, BTN_START); + ioctl(fd2, UI_SET_KEYBIT, BTN_MODE); + ioctl(fd2, UI_SET_KEYBIT, BTN_THUMBL); + ioctl(fd2, UI_SET_KEYBIT, BTN_THUMBR); + + + + #if 0 + ioctl(fd2, UI_SET_KEYBIT, BTN_TL); + ioctl(fd2, UI_SET_KEYBIT, BTN_TR); + ioctl(fd2, UI_SET_KEYBIT, BTN_TL2); + ioctl(fd2, UI_SET_KEYBIT, BTN_TR2); + ioctl(fd2, UI_SET_KEYBIT, BTN_START); + ioctl(fd2, UI_SET_KEYBIT, BTN_SELECT); + ioctl(fd2, UI_SET_KEYBIT, BTN_THUMBL); + ioctl(fd2, UI_SET_KEYBIT, BTN_THUMBR); + ioctl(fd2, UI_SET_KEYBIT, BTN_DPAD_UP); + ioctl(fd2, UI_SET_KEYBIT, BTN_DPAD_DOWN); + ioctl(fd2, UI_SET_KEYBIT, BTN_DPAD_LEFT); + ioctl(fd2, UI_SET_KEYBIT, BTN_DPAD_RIGHT); +#endif + + ioctl(fd2, UI_SET_EVBIT, EV_ABS); // enable analog absolute position handling + + setup_abs(fd2, ABS_X, -512, 512); + setup_abs(fd2, ABS_Y, -512, 512); + setup_abs(fd2, ABS_RX, -512, 512); + setup_abs(fd2, ABS_RY, -512, 512); + setup_abs(fd2, ABS_HAT0X, -1, 1); + setup_abs(fd2, ABS_HAT0X, -1, 1); + setup_abs(fd2, ABS_HAT0Y, -1, 1); + + +#if 0 + setup_abs(fd2, ABS_RX, 0, 100); + setup_abs(fd2, ABS_RY, 0, 100); + setup_abs(fd2, ABS_RZ, 0, 100); +#endif + /*ioctl(fd2, UI_SET_EVBIT, EV_REL); + ioctl(fd2, UI_SET_RELBIT, REL_X); + ioctl(fd2, UI_SET_RELBIT, REL_Y);*/ + + struct uinput_setup setup = {.name = "Pro Controller Emulator", + .id = { + .bustype = BUS_USB, + .vendor = 0x3, + .product = 0x3, + .version = 2, + }}; + + if (ioctl(fd2, UI_DEV_SETUP, &setup)) { + perror("UI_DEV_SETUP"); + return 1; + } + + if (ioctl(fd2, UI_DEV_CREATE)) { + perror("UI_DEV_CREATE"); + return 1; + } + + // you can write events one at a time, but to save overhead we'll + // update all of them in a single write + + // x and y are triangle wave 90 degrees out of phase + // z ramps slowly + // A B X Y buttons toggle at four different frequencies + + float x = 0; + float y = 0; + // int enabled = 0; + int clear = 0; + + // cycle + do { + struct input_event ev_input; + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev_input); + if (rc == 0) { + /*printf("Event: %s %s %d\n", + libevdev_event_type_get_name(ev_input.type), + libevdev_event_code_get_name(ev_input.type, ev_input.code), + ev_input.value);*/ + + if (ev_input.code == BTN_SIDE || ev_input.code == BTN_EXTRA) { + if (ev_input.value) { + grabbed = !grabbed; + //printf("grabbed %d\n", grabbed); + ioctl(fd1, EVIOCGRAB, grabbed ? (void *)1 : (void *)0); + } + } + if (ev_input.type == EV_REL) { + if (ev_input.code == REL_X) { + x += ev_input.value * 10; + x = clamp(x, -512, 512); + } + if (ev_input.code == REL_Y) { + y += ev_input.value * 10; + y = clamp(y, -512, 512); + } + } + } + + // x -= ((x>0) - (x<0)) * 2; + //x *= 0.997; + // x *= 0.998; + x *= 0.99; + y *= 0.99; + + if (fabs(x) > 0.5 || clear) { + clear = 0; + // printf("%f\n", x); + struct input_event ev[2]; + memset(&ev, 0, sizeof ev); + + /*for (int i = -1024; i < 1024; i += 32) { + putchar(i < x ? '|' : '-'); + } + putchar('\n');*/ + + ev[0].type = EV_ABS; + ev[0].code = ABS_RX; + ev[0].value = x; + + ev[1].type = EV_ABS; + ev[1].code = ABS_RY; + ev[1].value = y; + + ev[2].type = EV_SYN; + ev[2].code = SYN_REPORT; + ev[2].value = 0; + + if (write(fd2, &ev, sizeof ev) < 0) { + perror("write"); + return 1; + } + } + + //msleep(1); + usleep(500); + } while (keepRunning && (rc == 1 || rc == 0 || rc == -EAGAIN)); + + printf("Cleanup\n"); + if (grabbed) { + ioctl(fd1, EVIOCGRAB, (void *)0); + grabbed = 0; + } +}