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