Home | History | Annotate | Line # | Download | only in src
      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 
     21 static const char *uinput_filename;
     22 static int uinput_fd;
     23 static SpiceWatch *uinput_watch;
     24 static struct input_event inp_event;
     25 static int offset;
     26 
     27 static 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 
    100 void 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 
    127 void 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