Add non blocking virtio port code
[vd_agent/hramrach.git] / vdagentd.c
blobc870b24fbd9eb123e0fef43386389339fc2a93c1
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <inttypes.h>
5 #include <unistd.h>
6 #include <fcntl.h>
7 #include <errno.h>
8 #include <sys/select.h>
10 #include <linux/input.h>
11 #include <linux/uinput.h>
13 /* spice structs */
15 #include <spice/vd_agent.h>
17 #include "udscs.h"
18 #include "vdagentd-proto.h"
19 #include "vdagent-virtio-port.h"
21 typedef struct VDAgentHeader {
22 uint32_t port;
23 uint32_t size;
24 } VDAgentHeader;
26 /* variables */
28 static const char *portdev = "/dev/virtio-ports/com.redhat.spice.0";
29 static const char *uinput = "/dev/uinput";
31 static int tablet;
32 static int debug = 0;
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;
37 /* uinput */
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,
46 int rc;
48 rc = write(tablet, &device, sizeof(device));
49 if (rc != sizeof(device)) {
50 fprintf(stderr, "%s: write error\n", __FUNCTION__);
51 exit(1);
54 /* buttons */
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);
60 /* wheel */
61 ioctl(tablet, UI_SET_EVBIT, EV_REL);
62 ioctl(tablet, UI_SET_RELBIT, REL_WHEEL);
64 /* abs ptr */
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);
70 if (rc < 0) {
71 fprintf(stderr, "%s: create error\n", __FUNCTION__);
72 exit(1);
76 static void uinput_send_event(__u16 type, __u16 code, __s32 value)
78 struct input_event event = {
79 .type = type,
80 .code = code,
81 .value = value,
83 int rc;
85 rc = write(tablet, &event, sizeof(event));
86 if (rc != sizeof(event)) {
87 fprintf(stderr, "%s: write error\n", __FUNCTION__);
88 exit(1);
92 /* spice port */
94 static void do_mouse(VDAgentMouseState *mouse)
96 struct button_s {
97 const char *name;
98 int mask;
99 int btn;
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;
111 int i, down;
113 if (last.x != mouse->x) {
114 if (debug > 1)
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) {
119 if (debug > 1)
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))
125 continue;
126 down = !!(mouse->buttons & btns[i].mask);
127 if (debug > 1)
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))
134 continue;
135 if (mouse->buttons & wheel[i].mask) {
136 if (debug > 1)
137 fprintf(stderr, "mouse: wheel-%s\n", wheel[i].name);
138 uinput_send_event(EV_REL, REL_WHEEL, wheel[i].btn);
141 if (debug > 1)
142 fprintf(stderr, "mouse: syn\n");
143 uinput_send_event(EV_SYN, SYN_REPORT, 0);
145 last = *mouse;
148 static void do_monitors(VDAgentMonitorsConfig *monitors)
150 int i;
152 if (!debug)
153 return;
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,
167 uint8_t *data)
169 switch (message_header->type) {
170 case VD_AGENT_MOUSE_STATE:
171 do_mouse((VDAgentMouseState *)data);
172 break;
173 case VD_AGENT_MONITORS_CONFIG:
174 do_monitors((VDAgentMonitorsConfig *)data);
175 break;
176 default:
177 if (debug)
178 fprintf(stderr, "unknown message type %d\n", message_header->type);
179 break;
182 return 0;
185 /* main */
187 static void usage(FILE *fp)
189 fprintf(fp,
190 "vdagent -- handle spice agent mouse via uinput\n"
191 "options:\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);
201 void daemonize(void)
203 /* detach from terminal */
204 switch (fork()) {
205 case -1:
206 perror("fork");
207 exit(1);
208 case 0:
209 close(0); close(1); close(2);
210 setsid();
211 open("/dev/null",O_RDWR); dup(0); dup(0);
212 break;
213 default:
214 exit(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))
227 return -1;
229 width = res->width;
230 height = res->height;
231 close(tablet);
232 tablet = open(uinput, O_RDWR);
233 if (-1 == tablet) {
234 fprintf(stderr, "open %s: %s\n", uinput, strerror(errno));
235 exit(1);
237 uinput_setup();
238 break;
240 default:
241 fprintf(stderr, "unknown message from vdagent client: %u, ignoring\n",
242 header->type);
245 return 0;
248 void main_loop(void)
250 fd_set readfds, writefds;
251 int n, nfds;
253 while (virtio_port) {
254 FD_ZERO(&readfds);
255 FD_ZERO(&writefds);
257 nfds = udscs_server_fill_fds(server, &readfds, &writefds);
258 n = vdagent_virtio_port_fill_fds(virtio_port, &readfds, &writefds);
259 if (n >= nfds)
260 nfds = n + 1;
262 n = select(nfds, &readfds, &writefds, NULL, NULL);
263 if (n == -1) {
264 if (errno == EINTR)
265 continue;
266 perror("select");
267 exit(1);
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[])
277 int c;
279 for (;;) {
280 if (-1 == (c = getopt(argc, argv, "dhx:y:s:u:")))
281 break;
282 switch (c) {
283 case 'd':
284 debug++;
285 break;
286 case 'x':
287 width = atoi(optarg);
288 break;
289 case 'y':
290 height = atoi(optarg);
291 break;
292 case 's':
293 portdev = optarg;
294 break;
295 case 'u':
296 uinput = optarg;
297 break;
298 case 'h':
299 usage(stdout);
300 exit(0);
301 default:
302 usage(stderr);
303 exit(1);
307 /* Open virtio port connection */
308 virtio_port = vdagent_virtio_port_create(portdev,
309 virtio_port_read_complete, NULL);
310 if (!virtio_port)
311 exit(1);
313 /* Setup communication with vdagent process(es) */
314 server = udscs_create_server(VDAGENTD_SOCKET, client_read_complete, NULL);
315 if (!server)
316 exit(1);
318 tablet = open(uinput, O_RDWR);
319 if (-1 == tablet) {
320 fprintf(stderr, "open %s: %s\n", uinput, strerror(errno));
321 exit(1);
323 uinput_setup();
325 if (!debug)
326 daemonize();
328 main_loop();
330 udscs_destroy_server(server);
331 vdagent_virtio_port_destroy(&virtio_port);
333 return 0;