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