1 /* vdagentd-uinput.c vdagentd uinput handling code
3 Copyright 2010-2012 Red Hat, Inc.
6 Hans de Goede <hdegoede@redhat.com>
7 Gerd Hoffmann <kraxel@redhat.com>
9 This program is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
31 #include <linux/input.h>
32 #include <linux/uinput.h>
33 #include <spice/vd_agent.h>
34 #include "vdagentd-uinput.h"
36 struct vdagentd_uinput {
42 struct vdagentd_guest_xorg_resolution *screen_info;
44 VDAgentMouseState last;
48 struct vdagentd_uinput *vdagentd_uinput_create(const char *devname,
49 int width, int height,
50 struct vdagentd_guest_xorg_resolution *screen_info, int screen_count,
53 struct vdagentd_uinput *uinput;
55 uinput = calloc(1, sizeof(*uinput));
59 uinput->devname = devname;
60 uinput->fd = -1; /* Gets opened by vdagentd_uinput_update_size() */
61 uinput->debug = debug;
64 vdagentd_uinput_update_size(&uinput, width, height,
65 screen_info, screen_count);
70 void vdagentd_uinput_destroy(struct vdagentd_uinput **uinputp)
72 struct vdagentd_uinput *uinput = *uinputp;
83 void vdagentd_uinput_update_size(struct vdagentd_uinput **uinputp,
84 int width, int height,
85 struct vdagentd_guest_xorg_resolution *screen_info,
88 struct vdagentd_uinput *uinput = *uinputp;
89 struct uinput_user_dev device = {
90 .name = "spice vdagent tablet",
91 #ifdef WITH_STATIC_UINPUT
92 .absmax [ ABS_X ] = 32767,
93 .absmax [ ABS_Y ] = 32767,
95 .absmax [ ABS_X ] = width - 1,
96 .absmax [ ABS_Y ] = height - 1,
102 syslog(LOG_DEBUG, "uinput-update-size: %dx%d", width, height);
103 for (i = 0; i < screen_count; i++)
104 syslog(LOG_DEBUG, "screen %d: +%d+%d", i, screen_info[i].x,
108 uinput->screen_info = screen_info;
109 uinput->screen_count = screen_count;
111 if (uinput->width == width && uinput->height == height)
114 uinput->width = width;
115 uinput->height = height;
117 if (uinput->fd != -1)
118 #ifndef WITH_STATIC_UINPUT
124 uinput->fd = open(uinput->devname, uinput->fake ? O_WRONLY : O_RDWR);
125 if (uinput->fd == -1) {
126 syslog(LOG_ERR, "open %s: %m", uinput->devname);
127 vdagentd_uinput_destroy(uinputp);
132 /* fake device doesn't understand any ioctls and only writes events */
136 rc = write(uinput->fd, &device, sizeof(device));
137 if (rc != sizeof(device)) {
138 syslog(LOG_ERR, "write %s: %m", uinput->devname);
139 vdagentd_uinput_destroy(uinputp);
144 ioctl(uinput->fd, UI_SET_EVBIT, EV_KEY);
145 ioctl(uinput->fd, UI_SET_KEYBIT, BTN_LEFT);
146 ioctl(uinput->fd, UI_SET_KEYBIT, BTN_MIDDLE);
147 ioctl(uinput->fd, UI_SET_KEYBIT, BTN_RIGHT);
150 ioctl(uinput->fd, UI_SET_EVBIT, EV_REL);
151 ioctl(uinput->fd, UI_SET_RELBIT, REL_WHEEL);
154 ioctl(uinput->fd, UI_SET_EVBIT, EV_ABS);
155 ioctl(uinput->fd, UI_SET_ABSBIT, ABS_X);
156 ioctl(uinput->fd, UI_SET_ABSBIT, ABS_Y);
158 rc = ioctl(uinput->fd, UI_DEV_CREATE);
160 syslog(LOG_ERR, "create %s: %m", uinput->devname);
161 vdagentd_uinput_destroy(uinputp);
165 static void uinput_send_event(struct vdagentd_uinput **uinputp,
166 __u16 type, __u16 code, __s32 value)
168 struct vdagentd_uinput *uinput = *uinputp;
169 struct input_event event = {
176 rc = write(uinput->fd, &event, sizeof(event));
177 if (rc != sizeof(event)) {
178 syslog(LOG_ERR, "write %s: %m", uinput->devname);
179 vdagentd_uinput_destroy(uinputp);
183 void vdagentd_uinput_do_mouse(struct vdagentd_uinput **uinputp,
184 VDAgentMouseState *mouse)
186 struct vdagentd_uinput *uinput = *uinputp;
192 static const struct button_s btns[] = {
193 { .name = "left", .mask = VD_AGENT_LBUTTON_MASK, .btn = BTN_LEFT },
194 { .name = "middle", .mask = VD_AGENT_MBUTTON_MASK, .btn = BTN_MIDDLE },
195 { .name = "right", .mask = VD_AGENT_RBUTTON_MASK, .btn = BTN_RIGHT },
197 static const struct button_s wheel[] = {
198 { .name = "up", .mask = VD_AGENT_UBUTTON_MASK, .btn = 1 },
199 { .name = "down", .mask = VD_AGENT_DBUTTON_MASK, .btn = -1 },
204 if (mouse->display_id >= uinput->screen_count) {
205 syslog(LOG_WARNING, "mouse event for unknown monitor (%d >= %d)",
206 mouse->display_id, uinput->screen_count);
210 syslog(LOG_DEBUG, "mouse-event: mon %d %dx%d", mouse->display_id,
212 mouse->x += uinput->screen_info[mouse->display_id].x;
213 mouse->y += uinput->screen_info[mouse->display_id].y;
214 #ifdef WITH_STATIC_UINPUT
215 mouse->x = mouse->x * 32767 / (uinput->width - 1);
216 mouse->y = mouse->y * 32767 / (uinput->height - 1);
220 if (*uinputp && uinput->last.x != mouse->x) {
222 syslog(LOG_DEBUG, "mouse: abs-x %d", mouse->x);
223 uinput_send_event(uinputp, EV_ABS, ABS_X, mouse->x);
225 if (*uinputp && uinput->last.y != mouse->y) {
227 syslog(LOG_DEBUG, "mouse: abs-y %d", mouse->y);
228 uinput_send_event(uinputp, EV_ABS, ABS_Y, mouse->y);
230 for (i = 0; i < sizeof(btns)/sizeof(btns[0]) && *uinputp; i++) {
231 if ((uinput->last.buttons & btns[i].mask) ==
232 (mouse->buttons & btns[i].mask))
234 down = !!(mouse->buttons & btns[i].mask);
236 syslog(LOG_DEBUG, "mouse: btn-%s %s",
237 btns[i].name, down ? "down" : "up");
238 uinput_send_event(uinputp, EV_KEY, btns[i].btn, down);
240 for (i = 0; i < sizeof(wheel)/sizeof(wheel[0]) && *uinputp; i++) {
241 if ((uinput->last.buttons & wheel[i].mask) ==
242 (mouse->buttons & wheel[i].mask))
244 if (mouse->buttons & wheel[i].mask) {
246 syslog(LOG_DEBUG, "mouse: wheel-%s", wheel[i].name);
247 uinput_send_event(uinputp, EV_REL, REL_WHEEL, wheel[i].btn);
253 syslog(LOG_DEBUG, "mouse: syn");
254 uinput_send_event(uinputp, EV_SYN, SYN_REPORT, 0);
258 uinput->last = *mouse;