/* spacenavd - a free software replacement driver for 6dof space-mice. 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 the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "proto.h" #include "proto_unix.h" #include "spnavd.h" #ifdef USE_X11 #include "kbemu.h" #endif #ifndef isfinite #define isfinite(x) (!isnan(x)) #endif static int lsock = -1; static int handle_request(struct client *c, struct reqresp *req); static const char *reqstr(int req); int init_unix(void) { int s; mode_t prev_umask; struct sockaddr_un addr; if(lsock >= 0) return 0; if((s = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) { logmsg(LOG_ERR, "failed to create socket: %s\n", strerror(errno)); return -1; } unlink(SOCK_NAME); /* in case it already exists */ memset(&addr, 0, sizeof addr); addr.sun_family = AF_UNIX; strcpy(addr.sun_path, SOCK_NAME); prev_umask = umask(0); if(bind(s, (struct sockaddr*)&addr, sizeof addr) == -1) { logmsg(LOG_ERR, "failed to bind unix socket: %s: %s\n", SOCK_NAME, strerror(errno)); close(s); return -1; } umask(prev_umask); if(listen(s, 8) == -1) { logmsg(LOG_ERR, "listen failed: %s\n", strerror(errno)); close(s); unlink(SOCK_NAME); return -1; } lsock = s; return 0; } void close_unix(void) { if(lsock != -1) { close(lsock); lsock = -1; unlink(SOCK_NAME); } } int get_unix_socket(void) { return lsock; } void send_uevent(spnav_event *ev, struct client *c) { int i; int32_t data[8] = {0}; float motion_mul; if(lsock == -1) return; switch(ev->type) { case EVENT_MOTION: if(!(c->evmask & EVMASK_MOTION)) return; data[0] = UEV_MOTION; motion_mul = get_client_sensitivity(c); for(i=0; i<6; i++) { float val = (float)ev->motion.data[i] * motion_mul; data[i + 1] = (int32_t)val; } data[7] = ev->motion.period; break; case EVENT_RAWAXIS: if(!(c->evmask & EVMASK_RAWAXIS)) return; data[0] = UEV_RAWAXIS; data[1] = ev->axis.idx; data[2] = ev->axis.value; break; case EVENT_BUTTON: if(!(c->evmask & EVMASK_BUTTON)) return; data[0] = ev->button.press ? UEV_PRESS : UEV_RELEASE; data[1] = ev->button.bnum; data[2] = ev->button.press; break; case EVENT_RAWBUTTON: if(!(c->evmask & EVMASK_RAWBUTTON)) return; data[0] = UEV_RAWBUTTON; data[1] = ev->button.bnum; data[2] = ev->button.press; break; case EVENT_DEV: if(!(c->evmask & EVMASK_DEV)) return; data[0] = UEV_DEV; data[1] = ev->dev.op; data[2] = ev->dev.id; data[3] = ev->dev.devtype; data[4] = ev->dev.usbid[0]; data[5] = ev->dev.usbid[1]; break; case EVENT_CFG: if(!(c->evmask & EVMASK_CFG)) return; data[0] = UEV_CFG; data[1] = ev->cfg.cfg; memcpy(data + 2, ev->cfg.data, sizeof ev->cfg.data); break; default: return; } while(write(get_client_socket(c), data, sizeof data) == -1 && errno == EINTR); } int handle_uevents(fd_set *rset) { struct client *citer; struct reqresp *req; if(lsock == -1) { return -1; } if(FD_ISSET(lsock, rset)) { /* got an incoming connection */ int s; 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)); } } } /* all the UNIX socket clients */ citer = first_client(); while(citer) { struct client *c = citer; citer = next_client(); if(get_client_type(c) == CLIENT_UNIX) { int s = get_client_socket(c); if(FD_ISSET(s, rset)) { int rdbytes; int32_t msg; float sens; /* handle client requests */ switch(c->proto) { case 0: 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; } /* handle magic NaN protocol change requests */ if((msg & 0xffffff00) == (REQ_TAG | 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_TAG | REQ_CHANGE_PROTO | MAX_PROTO_VER; } write(s, &msg, sizeof msg); if(c->proto > 0) { /* set default event mask for proto-v1 clients */ c->evmask = EVMASK_MOTION | EVMASK_BUTTON | EVMASK_DEV; } continue; } /* 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 */ while((rdbytes = read(s, c->reqbuf + c->reqbytes, sizeof *req - c->reqbytes)) < 0 && errno == EINTR); if(rdbytes <= 0) { close(s); remove_client(c); continue; } c->reqbytes += rdbytes; if(c->reqbytes >= sizeof *req) { req = (struct reqresp*)c->reqbuf; c->reqbytes = 0; 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); } #define AXIS_VALID(x) ((x) >= 0 && (x) < MAX_AXES) #define BN_VALID(x) ((x) >= 0 && (x) < MAX_BUTTONS) #define BNACT_VALID(x) ((x) >= 0 && (x) < MAX_BNACT) static int handle_request(struct client *c, struct reqresp *req) { int i, idx, res; float fval, fvec[6]; struct device *dev; const char *str = 0; logmsg(LOG_DEBUG, "request %s - %x %x %x %x %x %x\n", reqstr(req->type), req->data[0], req->data[1], req->data[2], req->data[3], req->data[4], req->data[5], req->data[6]); switch(req->type & 0xffff) { case REQ_SET_NAME: if((res = spnav_recv_str(&c->strbuf, req)) == -1) { logmsg(LOG_ERR, "SET_NAME: failed to receive string\n"); break; } if(res) { c->name = c->strbuf.buf; c->strbuf.buf = 0; logmsg(LOG_INFO, "client name: %s\n", c->name); } break; 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_SET_EVMASK: c->evmask = req->data[0]; sendresp(c, req, 0); break; case REQ_GET_EVMASK: req->data[0] = c->evmask; sendresp(c, req, 0); break; case REQ_DEV_NAME: if((dev = get_client_device(c))) { spnav_send_str(get_client_socket(c), req->type, dev->name); } else { sendresp(c, req, -1); } break; case REQ_DEV_PATH: if((dev = get_client_device(c))) { spnav_send_str(get_client_socket(c), req->type, dev->path); } 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_DEV_USBID: if((dev = get_client_device(c)) && dev->usbid[0] && dev->usbid[1]) { req->data[0] = dev->usbid[0]; req->data[1] = dev->usbid[1]; sendresp(c, req, 0); } else { sendresp(c, req, -1); } break; case REQ_DEV_TYPE: if((dev = get_client_device(c))) { req->data[0] = dev->type; 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; case REQ_SCFG_DEADZONE: if(!AXIS_VALID(req->data[0])) { logmsg(LOG_WARNING, "client attempted to set invalid axis deadzone: %d\n", req->data[0]); sendresp(c, req, -1); return 0; } cfg.dead_threshold[req->data[0]] = req->data[1]; sendresp(c, req, 0); break; case REQ_GCFG_DEADZONE: if(!AXIS_VALID(req->data[0])) { logmsg(LOG_WARNING, "client requested invalid axis deadzone: %d\n", req->data[0]); sendresp(c, req, -1); return 0; } req->data[1] = cfg.dead_threshold[req->data[0]]; sendresp(c, req, 0); break; case REQ_SCFG_INVERT: for(i=0; i<6; i++) { cfg.invert[i] = req->data[i] ? 1 : 0; } sendresp(c, req, 0); break; case REQ_GCFG_INVERT: memcpy(req->data, cfg.invert, 6 * sizeof(int)); sendresp(c, req, 0); break; case REQ_SCFG_AXISMAP: if(!AXIS_VALID(req->data[0]) || req->data[1] < -1 || req->data[1] >= 6) { logmsg(LOG_WARNING, "client attempted to set invalid axis mapping: %d -> %d\n", req->data[0], req->data[1]); sendresp(c, req, -1); return 0; } cfg.map_axis[req->data[0]] = req->data[1]; sendresp(c, req, 0); break; case REQ_GCFG_AXISMAP: if(!AXIS_VALID(req->data[0])) { logmsg(LOG_WARNING, "client queried mapping of invalid axis: %d\n", req->data[0]); sendresp(c, req, -1); return 0; } req->data[1] = cfg.map_axis[req->data[0]]; sendresp(c, req, 0); break; case REQ_SCFG_BNMAP: if(!BN_VALID(req->data[0]) || !BN_VALID(req->data[1])) { logmsg(LOG_WARNING, "client attempted to set invalid button mapping: %d -> %d\n", req->data[0], req->data[1]); sendresp(c, req, -1); return 0; } cfg.map_button[req->data[0]] = req->data[1]; sendresp(c, req, 0); break; case REQ_GCFG_BNMAP: if(!BN_VALID(req->data[0])) { logmsg(LOG_WARNING, "client queried mapping of invalid button: %d\n", req->data[0]); sendresp(c, req, -1); return 0; } req->data[1] = cfg.map_button[req->data[0]]; sendresp(c, req, 0); break; case REQ_SCFG_BNACTION: if(!BN_VALID(req->data[0]) || !BNACT_VALID(req->data[1])) { logmsg(LOG_WARNING, "client attempted to set invalid button action: %d -> %d\n", req->data[0], req->data[1]); sendresp(c, req, -1); return 0; } cfg.bnact[req->data[0]] = req->data[1]; sendresp(c, req, 0); break; case REQ_GCFG_BNACTION: if(!BN_VALID(req->data[0])) { logmsg(LOG_WARNING, "client queried action bound to invalid button: %d\n", req->data[0]); sendresp(c, req, -1); return 0; } req->data[1] = cfg.bnact[req->data[0]]; sendresp(c, req, 0); break; case REQ_SCFG_KBMAP: #ifdef USE_X11 idx = req->data[0]; if(!BN_VALID(idx) || (req->data[1] > 0 && !(str = kbemu_keyname(req->data[1])))) { logmsg(LOG_WARNING, "client attempted to set invalid key map: %d -> %x\n", idx, (unsigned int)req->data[1]); sendresp(c, req, -1); return 0; } cfg.kbmap[idx] = req->data[1]; free(cfg.kbmap_str[idx]); cfg.kbmap_str[idx] = req->data[1] > 0 ? strdup(str) : 0; sendresp(c, req, 0); #else logmsg(LOG_WARNING, "unable to set keyboard mappings, daemon compiled without X11 support\n"); sendresp(c, req, -1); #endif break; case REQ_GCFG_KBMAP: #ifdef USE_X11 idx = req->data[0]; if(!BN_VALID(idx)) { logmsg(LOG_WARNING, "client queried keyboard mapping for invalid button: %d\n", idx); sendresp(c, req, -1); return 0; } if(cfg.kbmap_str[idx]) { if(!cfg.kbmap[idx]) { cfg.kbmap[idx] = kbemu_keysym(cfg.kbmap_str[idx]); } req->data[1] = cfg.kbmap[idx]; } else { req->data[1] = 0; } sendresp(c, req, 0); #else logmsg(LOG_WARNING, "unable to query keyboard mappings, daemon compiled without X11 support\n"); sendresp(c, req, -1); #endif break; case REQ_SCFG_SWAPYZ: cfg.swapyz = req->data[0] ? 1 : 0; sendresp(c, req, 0); case REQ_GCFG_SWAPYZ: req->data[0] = cfg.swapyz; sendresp(c, req, 0); break; case REQ_SCFG_LED: if(req->data[0] < 0 || req->data[0] >= 3) { sendresp(c, req, -1); break; } cfg.led = req->data[0]; cfg_changed(); sendresp(c, req, 0); break; case REQ_GCFG_LED: req->data[0] = cfg.led; sendresp(c, req, 0); break; case REQ_SCFG_GRAB: cfg.grab_device = req->data[0] ? 1 : 0; sendresp(c, req, 0); break; case REQ_GCFG_GRAB: req->data[0] = cfg.grab_device; sendresp(c, req, 0); break; case REQ_SCFG_SERDEV: if((res = spnav_recv_str(&c->strbuf, req)) == -1) { logmsg(LOG_ERR, "SCFG_SERDEV: failed to receive string\n"); break; } if(res) { strncpy(cfg.serial_dev, c->strbuf.buf, sizeof cfg.serial_dev - 1); cfg.serial_dev[sizeof cfg.serial_dev - 1] = 0; cfg_changed(); } break; case REQ_GCFG_SERDEV: spnav_send_str(c->sock, req->type, cfg.serial_dev); break; case REQ_CFG_SAVE: sendresp(c, req, write_cfg(cfgfile, &cfg)); break; case REQ_CFG_RESTORE: if(read_cfg(cfgfile, &cfg) == -1) { logmsg(LOG_INFO, "config restore requested but failed to read %s, restoring defaults instead\n", cfgfile); default_cfg(&cfg); } cfg_changed(); sendresp(c, req, 0); break; case REQ_CFG_RESET: default_cfg(&cfg); cfg_changed(); sendresp(c, req, 0); break; default: logmsg(LOG_WARNING, "invalid client request: %s\n", reqstr(req->type)); sendresp(c, req, -1); } return 0; } static const char *reqstr(int req) { static char buf[8]; req &= 0xffff; if(req >= 0x1000 && req < 0x1000 + spnav_reqnames_1000_size) { return spnav_reqnames_1000[req - 0x1000]; } if(req >= 0x2000 && req < 0x2000 + spnav_reqnames_2000_size) { return spnav_reqnames_2000[req - 0x2000]; } if(req >= 0x3000 && req < 0x3000 + spnav_reqnames_3000_size) { return spnav_reqnames_3000[req - 0x3000]; } switch(req) { case REQ_CFG_SAVE: return "CFG_SAVE"; case REQ_CFG_RESTORE: return "CFG_RESTORE"; case REQ_CFG_RESET: return "CFG_RESET"; default: break; } sprintf(buf, "0x%04x", req); return buf; }