From 7145788c5e1c0c381aec791e157dab6b6a4144ab Mon Sep 17 00:00:00 2001 From: John Tsiombikas Date: Thu, 10 Feb 2022 12:58:33 +0200 Subject: [PATCH] started working on the spacenavd protocol v1 --- src/client.c | 27 ++---- src/client.h | 20 ++++- src/event.c | 11 ++- src/proto_unix.c | 221 +++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 247 insertions(+), 32 deletions(-) diff --git a/src/client.c b/src/client.c index 5d13386..320d54a 100644 --- a/src/client.c +++ b/src/client.c @@ -28,21 +28,6 @@ along with this program. If not, see . #include #endif -struct client { - int type; - - int sock; /* UNIX domain socket */ -#ifdef USE_X11 - Window win; /* X11 client window */ -#endif - - float sens; /* sensitivity */ - int dev_idx; /* device index */ - - struct client *next; -}; - - static struct client *client_list = NULL; static struct client *client_iter; /* iterator (used by first/next calls) */ @@ -75,9 +60,11 @@ struct client *add_client(int type, void *cdata) client->win = *(Window*)cdata; #endif } + /* default to protocol version 0 until the client changes it */ + client->proto = 0; client->sens = 1.0f; - client->dev_idx = 0; /* default/first device */ + client->dev = 0; /* default/first device */ if(!client_list && cfg.led == LED_AUTO) { /* on first client, turn the led on */ @@ -144,14 +131,14 @@ float get_client_sensitivity(struct client *client) return client->sens; } -void set_client_device_index(struct client *client, int dev_idx) +void set_client_device(struct client *client, struct device *dev) { - client->dev_idx = dev_idx; + client->dev = dev; } -int get_client_device_index(struct client *client) +struct device *get_client_device(struct client *client) { - return client->dev_idx; + return client->dev ? client->dev : get_devices(); } struct client *first_client(void) diff --git a/src/client.h b/src/client.h index ec9c7c6..8855c6b 100644 --- a/src/client.h +++ b/src/client.h @@ -31,8 +31,22 @@ enum { CLIENT_UNIX /* through the new UNIX domain socket */ }; +struct device; -struct client; +struct client { + int type; + + int sock; /* UNIX domain socket */ + int proto; /* protocol version */ +#ifdef USE_X11 + Window win; /* X11 client window */ +#endif + + float sens; /* sensitivity */ + struct device *dev; + + struct client *next; +}; struct client *add_client(int type, void *cdata); void remove_client(struct client *client); @@ -46,8 +60,8 @@ Window get_client_window(struct client *client); void set_client_sensitivity(struct client *client, float sens); float get_client_sensitivity(struct client *client); -void set_client_device_index(struct client *client, int dev_idx); -int get_client_device_index(struct client *client); +void set_client_device(struct client *client, struct device *dev); +struct device *get_client_device(struct client *client); /* these two can be used to iterate over all clients */ struct client *first_client(void); diff --git a/src/event.c b/src/event.c index 2952cd1..b106daf 100644 --- a/src/event.c +++ b/src/event.c @@ -278,7 +278,7 @@ void repeat_last_event(struct device *dev) static void dispatch_event(struct dev_event *dev_ev) { struct client *c, *client_iter; - int dev_idx; + struct device *client_dev; if(dev_ev->event.type == EVENT_MOTION) { struct timeval tv; @@ -288,13 +288,18 @@ static void dispatch_event(struct dev_event *dev_ev) dev_ev->timeval = tv; } - dev_idx = get_device_index(dev_ev->dev); client_iter = first_client(); while(client_iter) { c = client_iter; client_iter = next_client(); - if(get_client_device_index(c) <= dev_idx) /* use <= until API changes, else == */ + + /* if the client has selected a single device to get input from, then + * don't send the event if it originates from a different device + */ + client_dev = get_client_device(c); + if(!client_dev || client_dev == dev_ev->dev) { send_event(&dev_ev->event, c); + } } } diff --git a/src/proto_unix.c b/src/proto_unix.c index 998f234..dc51cd8 100644 --- a/src/proto_unix.c +++ b/src/proto_unix.c @@ -1,6 +1,6 @@ /* spacenavd - a free software replacement driver for 6dof space-mice. -Copyright (C) 2007-2019 John Tsiombikas +Copyright (C) 2007-2022 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,14 +20,57 @@ along with this program. If not, see . #include #include #include +#include #include #include +#include #include #include #include #include "proto_unix.h" #include "spnavd.h" +/* maximum supported protocol version */ +#define MAX_PROTO_VER 1 + +struct reqresp { + int type; + int data[7]; +}; + +/* 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, /* 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 = 0x1000, /* get device name: R[0] length R[6] status followed + by 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 = 0x2000, /* 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 = 0x7faa5500 +}; + enum { UEV_TYPE_MOTION, UEV_TYPE_PRESS, @@ -36,6 +79,10 @@ enum { static int lsock = -1; + +static int handle_request(struct client *c, struct reqresp *req); + + int init_unix(void) { int s; @@ -125,6 +172,9 @@ void send_uevent(spnav_event *ev, struct client *c) int handle_uevents(fd_set *rset) { struct client *citer; + static char reqbuf[64]; + static int reqbytes; + struct reqresp *req; if(lsock == -1) { return -1; @@ -137,6 +187,9 @@ int handle_uevents(fd_set *rset) if((s = accept(lsock, 0, 0)) == -1) { logmsg(LOG_ERR, "error while accepting connection on the UNIX socket: %s\n", strerror(errno)); } else { + /* set socket as non-blocking and add client to the list */ + fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK); + if(!add_client(CLIENT_UNIX, &s)) { logmsg(LOG_ERR, "failed to add client: %s\n", strerror(errno)); } @@ -153,22 +206,178 @@ int handle_uevents(fd_set *rset) int s = get_client_socket(c); if(FD_ISSET(s, rset)) { - int rdbytes; + int rdbytes, msg; float sens; - /* got a request from a client, decode and execute it */ - /* XXX currently only sensitivity comes from clients */ + /* handle client requests */ - while((rdbytes = read(s, &sens, sizeof sens)) <= 0 && errno == EINTR); + while((rdbytes = read(s, &msg, sizeof msg)) <= 0 && errno == EINTR); if(rdbytes <= 0) { /* something went wrong... disconnect client */ close(get_client_socket(c)); remove_client(c); continue; } - set_client_sensitivity(c, sens); + /* only handle magic NaN protocol change requests, when the + * client is operating in protocol 0 mode + */ + if(c->proto == 0 && (msg & 0xffffff00) == REQ_CHANGE_PROTO) { + c->proto = msg & 0xff; + + /* if the client requests a protocol version higher than the + * daemon supports, return the maximum supported version and + * switch to that. + */ + if(c->proto > MAX_PROTO_VER) { + c->proto = MAX_PROTO_VER; + msg = REQ_CHANGE_PROTO | MAX_PROTO_VER; + write(s, &msg, sizeof msg); + } + continue; + } + + switch(c->proto) { + case 0: + /* protocol v0: only sensitivity comes from clients */ + sens = *(float*)&msg; + if(isfinite(sens)) { + set_client_sensitivity(c, sens); + } + break; + + case 1: + /* protocol v1: accumulate request bytes, and process */ + reqbytes += read(s, reqbuf + reqbytes, sizeof *req - reqbytes); + if(reqbytes >= sizeof *req) { + reqbytes = 0; + req = (struct reqresp*)reqbuf; + if(handle_request(c, req) == -1) { + close(s); + remove_client(c); + } + } + break; + } + } + } + } + + return 0; +} + +static int sendresp(struct client *c, struct reqresp *rr, int status) +{ + rr->data[6] = status; + return write(get_client_socket(c), rr, sizeof *rr); +} + +static int handle_request(struct client *c, struct reqresp *req) +{ + int i; + float fval, fvec[6]; + struct device *dev; + + switch(req->type) { + case REQ_SET_SENS: + fval = *(float*)req->data; + if(isfinite(fval)) { + set_client_sensitivity(c, fval); + sendresp(c, req, 0); + } else { + logmsg(LOG_WARNING, "client attempted to set invalid client sensitivity\n"); + sendresp(c, req, -1); + } + break; + + case REQ_GET_SENS: + fval = get_client_sensitivity(c); + req->data[0] = *(int*)&fval; + sendresp(c, req, 0); + break; + + case REQ_DEV_NAME: + if((dev = get_client_device(c))) { + req->data[0] = strlen(dev->name); + sendresp(c, req, 0); + write(get_client_socket(c), dev->name, req->data[0]); + } else { + sendresp(c, req, -1); + } + break; + + case REQ_DEV_PATH: + if((dev = get_client_device(c))) { + req->data[0] = strlen(dev->name); + sendresp(c, req, 0); + write(get_client_socket(c), dev->path, req->data[0]); + } else { + sendresp(c, req, -1); + } + break; + + case REQ_DEV_NAXES: + if((dev = get_client_device(c))) { + req->data[0] = dev->num_axes; + sendresp(c, req, 0); + } else { + sendresp(c, req, -1); + } + break; + + case REQ_DEV_NBUTTONS: + if((dev = get_client_device(c))) { + req->data[0] = dev->num_buttons; + sendresp(c, req, 0); + } else { + sendresp(c, req, -1); + } + break; + + case REQ_SCFG_SENS: + fval = *(float*)req->data; + if(isfinite(fval)) { + cfg.sensitivity = fval; + sendresp(c, req, 0); + } else { + logmsg(LOG_WARNING, "client attempted to set invalid global sensitivity\n"); + sendresp(c, req, -1); + } + break; + + case REQ_GCFG_SENS: + req->data[0] = *(int*)&cfg.sensitivity; + sendresp(c, req, 0); + break; + + case REQ_SCFG_SENS_AXIS: + for(i=0; i<6; i++) { + fvec[i] = ((float*)req->data)[i]; + if(!isfinite(fvec[i])) { + logmsg(LOG_WARNING, "client attempted to set invalid axis %d sensitivity\n", i); + sendresp(c, req, -1); + return 0; } } + for(i=0; i<3; i++) { + cfg.sens_trans[i] = fvec[i]; + cfg.sens_rot[i] = fvec[i + 3]; + } + sendresp(c, req, 0); + break; + + case REQ_GCFG_SENS_AXIS: + for(i=0; i<3; i++) { + req->data[i] = *(int*)(cfg.sens_trans + i); + req->data[i + 3] = *(int*)(cfg.sens_rot + i); + } + sendresp(c, req, 0); + break; + + /* TODO ... more */ + + default: + logmsg(LOG_WARNING, "invalid client request: %04xh\n", (unsigned int)req->type); + sendresp(c, req, -1); } return 0;