kopia lustrzana https://github.com/FreeSpacenav/libspnav
converted spnav string queries to use the improved version of the
protocol for transmitting stringspull/15/head
rodzic
f04eef2d15
commit
2c0bee29ce
|
@ -40,13 +40,13 @@ int main(void)
|
|||
* motion/button events through the 3dxsrv-compatible X11 protocol.
|
||||
*/
|
||||
if(spnav_x11_open(dpy, win) == -1) {
|
||||
fprintf(stderr, "failed to connect to the space navigator daemon\n");
|
||||
fprintf(stderr, "failed to connect to spacenavd\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
#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 spacenavd\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -78,6 +78,7 @@ int main(void)
|
|||
void print_dev_info(void)
|
||||
{
|
||||
int proto;
|
||||
char buf[256];
|
||||
|
||||
if((proto = spnav_protocol()) == -1) {
|
||||
fprintf(stderr, "failed to query protocol version\n");
|
||||
|
@ -88,8 +89,10 @@ void print_dev_info(void)
|
|||
spnav_client_name("simple example");
|
||||
|
||||
if(proto >= 1) {
|
||||
printf("Device: %s\n", spnav_dev_name(0, 0));
|
||||
printf("Path: %s\n", spnav_dev_path(0, 0));
|
||||
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());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#define DEF_PROTO_REQ_NAMES
|
||||
#include "proto.h"
|
||||
|
||||
|
||||
int proto_send_str(int fd, int req, const char *str)
|
||||
{
|
||||
int len;
|
||||
struct reqresp rr = {0};
|
||||
|
||||
if(fd == -1 || !str) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = strlen(str);
|
||||
rr.type = req;
|
||||
rr.data[6] = len;
|
||||
|
||||
while(len > 0) {
|
||||
memcpy(rr.data, str, len > REQSTR_CHUNK_SIZE ? REQSTR_CHUNK_SIZE : len);
|
||||
write(fd, &rr, sizeof rr);
|
||||
str += REQSTR_CHUNK_SIZE;
|
||||
len -= REQSTR_CHUNK_SIZE;
|
||||
rr.data[6] = len | REQSTR_CONT_BIT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int proto_recv_str(struct reqresp_strbuf *sbuf, struct reqresp *rr)
|
||||
{
|
||||
int len;
|
||||
|
||||
if(rr->data[6] < 0) return -1;
|
||||
len = REQSTR_REMLEN(rr);
|
||||
|
||||
if(REQSTR_FIRST(rr)) {
|
||||
/* first packet, allocate buffer */
|
||||
free(sbuf->buf);
|
||||
sbuf->expect = len;
|
||||
sbuf->size = sbuf->expect + 1;
|
||||
if(!(sbuf->buf = malloc(sbuf->size))) {
|
||||
return -1;
|
||||
}
|
||||
sbuf->endp = sbuf->buf;
|
||||
}
|
||||
|
||||
if(!sbuf->size || !sbuf->buf || !sbuf->endp) {
|
||||
return -1;
|
||||
}
|
||||
if(sbuf->endp < sbuf->buf || sbuf->endp >= sbuf->buf + sbuf->size) {
|
||||
return -1;
|
||||
}
|
||||
if(sbuf->expect > sbuf->size) return -1;
|
||||
if(len != sbuf->expect) return -1;
|
||||
|
||||
if(len > REQSTR_CHUNK_SIZE) {
|
||||
len = REQSTR_CHUNK_SIZE;
|
||||
}
|
||||
memcpy(sbuf->endp, rr->data, len);
|
||||
sbuf->endp += len;
|
||||
sbuf->expect -= len;
|
||||
|
||||
if(sbuf->expect < 0) return -1;
|
||||
|
||||
if(!sbuf->expect) {
|
||||
*sbuf->endp = 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
90
proto.h
90
proto.h
|
@ -1,6 +1,8 @@
|
|||
#ifndef PROTO_H_
|
||||
#define PROTO_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* maximum supported protocol version */
|
||||
#define MAX_PROTO_VER 1
|
||||
|
||||
|
@ -15,30 +17,38 @@ enum {
|
|||
};
|
||||
|
||||
struct reqresp {
|
||||
int type;
|
||||
int data[7];
|
||||
int32_t type;
|
||||
int32_t data[7];
|
||||
};
|
||||
|
||||
struct reqresp_strbuf {
|
||||
char *buf, *endp;
|
||||
int size;
|
||||
int expect;
|
||||
};
|
||||
|
||||
#define REQ_TAG 0x7faa0000
|
||||
#define REQ_BASE 0x1000
|
||||
|
||||
/* REQ_S* are set, REQ_G* are get requests.
|
||||
/* REQ_S* are set requests, 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
|
||||
*
|
||||
* "remaining length" fields in string transfers have 16 valid bits. Bit 16 is
|
||||
* used as a flag: 0 for the first packet, 1 for continuation packets.
|
||||
*/
|
||||
enum {
|
||||
/* per-client settings */
|
||||
REQ_SET_NAME = REQ_BASE,/* set client name: Q[0-6] name - R[6] status */
|
||||
REQ_SET_NAME = REQ_BASE,/* set client name: Q[0-5] next 24 bytes Q[6] remaining length - R[6] status */
|
||||
REQ_SET_SENS, /* set client sensitivity: Q[0] float - R[6] status */
|
||||
REQ_GET_SENS, /* get client sensitivity: R[0] float R[6] status */
|
||||
REQ_SET_EVMASK, /* set event mask: Q[0] mask - R[6] status */
|
||||
REQ_GET_EVMASK, /* get event mask: R[0] mask R[6] status */
|
||||
|
||||
/* device queries */
|
||||
REQ_DEV_NAME = 0x2000, /* get device name: R[0] length R[6] status followed
|
||||
by <length> bytes */
|
||||
REQ_DEV_NAME = 0x2000, /* get device name: R[0-5] next 24 bytes R[6] remaining length or -1 for failure */
|
||||
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 */
|
||||
|
@ -69,8 +79,8 @@ enum {
|
|||
REQ_GCFG_LED, /* get LED state: R[0] state R[6] status */
|
||||
REQ_SCFG_GRAB, /* set device grabbing: Q[0] state - R[6] status */
|
||||
REQ_GCFG_GRAB, /* get device grabbing: R[0] state R[6] status */
|
||||
REQ_SCFG_SERDEV, /* set serial device path: Q[0] length, followed by <length> bytes - R[6] status */
|
||||
REQ_GCFG_SERDEV, /* get serial device path: R[0] length R[6] status, followed by <length> bytes */
|
||||
REQ_SCFG_SERDEV, /* set serial device path: Q[0-5] next 24 bytes Q[6] remaining length - R[6] status */
|
||||
REQ_GCFG_SERDEV, /* get serial device path: R[0-5] next 24 bytes R[6] remaining length or -1 for failure */
|
||||
/* TODO ... more */
|
||||
REQ_CFG_SAVE = 0x3ffe, /* save config file: R[6] status */
|
||||
REQ_CFG_RESTORE, /* load config from file: R[6] status */
|
||||
|
@ -109,4 +119,68 @@ enum {
|
|||
DEV_SMMOD /* SpaceMouse Module */
|
||||
};
|
||||
|
||||
|
||||
#define REQSTR_CHUNK_SIZE 24
|
||||
#define REQSTR_CONT_BIT 0x10000
|
||||
#define REQSTR_FIRST(rr) (((rr)->data[6] & REQSTR_CONT_BIT) == 0)
|
||||
#define REQSTR_REMLEN(rr) ((rr)->data[6] & 0xffff)
|
||||
|
||||
int proto_send_str(int fd, int req, const char *str);
|
||||
int proto_recv_str(struct reqresp_strbuf *sbuf, struct reqresp *rr);
|
||||
|
||||
#ifdef DEF_PROTO_REQ_NAMES
|
||||
const char *reqnames_1000[] = {
|
||||
"SET_NAME",
|
||||
"SET_SENS",
|
||||
"GET_SENS",
|
||||
"SET_EVMASK",
|
||||
"GET_EVMASK"
|
||||
};
|
||||
const char *reqnames_2000[] = {
|
||||
"DEV_NAME",
|
||||
"DEV_PATH",
|
||||
"DEV_NAXES",
|
||||
"DEV_NBUTTONS",
|
||||
"DEV_USBID",
|
||||
"DEV_TYPE"
|
||||
};
|
||||
const char *reqnames_3000[] = {
|
||||
"SCFG_SENS",
|
||||
"GCFG_SENS",
|
||||
"SCFG_SENS_AXIS",
|
||||
"GCFG_SENS_AXIS",
|
||||
"SCFG_DEADZONE",
|
||||
"GCFG_DEADZONE",
|
||||
"SCFG_INVERT",
|
||||
"GCFG_INVERT",
|
||||
"SCFG_AXISMAP",
|
||||
"GCFG_AXISMAP",
|
||||
"SCFG_BNMAP",
|
||||
"GCFG_BNMAP",
|
||||
"SCFG_BNACTION",
|
||||
"GCFG_BNACTION",
|
||||
"SCFG_KBMAP",
|
||||
"GCFG_KBMAP",
|
||||
"SCFG_SWAPYZ",
|
||||
"GCFG_SWAPYZ",
|
||||
"SCFG_LED",
|
||||
"GCFG_LED",
|
||||
"SCFG_GRAB",
|
||||
"GCFG_GRAB",
|
||||
"SCFG_SERDEV",
|
||||
"GCFG_SERDEV"
|
||||
};
|
||||
|
||||
const int reqnames_1000_size = sizeof reqnames_1000 / sizeof *reqnames_1000;
|
||||
const int reqnames_2000_size = sizeof reqnames_2000 / sizeof *reqnames_2000;
|
||||
const int reqnames_3000_size = sizeof reqnames_3000 / sizeof *reqnames_3000;
|
||||
#else
|
||||
extern const char *reqnames_1000[];
|
||||
extern const char *reqnames_2000[];
|
||||
extern const char *reqnames_3000[];
|
||||
extern const int reqnames_1000_size;
|
||||
extern const int reqnames_2000_size;
|
||||
extern const int reqnames_3000_size;
|
||||
#endif /* DEF_PROTO_REQ_NAMES */
|
||||
|
||||
#endif /* PROTO_H_ */
|
||||
|
|
118
spnav.c
118
spnav.c
|
@ -55,6 +55,7 @@ static int proc_event(int *data, spnav_event *event);
|
|||
|
||||
static int wait_resp(void *buf, int sz, int timeout_ms);
|
||||
static int request(int req, struct reqresp *rr, int timeout_ms);
|
||||
static int request_str(int req, char *buf, int bufsz, int timeout_ms);
|
||||
|
||||
|
||||
static Display *dpy;
|
||||
|
@ -712,10 +713,43 @@ static int request(int req, struct reqresp *rr, int timeout_ms)
|
|||
}
|
||||
|
||||
/* XXX assuming data[6] is always status */
|
||||
if(rr->type != req || rr->data[6] != 0) return -1;
|
||||
if(rr->type != req || rr->data[6] < 0) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int request_str(int req, char *buf, int bufsz, int timeout_ms)
|
||||
{
|
||||
int res = -1;
|
||||
struct reqresp rr = {0};
|
||||
struct reqresp_strbuf sbuf = {0};
|
||||
|
||||
/* flush any pending data from the buffer which can cause de-syncing */
|
||||
while(wait_resp(&rr, sizeof rr, 1) != -1);
|
||||
|
||||
if(request(req, &rr, timeout_ms) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
while((res = proto_recv_str(&sbuf, &rr)) == 0) {
|
||||
if(wait_resp(&rr, sizeof rr, timeout_ms) == -1) {
|
||||
free(sbuf.buf);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if(res == -1) {
|
||||
free(sbuf.buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(buf) {
|
||||
strncpy(buf, sbuf.buf, bufsz - 1);
|
||||
buf[bufsz - 1] = 0;
|
||||
}
|
||||
free(sbuf.buf);
|
||||
return sbuf.size - 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int spnav_protocol(void)
|
||||
|
@ -725,68 +759,17 @@ int spnav_protocol(void)
|
|||
|
||||
int spnav_client_name(const char *name)
|
||||
{
|
||||
struct reqresp rr = {0};
|
||||
int len = strlen(name);
|
||||
if(len > sizeof rr.data) {
|
||||
len = sizeof rr.data;
|
||||
}
|
||||
memcpy(rr.data, name, len);
|
||||
|
||||
return request(REQ_SET_NAME, &rr, TIMEOUT);
|
||||
return proto_send_str(sock, REQ_SET_NAME, name);
|
||||
}
|
||||
|
||||
const char *query_str(char *buf, int bufsz, int req)
|
||||
int spnav_dev_name(char *buf, int bufsz)
|
||||
{
|
||||
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] + 1))) {
|
||||
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;
|
||||
return request_str(REQ_DEV_NAME, buf, bufsz, TIMEOUT);
|
||||
}
|
||||
|
||||
const char *spnav_dev_name(char *buf, int bufsz)
|
||||
int spnav_dev_path(char *buf, int bufsz)
|
||||
{
|
||||
return query_str(buf, bufsz, REQ_DEV_NAME);
|
||||
}
|
||||
|
||||
const char *spnav_dev_path(char *buf, int bufsz)
|
||||
{
|
||||
return query_str(buf, bufsz, REQ_DEV_PATH);
|
||||
return request_str(REQ_DEV_PATH, buf, bufsz, TIMEOUT);
|
||||
}
|
||||
|
||||
int spnav_dev_buttons(void)
|
||||
|
@ -1063,25 +1046,10 @@ int spnav_cfg_get_grab(void)
|
|||
|
||||
int spnav_cfg_set_serial(const char *devpath)
|
||||
{
|
||||
struct reqresp rr = {0};
|
||||
int i, len = strlen(devpath);
|
||||
|
||||
while(len > 0) {
|
||||
rr.data[0] = len;
|
||||
for(i=0; i<6; i++) {
|
||||
rr.data[i + 1] = *devpath ? *devpath++ : 0;
|
||||
}
|
||||
len -= 6;
|
||||
|
||||
if(request(REQ_SCFG_SERDEV, &rr, TIMEOUT) == -1) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return proto_send_str(sock, REQ_SCFG_SERDEV, devpath);
|
||||
}
|
||||
|
||||
const char *spnav_cfg_get_serial(char *buf, int bufsz)
|
||||
int spnav_cfg_get_serial(char *buf, int bufsz)
|
||||
{
|
||||
return query_str(buf, bufsz, REQ_GCFG_SERDEV);
|
||||
return request_str(REQ_GCFG_SERDEV, buf, bufsz, TIMEOUT);
|
||||
}
|
||||
|
||||
|
|
13
spnav.h
13
spnav.h
|
@ -186,16 +186,17 @@ 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.
|
||||
* If buf is not null, the name is copied into buf. No more than bufsz bytes are
|
||||
* written, including the zero terminator.
|
||||
* The number of bytes that would have been written assuming enough space in buf
|
||||
* are returned, including the zero terminator.
|
||||
*/
|
||||
const char *spnav_dev_name(char *buf, int bufsz);
|
||||
int 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);
|
||||
int spnav_dev_path(char *buf, int bufsz);
|
||||
|
||||
/* returns the number of buttons (defaults to 2 if unable to query) */
|
||||
int spnav_dev_buttons(void);
|
||||
|
@ -325,7 +326,7 @@ int spnav_cfg_set_grab(int state);
|
|||
int spnav_cfg_get_grab(void); /* returns 0:no-grab/1:grab, or -1 on error */
|
||||
|
||||
int spnav_cfg_set_serial(const char *devpath);
|
||||
const char *spnav_cfg_get_serial(char *buf, int bufsz);
|
||||
int spnav_cfg_get_serial(char *buf, int bufsz);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue