1 /* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3 Copyright (C) 2010 Red Hat, Inc.
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 */
18 #include "config.h"
19
20 #include "spice-client.h"
21 #include "spice-common.h"
22 #include "spice-channel-priv.h"
23
24 /**
25 * SECTION:channel-inputs
26 * @short_description: control the server mouse and keyboard
27 * @title: Inputs Channel
28 * @section_id:
29 * @see_also: #SpiceChannel, and the GTK widget #SpiceDisplay
30 * @stability: Stable
31 * @include: spice-client.h
32 *
33 * Spice supports sending keyboard key events and keyboard leds
34 * synchronization. The key events are sent using
35 * spice_inputs_key_press() and spice_inputs_key_release() using
36 * a modified variant of PC XT scancodes.
37 *
38 * Guest keyboard leds state can be manipulated with
39 * spice_inputs_set_key_locks(). When key lock change, a notification
40 * is emitted with #SpiceInputsChannel::inputs-modifiers signal.
41 */
42
43 struct _SpiceInputsChannelPrivate {
44 int bs;
45 int dx, dy;
46 unsigned int x, y, dpy;
47 int motion_count;
48 int modifiers;
49 guint32 locks;
50 };
51
52 G_DEFINE_TYPE_WITH_PRIVATE(SpiceInputsChannel, spice_inputs_channel, SPICE_TYPE_CHANNEL)
53
54 /* Properties */
55 enum {
56 PROP_0,
57 PROP_KEY_MODIFIERS,
58 };
59
60 /* Signals */
61 enum {
62 SPICE_INPUTS_MODIFIERS,
63
64 SPICE_INPUTS_LAST_SIGNAL,
65 };
66
67 static guint signals[SPICE_INPUTS_LAST_SIGNAL];
68
69 static void spice_inputs_channel_up(SpiceChannel *channel);
70 static void spice_inputs_channel_reset(SpiceChannel *channel, gboolean migrating);
71 static void channel_set_handlers(SpiceChannelClass *klass);
72
73 /* ------------------------------------------------------------------ */
74
spice_inputs_channel_init(SpiceInputsChannel * channel)75 static void spice_inputs_channel_init(SpiceInputsChannel *channel)
76 {
77 channel->priv = spice_inputs_channel_get_instance_private(channel);
78 }
79
spice_inputs_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)80 static void spice_inputs_get_property(GObject *object,
81 guint prop_id,
82 GValue *value,
83 GParamSpec *pspec)
84 {
85 SpiceInputsChannelPrivate *c = SPICE_INPUTS_CHANNEL(object)->priv;
86
87 switch (prop_id) {
88 case PROP_KEY_MODIFIERS:
89 g_value_set_int(value, c->modifiers);
90 break;
91 default:
92 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
93 break;
94 }
95 }
96
spice_inputs_channel_finalize(GObject * obj)97 static void spice_inputs_channel_finalize(GObject *obj)
98 {
99 if (G_OBJECT_CLASS(spice_inputs_channel_parent_class)->finalize)
100 G_OBJECT_CLASS(spice_inputs_channel_parent_class)->finalize(obj);
101 }
102
spice_inputs_channel_class_init(SpiceInputsChannelClass * klass)103 static void spice_inputs_channel_class_init(SpiceInputsChannelClass *klass)
104 {
105 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
106 SpiceChannelClass *channel_class = SPICE_CHANNEL_CLASS(klass);
107
108 gobject_class->finalize = spice_inputs_channel_finalize;
109 gobject_class->get_property = spice_inputs_get_property;
110 channel_class->channel_up = spice_inputs_channel_up;
111 channel_class->channel_reset = spice_inputs_channel_reset;
112
113 g_object_class_install_property
114 (gobject_class, PROP_KEY_MODIFIERS,
115 g_param_spec_int("key-modifiers",
116 "Key modifiers",
117 "Guest keyboard lock/led state",
118 0, INT_MAX, 0,
119 G_PARAM_READABLE |
120 G_PARAM_STATIC_NAME |
121 G_PARAM_STATIC_NICK |
122 G_PARAM_STATIC_BLURB));
123
124 /**
125 * SpiceInputsChannel::inputs-modifiers:
126 * @display: the #SpiceInputsChannel that emitted the signal
127 *
128 * The #SpiceInputsChannel::inputs-modifiers signal is emitted when
129 * the guest keyboard locks are changed. You can read the current
130 * state from #SpiceInputsChannel:key-modifiers property.
131 **/
132 /* TODO: use notify instead? */
133 signals[SPICE_INPUTS_MODIFIERS] =
134 g_signal_new("inputs-modifiers",
135 G_OBJECT_CLASS_TYPE(gobject_class),
136 G_SIGNAL_RUN_FIRST,
137 G_STRUCT_OFFSET(SpiceInputsChannelClass, inputs_modifiers),
138 NULL, NULL,
139 g_cclosure_marshal_VOID__VOID,
140 G_TYPE_NONE,
141 0);
142
143 channel_set_handlers(SPICE_CHANNEL_CLASS(klass));
144 }
145
146 /* ------------------------------------------------------------------ */
147
mouse_motion(SpiceInputsChannel * channel)148 static SpiceMsgOut* mouse_motion(SpiceInputsChannel *channel)
149 {
150 SpiceInputsChannelPrivate *c = channel->priv;
151 SpiceMsgcMouseMotion motion;
152 SpiceMsgOut *msg;
153
154 if (!c->dx && !c->dy)
155 return NULL;
156
157 motion.buttons_state = c->bs;
158 motion.dx = c->dx;
159 motion.dy = c->dy;
160 msg = spice_msg_out_new(SPICE_CHANNEL(channel),
161 SPICE_MSGC_INPUTS_MOUSE_MOTION);
162 msg->marshallers->msgc_inputs_mouse_motion(msg->marshaller, &motion);
163
164 c->motion_count++;
165 c->dx = 0;
166 c->dy = 0;
167
168 return msg;
169 }
170
mouse_position(SpiceInputsChannel * channel)171 static SpiceMsgOut* mouse_position(SpiceInputsChannel *channel)
172 {
173 SpiceInputsChannelPrivate *c = channel->priv;
174 SpiceMsgcMousePosition position;
175 SpiceMsgOut *msg;
176
177 if (c->dpy == -1)
178 return NULL;
179
180 /* CHANNEL_DEBUG(channel, "%s: +%d+%d", __FUNCTION__, c->x, c->y); */
181 position.buttons_state = c->bs;
182 position.x = c->x;
183 position.y = c->y;
184 position.display_id = c->dpy;
185 msg = spice_msg_out_new(SPICE_CHANNEL(channel),
186 SPICE_MSGC_INPUTS_MOUSE_POSITION);
187 msg->marshallers->msgc_inputs_mouse_position(msg->marshaller, &position);
188
189 c->motion_count++;
190 c->dpy = -1;
191
192 return msg;
193 }
194
195 /* main context */
send_position(SpiceInputsChannel * channel)196 static void send_position(SpiceInputsChannel *channel)
197 {
198 SpiceMsgOut *msg;
199
200 if (spice_channel_get_read_only(SPICE_CHANNEL(channel)))
201 return;
202
203 msg = mouse_position(channel);
204 if (!msg) /* if no motion */
205 return;
206
207 spice_msg_out_send(msg);
208 }
209
210 /* main context */
send_motion(SpiceInputsChannel * channel)211 static void send_motion(SpiceInputsChannel *channel)
212 {
213 SpiceMsgOut *msg;
214
215 if (spice_channel_get_read_only(SPICE_CHANNEL(channel)))
216 return;
217
218 msg = mouse_motion(channel);
219 if (!msg) /* if no motion */
220 return;
221
222 spice_msg_out_send(msg);
223 }
224
225 /* coroutine context */
inputs_handle_init(SpiceChannel * channel,SpiceMsgIn * in)226 static void inputs_handle_init(SpiceChannel *channel, SpiceMsgIn *in)
227 {
228 SpiceInputsChannelPrivate *c = SPICE_INPUTS_CHANNEL(channel)->priv;
229 SpiceMsgInputsInit *init = spice_msg_in_parsed(in);
230
231 c->modifiers = init->keyboard_modifiers;
232 g_coroutine_signal_emit(channel, signals[SPICE_INPUTS_MODIFIERS], 0);
233 }
234
235 /* coroutine context */
inputs_handle_modifiers(SpiceChannel * channel,SpiceMsgIn * in)236 static void inputs_handle_modifiers(SpiceChannel *channel, SpiceMsgIn *in)
237 {
238 SpiceInputsChannelPrivate *c = SPICE_INPUTS_CHANNEL(channel)->priv;
239 SpiceMsgInputsKeyModifiers *modifiers = spice_msg_in_parsed(in);
240
241 c->modifiers = modifiers->modifiers;
242 g_coroutine_signal_emit(channel, signals[SPICE_INPUTS_MODIFIERS], 0);
243 }
244
245 /* coroutine context */
inputs_handle_ack(SpiceChannel * channel,SpiceMsgIn * in)246 static void inputs_handle_ack(SpiceChannel *channel, SpiceMsgIn *in)
247 {
248 SpiceInputsChannelPrivate *c = SPICE_INPUTS_CHANNEL(channel)->priv;
249 SpiceMsgOut *msg;
250
251 c->motion_count -= SPICE_INPUT_MOTION_ACK_BUNCH;
252
253 msg = mouse_motion(SPICE_INPUTS_CHANNEL(channel));
254 if (msg) { /* if no motion, msg == NULL */
255 spice_msg_out_send_internal(msg);
256 }
257
258 msg = mouse_position(SPICE_INPUTS_CHANNEL(channel));
259 if (msg) {
260 spice_msg_out_send_internal(msg);
261 }
262 }
263
channel_set_handlers(SpiceChannelClass * klass)264 static void channel_set_handlers(SpiceChannelClass *klass)
265 {
266 static const spice_msg_handler handlers[] = {
267 [ SPICE_MSG_INPUTS_INIT ] = inputs_handle_init,
268 [ SPICE_MSG_INPUTS_KEY_MODIFIERS ] = inputs_handle_modifiers,
269 [ SPICE_MSG_INPUTS_MOUSE_MOTION_ACK ] = inputs_handle_ack,
270 };
271
272 spice_channel_set_handlers(klass, handlers, G_N_ELEMENTS(handlers));
273 }
274
275 /**
276 * spice_inputs_motion:
277 * @channel: a #SpiceInputsChannel
278 * @dx: delta X mouse coordinates
279 * @dy: delta Y mouse coordinates
280 * @button_state: SPICE_MOUSE_BUTTON_MASK flags
281 *
282 * Change mouse position (used in SPICE_MOUSE_MODE_SERVER).
283 *
284 * Deprecated: 0.35: use spice_inputs_channel_motion() instead.
285 **/
spice_inputs_motion(SpiceInputsChannel * channel,gint dx,gint dy,gint button_state)286 void spice_inputs_motion(SpiceInputsChannel *channel, gint dx, gint dy,
287 gint button_state)
288 {
289 spice_inputs_channel_motion(channel, dx, dy, button_state);
290 }
291
292 /**
293 * spice_inputs_channel_motion:
294 * @channel: a #SpiceInputsChannel
295 * @dx: delta X mouse coordinates
296 * @dy: delta Y mouse coordinates
297 * @button_state: SPICE_MOUSE_BUTTON_MASK flags
298 *
299 * Change mouse position (used in SPICE_MOUSE_MODE_SERVER).
300 *
301 * Since: 0.35
302 **/
spice_inputs_channel_motion(SpiceInputsChannel * channel,gint dx,gint dy,gint button_state)303 void spice_inputs_channel_motion(SpiceInputsChannel *channel, gint dx, gint dy,
304 gint button_state)
305 {
306 SpiceInputsChannelPrivate *c;
307
308 g_return_if_fail(channel != NULL);
309 g_return_if_fail(SPICE_CHANNEL(channel)->priv->state != SPICE_CHANNEL_STATE_UNCONNECTED);
310 if (SPICE_CHANNEL(channel)->priv->state != SPICE_CHANNEL_STATE_READY)
311 return;
312
313 if (dx == 0 && dy == 0)
314 return;
315
316 c = channel->priv;
317 c->bs = button_state;
318 c->dx += dx;
319 c->dy += dy;
320
321 if (c->motion_count < SPICE_INPUT_MOTION_ACK_BUNCH * 2) {
322 send_motion(channel);
323 }
324 }
325
326 /**
327 * spice_inputs_position:
328 * @channel: a #SpiceInputsChannel
329 * @x: X mouse coordinates
330 * @y: Y mouse coordinates
331 * @display: display channel id
332 * @button_state: SPICE_MOUSE_BUTTON_MASK flags
333 *
334 * Change mouse position (used in SPICE_MOUSE_MODE_CLIENT).
335 *
336 * Deprecated: 0.35: use spice_inputs_channel_position() instead.
337 **/
spice_inputs_position(SpiceInputsChannel * channel,gint x,gint y,gint display,gint button_state)338 void spice_inputs_position(SpiceInputsChannel *channel, gint x, gint y,
339 gint display, gint button_state)
340 {
341 spice_inputs_channel_position(channel, x, y, display, button_state);
342 }
343
344 /**
345 * spice_inputs_channel_position:
346 * @channel: a #SpiceInputsChannel
347 * @x: X mouse coordinates
348 * @y: Y mouse coordinates
349 * @display: display channel id
350 * @button_state: SPICE_MOUSE_BUTTON_MASK flags
351 *
352 * Change mouse position (used in SPICE_MOUSE_MODE_CLIENT).
353 *
354 * Since: 0.35
355 **/
spice_inputs_channel_position(SpiceInputsChannel * channel,gint x,gint y,gint display,gint button_state)356 void spice_inputs_channel_position(SpiceInputsChannel *channel, gint x, gint y,
357 gint display, gint button_state)
358 {
359 SpiceInputsChannelPrivate *c;
360
361 g_return_if_fail(channel != NULL);
362
363 if (SPICE_CHANNEL(channel)->priv->state != SPICE_CHANNEL_STATE_READY)
364 return;
365
366 c = channel->priv;
367 c->bs = button_state;
368 c->x = x;
369 c->y = y;
370 c->dpy = display;
371
372 if (c->motion_count < SPICE_INPUT_MOTION_ACK_BUNCH * 2) {
373 send_position(channel);
374 } else {
375 CHANNEL_DEBUG(channel, "over SPICE_INPUT_MOTION_ACK_BUNCH * 2, dropping");
376 }
377 }
378
379 /**
380 * spice_inputs_button_press:
381 * @channel: a #SpiceInputsChannel
382 * @button: a SPICE_MOUSE_BUTTON
383 * @button_state: SPICE_MOUSE_BUTTON_MASK flags
384 *
385 * Press a mouse button.
386 *
387 * Deprecated: 0.35: use spice_inputs_channel_button_press() instead.
388 **/
spice_inputs_button_press(SpiceInputsChannel * channel,gint button,gint button_state)389 void spice_inputs_button_press(SpiceInputsChannel *channel, gint button,
390 gint button_state)
391 {
392 spice_inputs_channel_button_press(channel, button, button_state);
393 }
394
395 /**
396 * spice_inputs_channel_button_press:
397 * @channel: a #SpiceInputsChannel
398 * @button: a SPICE_MOUSE_BUTTON
399 * @button_state: SPICE_MOUSE_BUTTON_MASK flags
400 *
401 * Press a mouse button.
402 *
403 * Since: 0.35
404 **/
spice_inputs_channel_button_press(SpiceInputsChannel * channel,gint button,gint button_state)405 void spice_inputs_channel_button_press(SpiceInputsChannel *channel, gint button,
406 gint button_state)
407 {
408 SpiceInputsChannelPrivate *c;
409 SpiceMsgcMousePress press;
410 SpiceMsgOut *msg;
411
412 g_return_if_fail(channel != NULL);
413
414 if (SPICE_CHANNEL(channel)->priv->state != SPICE_CHANNEL_STATE_READY)
415 return;
416 if (spice_channel_get_read_only(SPICE_CHANNEL(channel)))
417 return;
418
419 c = channel->priv;
420 switch (button) {
421 case SPICE_MOUSE_BUTTON_LEFT:
422 button_state |= SPICE_MOUSE_BUTTON_MASK_LEFT;
423 break;
424 case SPICE_MOUSE_BUTTON_MIDDLE:
425 button_state |= SPICE_MOUSE_BUTTON_MASK_MIDDLE;
426 break;
427 case SPICE_MOUSE_BUTTON_RIGHT:
428 button_state |= SPICE_MOUSE_BUTTON_MASK_RIGHT;
429 break;
430 }
431
432 c->bs = button_state;
433 send_motion(channel);
434 send_position(channel);
435
436 msg = spice_msg_out_new(SPICE_CHANNEL(channel),
437 SPICE_MSGC_INPUTS_MOUSE_PRESS);
438 press.button = button;
439 press.buttons_state = button_state;
440 msg->marshallers->msgc_inputs_mouse_press(msg->marshaller, &press);
441 spice_msg_out_send(msg);
442 }
443
444 /**
445 * spice_inputs_button_release:
446 * @channel: a #SpiceInputsChannel
447 * @button: a SPICE_MOUSE_BUTTON
448 * @button_state: SPICE_MOUSE_BUTTON_MASK flags
449 *
450 * Release a button.
451 *
452 * Deprecated: 0.35: use spice_inputs_channel_button_release() instead.
453 **/
spice_inputs_button_release(SpiceInputsChannel * channel,gint button,gint button_state)454 void spice_inputs_button_release(SpiceInputsChannel *channel, gint button,
455 gint button_state)
456 {
457 spice_inputs_channel_button_release(channel, button, button_state);
458 }
459
460 /**
461 * spice_inputs_channel_button_release:
462 * @channel: a #SpiceInputsChannel
463 * @button: a SPICE_MOUSE_BUTTON
464 * @button_state: SPICE_MOUSE_BUTTON_MASK flags
465 *
466 * Release a button.
467 *
468 * Since: 0.35
469 **/
spice_inputs_channel_button_release(SpiceInputsChannel * channel,gint button,gint button_state)470 void spice_inputs_channel_button_release(SpiceInputsChannel *channel, gint button,
471 gint button_state)
472 {
473 SpiceInputsChannelPrivate *c;
474 SpiceMsgcMouseRelease release;
475 SpiceMsgOut *msg;
476
477 g_return_if_fail(channel != NULL);
478
479 if (SPICE_CHANNEL(channel)->priv->state != SPICE_CHANNEL_STATE_READY)
480 return;
481 if (spice_channel_get_read_only(SPICE_CHANNEL(channel)))
482 return;
483
484 c = channel->priv;
485 switch (button) {
486 case SPICE_MOUSE_BUTTON_LEFT:
487 button_state &= ~SPICE_MOUSE_BUTTON_MASK_LEFT;
488 break;
489 case SPICE_MOUSE_BUTTON_MIDDLE:
490 button_state &= ~SPICE_MOUSE_BUTTON_MASK_MIDDLE;
491 break;
492 case SPICE_MOUSE_BUTTON_RIGHT:
493 button_state &= ~SPICE_MOUSE_BUTTON_MASK_RIGHT;
494 break;
495 }
496
497 c->bs = button_state;
498 send_motion(channel);
499 send_position(channel);
500
501 msg = spice_msg_out_new(SPICE_CHANNEL(channel),
502 SPICE_MSGC_INPUTS_MOUSE_RELEASE);
503 release.button = button;
504 release.buttons_state = button_state;
505 msg->marshallers->msgc_inputs_mouse_release(msg->marshaller, &release);
506 spice_msg_out_send(msg);
507 }
508
509 /**
510 * spice_inputs_key_press:
511 * @channel: a #SpiceInputsChannel
512 * @scancode: a PC XT (set 1) key scancode. For scancodes with an \%0xe0
513 * prefix, drop the prefix and OR the scancode with \%0x100.
514 *
515 * Press a key.
516 *
517 * Deprecated: 0.35: use spice_inputs_channel_key_press() instead.
518 **/
spice_inputs_key_press(SpiceInputsChannel * channel,guint scancode)519 void spice_inputs_key_press(SpiceInputsChannel *channel, guint scancode)
520 {
521 spice_inputs_channel_key_press(channel, scancode);
522 }
523
524 /**
525 * spice_inputs_channel_key_press:
526 * @channel: a #SpiceInputsChannel
527 * @scancode: a PC XT (set 1) key scancode. For scancodes with an \%0xe0
528 * prefix, drop the prefix and OR the scancode with \%0x100.
529 *
530 * Press a key.
531 *
532 * Since: 0.35
533 **/
spice_inputs_channel_key_press(SpiceInputsChannel * channel,guint scancode)534 void spice_inputs_channel_key_press(SpiceInputsChannel *channel, guint scancode)
535 {
536 SpiceMsgcKeyDown down;
537 SpiceMsgOut *msg;
538
539 g_return_if_fail(channel != NULL);
540 g_return_if_fail(SPICE_CHANNEL(channel)->priv->state != SPICE_CHANNEL_STATE_UNCONNECTED);
541 if (SPICE_CHANNEL(channel)->priv->state != SPICE_CHANNEL_STATE_READY)
542 return;
543 if (spice_channel_get_read_only(SPICE_CHANNEL(channel)))
544 return;
545
546 down.code = spice_make_scancode(scancode, FALSE);
547 msg = spice_msg_out_new(SPICE_CHANNEL(channel), SPICE_MSGC_INPUTS_KEY_DOWN);
548 msg->marshallers->msgc_inputs_key_down(msg->marshaller, &down);
549 spice_msg_out_send(msg);
550 }
551
552 /**
553 * spice_inputs_key_release:
554 * @channel: a #SpiceInputsChannel
555 * @scancode: a PC XT (set 1) key scancode. For scancodes with an \%0xe0
556 * prefix, drop the prefix and OR the scancode with \%0x100.
557 *
558 * Release a key.
559 *
560 * Deprecated: 0.35: use spice_inputs_channel_key_release() instead.
561 **/
spice_inputs_key_release(SpiceInputsChannel * channel,guint scancode)562 void spice_inputs_key_release(SpiceInputsChannel *channel, guint scancode)
563 {
564 spice_inputs_channel_key_release(channel, scancode);
565 }
566
567 /**
568 * spice_inputs_channel_key_release:
569 * @channel: a #SpiceInputsChannel
570 * @scancode: a PC XT (set 1) key scancode. For scancodes with an \%0xe0
571 * prefix, drop the prefix and OR the scancode with \%0x100.
572 *
573 * Release a key.
574 *
575 * Since: 0.35
576 **/
spice_inputs_channel_key_release(SpiceInputsChannel * channel,guint scancode)577 void spice_inputs_channel_key_release(SpiceInputsChannel *channel, guint scancode)
578 {
579 SpiceMsgcKeyUp up;
580 SpiceMsgOut *msg;
581
582 g_return_if_fail(channel != NULL);
583 g_return_if_fail(SPICE_CHANNEL(channel)->priv->state != SPICE_CHANNEL_STATE_UNCONNECTED);
584 if (SPICE_CHANNEL(channel)->priv->state != SPICE_CHANNEL_STATE_READY)
585 return;
586 if (spice_channel_get_read_only(SPICE_CHANNEL(channel)))
587 return;
588
589 up.code = spice_make_scancode(scancode, TRUE);
590 msg = spice_msg_out_new(SPICE_CHANNEL(channel), SPICE_MSGC_INPUTS_KEY_UP);
591 msg->marshallers->msgc_inputs_key_up(msg->marshaller, &up);
592 spice_msg_out_send(msg);
593 }
594
595 /**
596 * spice_inputs_key_press_and_release:
597 * @channel: a #SpiceInputsChannel
598 * @scancode: a PC XT (set 1) key scancode. For scancodes with an \%0xe0
599 * prefix, drop the prefix and OR the scancode with \%0x100.
600 *
601 * Press and release a key event atomically (in the same message).
602 *
603 * Since: 0.13
604 *
605 * Deprecated: 0.35
606 **/
spice_inputs_key_press_and_release(SpiceInputsChannel * input_channel,guint scancode)607 void spice_inputs_key_press_and_release(SpiceInputsChannel *input_channel, guint scancode)
608 {
609 spice_inputs_channel_key_press_and_release(input_channel, scancode);
610 }
611
612 /**
613 * spice_inputs_channel_key_press_and_release:
614 * @channel: a #SpiceInputsChannel
615 * @scancode: a PC XT (set 1) key scancode. For scancodes with an \%0xe0
616 * prefix, drop the prefix and OR the scancode with \%0x100.
617 *
618 * Press and release a key event atomically (in the same message).
619 *
620 * Since: 0.35
621 **/
spice_inputs_channel_key_press_and_release(SpiceInputsChannel * input_channel,guint scancode)622 void spice_inputs_channel_key_press_and_release(SpiceInputsChannel *input_channel, guint scancode)
623 {
624 SpiceChannel *channel = SPICE_CHANNEL(input_channel);
625
626 g_return_if_fail(channel != NULL);
627 g_return_if_fail(channel->priv->state != SPICE_CHANNEL_STATE_UNCONNECTED);
628
629 if (channel->priv->state != SPICE_CHANNEL_STATE_READY)
630 return;
631 if (spice_channel_get_read_only(channel))
632 return;
633
634 if (spice_channel_test_capability(channel, SPICE_INPUTS_CAP_KEY_SCANCODE)) {
635 SpiceMsgOut *msg;
636 guint16 code;
637 guint8 *buf;
638
639 msg = spice_msg_out_new(channel, SPICE_MSGC_INPUTS_KEY_SCANCODE);
640 if (scancode < 0x100) {
641 buf = (guint8*)spice_marshaller_reserve_space(msg->marshaller, 2);
642 buf[0] = spice_make_scancode(scancode, FALSE);
643 buf[1] = spice_make_scancode(scancode, TRUE);
644 } else {
645 buf = (guint8*)spice_marshaller_reserve_space(msg->marshaller, 4);
646 code = spice_make_scancode(scancode, FALSE);
647 buf[0] = code & 0xff;
648 buf[1] = code >> 8;
649 code = spice_make_scancode(scancode, TRUE);
650 buf[2] = code & 0xff;
651 buf[3] = code >> 8;
652 }
653 spice_msg_out_send(msg);
654 } else {
655 CHANNEL_DEBUG(channel, "The server doesn't support atomic press and release");
656 spice_inputs_channel_key_press(input_channel, scancode);
657 spice_inputs_channel_key_release(input_channel, scancode);
658 }
659 }
660
661 /* main or coroutine context */
set_key_locks(SpiceInputsChannel * channel,guint locks)662 static SpiceMsgOut* set_key_locks(SpiceInputsChannel *channel, guint locks)
663 {
664 SpiceMsgcKeyModifiers modifiers;
665 SpiceMsgOut *msg;
666 SpiceInputsChannelPrivate *ic;
667 SpiceChannelPrivate *c;
668
669 g_return_val_if_fail(SPICE_IS_INPUTS_CHANNEL(channel), NULL);
670
671 ic = channel->priv;
672 c = SPICE_CHANNEL(channel)->priv;
673
674 ic->locks = locks;
675 if (c->state != SPICE_CHANNEL_STATE_READY)
676 return NULL;
677
678 msg = spice_msg_out_new(SPICE_CHANNEL(channel),
679 SPICE_MSGC_INPUTS_KEY_MODIFIERS);
680 modifiers.modifiers = locks;
681 msg->marshallers->msgc_inputs_key_modifiers(msg->marshaller, &modifiers);
682 return msg;
683 }
684
685 /**
686 * spice_inputs_set_key_locks:
687 * @channel: a #SpiceInputsChannel
688 * @locks: #SpiceInputsLock modifiers flags
689 *
690 * Set the keyboard locks on the guest (Caps, Num, Scroll..)
691 *
692 * Deprecated: 0.35: use spice_inputs_channel_set_key_locks() instead.
693 **/
spice_inputs_set_key_locks(SpiceInputsChannel * channel,guint locks)694 void spice_inputs_set_key_locks(SpiceInputsChannel *channel, guint locks)
695 {
696 spice_inputs_channel_set_key_locks(channel, locks);
697 }
698
699 /**
700 * spice_inputs_channel_set_key_locks:
701 * @channel: a #SpiceInputsChannel
702 * @locks: #SpiceInputsLock modifiers flags
703 *
704 * Set the keyboard locks on the guest (Caps, Num, Scroll..)
705 *
706 * Since: 0.35
707 **/
spice_inputs_channel_set_key_locks(SpiceInputsChannel * channel,guint locks)708 void spice_inputs_channel_set_key_locks(SpiceInputsChannel *channel, guint locks)
709 {
710 SpiceMsgOut *msg;
711
712 if (spice_channel_get_read_only(SPICE_CHANNEL(channel)))
713 return;
714
715 msg = set_key_locks(channel, locks);
716 if (!msg) /* you can set_key_locks() even if the channel is not ready */
717 return;
718
719 spice_msg_out_send(msg); /* main -> coroutine */
720 }
721
722 /* coroutine context */
spice_inputs_channel_up(SpiceChannel * channel)723 static void spice_inputs_channel_up(SpiceChannel *channel)
724 {
725 SpiceInputsChannelPrivate *c = SPICE_INPUTS_CHANNEL(channel)->priv;
726 SpiceMsgOut *msg;
727
728 if (spice_channel_get_read_only(channel))
729 return;
730
731 msg = set_key_locks(SPICE_INPUTS_CHANNEL(channel), c->locks);
732 spice_msg_out_send_internal(msg);
733 }
734
spice_inputs_channel_reset(SpiceChannel * channel,gboolean migrating)735 static void spice_inputs_channel_reset(SpiceChannel *channel, gboolean migrating)
736 {
737 SpiceInputsChannelPrivate *c = SPICE_INPUTS_CHANNEL(channel)->priv;
738 c->motion_count = 0;
739
740 SPICE_CHANNEL_CLASS(spice_inputs_channel_parent_class)->channel_reset(channel, migrating);
741 }
742