Created
May 22, 2024 11:46
-
-
Save ftk/0d70e527c05cc7c7d25bd57019a829a9 to your computer and use it in GitHub Desktop.
libspnav example controlling mouse using spaceball (uinput)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# build with spnav | |
gcc simple.c `pkg-config spnav --cflags --libs` -lm -O3 -flto -osimple_uinput | |
# build without spnav | |
gcc simple.c spnav.c proto.c -O3 -lm -flto -osimple_uinput |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* libspnav example | |
* Control mouse using spaceball (X, Z axis) + 2D mouse wheel (RX, RZ axis) | |
*/ | |
#include "spnav.h" | |
#include <signal.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <fcntl.h> | |
#include <linux/uinput.h> | |
#include <stdbool.h> | |
#include <string.h> | |
#include <unistd.h> | |
void print_dev_info(void); | |
static int fd; // uinput fd | |
void sig(int s) { | |
spnav_close(); | |
ioctl(fd, UI_DEV_DESTROY); | |
close(fd); | |
exit(0); | |
} | |
void emit(int fd, int type, int code, int val) { | |
struct input_event ie; | |
ie.type = type; | |
ie.code = code; | |
ie.value = val; | |
/* timestamp values below are ignored */ | |
ie.time.tv_sec = 0; | |
ie.time.tv_usec = 0; | |
write(fd, &ie, sizeof(ie)); | |
} | |
int main(void) { | |
struct uinput_setup usetup; | |
const int button_keys[12] = { | |
KEY_F13, // 1 | |
KEY_F14, // 2 | |
KEY_F15, // 3 | |
KEY_F16, // 4 | |
KEY_F17, // 5 | |
KEY_F18, // 6 | |
KEY_F19, // 7 | |
KEY_F20, // 8 | |
KEY_F21, // 9 | |
KEY_F22, // A | |
KEY_F23, // B | |
BTN_LEFT // C | |
}; | |
fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK); | |
if (fd < 0) { | |
perror("Can't open /dev/uinput"); | |
return 1; | |
} | |
/* enable button events */ | |
ioctl(fd, UI_SET_EVBIT, EV_KEY); | |
for (int i = 0; i < 12; i++) | |
ioctl(fd, UI_SET_KEYBIT, button_keys[i]); | |
/* enable motion events */ | |
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_HWHEEL_HI_RES); | |
ioctl(fd, UI_SET_RELBIT, REL_WHEEL_HI_RES); | |
spnav_event sev; | |
signal(SIGINT, sig); | |
if (spnav_open() == -1) { | |
fprintf(stderr, "failed to connect to spacenavd\n"); | |
return 1; | |
} | |
print_dev_info(); | |
memset(&usetup, 0, sizeof(usetup)); | |
usetup.id.bustype = BUS_USB; | |
usetup.id.vendor = 0x1234; /* sample vendor */ | |
usetup.id.product = 0x5678; /* sample product */ | |
spnav_dev_name(usetup.name, sizeof usetup.name); | |
spnav_sensitivity(0.25); | |
ioctl(fd, UI_DEV_SETUP, &usetup); | |
ioctl(fd, UI_DEV_CREATE); | |
/* spnav_wait_event() and spnav_poll_event(), will silently ignore any | |
* non-spnav X11 events. | |
* | |
* If you need to handle other X11 events you will have to use a regular | |
* XNextEvent() loop, and pass any ClientMessage events to spnav_x11_event, | |
* which will return the event type or zero if it's not an spnav event (see | |
* spnav.h). | |
*/ | |
bool idle = true; | |
while (true) { | |
int result; | |
if (idle) | |
result = spnav_wait_event(&sev); | |
else | |
result = spnav_poll_event(&sev); | |
if (!result) { | |
if (idle) | |
break; | |
sev.type = SPNAV_EVENT_MOTION; // repeat last motion every 10ms | |
usleep(10 * 1000); | |
result = spnav_poll_event(&sev); | |
} | |
if (sev.type == SPNAV_EVENT_MOTION) { | |
// printf("got motion event: t(%d, %d, %d) ", sev.motion.x, sev.motion.y, | |
// sev.motion.z); printf("r(%d, %d, %d)\n", sev.motion.rx, sev.motion.ry, | |
// sev.motion.rz); | |
if (sev.motion.x || sev.motion.y || sev.motion.z || sev.motion.rx || | |
sev.motion.ry || sev.motion.rz) | |
idle = false; | |
else | |
idle = true; | |
emit(fd, EV_REL, REL_X, sev.motion.x); | |
emit(fd, EV_REL, REL_Y, -sev.motion.z); | |
emit(fd, EV_REL, REL_WHEEL_HI_RES, -sev.motion.rx); | |
emit(fd, EV_REL, REL_HWHEEL_HI_RES, sev.motion.rz); | |
emit(fd, EV_SYN, SYN_REPORT, 0); | |
} else { /* SPNAV_EVENT_BUTTON */ | |
printf("got button %s event b(%d)\n", | |
sev.button.press ? "press" : "release", sev.button.bnum); | |
if (sev.button.bnum < 12) { | |
emit(fd, EV_KEY, button_keys[sev.button.bnum], sev.button.press); | |
emit(fd, EV_SYN, SYN_REPORT, 0); | |
} | |
} | |
} | |
spnav_close(); | |
ioctl(fd, UI_DEV_DESTROY); | |
close(fd); | |
return 0; | |
} | |
void print_dev_info(void) { | |
int proto; | |
char buf[256]; | |
if ((proto = spnav_protocol()) == -1) { | |
fprintf(stderr, "failed to query protocol version\n"); | |
return; | |
} | |
printf("spacenav AF_UNIX protocol version: %d\n", proto); | |
spnav_client_name("simple example"); | |
if (proto >= 1) { | |
spnav_dev_name(buf, sizeof buf); | |
printf("Device: %s\n", buf); | |
spnav_dev_path(buf, sizeof buf); | |
printf("Path: %s\n", buf); | |
printf("Buttons: %d\n", spnav_dev_buttons()); | |
printf("Axes: %d\n", spnav_dev_axes()); | |
} | |
putchar('\n'); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment