inputs-channel.c 23.6 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
   Copyright (C) 2009 Red Hat, Inc.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
18
#include <config.h>
19
20
21
22

#include <stddef.h> // NULL
#include <spice/macros.h>
#include <spice/vd_agent.h>
23
#include <spice/protocol.h>
24

25
26
27
#include <common/marshaller.h>
#include <common/messages.h>
#include <common/generated_server_marshallers.h>
28
#include <common/demarshallers.h>
29

30
#include "spice.h"
Frediano Ziglio's avatar
Frediano Ziglio committed
31
#include "red-common.h"
32
#include "reds.h"
33
#include "red-stream.h"
Frediano Ziglio's avatar
Frediano Ziglio committed
34
#include "red-channel.h"
35
#include "red-channel-client.h"
36
#include "red-client.h"
37
#include "inputs-channel-client.h"
38
#include "main-channel-client.h"
Frediano Ziglio's avatar
Frediano Ziglio committed
39
40
#include "inputs-channel.h"
#include "migration-protocol.h"
41
#include "utils.h"
42

43
44
45
46
47
48
49
50
struct InputsChannel
{
    RedChannel parent;

    VDAgentMouseState mouse_state;
    int src_during_migrate;
    SpiceTimer *key_modifiers_timer;

51
52
53
54
55
    // actual ideal modifier states, that the guest should have
    uint8_t modifiers;
    // current pressed modifiers
    uint8_t modifiers_pressed;

56
57
58
59
60
61
62
63
64
65
66
67
    SpiceKbdInstance *keyboard;
    SpiceMouseInstance *mouse;
    SpiceTabletInstance *tablet;
};

struct InputsChannelClass
{
    RedChannelClass parent_class;
};

G_DEFINE_TYPE(InputsChannel, inputs_channel, RED_TYPE_CHANNEL)

68
struct SpiceKbdState {
69
    uint8_t push_ext_type;
70
71

    /* track key press state */
72
73
    bool key[0x80];
    bool key_ext[0x80];
74
    InputsChannel *inputs;
75
76
};

Frediano Ziglio's avatar
Frediano Ziglio committed
77
78
79
80
static SpiceKbdInstance* inputs_channel_get_keyboard(InputsChannel *inputs);
static SpiceMouseInstance* inputs_channel_get_mouse(InputsChannel *inputs);
static SpiceTabletInstance* inputs_channel_get_tablet(InputsChannel *inputs);

81
static SpiceKbdState* spice_kbd_state_new(InputsChannel *inputs)
82
{
83
    SpiceKbdState *st = g_new0(SpiceKbdState, 1);
84
    st->inputs = inputs;
85
86
87
    return st;
}

88
89
90
91
struct SpiceMouseState {
    int dummy;
};

92
static SpiceMouseState* spice_mouse_state_new(void)
93
{
94
    return g_new0(SpiceMouseState, 1);
95
96
}

97
struct SpiceTabletState {
98
    RedsState *reds;
99
100
};

101
static SpiceTabletState* spice_tablet_state_new(RedsState* reds)
102
{
103
104
105
106
107
108
109
110
    SpiceTabletState *st = g_new0(SpiceTabletState, 1);
    st->reds = reds;
    return st;
}

static void spice_tablet_state_free(SpiceTabletState* st)
{
    g_free(st);
111
112
}

113
114
115
116
117
RedsState* spice_tablet_state_get_server(SpiceTabletState *st)
{
    return st->reds;
}

118
typedef struct RedKeyModifiersPipeItem {
119
    RedPipeItem base;
120
    uint8_t modifiers;
121
} RedKeyModifiersPipeItem;
122

123
typedef struct RedInputsInitPipeItem {
124
    RedPipeItem base;
125
    uint8_t modifiers;
126
} RedInputsInitPipeItem;
127

128

129
#define KEY_MODIFIERS_TTL (MSEC_PER_SEC * 2)
130

