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