Skip to content

Instantly share code, notes, and snippets.

@ftk
Created May 22, 2024 11:46
Show Gist options
  • Save ftk/0d70e527c05cc7c7d25bd57019a829a9 to your computer and use it in GitHub Desktop.
Save ftk/0d70e527c05cc7c7d25bd57019a829a9 to your computer and use it in GitHub Desktop.
libspnav example controlling mouse using spaceball (uinput)
# 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
/* 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