131
#define SCAN_CODE_RELEASE 0x80
132
133
134
135
#define SCROLL_LOCK_SCAN_CODE 0x46
#define NUM_LOCK_SCAN_CODE 0x45
#define CAPS_LOCK_SCAN_CODE 0x3a

136
void inputs_channel_set_tablet_logical_size(InputsChannel *inputs, int x_res, int y_res)
137
138
139
{
    SpiceTabletInterface *sif;

Frediano Ziglio's avatar
Frediano Ziglio committed
140
    sif = SPICE_UPCAST(SpiceTabletInterface, inputs->tablet->base.sif);
141
    sif->set_logical_size(inputs->tablet, x_res, y_res);
142
143
}

144
const VDAgentMouseState *inputs_channel_get_mouse_state(InputsChannel *inputs)
145
{
146
    return &inputs->mouse_state;
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
}

#define OUTGOING_OK 0
#define OUTGOING_FAILED -1
#define OUTGOING_BLOCKED 1

#define RED_MOUSE_STATE_TO_LOCAL(state)     \
    ((state & SPICE_MOUSE_BUTTON_MASK_LEFT) |          \
     ((state & SPICE_MOUSE_BUTTON_MASK_MIDDLE) << 1) |   \
     ((state & SPICE_MOUSE_BUTTON_MASK_RIGHT) >> 1))

#define RED_MOUSE_BUTTON_STATE_TO_AGENT(state)                      \
    (((state & SPICE_MOUSE_BUTTON_MASK_LEFT) ? VD_AGENT_LBUTTON_MASK : 0) |    \
     ((state & SPICE_MOUSE_BUTTON_MASK_MIDDLE) ? VD_AGENT_MBUTTON_MASK : 0) |    \
     ((state & SPICE_MOUSE_BUTTON_MASK_RIGHT) ? VD_AGENT_RBUTTON_MASK : 0))

163
static void activate_modifiers_watch(InputsChannel *inputs)
164
{
165
166
    SpiceCoreInterfaceInternal *core = red_channel_get_core_interface(RED_CHANNEL(inputs));
    core->timer_start(core, inputs->key_modifiers_timer, KEY_MODIFIERS_TTL);
167
168
169
170
171
172
173
174
175
}

static void kbd_push_scan(SpiceKbdInstance *sin, uint8_t scan)
{
    SpiceKbdInterface *sif;

    if (!sin) {
        return;
    }
176
    sif = SPICE_UPCAST(SpiceKbdInterface, sin->base.sif);
177
178

    /* track XT scan code set 1 key state */
179
180
    if (scan >= 0xe0 && scan <= 0xe2) {
        sin->st->push_ext_type = scan;
181
    } else {
182
183
        if (sin->st->push_ext_type == 0 || sin->st->push_ext_type == 0xe0) {
            bool *state = sin->st->push_ext_type ? sin->st->key_ext : sin->st->key;
184
            state[scan & 0x7f] = !(scan & SCAN_CODE_RELEASE);
185
186
        }
        sin->st->push_ext_type = 0;
187
188
    }

189
190
191
    sif->push_scan_freg(sin, scan);
}

192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
static uint8_t scancode_to_modifier_flag(uint8_t scancode)
{
    switch (scancode & ~SCAN_CODE_RELEASE) {
    case CAPS_LOCK_SCAN_CODE:
        return SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK;
    case NUM_LOCK_SCAN_CODE:
        return SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK;
    case SCROLL_LOCK_SCAN_CODE:
        return SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK;
    }
    return 0;
}

static void inputs_channel_sync_locks(InputsChannel *inputs_channel, uint8_t scan)
{
    uint8_t change_modifier = scancode_to_modifier_flag(scan);

    if (scan & SCAN_CODE_RELEASE) { /* KEY_UP */
        inputs_channel->modifiers_pressed &= ~change_modifier;
    } else {  /* KEY_DOWN */
        if (change_modifier && !(inputs_channel->modifiers_pressed & change_modifier)) {
            inputs_channel->modifiers ^= change_modifier;
            inputs_channel->modifiers_pressed |= change_modifier;
            activate_modifiers_watch(inputs_channel);
        }
    }
}

