kopia lustrzana https://github.com/FreeSpacenav/libspnav
libspnav protocol v1 partial support and extended API
rodzic
22f354a083
commit
c4b098a02c
|
@ -4,6 +4,10 @@
|
|||
#include <X11/Xlib.h>
|
||||
#include <spnav.h>
|
||||
|
||||
#if defined(BUILD_AF_UNIX)
|
||||
void print_dev_info(void);
|
||||
#endif
|
||||
|
||||
void sig(int s)
|
||||
{
|
||||
spnav_close();
|
||||
|
@ -42,9 +46,11 @@ int main(void)
|
|||
|
||||
#elif defined(BUILD_AF_UNIX)
|
||||
if(spnav_open()==-1) {
|
||||
fprintf(stderr, "failed to connect to the space navigator daemon\n");
|
||||
fprintf(stderr, "failed to connect to the space navigator daemon\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
print_dev_info();
|
||||
#else
|
||||
#error Unknown build type!
|
||||
#endif
|
||||
|
@ -67,3 +73,25 @@ int main(void)
|
|||
spnav_close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(BUILD_AF_UNIX)
|
||||
void print_dev_info(void)
|
||||
{
|
||||
int proto;
|
||||
|
||||
if((proto = spnav_protocol()) == -1) {
|
||||
fprintf(stderr, "failed to query protocol version\n");
|
||||
return;
|
||||
}
|
||||
printf("spacenav AF_UNIX protocol version: %d\n", proto);
|
||||
|
||||
if(proto >= 1) {
|
||||
printf("Device: %s\n", spnav_dev_name(0, 0));
|
||||
printf("Path: %s\n", spnav_dev_path(0, 0));
|
||||
printf("Buttons: %d\n", spnav_dev_buttons());
|
||||
printf("Axes: %d\n", spnav_dev_axes());
|
||||
}
|
||||
|
||||
putchar('\n');
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
#ifndef PROTO_H_
|
||||
#define PROTO_H_
|
||||
|
||||
/* maximum supported protocol version */
|
||||
#define MAX_PROTO_VER 1
|
||||
|
||||
struct reqresp {
|
||||
int type;
|
||||
int data[7];
|
||||
};
|
||||
|
||||
#define REQ_TAG 0x7faa0000
|
||||
#define REQ_BASE 0x1000
|
||||
|
||||
/* REQ_S* are set, REQ_G* are get requests.
|
||||
* Quick-reference for request-response data in the comments next to each
|
||||
* request: Q[n] defines request data item n, R[n] defines response data item n
|
||||
*
|
||||
* status responses are 0 for success, non-zero for failure
|
||||
*/
|
||||
enum {
|
||||
/* per-client settings */
|
||||
REQ_SET_SENS = REQ_BASE,/* set client sensitivity: Q[0] float - R[6] status */
|
||||
REQ_GET_SENS, /* get client sensitivity: R[0] float R[6] status */
|
||||
|
||||
/* device queries */
|
||||
REQ_DEV_NAME = 0x2000, /* get device name: R[0] length R[6] status followed
|
||||
by <length> bytes */
|
||||
REQ_DEV_PATH, /* get device path: same as above */
|
||||
REQ_DEV_NAXES, /* get number of axes: R[0] num axes R[6] status */
|
||||
REQ_DEV_NBUTTONS, /* get number of buttons: same as above */
|
||||
/* TODO: features like LCD, LEDs ... */
|
||||
|
||||
/* configuration settings */
|
||||
REQ_SCFG_SENS = 0x3000, /* set global sensitivity: Q[0] float - R[6] status */
|
||||
REQ_GCFG_SENS, /* get global sens: R[0] float R[6] status */
|
||||
REQ_SCFG_SENS_AXIS, /* set per-axis sens/ty: Q[0-5] values - R[6] status */
|
||||
REQ_GCFG_SENS_AXIS, /* get per-axis sens/ty: R[0-5] values R[6] status */
|
||||
REQ_SCFG_DEADZONE, /* set deadzones: Q[0-5] values - R[6] status */
|
||||
REQ_GCFG_DEADZONE, /* get deadzones: R[0-5] values R[6] status */
|
||||
REQ_SCFG_INVERT, /* set invert axes: Q[0-5] invert - R[6] status */
|
||||
REQ_GCFG_INVERT, /* get invert axes: R[0-5] invert R[6] status */
|
||||
/* TODO ... more */
|
||||
|
||||
REQ_CHANGE_PROTO = 0x5500
|
||||
};
|
||||
|
||||
#endif /* PROTO_H_ */
|
182
spnav.c
182
spnav.c
|
@ -36,7 +36,11 @@ OF SUCH DAMAGE.
|
|||
#include <sys/un.h>
|
||||
#include <sys/select.h>
|
||||
#include "spnav.h"
|
||||
#include "proto.h"
|
||||
|
||||
/* default timeout for request responses*/
|
||||
#define TIMEOUT 256
|
||||
/* default socket path */
|
||||
#define SPNAV_SOCK_PATH "/var/run/spnav.sock"
|
||||
|
||||
#ifdef USE_X11
|
||||
|
@ -46,6 +50,10 @@ OF SUCH DAMAGE.
|
|||
static Window get_daemon_window(Display *dpy);
|
||||
static int catch_badwin(Display *dpy, XErrorEvent *err);
|
||||
|
||||
static int wait_resp(void *buf, int sz, int timeout_ms);
|
||||
static int request(int req, struct reqresp *rr, int timeout_ms);
|
||||
|
||||
|
||||
static Display *dpy;
|
||||
static Window app_win;
|
||||
static Atom motion_event, button_press_event, button_release_event, command_event;
|
||||
|
@ -70,6 +78,7 @@ static struct event_node *ev_queue, *ev_queue_tail;
|
|||
|
||||
/* AF_UNIX socket used for alternative communication with daemon */
|
||||
static int sock = -1;
|
||||
static int proto;
|
||||
|
||||
static int connect_afunix(int s, const char *path)
|
||||
{
|
||||
|
@ -83,7 +92,7 @@ static int connect_afunix(int s, const char *path)
|
|||
|
||||
int spnav_open(void)
|
||||
{
|
||||
int s;
|
||||
int s, cmd;
|
||||
char *path;
|
||||
FILE *fp;
|
||||
char buf[256], *ptr;
|
||||
|
@ -135,6 +144,20 @@ int spnav_open(void)
|
|||
|
||||
success:
|
||||
sock = s;
|
||||
proto = 0;
|
||||
|
||||
/* send protocol change request and wait for a response.
|
||||
* if we time out, assume we're talking with an old version of spacenavd,
|
||||
* which took our packet as a sensitivity value, so restore sensitivity to
|
||||
* 1.0 and continue with protocol v0
|
||||
*/
|
||||
cmd = REQ_TAG | REQ_CHANGE_PROTO | MAX_PROTO_VER;
|
||||
write(s, &cmd, sizeof cmd);
|
||||
if(wait_resp(&cmd, sizeof cmd, TIMEOUT) == -1) {
|
||||
spnav_sensitivity(1.0f);
|
||||
} else {
|
||||
proto = cmd & 0xff;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -269,24 +292,33 @@ static int x11_sensitivity(double sens)
|
|||
|
||||
int spnav_sensitivity(double sens)
|
||||
{
|
||||
struct reqresp rr;
|
||||
|
||||
#ifdef USE_X11
|
||||
if(dpy) {
|
||||
return x11_sensitivity(sens);
|
||||
}
|
||||
#endif
|
||||
|
||||
if(sock) {
|
||||
ssize_t bytes;
|
||||
float fval = sens;
|
||||
if(proto == 0) {
|
||||
if(sock) {
|
||||
ssize_t bytes;
|
||||
float fval = sens;
|
||||
|
||||
while((bytes = write(sock, &fval, sizeof fval)) <= 0 && errno == EINTR);
|
||||
if(bytes <= 0) {
|
||||
return -1;
|
||||
while((bytes = write(sock, &fval, sizeof fval)) <= 0 && errno == EINTR);
|
||||
if(bytes <= 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
rr.data[0] = *(int*)&sens;
|
||||
if(request(REQ_SET_SENS, &rr, TIMEOUT) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spnav_fd(void)
|
||||
|
@ -595,3 +627,135 @@ int catch_badwin(Display *dpy, XErrorEvent *err)
|
|||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int wait_resp(void *buf, int sz, int timeout_ms)
|
||||
{
|
||||
int res;
|
||||
fd_set rdset;
|
||||
struct timeval tv;
|
||||
char *ptr;
|
||||
|
||||
if(timeout_ms) {
|
||||
FD_ZERO(&rdset);
|
||||
FD_SET(sock, &rdset);
|
||||
|
||||
if(timeout_ms > 0) {
|
||||
tv.tv_sec = timeout_ms / 1000;
|
||||
tv.tv_usec = (timeout_ms % 1000) * 1000;
|
||||
}
|
||||
|
||||
while((res = select(sock + 1, &rdset, 0, 0, timeout_ms < 0 ? 0 : &tv)) == -1 && errno == EINTR);
|
||||
}
|
||||
|
||||
if(!timeout_ms || (res > 0 && FD_ISSET(sock, &rdset))) {
|
||||
ptr = buf;
|
||||
while(sz > 0) {
|
||||
if((res = read(sock, ptr, sz)) <= 0 && errno != EINTR) {
|
||||
return -1;
|
||||
}
|
||||
ptr += res;
|
||||
sz -= res;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int request(int req, struct reqresp *rr, int timeout_ms)
|
||||
{
|
||||
if(sock < 0) return -1;
|
||||
|
||||
req |= REQ_TAG;
|
||||
rr->type = req;
|
||||
|
||||
write(sock, rr, sizeof *rr);
|
||||
if(wait_resp(rr, sizeof *rr, TIMEOUT) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(rr->type != req) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int spnav_protocol(void)
|
||||
{
|
||||
return proto;
|
||||
}
|
||||
|
||||
const char *query_str(char *buf, int bufsz, int req)
|
||||
{
|
||||
struct reqresp rr = {0};
|
||||
static char strbuf[512];
|
||||
char *str;
|
||||
|
||||
if(!buf) {
|
||||
buf = strbuf;
|
||||
bufsz = sizeof strbuf;
|
||||
}
|
||||
|
||||
/* if we get a huge name, something is probably wrong, assume it's a mistake */
|
||||
if(request(req, &rr, TIMEOUT) == -1 || rr.data[6] != 0 || rr.data[0] >= 512 || rr.data[0] <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(bufsz < rr.data[0]) {
|
||||
if(!(str = malloc(rr.data[0]))) {
|
||||
str = strbuf;
|
||||
bufsz = sizeof strbuf;
|
||||
}
|
||||
} else {
|
||||
str = buf;
|
||||
}
|
||||
|
||||
if(wait_resp(str, rr.data[0], TIMEOUT) == -1) {
|
||||
if(str != strbuf && str != buf) {
|
||||
free(str);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
str[rr.data[0]] = 0;
|
||||
|
||||
if(str != buf) {
|
||||
strncpy(buf, str, bufsz);
|
||||
buf[bufsz - 1] = 0;
|
||||
}
|
||||
|
||||
if(str != strbuf && str != buf) {
|
||||
free(str);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
const char *spnav_dev_name(char *buf, int bufsz)
|
||||
{
|
||||
if(proto < 1) return 0;
|
||||
return query_str(buf, bufsz, REQ_DEV_NAME);
|
||||
}
|
||||
|
||||
const char *spnav_dev_path(char *buf, int bufsz)
|
||||
{
|
||||
if(proto < 1) return 0;
|
||||
return query_str(buf, bufsz, REQ_DEV_PATH);
|
||||
}
|
||||
|
||||
int spnav_dev_buttons(void)
|
||||
{
|
||||
struct reqresp rr = {0};
|
||||
|
||||
if(proto < 1 || request(REQ_DEV_NBUTTONS, &rr, TIMEOUT) == -1 || rr.data[6] != 0) {
|
||||
return 2; /* default */
|
||||
}
|
||||
return rr.data[0];
|
||||
}
|
||||
|
||||
int spnav_dev_axes(void)
|
||||
{
|
||||
struct reqresp rr = {0};
|
||||
|
||||
if(proto < 1 || request(REQ_DEV_NAXES, &rr, TIMEOUT) == -1 || rr.data[6] != 0) {
|
||||
return 6; /* default */
|
||||
}
|
||||
return rr.data[0];
|
||||
}
|
||||
|
|
37
spnav.h
37
spnav.h
|
@ -130,6 +130,43 @@ int spnav_x11_window(Window win);
|
|||
int spnav_x11_event(const XEvent *xev, spnav_event *event);
|
||||
#endif
|
||||
|
||||
/* returns the protocol version understood by the running spacenavd
|
||||
* -1 on error, or if the connection was established using the X11
|
||||
* protocol (spnav_x11_open).
|
||||
*/
|
||||
int spnav_protocol(void);
|
||||
|
||||
|
||||
/*
|
||||
* Everything from this point on will either fail, or return hardcoded default
|
||||
* values when communicating over the X11 protocol (spnav_x11_open), or when
|
||||
* using a spacenav protocol version less than 1. See spnav_protocol().
|
||||
*/
|
||||
|
||||
/* TODO multi-device support and device selection not implemented yet
|
||||
int spnav_num_devices(void);
|
||||
int spnav_select_device(int dev);
|
||||
*/
|
||||
|
||||
/* Returns a descriptive device name.
|
||||
* The name is copied into buf, and a pointer to it is returned. No more than
|
||||
* bufsz bytes are written including the zero terminator.
|
||||
* buf can be null, in which case a pointer to a static string is returned.
|
||||
*/
|
||||
const char *spnav_dev_name(char *buf, int bufsz);
|
||||
|
||||
/* Returns the path to the device file if applicable.
|
||||
* Usage same as spnav_dev_name.
|
||||
*/
|
||||
const char *spnav_dev_path(char *buf, int bufsz);
|
||||
|
||||
/* returns the number of buttons (defaults to 2 if unable to query) */
|
||||
int spnav_dev_buttons(void);
|
||||
/* returns the number of axes (defaults to 6 if unable to query) */
|
||||
int spnav_dev_axes(void);
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
Ładowanie…
Reference in New Issue