#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include "qxl_option_helpers.h" #include "spiceqxl_util.h" #include "spiceqxl_inputs.h" #include "spiceqxl_uinput.h" static const char *uinput_filename; static int uinput_fd; static SpiceWatch *uinput_watch; static struct input_event inp_event; static int offset; static void spiceqxl_uinput_read_cb(int fd, int event, void *opaque) { int n; static int x = -1; static int y = -1; static int buttons_state = 0; int button = -1; n = read(uinput_fd, (char *)&inp_event + offset, sizeof(inp_event) - offset); if (n == -1) { if (errno != EAGAIN && errno != EINTR && errno != EWOULDBLOCK) { fprintf(stderr, "spice: uinput read failed: %s\n", strerror(errno)); } return; } offset += n; if (offset < sizeof(inp_event)) { return; } offset = 0; switch (inp_event.type) { case EV_KEY: /* XXX Here we hardcode vdagent-uinput.c mapping since we don't support ioctls. * We could replace the ioctls with additional non uinput messages * used in vdagentd fake uinput mode. */ switch (inp_event.code) { case BTN_LEFT: button = 1 << 0; break; case BTN_MIDDLE: button = 1 << 1; break; case BTN_RIGHT: button = 1 << 2; break; } if (inp_event.value > 0) { buttons_state |= button; } else { buttons_state &= ~button; } spiceqxl_tablet_buttons(buttons_state); break; case EV_REL: button = 1; if (inp_event.value == 1) { button = 1 << 3; } else { button = 1 << 4; } buttons_state |= button; spiceqxl_tablet_buttons(buttons_state); buttons_state &= ~button; spiceqxl_tablet_buttons(buttons_state); break; case EV_ABS: switch (inp_event.code) { case ABS_X: x = inp_event.value; break; case ABS_Y: y = inp_event.value; break; default: fprintf(stderr, "%s: unknown axis %d, ignoring\n", __func__, inp_event.code); return; break; } spiceqxl_tablet_position(x, y, buttons_state); break; } } void spiceqxl_uinput_init(qxl_screen_t *qxl) { int ret; int enabled; uinput_filename = get_str_option(qxl->options, OPTION_SPICE_VDAGENT_UINPUT_PATH, "XSPICE_VDAGENT_UINPUT_PATH"); enabled = get_bool_option(qxl->options, OPTION_SPICE_VDAGENT_ENABLED, "XSPICE_VDAGENT_ENABLED"); if (!enabled || uinput_filename == NULL) { return; } ret = mkfifo(uinput_filename, 0666); if (ret != 0) { fprintf(stderr, "spice: failed to create uinput fifo %s: %s\n", uinput_filename, strerror(errno)); return; } spiceqxl_chown_agent_file(qxl, uinput_filename); uinput_fd = open(uinput_filename, O_RDONLY | O_NONBLOCK, 0666); if (uinput_fd == -1) { fprintf(stderr, "spice: failed creating uinput file %s: %s\n", uinput_filename, strerror(errno)); return; } } void spiceqxl_uinput_watch(qxl_screen_t *qxl, Bool on) { if (uinput_watch) { qxl->core->watch_remove(uinput_watch); uinput_watch = NULL; } if (on) uinput_watch = qxl->core->watch_add(uinput_fd, SPICE_WATCH_EVENT_READ, spiceqxl_uinput_read_cb, qxl); }