220
221
222
223
224
225
226
static uint8_t kbd_get_leds(SpiceKbdInstance *sin)
{
    SpiceKbdInterface *sif;

    if (!sin) {
        return 0;
    }
227
    sif = SPICE_UPCAST(SpiceKbdInterface, sin->base.sif);
228
229
230
    return sif->get_leds(sin);
}

231
static RedPipeItem *red_inputs_key_modifiers_item_new(uint8_t modifiers)
232
{
233
    RedKeyModifiersPipeItem *item = g_new(RedKeyModifiersPipeItem, 1);
234

235
    red_pipe_item_init(&item->base, RED_PIPE_ITEM_KEY_MODIFIERS);
236
    item->modifiers = modifiers;
237
    return &item->base;
238
}
239

240
static void inputs_channel_send_item(RedChannelClient *rcc, RedPipeItem *base)
241
{
242
    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
243

244
    switch (base->type) {
245
        case RED_PIPE_ITEM_KEY_MODIFIERS:
246
247
248
        {
            SpiceMsgInputsKeyModifiers key_modifiers;

249
            red_channel_client_init_send_data(rcc, SPICE_MSG_INPUTS_KEY_MODIFIERS);
250
            key_modifiers.modifiers =
Frediano Ziglio's avatar
Frediano Ziglio committed
251
                SPICE_UPCAST(RedKeyModifiersPipeItem, base)->modifiers;
252
            spice_marshall_msg_inputs_key_modifiers(m, &key_modifiers);
253
            break;
254
        }
255
        case RED_PIPE_ITEM_INPUTS_INIT:
256
257
258
        {
            SpiceMsgInputsInit inputs_init;

259
            red_channel_client_init_send_data(rcc, SPICE_MSG_INPUTS_INIT);
260
            inputs_init.keyboard_modifiers =
Frediano Ziglio's avatar
Frediano Ziglio committed
261
                SPICE_UPCAST(RedInputsInitPipeItem, base)->modifiers;
262
263
264
            spice_marshall_msg_inputs_init(m, &inputs_init);
            break;
        }
265
        case RED_PIPE_ITEM_MOUSE_MOTION_ACK:
266
            red_channel_client_init_send_data(rcc, SPICE_MSG_INPUTS_MOUSE_MOTION_ACK);
267
            break;
268
        case RED_PIPE_ITEM_MIGRATE_DATA:
269
            INPUTS_CHANNEL(red_channel_client_get_channel(rcc))->src_during_migrate = FALSE;
270
            inputs_channel_client_send_migrate_data(rcc, m, base);
271
            break;
272
        default:
273
            spice_warning("invalid pipe iten %d", base->type);
274
            break;
275
    }
276
    red_channel_client_begin_send_message(rcc);
277
278
}

279
280
static bool inputs_channel_handle_message(RedChannelClient *rcc, uint16_t type,
                                          uint32_t size, void *message)
281
{
282
    InputsChannel *inputs_channel = INPUTS_CHANNEL(red_channel_client_get_channel(rcc));
283
    InputsChannelClient *icc = INPUTS_CHANNEL_CLIENT(rcc);
284
    uint32_t i;
285
    RedsState *reds = red_channel_get_server(RED_CHANNEL(inputs_channel));
286
287
288

    switch (type) {
    case SPICE_MSGC_INPUTS_KEY_DOWN: {
289
        SpiceMsgcKeyDown *key_down = message;
290
        inputs_channel_sync_locks(inputs_channel, key_down->code);
291
    }
292
        /* fallthrough */
293
    case SPICE_MSGC_INPUTS_KEY_UP: {
294
        SpiceMsgcKeyUp *key_up = message;
295
        for (i = 0; i < 4; i++) {
296
            uint8_t code = (key_up->code >> (i * 8)) & 0xff;
297
298
299
            if (code == 0) {
                break;
            }
300
            kbd_push_scan(inputs_channel_get_keyboard(inputs_channel), code);
301
            inputs_channel_sync_locks(inputs_channel, code);
302
303
304
        }
        break;
    }
305
    case SPICE_MSGC_INPUTS_KEY_SCANCODE: {
306
        uint8_t *code = message;
307
        for (i = 0; i < size; i++) {
308
            kbd_push_scan(inputs_channel_get_keyboard(inputs_channel), code[i]);
309
            inputs_channel_sync_locks(inputs_channel, code[i]);
310
311
312
        }
        break;
    }
313
    case SPICE_MSGC_INPUTS_MOUSE_MOTION: {
314
        SpiceMouseInstance *mouse = inputs_channel_get_mouse(inputs_channel);
315
        SpiceMsgcMouseMotion *mouse_motion = message;
316

317
        inputs_channel_client_on_mouse_motion(icc);
318
        if (mouse && reds_get_mouse_mode(reds) == SPICE_MOUSE_MODE_SERVER) {
319
            SpiceMouseInterface *sif;
320
            sif = SPICE_UPCAST(SpiceMouseInterface, mouse->base.sif);
321
322
323
324
325
326
327
            sif->motion(mouse,
                        mouse_motion->dx, mouse_motion->dy, 0,
                        RED_MOUSE_STATE_TO_LOCAL(mouse_motion->buttons_state));
        }
        break;
    }
    case SPICE_MSGC_INPUTS_MOUSE_POSITION: {
328
        SpiceMsgcMousePosition *pos = message;
329
        SpiceTabletInstance *tablet = inputs_channel_get_tablet(inputs_channel);
330

331
        inputs_channel_client_on_mouse_motion(icc);
332
        if (reds_get_mouse_mode(reds) != SPICE_MOUSE_MODE_CLIENT) {
333
334
            break;
        }
335
336
        spice_assert((reds_config_get_agent_mouse(reds) && reds_has_vdagent(reds)) || tablet);
        if (!reds_config_get_agent_mouse(reds) || !reds_has_vdagent(reds)) {
337
            SpiceTabletInterface *sif;
338
            sif = SPICE_UPCAST(SpiceTabletInterface, tablet->base.sif);
339
340
341
            sif->position(tablet, pos->x, pos->y, RED_MOUSE_STATE_TO_LOCAL(pos->buttons_state));
            break;
        }
342
        VDAgentMouseState *mouse_state = &inputs_channel->mouse_state;
343
344
345
346
        mouse_state->x = pos->x;
        mouse_state->y = pos->y;
        mouse_state->buttons = RED_MOUSE_BUTTON_STATE_TO_AGENT(pos->buttons_state);
        mouse_state->display_id = pos->display_id;
347
        reds_handle_agent_mouse_event(reds, mouse_state);
348
349
350
        break;
    }
    case SPICE_MSGC_INPUTS_MOUSE_PRESS: {
351
        SpiceMsgcMousePress *mouse_press = message;
352
353
354
355
356
357
        int dz = 0;
        if (mouse_press->button == SPICE_MOUSE_BUTTON_UP) {
            dz = -1;
        } else if (mouse_press->button == SPICE_MOUSE_BUTTON_DOWN) {
            dz = 1;
        }
358
        if (reds_get_mouse_mode(reds) == SPICE_MOUSE_MODE_CLIENT) {
359
            if (reds_config_get_agent_mouse(reds) && reds_has_vdagent(reds)) {
360
                inputs_channel->mouse_state.buttons =
361
362
363
                    RED_MOUSE_BUTTON_STATE_TO_AGENT(mouse_press->buttons_state) |
                    (dz == -1 ? VD_AGENT_UBUTTON_MASK : 0) |
                    (dz == 1 ? VD_AGENT_DBUTTON_MASK : 0);
364
                reds_handle_agent_mouse_event(reds, &inputs_channel->mouse_state);
365
            } else if (inputs_channel_get_tablet(inputs_channel)) {
366
                SpiceTabletInterface *sif;
367
368
369
370
                sif = SPICE_CONTAINEROF(inputs_channel_get_tablet(inputs_channel)->base.sif,
                                        SpiceTabletInterface, base);
                sif->wheel(inputs_channel_get_tablet(inputs_channel), dz,
                           RED_MOUSE_STATE_TO_LOCAL(mouse_press->buttons_state));
371
            }
372
        } else if (inputs_channel_get_mouse(inputs_channel)) {
373
            SpiceMouseInterface *sif;
374
375
            sif = SPICE_CONTAINEROF(inputs_channel_get_mouse(inputs_channel)->base.sif,
                                    SpiceMouseInterface, base);
376
            sif->motion(inputs_channel_get_mouse(inputs_channel), 0, 0, dz,
377
378
379
380
381
                        RED_MOUSE_STATE_TO_LOCAL(mouse_press->buttons_state));
        }
        break;
    }
    case SPICE_MSGC_INPUTS_MOUSE_RELEASE: {
382
        SpiceMsgcMouseRelease *mouse_release = message;
383
        if (reds_get_mouse_mode(reds) == SPICE_MOUSE_MODE_CLIENT) {
384
            if (reds_config_get_agent_mouse(reds) && reds_has_vdagent(reds)) {
385
                inputs_channel->mouse_state.buttons =
386
                    RED_MOUSE_BUTTON_STATE_TO_AGENT(mouse_release->buttons_state);
387
                reds_handle_agent_mouse_event(reds, &inputs_channel->mouse_state);
388
            } else if (inputs_channel_get_tablet(inputs_channel)) {
389
                SpiceTabletInterface *sif;
390
391
392
393
                sif = SPICE_CONTAINEROF(inputs_channel_get_tablet(inputs_channel)->base.sif,
                                        SpiceTabletInterface, base);
                sif->buttons(inputs_channel_get_tablet(inputs_channel),
                             RED_MOUSE_STATE_TO_LOCAL(mouse_release->buttons_state));
394
            }
395
        } else if (inputs_channel_get_mouse(inputs_channel)) {
396
            SpiceMouseInterface *sif;
397
398
            sif = SPICE_CONTAINEROF(inputs_channel_get_mouse(inputs_channel)->base.sif,
                                    SpiceMouseInterface, base);
399
            sif->buttons(inputs_channel_get_mouse(inputs_channel),
400
401
402
403
404
                         RED_MOUSE_STATE_TO_LOCAL(mouse_release->buttons_state));
        }
        break;
    }
    case SPICE_MSGC_INPUTS_KEY_MODIFIERS: {
405
        SpiceMsgcKeyModifiers *modifiers = message;
406
        uint8_t leds;
407
        SpiceKbdInstance *keyboard = inputs_channel_get_keyboard(inputs_channel);
408
409
410
411

        if (!keyboard) {
            break;
        }
412
413
414
415
        leds = inputs_channel->modifiers;
        if (!(inputs_channel->modifiers_pressed & SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK) &&
            ((modifiers->modifiers & SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK) !=
             (leds & SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK))) {
416
            kbd_push_scan(keyboard, SCROLL_LOCK_SCAN_CODE);
417
            kbd_push_scan(keyboard, SCROLL_LOCK_SCAN_CODE | SCAN_CODE_RELEASE);
418
            inputs_channel->modifiers ^= SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK;
419
        }
420
421
422
        if (!(inputs_channel->modifiers_pressed & SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK) &&
            ((modifiers->modifiers & SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK) !=
             (leds & SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK))) {
423
            kbd_push_scan(keyboard, NUM_LOCK_SCAN_CODE);
424
            kbd_push_scan(keyboard, NUM_LOCK_SCAN_CODE | SCAN_CODE_RELEASE);
425
            inputs_channel->modifiers ^= SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK;
426
        }
427
428
429
        if (!(inputs_channel->modifiers_pressed & SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK) &&
            ((modifiers->modifiers & SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK) !=
             (leds & SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK))) {
430
            kbd_push_scan(keyboard, CAPS_LOCK_SCAN_CODE);
431
            kbd_push_scan(keyboard, CAPS_LOCK_SCAN_CODE | SCAN_CODE_RELEASE);
432
            inputs_channel->modifiers ^= SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK;
433
        }
434
        activate_modifiers_watch(inputs_channel);
435
436
437
        break;
    }
    default:
438
        return red_channel_client_handle_message(rcc, type, size, message);
439
    }
440
    return TRUE;
441
442
}

443
void inputs_release_keys(InputsChannel *inputs)
444
{
445
    int i;
446
    SpiceKbdState *st;
447
    SpiceKbdInstance *keyboard = inputs_channel_get_keyboard(inputs);
448
449
450
451
452

    if (!keyboard) {
        return;
    }
    st = keyboard->st;
453
454
455
456
457
458

    for (i = 0; i < SPICE_N_ELEMENTS(st->key); i++) {
        if (!st->key[i])
            continue;

        st->key[i] = FALSE;
459
        kbd_push_scan(keyboard, i | SCAN_CODE_RELEASE);
460
461
462
463
464
465
466
467
    }

    for (i = 0; i < SPICE_N_ELEMENTS(st->key_ext); i++) {
        if (!st->key_ext[i])
            continue;

        st->key_ext[i] = FALSE;
        kbd_push_scan(keyboard, 0xe0);
468
        kbd_push_scan(keyboard, i | SCAN_CODE_RELEASE);
469
    }
470
471
}

472
static void inputs_pipe_add_init(RedChannelClient *rcc)
473
{
474
    RedInputsInitPipeItem *item = g_new(RedInputsInitPipeItem, 1);
475
    InputsChannel *inputs = INPUTS_CHANNEL(red_channel_client_get_channel(rcc));
476

477
    red_pipe_item_init(&item->base, RED_PIPE_ITEM_INPUTS_INIT);
478
    item->modifiers = kbd_get_leds(inputs_channel_get_keyboard(inputs));
479
    red_channel_client_pipe_add_push(rcc, &item->base);
480
481
}

482
static void inputs_connect(RedChannel *channel, RedClient *client,
483
                           RedStream *stream, int migration,
484
                           RedChannelCapabilities *caps)
485
{
486
    RedChannelClient *rcc;
487

488
    if (!red_stream_is_ssl(stream) && !red_client_during_migrate_at_target(client)) {
489
490
491
492
        main_channel_client_push_notify(red_client_get_main(client),
                                        "keyboard channel is insecure");
    }

493
    rcc = inputs_channel_client_create(channel, client, stream, caps);
494
    if (!rcc) {
495
496
        return;
    }
497
    inputs_pipe_add_init(rcc);
498
499
}

500
501
static void inputs_migrate(RedChannelClient *rcc)
{
502
    InputsChannel *inputs = INPUTS_CHANNEL(red_channel_client_get_channel(rcc));
503
    inputs->src_during_migrate = TRUE;
504
505
506
    red_channel_client_default_migrate(rcc);
}

507
static void inputs_channel_push_keyboard_modifiers(InputsChannel *inputs, uint8_t modifiers)
508
{
509
    if (!inputs || !red_channel_is_connected(RED_CHANNEL(inputs)) ||
510
        inputs->src_during_migrate) {
511
512
        return;
    }
513
514
    red_channel_pipes_add(RED_CHANNEL(inputs),
                          red_inputs_key_modifiers_item_new(modifiers));
515
516
}

517
SPICE_GNUC_VISIBLE int spice_server_kbd_leds(SpiceKbdInstance *sin, int leds)
518
{
519
520
521
522
523
    InputsChannel *inputs_channel = sin->st->inputs;
    if (inputs_channel) {
        inputs_channel->modifiers = leds;
        inputs_channel_push_keyboard_modifiers(inputs_channel, leds);
    }
524
    return 0;
525
526
527
528
}

static void key_modifiers_sender(void *opaque)
{
529
    InputsChannel *inputs = opaque;
530
    inputs_channel_push_keyboard_modifiers(inputs, inputs->modifiers);
531
532
}

533
static bool inputs_channel_handle_migrate_flush_mark(RedChannelClient *rcc)
534
{
535
    red_channel_client_pipe_add_type(rcc, RED_PIPE_ITEM_MIGRATE_DATA);
536
537
538
    return TRUE;
}

539
540
541
static bool inputs_channel_handle_migrate_data(RedChannelClient *rcc,
                                               uint32_t size,
                                               void *message)
542
{
543
    InputsChannelClient *icc = INPUTS_CHANNEL_CLIENT(rcc);
544
    InputsChannel *inputs = INPUTS_CHANNEL(red_channel_client_get_channel(rcc));
545
546
547
    SpiceMigrateDataHeader *header;
    SpiceMigrateDataInputs *mig_data;

548
549
550
551
552
    if (size < sizeof(SpiceMigrateDataHeader) + sizeof(SpiceMigrateDataInputs)) {
        spice_warning("bad message size %u", size);
        return FALSE;
    }

553
554
555
556
557
558
559
560
561
    header = (SpiceMigrateDataHeader *)message;
    mig_data = (SpiceMigrateDataInputs *)(header + 1);

    if (!migration_protocol_validate_header(header,
                                            SPICE_MIGRATE_DATA_INPUTS_MAGIC,
                                            SPICE_MIGRATE_DATA_INPUTS_VERSION)) {
        spice_error("bad header");
        return FALSE;
    }
562
    key_modifiers_sender(inputs);
563
    inputs_channel_client_handle_migrate_data(icc, mig_data->motion_count);
564
565
566
    return TRUE;
}

567
InputsChannel* inputs_channel_new(RedsState *reds)
568
{
569
570
571
572
573
574
    return  g_object_new(TYPE_INPUTS_CHANNEL,
                         "spice-server", reds,
                         "core-interface", reds_get_core_interface(reds),
                         "channel-type", (int)SPICE_CHANNEL_INPUTS,
                         "id", 0,
                         "handle-acks", FALSE,
575
                         "migration-flags",
576
577
578
579
580
581
582
583
584
585
                         (guint)(SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER),
                         NULL);

}

static void
inputs_channel_constructed(GObject *object)
{
    InputsChannel *self = INPUTS_CHANNEL(object);
    RedsState *reds = red_channel_get_server(RED_CHANNEL(self));
586
    SpiceCoreInterfaceInternal *core = red_channel_get_core_interface(RED_CHANNEL(self));
587
588

    G_OBJECT_CLASS(inputs_channel_parent_class)->constructed(object);
589

590
591
    red_channel_set_cap(RED_CHANNEL(self), SPICE_INPUTS_CAP_KEY_SCANCODE);
    reds_register_channel(reds, RED_CHANNEL(self));
592

593
    self->key_modifiers_timer = core->timer_add(core, key_modifiers_sender, self);
594
    if (!self->key_modifiers_timer) {
595
        spice_error("key modifiers timer create failed");
596
    }
597
598
}

599
600
601
602
static void
inputs_channel_finalize(GObject *object)
{
    InputsChannel *self = INPUTS_CHANNEL(object);
603
    SpiceCoreInterfaceInternal *core = red_channel_get_core_interface(RED_CHANNEL(self));
604

605
    inputs_channel_detach_tablet(self, self->tablet);
606
    core->timer_remove(core, self->key_modifiers_timer);
607

608
609
610
    G_OBJECT_CLASS(inputs_channel_parent_class)->finalize(object);
}

