1#ifdef HAVE_CONFIG_H 2#include "config.h" 3#endif 4 5#include <sys/types.h> 6#include <sys/stat.h> 7#include <fcntl.h> 8#include <unistd.h> 9#include <errno.h> 10#include <stdio.h> 11 12#include <linux/input.h> 13#include <linux/uinput.h> 14 15#include "qxl_option_helpers.h" 16#include "spiceqxl_util.h" 17#include "spiceqxl_inputs.h" 18 19#include "spiceqxl_uinput.h" 20 21static const char *uinput_filename; 22static int uinput_fd; 23static SpiceWatch *uinput_watch; 24static struct input_event inp_event; 25static int offset; 26 27static void spiceqxl_uinput_read_cb(int fd, int event, void *opaque) 28{ 29 int n; 30 static int x = -1; 31 static int y = -1; 32 static int buttons_state = 0; 33 int button = -1; 34 35 n = read(uinput_fd, (char *)&inp_event + offset, sizeof(inp_event) - offset); 36 if (n == -1) { 37 if (errno != EAGAIN && errno != EINTR && errno != EWOULDBLOCK) { 38 fprintf(stderr, "spice: uinput read failed: %s\n", strerror(errno)); 39 } 40 return; 41 } 42 offset += n; 43 if (offset < sizeof(inp_event)) { 44 return; 45 } 46 offset = 0; 47 switch (inp_event.type) { 48 case EV_KEY: 49 /* XXX Here we hardcode vdagent-uinput.c mapping since we don't support ioctls. 50 * We could replace the ioctls with additional non uinput messages 51 * used in vdagentd fake uinput mode. */ 52 switch (inp_event.code) { 53 case BTN_LEFT: 54 button = 1 << 0; 55 break; 56 case BTN_MIDDLE: 57 button = 1 << 1; 58 break; 59 case BTN_RIGHT: 60 button = 1 << 2; 61 break; 62 } 63 if (inp_event.value > 0) { 64 buttons_state |= button; 65 } else { 66 buttons_state &= ~button; 67 } 68 spiceqxl_tablet_buttons(buttons_state); 69 break; 70 case EV_REL: 71 button = 1; 72 if (inp_event.value == 1) { 73 button = 1 << 3; 74 } else { 75 button = 1 << 4; 76 } 77 buttons_state |= button; 78 spiceqxl_tablet_buttons(buttons_state); 79 buttons_state &= ~button; 80 spiceqxl_tablet_buttons(buttons_state); 81 break; 82 case EV_ABS: 83 switch (inp_event.code) { 84 case ABS_X: 85 x = inp_event.value; 86 break; 87 case ABS_Y: 88 y = inp_event.value; 89 break; 90 default: 91 fprintf(stderr, "%s: unknown axis %d, ignoring\n", __func__, inp_event.code); 92 return; 93 break; 94 } 95 spiceqxl_tablet_position(x, y, buttons_state); 96 break; 97 } 98} 99 100void spiceqxl_uinput_init(qxl_screen_t *qxl) 101{ 102 int ret; 103 int enabled; 104 105 uinput_filename = get_str_option(qxl->options, OPTION_SPICE_VDAGENT_UINPUT_PATH, 106 "XSPICE_VDAGENT_UINPUT_PATH"); 107 enabled = get_bool_option(qxl->options, OPTION_SPICE_VDAGENT_ENABLED, "XSPICE_VDAGENT_ENABLED"); 108 109 if (!enabled || uinput_filename == NULL) { 110 return; 111 } 112 ret = mkfifo(uinput_filename, 0666); 113 if (ret != 0) { 114 fprintf(stderr, "spice: failed to create uinput fifo %s: %s\n", 115 uinput_filename, strerror(errno)); 116 return; 117 } 118 spiceqxl_chown_agent_file(qxl, uinput_filename); 119 uinput_fd = open(uinput_filename, O_RDONLY | O_NONBLOCK, 0666); 120 if (uinput_fd == -1) { 121 fprintf(stderr, "spice: failed creating uinput file %s: %s\n", 122 uinput_filename, strerror(errno)); 123 return; 124 } 125} 126 127void spiceqxl_uinput_watch(qxl_screen_t *qxl, Bool on) 128{ 129 if (uinput_watch) { 130 qxl->core->watch_remove(uinput_watch); 131 uinput_watch = NULL; 132 } 133 134 if (on) 135 uinput_watch = qxl->core->watch_add(uinput_fd, SPICE_WATCH_EVENT_READ, 136 spiceqxl_uinput_read_cb, qxl); 137} 138