8 #include <sys/select.h>
10 #include <linux/input.h>
11 #include <linux/uinput.h>
15 #include <spice/vd_agent.h>
18 #include "vdagentd-proto.h"
19 #include "vdagent-virtio-port.h"
21 typedef struct VDAgentHeader
{
28 static const char *portdev
= "/dev/virtio-ports/com.redhat.spice.0";
29 static const char *uinput
= "/dev/uinput";
33 static int width
= 1024, height
= 768; /* FIXME: don't hardcode */
34 static struct udscs_server
*server
= NULL
;
35 static struct vdagent_virtio_port
*virtio_port
= NULL
;
39 static void uinput_setup(void)
41 struct uinput_user_dev device
= {
42 .name
= "spice vdagent tablet",
43 .absmax
[ ABS_X
] = width
,
44 .absmax
[ ABS_Y
] = height
,
48 rc
= write(tablet
, &device
, sizeof(device
));
49 if (rc
!= sizeof(device
)) {
50 fprintf(stderr
, "%s: write error\n", __FUNCTION__
);
55 ioctl(tablet
, UI_SET_EVBIT
, EV_KEY
);
56 ioctl(tablet
, UI_SET_KEYBIT
, BTN_LEFT
);
57 ioctl(tablet
, UI_SET_KEYBIT
, BTN_MIDDLE
);
58 ioctl(tablet
, UI_SET_KEYBIT
, BTN_RIGHT
);
61 ioctl(tablet
, UI_SET_EVBIT
, EV_REL
);
62 ioctl(tablet
, UI_SET_RELBIT
, REL_WHEEL
);
65 ioctl(tablet
, UI_SET_EVBIT
, EV_ABS
);
66 ioctl(tablet
, UI_SET_ABSBIT
, ABS_X
);
67 ioctl(tablet
, UI_SET_ABSBIT
, ABS_Y
);
69 rc
= ioctl(tablet
, UI_DEV_CREATE
);
71 fprintf(stderr
, "%s: create error\n", __FUNCTION__
);
76 static void uinput_send_event(__u16 type
, __u16 code
, __s32 value
)
78 struct input_event event
= {
85 rc
= write(tablet
, &event
, sizeof(event
));
86 if (rc
!= sizeof(event
)) {
87 fprintf(stderr
, "%s: write error\n", __FUNCTION__
);
94 static void do_mouse(VDAgentMouseState
*mouse
)
101 static const struct button_s btns
[] = {
102 { .name
= "left", .mask
= VD_AGENT_LBUTTON_MASK
, .btn
= BTN_LEFT
},
103 { .name
= "middle", .mask
= VD_AGENT_MBUTTON_MASK
, .btn
= BTN_MIDDLE
},
104 { .name
= "right", .mask
= VD_AGENT_RBUTTON_MASK
, .btn
= BTN_RIGHT
},
106 static const struct button_s wheel
[] = {
107 { .name
= "up", .mask
= VD_AGENT_UBUTTON_MASK
, .btn
= 1 },
108 { .name
= "down", .mask
= VD_AGENT_DBUTTON_MASK
, .btn
= -1 },
110 static VDAgentMouseState last
;
113 if (last
.x
!= mouse
->x
) {
115 fprintf(stderr
, "mouse: abs-x %d\n", mouse
->x
);
116 uinput_send_event(EV_ABS
, ABS_X
, mouse
->x
);
118 if (last
.y
!= mouse
->y
) {
120 fprintf(stderr
, "mouse: abs-y %d\n", mouse
->y
);
121 uinput_send_event(EV_ABS
, ABS_Y
, mouse
->y
);
123 for (i
= 0; i
< sizeof(btns
)/sizeof(btns
[0]); i
++) {
124 if ((last
.buttons
& btns
[i
].mask
) == (mouse
->buttons
& btns
[i
].mask
))
126 down
= !!(mouse
->buttons
& btns
[i
].mask
);
128 fprintf(stderr
, "mouse: btn-%s %s\n",
129 btns
[i
].name
, down
? "down" : "up");
130 uinput_send_event(EV_KEY
, btns
[i
].btn
, down
);
132 for (i
= 0; i
< sizeof(wheel
)/sizeof(wheel
[0]); i
++) {
133 if ((last
.buttons
& wheel
[i
].mask
) == (mouse
->buttons
& wheel
[i
].mask
))
135 if (mouse
->buttons
& wheel
[i
].mask
) {
137 fprintf(stderr
, "mouse: wheel-%s\n", wheel
[i
].name
);
138 uinput_send_event(EV_REL
, REL_WHEEL
, wheel
[i
].btn
);
142 fprintf(stderr
, "mouse: syn\n");
143 uinput_send_event(EV_SYN
, SYN_REPORT
, 0);
148 static void do_monitors(VDAgentMonitorsConfig
*monitors
)
154 fprintf(stderr
, "monitors: %d\n", monitors
->num_of_monitors
);
155 for (i
= 0; i
< monitors
->num_of_monitors
; i
++) {
156 fprintf(stderr
, " #%d: size %dx%d pos +%d+%d depth %d\n", i
,
157 monitors
->monitors
[i
].width
, monitors
->monitors
[i
].height
,
158 monitors
->monitors
[i
].x
, monitors
->monitors
[i
].y
,
159 monitors
->monitors
[i
].depth
);
163 int virtio_port_read_complete(
164 struct vdagent_virtio_port
*port
,
165 VDIChunkHeader
*chunk_header
,
166 VDAgentMessage
*message_header
,
169 switch (message_header
->type
) {
170 case VD_AGENT_MOUSE_STATE
:
171 do_mouse((VDAgentMouseState
*)data
);
173 case VD_AGENT_MONITORS_CONFIG
:
174 do_monitors((VDAgentMonitorsConfig
*)data
);
178 fprintf(stderr
, "unknown message type %d\n", message_header
->type
);
187 static void usage(FILE *fp
)
190 "vdagent -- handle spice agent mouse via uinput\n"
192 " -h print this text\n"
193 " -d print debug messages (and don't daemonize)\n"
194 " -x width set display width [%d]\n"
195 " -y height set display height [%d]\n"
196 " -s <port> set virtio serial port [%s]\n"
197 " -u <dev> set uinput device [%s]\n",
198 width
, height
, portdev
, uinput
);
203 /* detach from terminal */
209 close(0); close(1); close(2);
211 open("/dev/null",O_RDWR
); dup(0); dup(0);
218 int client_read_complete(struct udscs_connection
*conn
,
219 struct udscs_message_header
*header
, const uint8_t *data
)
221 switch (header
->type
) {
222 case VDAGENTD_GUEST_XORG_RESOLUTION
: {
223 struct vdagentd_guest_xorg_resolution
*res
=
224 (struct vdagentd_guest_xorg_resolution
*)data
;
226 if (header
->size
!= sizeof(*res
))
230 height
= res
->height
;
232 tablet
= open(uinput
, O_RDWR
);
234 fprintf(stderr
, "open %s: %s\n", uinput
, strerror(errno
));
241 fprintf(stderr
, "unknown message from vdagent client: %u, ignoring\n",
250 fd_set readfds
, writefds
;
253 while (virtio_port
) {
257 nfds
= udscs_server_fill_fds(server
, &readfds
, &writefds
);
258 n
= vdagent_virtio_port_fill_fds(virtio_port
, &readfds
, &writefds
);
262 n
= select(nfds
, &readfds
, &writefds
, NULL
, NULL
);
270 udscs_server_handle_fds(server
, &readfds
, &writefds
);
271 vdagent_virtio_port_handle_fds(&virtio_port
, &readfds
, &writefds
);
275 int main(int argc
, char *argv
[])
280 if (-1 == (c
= getopt(argc
, argv
, "dhx:y:s:u:")))
287 width
= atoi(optarg
);
290 height
= atoi(optarg
);
307 /* Open virtio port connection */
308 virtio_port
= vdagent_virtio_port_create(portdev
,
309 virtio_port_read_complete
, NULL
);
313 /* Setup communication with vdagent process(es) */
314 server
= udscs_create_server(VDAGENTD_SOCKET
, client_read_complete
, NULL
);
318 tablet
= open(uinput
, O_RDWR
);
320 fprintf(stderr
, "open %s: %s\n", uinput
, strerror(errno
));
330 udscs_destroy_server(server
);
331 vdagent_virtio_port_destroy(&virtio_port
);