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"
20 typedef struct VDAgentHeader
{
27 static const char *portdev
= "/dev/virtio-ports/com.redhat.spice.0";
28 static const char *uinput
= "/dev/uinput";
30 static int vdagent
, tablet
;
32 static int width
= 1024, height
= 768; /* FIXME: don't hardcode */
33 static struct udscs_server
*server
= NULL
;
37 static void uinput_setup(void)
39 struct uinput_user_dev device
= {
40 .name
= "spice vdagent tablet",
41 .absmax
[ ABS_X
] = width
,
42 .absmax
[ ABS_Y
] = height
,
46 rc
= write(tablet
, &device
, sizeof(device
));
47 if (rc
!= sizeof(device
)) {
48 fprintf(stderr
, "%s: write error\n", __FUNCTION__
);
53 ioctl(tablet
, UI_SET_EVBIT
, EV_KEY
);
54 ioctl(tablet
, UI_SET_KEYBIT
, BTN_LEFT
);
55 ioctl(tablet
, UI_SET_KEYBIT
, BTN_MIDDLE
);
56 ioctl(tablet
, UI_SET_KEYBIT
, BTN_RIGHT
);
57 ioctl(tablet
, UI_SET_KEYBIT
, BTN_GEAR_UP
);
58 ioctl(tablet
, UI_SET_KEYBIT
, BTN_GEAR_DOWN
);
61 ioctl(tablet
, UI_SET_EVBIT
, EV_ABS
);
62 ioctl(tablet
, UI_SET_ABSBIT
, ABS_X
);
63 ioctl(tablet
, UI_SET_ABSBIT
, ABS_Y
);
65 rc
= ioctl(tablet
, UI_DEV_CREATE
);
67 fprintf(stderr
, "%s: create error\n", __FUNCTION__
);
72 static void uinput_send_event(__u16 type
, __u16 code
, __s32 value
)
74 struct input_event event
= {
81 rc
= write(tablet
, &event
, sizeof(event
));
82 if (rc
!= sizeof(event
)) {
83 fprintf(stderr
, "%s: write error\n", __FUNCTION__
);
90 static void do_mouse(VDAgentMouseState
*mouse
)
97 { .name
= "left", .mask
= VD_AGENT_LBUTTON_MASK
, .btn
= BTN_LEFT
},
98 { .name
= "middle", .mask
= VD_AGENT_MBUTTON_MASK
, .btn
= BTN_MIDDLE
},
99 { .name
= "right", .mask
= VD_AGENT_RBUTTON_MASK
, .btn
= BTN_RIGHT
},
100 { .name
= "up", .mask
= VD_AGENT_UBUTTON_MASK
, .btn
= BTN_GEAR_UP
},
101 { .name
= "down", .mask
= VD_AGENT_DBUTTON_MASK
, .btn
= BTN_GEAR_DOWN
},
103 static VDAgentMouseState last
;
106 if (last
.x
!= mouse
->x
) {
108 fprintf(stderr
, "mouse: abs-x %d\n", mouse
->x
);
109 uinput_send_event(EV_ABS
, ABS_X
, mouse
->x
);
111 if (last
.y
!= mouse
->y
) {
113 fprintf(stderr
, "mouse: abs-y %d\n", mouse
->y
);
114 uinput_send_event(EV_ABS
, ABS_Y
, mouse
->y
);
116 for (i
= 0; i
< sizeof(btns
)/sizeof(btns
[0]); i
++) {
117 if ((last
.buttons
& btns
[i
].mask
) == (mouse
->buttons
& btns
[i
].mask
))
119 down
= !!(mouse
->buttons
& btns
[i
].mask
);
121 fprintf(stderr
, "mouse: btn-%s %s\n",
122 btns
[i
].name
, down
? "down" : "up");
123 uinput_send_event(EV_KEY
, btns
[i
].btn
, down
);
126 fprintf(stderr
, "mouse: syn\n");
127 uinput_send_event(EV_SYN
, SYN_REPORT
, 0);
132 static void do_monitors(VDAgentMonitorsConfig
*monitors
)
138 fprintf(stderr
, "monitors: %d\n", monitors
->num_of_monitors
);
139 for (i
= 0; i
< monitors
->num_of_monitors
; i
++) {
140 fprintf(stderr
, " #%d: size %dx%d pos +%d+%d depth %d\n", i
,
141 monitors
->monitors
[i
].width
, monitors
->monitors
[i
].height
,
142 monitors
->monitors
[i
].x
, monitors
->monitors
[i
].y
,
143 monitors
->monitors
[i
].depth
);
147 void vdagent_read(void)
149 VDAgentHeader header
;
150 VDAgentMessage
*message
;
154 rc
= read(vdagent
, &header
, sizeof(header
));
155 if (rc
!= sizeof(header
)) {
156 fprintf(stderr
, "vdagent header read error (%d/%zd)\n", rc
, sizeof(header
));
160 message
= malloc(header
.size
);
161 rc
= read(vdagent
, message
, header
.size
);
162 if (rc
!= header
.size
) {
163 fprintf(stderr
, "vdagent message read error (%d/%d)\n", rc
, header
.size
);
166 data
= message
->data
;
168 switch (message
->type
) {
169 case VD_AGENT_MOUSE_STATE
:
172 case VD_AGENT_MONITORS_CONFIG
:
177 fprintf(stderr
, "unknown message type %d\n", message
->type
);
186 static void usage(FILE *fp
)
189 "vdagent -- handle spice agent mouse via uinput\n"
191 " -h print this text\n"
192 " -d print debug messages (and don't daemonize)\n"
193 " -x width set display width [%d]\n"
194 " -y height set display height [%d]\n"
195 " -s <port> set virtio serial port [%s]\n"
196 " -u <dev> set uinput device [%s]\n",
197 width
, height
, portdev
, uinput
);
202 /* detach from terminal */
208 close(0); close(1); close(2);
210 open("/dev/null",O_RDWR
); dup(0); dup(0);
217 void client_read_complete(struct udscs_connection
*conn
,
218 struct udscs_message_header
*header
, const uint8_t *data
)
220 switch (header
->type
) {
221 case VDAGENTD_GUEST_XORG_RESOLUTION
: {
222 struct vdagentd_guest_xorg_resolution
*res
=
223 (struct vdagentd_guest_xorg_resolution
*)data
;
225 if (header
->size
!= sizeof(*res
)) {
226 /* FIXME destroy connection, but this will cause a double
232 height
= res
->height
;
234 tablet
= open(uinput
, O_RDWR
);
236 fprintf(stderr
, "open %s: %s\n", uinput
, strerror(errno
));
243 fprintf(stderr
, "unknown message from vdagent client: %u, ignoring\n",
250 fd_set readfds
, writefds
;
257 nfds
= udscs_server_fill_fds(server
, &readfds
, &writefds
);
259 FD_SET(vdagent
, &readfds
);
263 n
= select(nfds
, &readfds
, &writefds
, NULL
, NULL
);
271 udscs_server_handle_fds(server
, &readfds
, &writefds
);
272 if (FD_ISSET(vdagent
, &readfds
))
277 int main(int argc
, char *argv
[])
282 if (-1 == (c
= getopt(argc
, argv
, "dhx:y:s:u:")))
289 width
= atoi(optarg
);
292 height
= atoi(optarg
);
309 /* Open virtio port connection */
310 vdagent
= open(portdev
, O_RDWR
);
312 fprintf(stderr
, "open %s: %s\n", portdev
, strerror(errno
));
316 /* Setup communication with vdagent process(es) */
317 server
= udscs_create_server(VDAGENTD_SOCKET
, client_read_complete
, NULL
);
321 tablet
= open(uinput
, O_RDWR
);
323 fprintf(stderr
, "open %s: %s\n", uinput
, strerror(errno
));
333 udscs_destroy_server(server
);