611
612
613
614
615
616
617
618
619
620
621
622
623
static void
inputs_channel_init(InputsChannel *self)
{
}


static void
inputs_channel_class_init(InputsChannelClass *klass)
{
    GObjectClass *object_class = G_OBJECT_CLASS(klass);
    RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass);

    object_class->constructed = inputs_channel_constructed;
624
    object_class->finalize = inputs_channel_finalize;
625
626

    channel_class->parser = spice_get_client_channel_parser(SPICE_CHANNEL_INPUTS, NULL);
627
    channel_class->handle_message = inputs_channel_handle_message;
628
629
630
631
632

    /* channel callbacks */
    channel_class->send_item = inputs_channel_send_item;
    channel_class->handle_migrate_data = inputs_channel_handle_migrate_data;
    channel_class->handle_migrate_flush_mark = inputs_channel_handle_migrate_flush_mark;
633
634
635
636

    // client callbacks
    channel_class->connect = inputs_connect;
    channel_class->migrate = inputs_migrate;
637
638
}

Frediano Ziglio's avatar
Frediano Ziglio committed
639
static SpiceKbdInstance* inputs_channel_get_keyboard(InputsChannel *inputs)
640
641
642
643
644
645
646
{
    return inputs->keyboard;
}

int inputs_channel_set_keyboard(InputsChannel *inputs, SpiceKbdInstance *keyboard)
{
    if (inputs->keyboard) {
647
        red_channel_warning(RED_CHANNEL(inputs), "already have keyboard");
648
649
650
        return -1;
    }
    inputs->keyboard = keyboard;
651
    inputs->keyboard->st = spice_kbd_state_new(inputs);
652
653
654
    return 0;
}

Frediano Ziglio's avatar
Frediano Ziglio committed
655
static SpiceMouseInstance* inputs_channel_get_mouse(InputsChannel *inputs)
656
657
658
659
660
661
662
{
    return inputs->mouse;
}

int inputs_channel_set_mouse(InputsChannel *inputs, SpiceMouseInstance *mouse)
{
    if (inputs->mouse) {
663
        red_channel_warning(RED_CHANNEL(inputs), "already have mouse");
664
665
666
667
668
669
670
        return -1;
    }
    inputs->mouse = mouse;
    inputs->mouse->st = spice_mouse_state_new();
    return 0;
}

Frediano Ziglio's avatar
Frediano Ziglio committed
671
static SpiceTabletInstance* inputs_channel_get_tablet(InputsChannel *inputs)
672
673
{
    return inputs->tablet;
674
}
675

676
int inputs_channel_set_tablet(InputsChannel *inputs, SpiceTabletInstance *tablet)
677
678
{
    if (inputs->tablet) {
679
        red_channel_warning(RED_CHANNEL(inputs), "already have tablet");
680
681
682
        return -1;
    }
    inputs->tablet = tablet;
683
    inputs->tablet->st = spice_tablet_state_new(red_channel_get_server(RED_CHANNEL(inputs)));
684
685
686
687
688
689
690
691
692
693
    return 0;
}

int inputs_channel_has_tablet(InputsChannel *inputs)
{
    return inputs != NULL && inputs->tablet != NULL;
}

void inputs_channel_detach_tablet(InputsChannel *inputs, SpiceTabletInstance *tablet)
{
694
    if (tablet != NULL && tablet == inputs->tablet) {
695
        spice_tablet_state_free(tablet->st);
696
697
        tablet->st = NULL;
    }
698
699
700
    inputs->tablet = NULL;
}

701
702
703
704
gboolean inputs_channel_is_src_during_migrate(InputsChannel *inputs)
{
    return inputs->src_during_migrate;
}