]>
Commit | Line | Data |
---|---|---|
6105683d LV |
1 | /* |
2 | * SPDX-License-Identifier: GPL-2.0-or-later | |
3 | * | |
4 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
5 | * See the COPYING file in the top-level directory. | |
6 | */ | |
7 | ||
8 | #include "qemu/osdep.h" | |
9 | #include "sysemu/sysemu.h" | |
10 | #include "qemu/main-loop.h" | |
11 | #include "qemu/sockets.h" | |
12 | #include "qapi/error.h" | |
13 | #include "qom/object_interfaces.h" | |
14 | #include "io/channel-socket.h" | |
15 | #include "ui/input.h" | |
db1015e9 | 16 | #include "qom/object.h" |
6105683d LV |
17 | #include "ui/vnc_keysym.h" /* use name2keysym from VNC as we use X11 values */ |
18 | #include "qemu/cutils.h" | |
19 | #include "qapi/qmp/qerror.h" | |
20 | #include "input-barrier.h" | |
21 | ||
22 | #define TYPE_INPUT_BARRIER "input-barrier" | |
30b5707c | 23 | OBJECT_DECLARE_SIMPLE_TYPE(InputBarrier, |
c734cd40 | 24 | INPUT_BARRIER) |
6105683d | 25 | |
6105683d LV |
26 | |
27 | #define MAX_HELLO_LENGTH 1024 | |
28 | ||
29 | struct InputBarrier { | |
30 | Object parent; | |
31 | ||
32 | QIOChannelSocket *sioc; | |
33 | guint ioc_tag; | |
34 | ||
35 | /* display properties */ | |
36 | gchar *name; | |
37 | int16_t x_origin, y_origin; | |
38 | int16_t width, height; | |
39 | ||
40 | /* keyboard/mouse server */ | |
41 | ||
42 | SocketAddress saddr; | |
43 | ||
44 | char buffer[MAX_HELLO_LENGTH]; | |
45 | }; | |
46 | ||
6105683d LV |
47 | |
48 | static const char *cmd_names[] = { | |
49 | [barrierCmdCNoop] = "CNOP", | |
50 | [barrierCmdCClose] = "CBYE", | |
51 | [barrierCmdCEnter] = "CINN", | |
52 | [barrierCmdCLeave] = "COUT", | |
53 | [barrierCmdCClipboard] = "CCLP", | |
54 | [barrierCmdCScreenSaver] = "CSEC", | |
55 | [barrierCmdCResetOptions] = "CROP", | |
56 | [barrierCmdCInfoAck] = "CIAK", | |
57 | [barrierCmdCKeepAlive] = "CALV", | |
58 | [barrierCmdDKeyDown] = "DKDN", | |
59 | [barrierCmdDKeyRepeat] = "DKRP", | |
60 | [barrierCmdDKeyUp] = "DKUP", | |
61 | [barrierCmdDMouseDown] = "DMDN", | |
62 | [barrierCmdDMouseUp] = "DMUP", | |
63 | [barrierCmdDMouseMove] = "DMMV", | |
64 | [barrierCmdDMouseRelMove] = "DMRM", | |
65 | [barrierCmdDMouseWheel] = "DMWM", | |
66 | [barrierCmdDClipboard] = "DCLP", | |
67 | [barrierCmdDInfo] = "DINF", | |
68 | [barrierCmdDSetOptions] = "DSOP", | |
69 | [barrierCmdDFileTransfer] = "DFTR", | |
70 | [barrierCmdDDragInfo] = "DDRG", | |
71 | [barrierCmdQInfo] = "QINF", | |
72 | [barrierCmdEIncompatible] = "EICV", | |
73 | [barrierCmdEBusy] = "EBSY", | |
74 | [barrierCmdEUnknown] = "EUNK", | |
75 | [barrierCmdEBad] = "EBAD", | |
76 | [barrierCmdHello] = "Barrier", | |
77 | [barrierCmdHelloBack] = "Barrier", | |
78 | }; | |
79 | ||
80 | static kbd_layout_t *kbd_layout; | |
81 | ||
82 | static int input_barrier_to_qcode(uint16_t keyid, uint16_t keycode) | |
83 | { | |
84 | /* keycode is optional, if it is not provided use keyid */ | |
85 | if (keycode && keycode <= qemu_input_map_xorgkbd_to_qcode_len) { | |
86 | return qemu_input_map_xorgkbd_to_qcode[keycode]; | |
87 | } | |
88 | ||
89 | if (keyid >= 0xE000 && keyid <= 0xEFFF) { | |
90 | keyid += 0x1000; | |
91 | } | |
92 | ||
93 | /* keyid is the X11 key id */ | |
94 | if (kbd_layout) { | |
95 | keycode = keysym2scancode(kbd_layout, keyid, NULL, false); | |
96 | ||
97 | return qemu_input_key_number_to_qcode(keycode); | |
98 | } | |
99 | ||
100 | return qemu_input_map_x11_to_qcode[keyid]; | |
101 | } | |
102 | ||
103 | static int input_barrier_to_mouse(uint8_t buttonid) | |
104 | { | |
105 | switch (buttonid) { | |
106 | case barrierButtonLeft: | |
107 | return INPUT_BUTTON_LEFT; | |
108 | case barrierButtonMiddle: | |
109 | return INPUT_BUTTON_MIDDLE; | |
110 | case barrierButtonRight: | |
111 | return INPUT_BUTTON_RIGHT; | |
112 | case barrierButtonExtra0: | |
113 | return INPUT_BUTTON_SIDE; | |
114 | } | |
115 | return buttonid; | |
116 | } | |
117 | ||
118 | #define read_char(x, p, l) \ | |
119 | do { \ | |
120 | int size = sizeof(char); \ | |
121 | if (l < size) { \ | |
122 | return G_SOURCE_REMOVE; \ | |
123 | } \ | |
124 | x = *(char *)p; \ | |
125 | p += size; \ | |
126 | l -= size; \ | |
127 | } while (0) | |
128 | ||
129 | #define read_short(x, p, l) \ | |
130 | do { \ | |
131 | int size = sizeof(short); \ | |
132 | if (l < size) { \ | |
133 | return G_SOURCE_REMOVE; \ | |
134 | } \ | |
135 | x = ntohs(*(short *)p); \ | |
136 | p += size; \ | |
137 | l -= size; \ | |
138 | } while (0) | |
139 | ||
140 | #define write_short(p, x, l) \ | |
141 | do { \ | |
142 | int size = sizeof(short); \ | |
143 | if (l < size) { \ | |
144 | return G_SOURCE_REMOVE; \ | |
145 | } \ | |
146 | *(short *)p = htons(x); \ | |
147 | p += size; \ | |
148 | l -= size; \ | |
149 | } while (0) | |
150 | ||
151 | #define read_int(x, p, l) \ | |
152 | do { \ | |
153 | int size = sizeof(int); \ | |
154 | if (l < size) { \ | |
155 | return G_SOURCE_REMOVE; \ | |
156 | } \ | |
157 | x = ntohl(*(int *)p); \ | |
158 | p += size; \ | |
159 | l -= size; \ | |
160 | } while (0) | |
161 | ||
162 | #define write_int(p, x, l) \ | |
163 | do { \ | |
164 | int size = sizeof(int); \ | |
165 | if (l < size) { \ | |
166 | return G_SOURCE_REMOVE; \ | |
167 | } \ | |
168 | *(int *)p = htonl(x); \ | |
169 | p += size; \ | |
170 | l -= size; \ | |
171 | } while (0) | |
172 | ||
173 | #define write_cmd(p, c, l) \ | |
174 | do { \ | |
175 | int size = strlen(cmd_names[c]); \ | |
176 | if (l < size) { \ | |
177 | return G_SOURCE_REMOVE; \ | |
178 | } \ | |
179 | memcpy(p, cmd_names[c], size); \ | |
180 | p += size; \ | |
181 | l -= size; \ | |
182 | } while (0) | |
183 | ||
184 | #define write_string(p, s, l) \ | |
185 | do { \ | |
186 | int size = strlen(s); \ | |
187 | if (l < size + sizeof(int)) { \ | |
188 | return G_SOURCE_REMOVE; \ | |
189 | } \ | |
190 | *(int *)p = htonl(size); \ | |
191 | p += sizeof(size); \ | |
192 | l -= sizeof(size); \ | |
193 | memcpy(p, s, size); \ | |
194 | p += size; \ | |
195 | l -= size; \ | |
196 | } while (0) | |
197 | ||
198 | static gboolean readcmd(InputBarrier *ib, struct barrierMsg *msg) | |
199 | { | |
200 | int ret, len, i; | |
201 | enum barrierCmd cmd; | |
202 | char *p; | |
203 | ||
204 | ret = qio_channel_read(QIO_CHANNEL(ib->sioc), (char *)&len, sizeof(len), | |
205 | NULL); | |
206 | if (ret < 0) { | |
207 | return G_SOURCE_REMOVE; | |
208 | } | |
209 | ||
210 | len = ntohl(len); | |
211 | if (len > MAX_HELLO_LENGTH) { | |
212 | return G_SOURCE_REMOVE; | |
213 | } | |
214 | ||
215 | ret = qio_channel_read(QIO_CHANNEL(ib->sioc), ib->buffer, len, NULL); | |
216 | if (ret < 0) { | |
217 | return G_SOURCE_REMOVE; | |
218 | } | |
219 | ||
220 | p = ib->buffer; | |
221 | if (len >= strlen(cmd_names[barrierCmdHello]) && | |
222 | memcmp(p, cmd_names[barrierCmdHello], | |
223 | strlen(cmd_names[barrierCmdHello])) == 0) { | |
224 | cmd = barrierCmdHello; | |
225 | p += strlen(cmd_names[barrierCmdHello]); | |
226 | len -= strlen(cmd_names[barrierCmdHello]); | |
227 | } else { | |
228 | for (cmd = 0; cmd < barrierCmdHello; cmd++) { | |
229 | if (memcmp(ib->buffer, cmd_names[cmd], 4) == 0) { | |
230 | break; | |
231 | } | |
232 | } | |
233 | ||
234 | if (cmd == barrierCmdHello) { | |
235 | return G_SOURCE_REMOVE; | |
236 | } | |
237 | p += 4; | |
238 | len -= 4; | |
239 | } | |
240 | ||
241 | msg->cmd = cmd; | |
242 | switch (cmd) { | |
243 | /* connection */ | |
244 | case barrierCmdHello: | |
245 | read_short(msg->version.major, p, len); | |
246 | read_short(msg->version.minor, p, len); | |
247 | break; | |
248 | case barrierCmdDSetOptions: | |
249 | read_int(msg->set.nb, p, len); | |
250 | msg->set.nb /= 2; | |
251 | if (msg->set.nb > BARRIER_MAX_OPTIONS) { | |
252 | msg->set.nb = BARRIER_MAX_OPTIONS; | |
253 | } | |
254 | i = 0; | |
255 | while (len && i < msg->set.nb) { | |
256 | read_int(msg->set.option[i].id, p, len); | |
257 | /* it's a string, restore endianness */ | |
258 | msg->set.option[i].id = htonl(msg->set.option[i].id); | |
259 | msg->set.option[i].nul = 0; | |
260 | read_int(msg->set.option[i].value, p, len); | |
261 | i++; | |
262 | } | |
263 | break; | |
264 | case barrierCmdQInfo: | |
265 | break; | |
266 | ||
267 | /* mouse */ | |
268 | case barrierCmdDMouseMove: | |
269 | case barrierCmdDMouseRelMove: | |
270 | read_short(msg->mousepos.x, p, len); | |
271 | read_short(msg->mousepos.y, p, len); | |
272 | break; | |
273 | case barrierCmdDMouseDown: | |
274 | case barrierCmdDMouseUp: | |
275 | read_char(msg->mousebutton.buttonid, p, len); | |
276 | break; | |
277 | case barrierCmdDMouseWheel: | |
278 | read_short(msg->mousepos.y, p, len); | |
279 | msg->mousepos.x = 0; | |
280 | if (len) { | |
281 | msg->mousepos.x = msg->mousepos.y; | |
282 | read_short(msg->mousepos.y, p, len); | |
283 | } | |
284 | break; | |
285 | ||
286 | /* keyboard */ | |
287 | case barrierCmdDKeyDown: | |
288 | case barrierCmdDKeyUp: | |
289 | read_short(msg->key.keyid, p, len); | |
290 | read_short(msg->key.modifier, p, len); | |
291 | msg->key.button = 0; | |
292 | if (len) { | |
293 | read_short(msg->key.button, p, len); | |
294 | } | |
295 | break; | |
296 | case barrierCmdDKeyRepeat: | |
297 | read_short(msg->repeat.keyid, p, len); | |
298 | read_short(msg->repeat.modifier, p, len); | |
299 | read_short(msg->repeat.repeat, p, len); | |
300 | msg->repeat.button = 0; | |
301 | if (len) { | |
302 | read_short(msg->repeat.button, p, len); | |
303 | } | |
304 | break; | |
305 | case barrierCmdCInfoAck: | |
306 | case barrierCmdCResetOptions: | |
307 | case barrierCmdCEnter: | |
308 | case barrierCmdDClipboard: | |
309 | case barrierCmdCKeepAlive: | |
310 | case barrierCmdCLeave: | |
311 | case barrierCmdCClose: | |
312 | break; | |
313 | ||
314 | /* Invalid from the server */ | |
315 | case barrierCmdHelloBack: | |
316 | case barrierCmdCNoop: | |
317 | case barrierCmdDInfo: | |
318 | break; | |
319 | ||
320 | /* Error codes */ | |
321 | case barrierCmdEIncompatible: | |
322 | read_short(msg->version.major, p, len); | |
323 | read_short(msg->version.minor, p, len); | |
324 | break; | |
325 | case barrierCmdEBusy: | |
326 | case barrierCmdEUnknown: | |
327 | case barrierCmdEBad: | |
328 | break; | |
329 | default: | |
330 | return G_SOURCE_REMOVE; | |
331 | } | |
332 | ||
333 | return G_SOURCE_CONTINUE; | |
334 | } | |
335 | ||
336 | static gboolean writecmd(InputBarrier *ib, struct barrierMsg *msg) | |
337 | { | |
338 | char *p; | |
339 | int ret, i; | |
340 | int avail, len; | |
341 | ||
342 | p = ib->buffer; | |
343 | avail = MAX_HELLO_LENGTH; | |
344 | ||
345 | /* reserve space to store the length */ | |
346 | p += sizeof(int); | |
347 | avail -= sizeof(int); | |
348 | ||
349 | switch (msg->cmd) { | |
350 | case barrierCmdHello: | |
351 | if (msg->version.major < BARRIER_VERSION_MAJOR || | |
352 | (msg->version.major == BARRIER_VERSION_MAJOR && | |
353 | msg->version.minor < BARRIER_VERSION_MINOR)) { | |
354 | ib->ioc_tag = 0; | |
355 | return G_SOURCE_REMOVE; | |
356 | } | |
357 | write_cmd(p, barrierCmdHelloBack, avail); | |
358 | write_short(p, BARRIER_VERSION_MAJOR, avail); | |
359 | write_short(p, BARRIER_VERSION_MINOR, avail); | |
360 | write_string(p, ib->name, avail); | |
361 | break; | |
362 | case barrierCmdCClose: | |
363 | ib->ioc_tag = 0; | |
364 | return G_SOURCE_REMOVE; | |
365 | case barrierCmdQInfo: | |
366 | write_cmd(p, barrierCmdDInfo, avail); | |
367 | write_short(p, ib->x_origin, avail); | |
368 | write_short(p, ib->y_origin, avail); | |
369 | write_short(p, ib->width, avail); | |
370 | write_short(p, ib->height, avail); | |
371 | write_short(p, 0, avail); /* warpsize (obsolete) */ | |
372 | write_short(p, 0, avail); /* mouse x */ | |
373 | write_short(p, 0, avail); /* mouse y */ | |
374 | break; | |
375 | case barrierCmdCInfoAck: | |
376 | break; | |
377 | case barrierCmdCResetOptions: | |
378 | /* TODO: reset options */ | |
379 | break; | |
380 | case barrierCmdDSetOptions: | |
381 | /* TODO: set options */ | |
382 | break; | |
383 | case barrierCmdCEnter: | |
384 | break; | |
385 | case barrierCmdDClipboard: | |
386 | break; | |
387 | case barrierCmdCKeepAlive: | |
388 | write_cmd(p, barrierCmdCKeepAlive, avail); | |
389 | break; | |
390 | case barrierCmdCLeave: | |
391 | break; | |
392 | ||
393 | /* mouse */ | |
394 | case barrierCmdDMouseMove: | |
395 | qemu_input_queue_abs(NULL, INPUT_AXIS_X, msg->mousepos.x, | |
396 | ib->x_origin, ib->width); | |
397 | qemu_input_queue_abs(NULL, INPUT_AXIS_Y, msg->mousepos.y, | |
398 | ib->y_origin, ib->height); | |
399 | qemu_input_event_sync(); | |
400 | break; | |
401 | case barrierCmdDMouseRelMove: | |
402 | qemu_input_queue_rel(NULL, INPUT_AXIS_X, msg->mousepos.x); | |
403 | qemu_input_queue_rel(NULL, INPUT_AXIS_Y, msg->mousepos.y); | |
404 | qemu_input_event_sync(); | |
405 | break; | |
406 | case barrierCmdDMouseDown: | |
407 | qemu_input_queue_btn(NULL, | |
408 | input_barrier_to_mouse(msg->mousebutton.buttonid), | |
409 | true); | |
410 | qemu_input_event_sync(); | |
411 | break; | |
412 | case barrierCmdDMouseUp: | |
413 | qemu_input_queue_btn(NULL, | |
414 | input_barrier_to_mouse(msg->mousebutton.buttonid), | |
415 | false); | |
416 | qemu_input_event_sync(); | |
417 | break; | |
418 | case barrierCmdDMouseWheel: | |
419 | qemu_input_queue_btn(NULL, (msg->mousepos.y > 0) ? INPUT_BUTTON_WHEEL_UP | |
420 | : INPUT_BUTTON_WHEEL_DOWN, true); | |
421 | qemu_input_event_sync(); | |
422 | qemu_input_queue_btn(NULL, (msg->mousepos.y > 0) ? INPUT_BUTTON_WHEEL_UP | |
423 | : INPUT_BUTTON_WHEEL_DOWN, false); | |
424 | qemu_input_event_sync(); | |
425 | break; | |
426 | ||
427 | /* keyboard */ | |
428 | case barrierCmdDKeyDown: | |
429 | qemu_input_event_send_key_qcode(NULL, | |
430 | input_barrier_to_qcode(msg->key.keyid, msg->key.button), | |
431 | true); | |
432 | break; | |
433 | case barrierCmdDKeyRepeat: | |
434 | for (i = 0; i < msg->repeat.repeat; i++) { | |
435 | qemu_input_event_send_key_qcode(NULL, | |
436 | input_barrier_to_qcode(msg->repeat.keyid, msg->repeat.button), | |
437 | false); | |
438 | qemu_input_event_send_key_qcode(NULL, | |
439 | input_barrier_to_qcode(msg->repeat.keyid, msg->repeat.button), | |
440 | true); | |
441 | } | |
442 | break; | |
443 | case barrierCmdDKeyUp: | |
444 | qemu_input_event_send_key_qcode(NULL, | |
445 | input_barrier_to_qcode(msg->key.keyid, msg->key.button), | |
446 | false); | |
447 | break; | |
448 | default: | |
449 | write_cmd(p, barrierCmdEUnknown, avail); | |
29453426 | 450 | break; |
6105683d LV |
451 | } |
452 | ||
453 | len = MAX_HELLO_LENGTH - avail - sizeof(int); | |
454 | if (len) { | |
455 | p = ib->buffer; | |
456 | avail = sizeof(len); | |
457 | write_int(p, len, avail); | |
458 | ret = qio_channel_write(QIO_CHANNEL(ib->sioc), ib->buffer, | |
459 | len + sizeof(len), NULL); | |
460 | if (ret < 0) { | |
461 | ib->ioc_tag = 0; | |
462 | return G_SOURCE_REMOVE; | |
463 | } | |
464 | } | |
465 | ||
466 | return G_SOURCE_CONTINUE; | |
467 | } | |
468 | ||
469 | static gboolean input_barrier_event(QIOChannel *ioc G_GNUC_UNUSED, | |
470 | GIOCondition condition, void *opaque) | |
471 | { | |
472 | InputBarrier *ib = opaque; | |
473 | int ret; | |
474 | struct barrierMsg msg; | |
475 | ||
476 | ret = readcmd(ib, &msg); | |
477 | if (ret == G_SOURCE_REMOVE) { | |
478 | ib->ioc_tag = 0; | |
479 | return G_SOURCE_REMOVE; | |
480 | } | |
481 | ||
482 | return writecmd(ib, &msg); | |
483 | } | |
484 | ||
485 | static void input_barrier_complete(UserCreatable *uc, Error **errp) | |
486 | { | |
487 | InputBarrier *ib = INPUT_BARRIER(uc); | |
488 | Error *local_err = NULL; | |
489 | ||
490 | if (!ib->name) { | |
491 | error_setg(errp, QERR_MISSING_PARAMETER, "name"); | |
492 | return; | |
493 | } | |
494 | ||
495 | /* | |
496 | * Connect to the primary | |
497 | * Primary is the server where the keyboard and the mouse | |
498 | * are connected and forwarded to the secondary (the client) | |
499 | */ | |
500 | ||
501 | ib->sioc = qio_channel_socket_new(); | |
502 | qio_channel_set_name(QIO_CHANNEL(ib->sioc), "barrier-client"); | |
503 | ||
504 | qio_channel_socket_connect_sync(ib->sioc, &ib->saddr, &local_err); | |
505 | if (local_err) { | |
506 | error_propagate(errp, local_err); | |
507 | return; | |
508 | } | |
509 | ||
510 | qio_channel_set_delay(QIO_CHANNEL(ib->sioc), false); | |
511 | ||
512 | ib->ioc_tag = qio_channel_add_watch(QIO_CHANNEL(ib->sioc), G_IO_IN, | |
513 | input_barrier_event, ib, NULL); | |
514 | } | |
515 | ||
516 | static void input_barrier_instance_finalize(Object *obj) | |
517 | { | |
518 | InputBarrier *ib = INPUT_BARRIER(obj); | |
519 | ||
520 | if (ib->ioc_tag) { | |
521 | g_source_remove(ib->ioc_tag); | |
522 | ib->ioc_tag = 0; | |
523 | } | |
524 | ||
525 | if (ib->sioc) { | |
526 | qio_channel_close(QIO_CHANNEL(ib->sioc), NULL); | |
527 | object_unref(OBJECT(ib->sioc)); | |
528 | } | |
529 | g_free(ib->name); | |
530 | g_free(ib->saddr.u.inet.host); | |
531 | g_free(ib->saddr.u.inet.port); | |
532 | } | |
533 | ||
534 | static char *input_barrier_get_name(Object *obj, Error **errp) | |
535 | { | |
536 | InputBarrier *ib = INPUT_BARRIER(obj); | |
537 | ||
538 | return g_strdup(ib->name); | |
539 | } | |
540 | ||
541 | static void input_barrier_set_name(Object *obj, const char *value, | |
542 | Error **errp) | |
543 | { | |
544 | InputBarrier *ib = INPUT_BARRIER(obj); | |
545 | ||
546 | if (ib->name) { | |
547 | error_setg(errp, "name property already set"); | |
548 | return; | |
549 | } | |
550 | ib->name = g_strdup(value); | |
551 | } | |
552 | ||
553 | static char *input_barrier_get_server(Object *obj, Error **errp) | |
554 | { | |
555 | InputBarrier *ib = INPUT_BARRIER(obj); | |
556 | ||
557 | return g_strdup(ib->saddr.u.inet.host); | |
558 | } | |
559 | ||
560 | static void input_barrier_set_server(Object *obj, const char *value, | |
561 | Error **errp) | |
562 | { | |
563 | InputBarrier *ib = INPUT_BARRIER(obj); | |
564 | ||
565 | g_free(ib->saddr.u.inet.host); | |
566 | ib->saddr.u.inet.host = g_strdup(value); | |
567 | } | |
568 | ||
569 | static char *input_barrier_get_port(Object *obj, Error **errp) | |
570 | { | |
571 | InputBarrier *ib = INPUT_BARRIER(obj); | |
572 | ||
573 | return g_strdup(ib->saddr.u.inet.port); | |
574 | } | |
575 | ||
576 | static void input_barrier_set_port(Object *obj, const char *value, | |
577 | Error **errp) | |
578 | { | |
579 | InputBarrier *ib = INPUT_BARRIER(obj); | |
580 | ||
581 | g_free(ib->saddr.u.inet.port); | |
582 | ib->saddr.u.inet.port = g_strdup(value); | |
583 | } | |
584 | ||
585 | static void input_barrier_set_x_origin(Object *obj, const char *value, | |
586 | Error **errp) | |
587 | { | |
588 | InputBarrier *ib = INPUT_BARRIER(obj); | |
589 | int result, err; | |
590 | ||
591 | err = qemu_strtoi(value, NULL, 0, &result); | |
592 | if (err < 0 || result < 0 || result > SHRT_MAX) { | |
593 | error_setg(errp, | |
594 | "x-origin property must be in the range [0..%d]", SHRT_MAX); | |
595 | return; | |
596 | } | |
597 | ib->x_origin = result; | |
598 | } | |
599 | ||
600 | static char *input_barrier_get_x_origin(Object *obj, Error **errp) | |
601 | { | |
602 | InputBarrier *ib = INPUT_BARRIER(obj); | |
603 | ||
604 | return g_strdup_printf("%d", ib->x_origin); | |
605 | } | |
606 | ||
607 | static void input_barrier_set_y_origin(Object *obj, const char *value, | |
608 | Error **errp) | |
609 | { | |
610 | InputBarrier *ib = INPUT_BARRIER(obj); | |
611 | int result, err; | |
612 | ||
613 | err = qemu_strtoi(value, NULL, 0, &result); | |
614 | if (err < 0 || result < 0 || result > SHRT_MAX) { | |
615 | error_setg(errp, | |
616 | "y-origin property must be in the range [0..%d]", SHRT_MAX); | |
617 | return; | |
618 | } | |
619 | ib->y_origin = result; | |
620 | } | |
621 | ||
622 | static char *input_barrier_get_y_origin(Object *obj, Error **errp) | |
623 | { | |
624 | InputBarrier *ib = INPUT_BARRIER(obj); | |
625 | ||
626 | return g_strdup_printf("%d", ib->y_origin); | |
627 | } | |
628 | ||
629 | static void input_barrier_set_width(Object *obj, const char *value, | |
630 | Error **errp) | |
631 | { | |
632 | InputBarrier *ib = INPUT_BARRIER(obj); | |
633 | int result, err; | |
634 | ||
635 | err = qemu_strtoi(value, NULL, 0, &result); | |
636 | if (err < 0 || result < 0 || result > SHRT_MAX) { | |
637 | error_setg(errp, | |
638 | "width property must be in the range [0..%d]", SHRT_MAX); | |
639 | return; | |
640 | } | |
641 | ib->width = result; | |
642 | } | |
643 | ||
644 | static char *input_barrier_get_width(Object *obj, Error **errp) | |
645 | { | |
646 | InputBarrier *ib = INPUT_BARRIER(obj); | |
647 | ||
648 | return g_strdup_printf("%d", ib->width); | |
649 | } | |
650 | ||
651 | static void input_barrier_set_height(Object *obj, const char *value, | |
652 | Error **errp) | |
653 | { | |
654 | InputBarrier *ib = INPUT_BARRIER(obj); | |
655 | int result, err; | |
656 | ||
657 | err = qemu_strtoi(value, NULL, 0, &result); | |
658 | if (err < 0 || result < 0 || result > SHRT_MAX) { | |
659 | error_setg(errp, | |
660 | "height property must be in the range [0..%d]", SHRT_MAX); | |
661 | return; | |
662 | } | |
663 | ib->height = result; | |
664 | } | |
665 | ||
666 | static char *input_barrier_get_height(Object *obj, Error **errp) | |
667 | { | |
668 | InputBarrier *ib = INPUT_BARRIER(obj); | |
669 | ||
670 | return g_strdup_printf("%d", ib->height); | |
671 | } | |
672 | ||
673 | static void input_barrier_instance_init(Object *obj) | |
674 | { | |
675 | InputBarrier *ib = INPUT_BARRIER(obj); | |
676 | ||
707f7507 LV |
677 | /* always use generic keymaps */ |
678 | if (keyboard_layout && !kbd_layout) { | |
679 | /* We use X11 key id, so use VNC name2keysym */ | |
680 | kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout, | |
681 | &error_fatal); | |
682 | } | |
683 | ||
6105683d LV |
684 | ib->saddr.type = SOCKET_ADDRESS_TYPE_INET; |
685 | ib->saddr.u.inet.host = g_strdup("localhost"); | |
686 | ib->saddr.u.inet.port = g_strdup("24800"); | |
687 | ||
688 | ib->x_origin = 0; | |
689 | ib->y_origin = 0; | |
690 | ib->width = 1920; | |
691 | ib->height = 1080; | |
6105683d LV |
692 | } |
693 | ||
694 | static void input_barrier_class_init(ObjectClass *oc, void *data) | |
695 | { | |
696 | UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); | |
697 | ||
698 | ucc->complete = input_barrier_complete; | |
d85855b8 EH |
699 | |
700 | object_class_property_add_str(oc, "name", | |
701 | input_barrier_get_name, | |
702 | input_barrier_set_name); | |
703 | object_class_property_add_str(oc, "server", | |
704 | input_barrier_get_server, | |
705 | input_barrier_set_server); | |
706 | object_class_property_add_str(oc, "port", | |
707 | input_barrier_get_port, | |
708 | input_barrier_set_port); | |
709 | object_class_property_add_str(oc, "x-origin", | |
710 | input_barrier_get_x_origin, | |
711 | input_barrier_set_x_origin); | |
712 | object_class_property_add_str(oc, "y-origin", | |
713 | input_barrier_get_y_origin, | |
714 | input_barrier_set_y_origin); | |
715 | object_class_property_add_str(oc, "width", | |
716 | input_barrier_get_width, | |
717 | input_barrier_set_width); | |
718 | object_class_property_add_str(oc, "height", | |
719 | input_barrier_get_height, | |
720 | input_barrier_set_height); | |
6105683d LV |
721 | } |
722 | ||
723 | static const TypeInfo input_barrier_info = { | |
724 | .name = TYPE_INPUT_BARRIER, | |
725 | .parent = TYPE_OBJECT, | |
6105683d LV |
726 | .class_init = input_barrier_class_init, |
727 | .instance_size = sizeof(InputBarrier), | |
728 | .instance_init = input_barrier_instance_init, | |
729 | .instance_finalize = input_barrier_instance_finalize, | |
730 | .interfaces = (InterfaceInfo[]) { | |
731 | { TYPE_USER_CREATABLE }, | |
732 | { } | |
733 | } | |
734 | }; | |
735 | ||
736 | static void register_types(void) | |
737 | { | |
738 | type_register_static(&input_barrier_info); | |
739 | } | |
740 | ||
741 | type_init(register_types); |