]> git.proxmox.com Git - spiceterm.git/blame - input.c
d/control: fix priority-extra-is-replaced-by-priority-optional
[spiceterm.git] / input.c
CommitLineData
f424be98
DM
1/*
2
3 Copyright (C) 2013 Proxmox Server Solutions GmbH
4
5 Copyright: spiceterm is under GNU GPL, the GNU General Public License.
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; version 2 dated June, 1991.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 02111-1307, USA.
20
21 Author: Dietmar Maurer <dietmar@proxmox.com>
22*/
23
24#define _GNU_SOURCE
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <unistd.h>
29#include <sys/types.h>
30#include <string.h>
7f3ff8c0 31#include <errno.h>
f424be98
DM
32
33#include "spiceterm.h"
7f3ff8c0 34#include "keysyms.h"
f424be98
DM
35
36#include <glib.h>
37#include <spice.h>
38#include <spice/enums.h>
39#include <spice/macros.h>
40#include <spice/qxl_dev.h>
41
42#include "event_loop.h"
43
44static int debug = 0;
45
46#define DPRINTF(x, format, ...) { \
47 if (x <= debug) { \
48 printf("%s: " format "\n" , __FUNCTION__, ## __VA_ARGS__); \
49 } \
50}
51
52static uint8_t
53my_kbd_get_leds(SpiceKbdInstance *sin)
54{
55 return 0;
56}
57
7f3ff8c0
DM
58#define MOD_MASK_SHIFT (1<<0)
59#define MOD_MASK_ALTGR (1<<1)
60#define MOD_MASK_NUMLOCK (1<<2)
61
62
63typedef struct keymap_entry {
64 gint hkey; //(mask << 8 || keycode)
65 guint8 mask; // MOD_MASK_*
66 guint8 keycode;
67 guint keysym;
68 guint unicode;
69} keymap_entry;
70
71static GHashTable *keymap = NULL;
72
f424be98
DM
73#define KBD_MOD_CONTROL_L_FLAG (1<<0)
74#define KBD_MOD_CONTROL_R_FLAG (1<<1)
75#define KBD_MOD_SHIFT_L_FLAG (1<<2)
76#define KBD_MOD_SHIFT_R_FLAG (1<<3)
7f3ff8c0
DM
77#define KBD_MOD_ALTGR_FLAG (1<<4)
78#define KBD_MOD_NUMLOCK (1<<5)
79#define KBD_MOD_SHIFTLOCK (1<<6)
93f3f187 80#define KBD_MOD_ALT_FLAG (1<<7)
f424be98
DM
81
82static int kbd_flags = 0;
83
7f3ff8c0
DM
84static const name2keysym_t *
85lookup_keysym(const char *name)
86{
87 const name2keysym_t *p;
88 for(p = name2keysym; p->name != NULL; p++) {
89 if (!strcmp(p->name, name))
90 return p;
91 }
92 return NULL;
93}
94
f424be98
DM
95static void
96my_kbd_push_key(SpiceKbdInstance *sin, uint8_t frag)
97{
98 spiceTerm *vt = SPICE_CONTAINEROF(sin, spiceTerm, keyboard_sin);
99
100 char *esc = NULL; // used to send special keys
101
102 static int e0_mode = 0;
103
104 DPRINTF(1, "enter frag=%02x flags=%08x", frag, kbd_flags);
7f3ff8c0 105
f424be98
DM
106 if (e0_mode) {
107 e0_mode = 0;
108 switch (frag) {
109 case 0x1d: // press Control_R
110 kbd_flags |= KBD_MOD_CONTROL_R_FLAG;
111 break;
112 case 0x9d: // release Control_R
113 kbd_flags &= ~KBD_MOD_CONTROL_R_FLAG;
114 break;
7f3ff8c0
DM
115 case 0x38: // press ALTGR
116 kbd_flags |= KBD_MOD_ALTGR_FLAG;
117 break;
118 case 0xb8: // release ALTGR
119 kbd_flags &= ~KBD_MOD_ALTGR_FLAG;
120 break;
f424be98
DM
121 case 0x47: // press Home
122 esc = "OH";
123 break;
124 case 0x4f: // press END
125 esc = "OF";
126 break;
127 case 0x48: // press UP
128 esc = "OA";
129 break;
130 case 0x50: // press DOWN
131 esc = "OB";
132 break;
133 case 0x4b: // press LEFT
134 esc = "OD";
135 break;
136 case 0x4d: // press RIGHT
137 esc = "OC";
138 break;
139 case 0x52: // press INSERT
140 esc = "[2~";
141 break;
7f3ff8c0
DM
142 case 0x53: // press Delete
143 esc = "[3~";
144 break;
f424be98
DM
145 case 0x49: // press PAGE_UP
146 if (kbd_flags & (KBD_MOD_SHIFT_L_FLAG|KBD_MOD_SHIFT_R_FLAG)) {
147 spiceterm_virtual_scroll(vt, -vt->height/2);
148 }
149 break;
150 case 0x51: // press PAGE_DOWN
151 if (kbd_flags & (KBD_MOD_SHIFT_L_FLAG|KBD_MOD_SHIFT_R_FLAG)) {
152 spiceterm_virtual_scroll(vt, vt->height/2);
153 }
154 break;
155 }
156 } else {
157 switch (frag) {
158 case 0xe0:
159 e0_mode = 1;
160 break;
161 case 0x1d: // press Control_L
162 kbd_flags |= KBD_MOD_CONTROL_L_FLAG;
163 break;
164 case 0x9d: // release Control_L
165 kbd_flags &= ~KBD_MOD_CONTROL_L_FLAG;
166 break;
167 case 0x2a: // press Shift_L
168 kbd_flags |= KBD_MOD_SHIFT_L_FLAG;
169 break;
170 case 0xaa: // release Shift_L
171 kbd_flags &= ~KBD_MOD_SHIFT_L_FLAG;
172 break;
173 case 0x36: // press Shift_R
174 kbd_flags |= KBD_MOD_SHIFT_R_FLAG;
175 break;
176 case 0xb6: // release Shift_R
177 kbd_flags &= ~KBD_MOD_SHIFT_R_FLAG;
178 break;
93f3f187
DM
179 case 0x38: // press ALT
180 kbd_flags |= KBD_MOD_ALT_FLAG;
181 break;
182 case 0xb8: // release ALT
183 kbd_flags &= ~KBD_MOD_ALT_FLAG;
184 break;
f424be98 185 case 0x52: // press KP_INSERT
7f3ff8c0
DM
186 if (!(kbd_flags & KBD_MOD_NUMLOCK))
187 esc = "[2~";
f424be98
DM
188 break;
189 case 0x53: // press KP_Delete
7f3ff8c0
DM
190 if (!(kbd_flags & KBD_MOD_NUMLOCK))
191 esc = "[3~";
192 break;
193 case 0x45: // press Numlock
194 if (kbd_flags & KBD_MOD_NUMLOCK) {
195 kbd_flags &= ~KBD_MOD_NUMLOCK;
196 } else {
197 kbd_flags |= KBD_MOD_NUMLOCK;
198 }
f424be98 199 break;
7f3ff8c0
DM
200 case 0x3a: // press Shiftlock
201 if (kbd_flags & KBD_MOD_SHIFTLOCK) {
202 kbd_flags &= ~KBD_MOD_SHIFTLOCK;
203 } else {
204 kbd_flags |= KBD_MOD_SHIFTLOCK;
205 }
206 break;
207 case 0x47: // press KP_Home
208 if (!(kbd_flags & KBD_MOD_NUMLOCK))
209 esc = "OH";
f424be98
DM
210 break;
211 case 0x4f: // press KP_END
7f3ff8c0
DM
212 if (!(kbd_flags & KBD_MOD_NUMLOCK))
213 esc = "OF";
f424be98
DM
214 break;
215 case 0x48: // press KP_UP
7f3ff8c0
DM
216 if (!(kbd_flags & KBD_MOD_NUMLOCK))
217 esc = "OA";
f424be98
DM
218 break;
219 case 0x50: // press KP_DOWN
7f3ff8c0
DM
220 if (!(kbd_flags & KBD_MOD_NUMLOCK))
221 esc = "OB";
f424be98
DM
222 break;
223 case 0x4b: // press KP_LEFT
7f3ff8c0
DM
224 if (!(kbd_flags & KBD_MOD_NUMLOCK))
225 esc = "OD";
f424be98
DM
226 break;
227 case 0x4d: // press KP_RIGHT
7f3ff8c0
DM
228 if (!(kbd_flags & KBD_MOD_NUMLOCK))
229 esc = "OC";
f424be98
DM
230 break;
231 case 0x3b: // press F1
232 esc = "OP";
233 break;
234 case 0x3c: // press F2
235 esc = "OQ";
236 break;
237 case 0x3d: // press F3
238 esc = "OR";
239 break;
240 case 0x3e: // press F4
241 esc = "OS";
242 break;
243 case 0x3f: // press F5
244 esc = "[15~";
245 break;
246 case 0x40: // press F6
247 esc = "[17~";
248 break;
249 case 0x41: // press F7
250 esc = "[18~";
251 break;
252 case 0x42: // press F8
253 esc = "[19~";
254 break;
255 case 0x43: // press F9
256 esc = "[20~";
257 break;
258 case 0x44: // press F10
259 esc = "[21~";
260 break;
261 case 0x57: // press F11
262 esc = "[23~";
263 break;
264 case 0x58: // press F12
265 esc = "[24~";
266 break;
267 }
268 }
269
270 if (esc) {
271 DPRINTF(1, "escape=%s", esc);
272 spiceterm_respond_esc(vt, esc);
273
274 if (vt->y_displ != vt->y_base) {
275 vt->y_displ = vt->y_base;
276 spiceterm_refresh(vt);
277 }
278
279 spiceterm_update_watch_mask(vt, TRUE);
7f3ff8c0
DM
280 } else if (frag < 128) {
281
282 guint mask = 0;
283 if (kbd_flags & (KBD_MOD_SHIFT_L_FLAG|KBD_MOD_SHIFT_R_FLAG)) {
284 mask |= MOD_MASK_SHIFT;
285 }
286 if (kbd_flags & KBD_MOD_SHIFTLOCK) {
287 if (mask & MOD_MASK_SHIFT) {
288 mask &= ~MOD_MASK_SHIFT;
289 } else {
290 mask |= MOD_MASK_SHIFT;
291 }
292 }
293 if (kbd_flags & KBD_MOD_ALTGR_FLAG) {
294 mask |= MOD_MASK_ALTGR;
295 }
296 if (kbd_flags & KBD_MOD_NUMLOCK) {
297 mask |= MOD_MASK_NUMLOCK;
298 }
f424be98 299
f424be98 300
7f3ff8c0
DM
301 gint hkey = mask << 8 | (frag & 127);
302 keymap_entry *e = (keymap_entry *)g_hash_table_lookup(keymap, &hkey);
303 if (!e && (kbd_flags & KBD_MOD_NUMLOCK)) {
304 mask &= ~ MOD_MASK_NUMLOCK;
305 hkey = mask << 8 | (frag & 127);
306 e = (keymap_entry *)g_hash_table_lookup(keymap, &hkey);
f424be98 307 }
f424be98 308
7f3ff8c0
DM
309 if (e && e->unicode) {
310 guint32 uc = e->unicode;
311 gchar buf[32];
312 guint8 len;
313 if (uc && ((len = g_unichar_to_utf8(uc, buf)) > 0)) {
93f3f187
DM
314 /* NOTE: window client send CONTROL_L/ALTGR instead of simple ALTGR */
315 if ((kbd_flags & (KBD_MOD_CONTROL_L_FLAG|KBD_MOD_CONTROL_R_FLAG)) &&
316 !(kbd_flags & KBD_MOD_ALTGR_FLAG)) {
7f3ff8c0
DM
317 if (buf[0] >= 'a' && buf[0] <= 'z') {
318 uint8_t ctrl[1] = { buf[0] - 'a' + 1 };
319 spiceterm_respond_data(vt, 1, ctrl);
320 spiceterm_update_watch_mask(vt, TRUE);
321 } else if (buf[0] >= 'A' && buf[0] <= 'Z') {
322 uint8_t ctrl[1] = { buf[0] - 'A' + 1 };
323 spiceterm_respond_data(vt, 1, ctrl);
324 spiceterm_update_watch_mask(vt, TRUE);
325 }
326 } else {
327 spiceterm_respond_data(vt, len, (uint8_t *)buf);
328 spiceterm_update_watch_mask(vt, TRUE);
329 }
330 }
331 }
f424be98 332
7f3ff8c0
DM
333 }
334 DPRINTF(1, "leave frag=%02x flags=%08x", frag, kbd_flags);
f424be98
DM
335 return;
336}
337
338static SpiceKbdInterface my_keyboard_sif = {
339 .base.type = SPICE_INTERFACE_KEYBOARD,
340 .base.description = "spiceterm keyboard device",
341 .base.major_version = SPICE_INTERFACE_KEYBOARD_MAJOR,
342 .base.minor_version = SPICE_INTERFACE_KEYBOARD_MINOR,
343 .push_scan_freg = my_kbd_push_key,
344 .get_leds = my_kbd_get_leds,
f424be98
DM
345};
346
347
348/* vdagent interface - to get mouse/clipboard support */
349
350#define VDAGENT_WBUF_SIZE (1024*50)
351static unsigned char vdagent_write_buffer[VDAGENT_WBUF_SIZE];
352static int vdagent_write_buffer_pos = 0;
353static int agent_owns_clipboard[256] = { 0, };
354
355static void
356vdagent_reply(spiceTerm *vt, uint32_t type, uint32_t error)
357{
358 uint32_t size;
359
360 size = sizeof(VDAgentReply);
361
362 int msg_size = sizeof(VDIChunkHeader) + sizeof(VDAgentMessage) + size;
363 g_assert((vdagent_write_buffer_pos + msg_size) < VDAGENT_WBUF_SIZE);
364
365 unsigned char *buf = vdagent_write_buffer + vdagent_write_buffer_pos;
366 vdagent_write_buffer_pos += msg_size;
367
368 memset(buf, 0, msg_size);
369
370 VDIChunkHeader *hdr = (VDIChunkHeader *)buf;
371 VDAgentMessage *msg = (VDAgentMessage *)&hdr[1];
372 VDAgentReply *reply = (VDAgentReply *)&msg[1];
373 reply->type = type;
374 reply->error = error;
375
376 hdr->port = VDP_CLIENT_PORT;
377 hdr->size = sizeof(VDAgentMessage) + size;
378
379 msg->protocol = VD_AGENT_PROTOCOL;
380 msg->type = VD_AGENT_REPLY;
381 msg->opaque = 0;
382 msg->size = size;
383
384 spice_server_char_device_wakeup(&vt->vdagent_sin);
385}
386
387static void
388dump_message(unsigned char *buf, int size)
389{
390 int i;
391
392 for (i = 0; i < size; i++) {
393 printf("%d %02X\n", i, buf[i]);
394 }
395
396 // exit(0);
397}
398
399static void
400vdagent_send_capabilities(spiceTerm *vt, uint32_t request)
401{
402 VDAgentAnnounceCapabilities *caps;
403 uint32_t size;
404
405 size = sizeof(*caps) + VD_AGENT_CAPS_BYTES;
406 caps = calloc(1, size);
407 g_assert(caps != NULL);
408
409 caps->request = request;
410 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MOUSE_STATE);
411 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MONITORS_CONFIG);
412 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_REPLY);
413 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_CLIPBOARD_BY_DEMAND);
414 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_CLIPBOARD_SELECTION);
415 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_SPARSE_MONITORS_CONFIG);
416 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_GUEST_LINEEND_LF);
417
418 int msg_size = sizeof(VDIChunkHeader) + sizeof(VDAgentMessage) + size;
419 g_assert((vdagent_write_buffer_pos + msg_size) < VDAGENT_WBUF_SIZE);
420
421 unsigned char *buf = vdagent_write_buffer + vdagent_write_buffer_pos;
422 vdagent_write_buffer_pos += msg_size;
423
424 VDIChunkHeader *hdr = (VDIChunkHeader *)buf;
425 VDAgentMessage *msg = (VDAgentMessage *)&hdr[1];
426
427 hdr->port = VDP_CLIENT_PORT;
428 hdr->size = sizeof(VDAgentMessage) + size;
429 msg->protocol = VD_AGENT_PROTOCOL;
430 msg->type = VD_AGENT_ANNOUNCE_CAPABILITIES;
431 msg->opaque = 0;
432 msg->size = size;
433
434 memcpy(buf + sizeof(VDIChunkHeader) + sizeof(VDAgentMessage), (uint8_t *)caps, size);
435
436 if (0) dump_message(buf, msg_size);
437
438 spice_server_char_device_wakeup(&vt->vdagent_sin);
439
440 free(caps);
441}
442
443gboolean
444vdagent_owns_clipboard(spiceTerm *vt)
445{
446 return !!agent_owns_clipboard[VD_AGENT_CLIPBOARD_SELECTION_PRIMARY];
447}
448
449void
450vdagent_grab_clipboard(spiceTerm *vt)
451{
452 uint32_t size;
453
454 uint8_t selection = VD_AGENT_CLIPBOARD_SELECTION_PRIMARY;
455
456 agent_owns_clipboard[selection] = 1;
457
458 size = 8;
459
460 int msg_size = sizeof(VDIChunkHeader) + sizeof(VDAgentMessage) + size;
461 g_assert((vdagent_write_buffer_pos + msg_size) < VDAGENT_WBUF_SIZE);
462
463 unsigned char *buf = vdagent_write_buffer + vdagent_write_buffer_pos;
464 vdagent_write_buffer_pos += msg_size;
465
466 memset(buf, 0, msg_size);
467
468 VDIChunkHeader *hdr = (VDIChunkHeader *)buf;
469 VDAgentMessage *msg = (VDAgentMessage *)&hdr[1];
470 uint8_t *grab = (uint8_t *)&msg[1];
471 *((uint8_t *)grab) = selection;
472 *((uint32_t *)(grab + 4)) = VD_AGENT_CLIPBOARD_UTF8_TEXT;
473
474 hdr->port = VDP_CLIENT_PORT;
475 hdr->size = sizeof(VDAgentMessage) + size;
476
477 msg->protocol = VD_AGENT_PROTOCOL;
478 msg->type = VD_AGENT_CLIPBOARD_GRAB;
479 msg->opaque = 0;
480 msg->size = size;
481
482 if (0) dump_message(buf, msg_size);
483
484 spice_server_char_device_wakeup(&vt->vdagent_sin);
485}
486
487void
488vdagent_request_clipboard(spiceTerm *vt)
489{
490 uint32_t size;
491
492 uint8_t selection = VD_AGENT_CLIPBOARD_SELECTION_PRIMARY;
493
494 size = 4 + sizeof(VDAgentClipboardRequest);
495
496 int msg_size = sizeof(VDIChunkHeader) + sizeof(VDAgentMessage) + size;
497 g_assert((vdagent_write_buffer_pos + msg_size) < VDAGENT_WBUF_SIZE);
498
499 unsigned char *buf = vdagent_write_buffer + vdagent_write_buffer_pos;
500 vdagent_write_buffer_pos += msg_size;
501
502 memset(buf, 0, msg_size);
503
504 VDIChunkHeader *hdr = (VDIChunkHeader *)buf;
505 VDAgentMessage *msg = (VDAgentMessage *)&hdr[1];
506 uint8_t *data = (uint8_t *)&msg[1];
507 *((uint32_t *)data) = 0;
508 data[0] = selection;
509 ((uint32_t *)data)[1] = VD_AGENT_CLIPBOARD_UTF8_TEXT;
510
511 hdr->port = VDP_CLIENT_PORT;
512 hdr->size = sizeof(VDAgentMessage) + size;
513
514 msg->protocol = VD_AGENT_PROTOCOL;
515 msg->type = VD_AGENT_CLIPBOARD_REQUEST;
516 msg->opaque = 0;
517 msg->size = size;
518
519 if (0) dump_message(buf, msg_size);
520
521 spice_server_char_device_wakeup(&vt->vdagent_sin);
522}
523
524static void
525vdagent_send_clipboard(spiceTerm *vt, uint8_t selection)
526{
527 uint32_t size;
528
529 if (selection != VD_AGENT_CLIPBOARD_SELECTION_PRIMARY) {
530 fprintf(stderr, "clipboard select %d is not supported\n", selection);
531 return;
532 }
533
534 gchar *sel_data;
535 glong sel_len;
536 if (vt->utf8) {
537 sel_data = g_utf16_to_utf8(vt->selection, vt->selection_len, NULL, &sel_len, NULL);
538 } else {
539 sel_len = vt->selection_len;
540 sel_data = g_malloc(sel_len);
541 int i;
542 for (i = 0; i < sel_len; i++) { sel_data[i] = (char)vt->selection[i]; }
543 sel_data[sel_len] = 0;
544 }
545
546 size = 8 + sel_len;
547
548 int msg_size = sizeof(VDIChunkHeader) + sizeof(VDAgentMessage) + size;
549 g_assert((vdagent_write_buffer_pos + msg_size) < VDAGENT_WBUF_SIZE);
550
551 unsigned char *buf = vdagent_write_buffer + vdagent_write_buffer_pos;
552 vdagent_write_buffer_pos += msg_size;
553
554 memset(buf, 0, msg_size);
555
556 VDIChunkHeader *hdr = (VDIChunkHeader *)buf;
557 VDAgentMessage *msg = (VDAgentMessage *)&hdr[1];
558 uint8_t *data = (uint8_t *)&msg[1];
559 *((uint8_t *)data) = selection;
560 data += 4;
561 *((uint32_t *)data) = VD_AGENT_CLIPBOARD_UTF8_TEXT;
562 data += 4;
563
564 memcpy(data, sel_data, sel_len);
565 g_free(sel_data);
566
567 hdr->port = VDP_CLIENT_PORT;
568 hdr->size = sizeof(VDAgentMessage) + size;
569
570 msg->protocol = VD_AGENT_PROTOCOL;
571 msg->type = VD_AGENT_CLIPBOARD;
572 msg->opaque = 0;
573 msg->size = size;
574
575 spice_server_char_device_wakeup(&vt->vdagent_sin);
576}
577
578static int
579vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
580{
581 spiceTerm *vt = SPICE_CONTAINEROF(sin, spiceTerm, vdagent_sin);
582
583 VDIChunkHeader *hdr = (VDIChunkHeader *)buf;
584 VDAgentMessage *msg = (VDAgentMessage *)&hdr[1];
585
586 //g_assert(hdr->port == VDP_SERVER_PORT);
587 g_assert(msg->protocol == VD_AGENT_PROTOCOL);
588
589 DPRINTF(1, "%d %d %d %d", len, hdr->port, msg->protocol, msg->type);
590
591 switch (msg->type) {
592 case VD_AGENT_MOUSE_STATE: {
593 VDAgentMouseState *info = (VDAgentMouseState *)&msg[1];
594 spiceterm_motion_event(vt, info->x, info->y, info->buttons);
595 break;
596 }
597 case VD_AGENT_ANNOUNCE_CAPABILITIES: {
598 VDAgentAnnounceCapabilities *caps = (VDAgentAnnounceCapabilities *)&msg[1];
599 DPRINTF(1, "VD_AGENT_ANNOUNCE_CAPABILITIES %d", caps->request);
600 int i;
601
602 int caps_size = VD_AGENT_CAPS_SIZE_FROM_MSG_SIZE(hdr->size);
603 for (i = 0; i < VD_AGENT_END_CAP; i++) {
604 DPRINTF(1, "CAPABILITIES %d %d", i, VD_AGENT_HAS_CAPABILITY(caps->caps, caps_size, i));
605 }
606
607 vdagent_send_capabilities(vt, 0);
608 break;
609 }
610 case VD_AGENT_CLIPBOARD_GRAB: {
611 VDAgentClipboardGrab *grab = (VDAgentClipboardGrab *)&msg[1];
612 uint8_t selection = *((uint8_t *)grab);
613 DPRINTF(1, "VD_AGENT_CLIPBOARD_GRAB %d", selection);
614 agent_owns_clipboard[selection] = 0;
615 spiceterm_clear_selection(vt);
616 break;
617 }
618 case VD_AGENT_CLIPBOARD_REQUEST: {
619 uint8_t *req = (uint8_t *)&msg[1];
620 uint8_t selection = *((uint8_t *)req);
621 uint32_t type = *((uint32_t *)(req + 4));
622
623 DPRINTF(1, "VD_AGENT_CLIPBOARD_REQUEST %d %d", selection, type);
624
625 vdagent_send_clipboard(vt, selection);
626
627 break;
628 }
629 case VD_AGENT_CLIPBOARD: {
630 uint8_t *data = (uint8_t *)&msg[1];
631 uint8_t selection = data[0];
632 uint32_t type = *(uint32_t *)(data + 4);
633 int size = msg->size - 8;
634 DPRINTF(1, "VD_AGENT_CLIPBOARD %d %d %d", selection, type, size);
635
636 if (type == VD_AGENT_CLIPBOARD_UTF8_TEXT) {
637 spiceterm_respond_data(vt, size, data + 8);
638 spiceterm_update_watch_mask(vt, TRUE);
639 }
640 break;
641 }
642 case VD_AGENT_CLIPBOARD_RELEASE: {
643 uint8_t *data = (uint8_t *)&msg[1];
644 uint8_t selection = data[0];
645
646 DPRINTF(1, "VD_AGENT_CLIPBOARD_RELEASE %d", selection);
647
648 break;
649 }
650 case VD_AGENT_MONITORS_CONFIG: {
651 VDAgentMonitorsConfig *list = (VDAgentMonitorsConfig *)&msg[1];
652 g_assert(list->num_of_monitors > 0);
653 DPRINTF(1, "VD_AGENT_MONITORS_CONFIG %d %d %d", list->num_of_monitors,
654 list->monitors[0].width, list->monitors[0].height);
655
656 spiceterm_resize(vt, list->monitors[0].width, list->monitors[0].height);
657
658 vdagent_reply(vt, VD_AGENT_MONITORS_CONFIG, VD_AGENT_SUCCESS);
659 break;
660 }
661 default:
662 DPRINTF(1, "got uknown vdagent message type %d\n", msg->type);
663 }
664
665 return len;
666}
667
668static int
669vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
670{
671 DPRINTF(1, "%d %d", len, vdagent_write_buffer_pos);
672 g_assert(len >= 8);
673
674 if (!vdagent_write_buffer_pos) {
675 return 0;
676 }
677
678 int size = (len >= vdagent_write_buffer_pos) ? vdagent_write_buffer_pos : len;
679 memcpy(buf, vdagent_write_buffer, size);
680 if (size < vdagent_write_buffer_pos) {
681 memmove(vdagent_write_buffer, vdagent_write_buffer + size,
682 vdagent_write_buffer_pos - size);
683 }
684 vdagent_write_buffer_pos -= size;
685
686 DPRINTF(1, "RET %d %d", size, vdagent_write_buffer_pos);
687 return size;
688}
689
690static void
691vmc_state(SpiceCharDeviceInstance *sin, int connected)
692{
693 /* IGNORE */
694}
695
696static SpiceCharDeviceInterface my_vdagent_sif = {
697 .base.type = SPICE_INTERFACE_CHAR_DEVICE,
698 .base.description = "spice virtual channel char device",
699 .base.major_version = SPICE_INTERFACE_CHAR_DEVICE_MAJOR,
700 .base.minor_version = SPICE_INTERFACE_CHAR_DEVICE_MINOR,
701 .state = vmc_state,
702 .write = vmc_write,
703 .read = vmc_read,
704};
705
7f3ff8c0
DM
706static void
707add_keymap_entry(guint8 mask, guint8 keycode, guint keysym, guint unicode)
708{
709 keymap_entry *e = g_new0(keymap_entry, 1);
710 e->mask = mask;
711 e->keysym = keysym;
712 e->unicode = unicode;
713 e->keycode = keycode;
714 e->hkey = mask << 8 | (keycode & 255);
715
995ba329 716 g_hash_table_replace(keymap, &e->hkey, e);
7f3ff8c0
DM
717}
718
c4d1da14
DM
719
720static gboolean
7f3ff8c0
DM
721parse_keymap(const char *language)
722{
723 char line[1024];
724 int len;
c4d1da14
DM
725 static GRegex *uregex = NULL;
726 name2keysym_t tmap = { .keysym = 0, .unicode = 0 };
727
728 if (uregex == NULL) {
729 if (!(uregex = g_regex_new("^U\\+?[a-fA-F0-9]{4,6}$", 0, 0, NULL))) {
730 fprintf(stderr, "unable to compile regex\n");
731 return FALSE;
732 }
733 }
7f3ff8c0
DM
734
735 char *filename = g_strdup_printf("/usr/share/kvm/keymaps/%s", language);
736 FILE *f = fopen(filename, "r");
737 g_free(filename);
738 if (!f) {
739 fprintf(stderr, "Could not read keymap file: '%s'\n", language);
c4d1da14 740 return FALSE;
7f3ff8c0
DM
741 }
742
743 for(;;) {
744 if (fgets(line, 1024, f) == NULL)
745 break;
746 len = strlen(line);
747 if (len > 0 && line[len - 1] == '\n')
748 line[len - 1] = '\0';
749 if (line[0] == '#' || line[0] == '\0')
750 continue;
751 if (!strncmp(line, "map ", 4))
752 continue;
753 if (!strncmp(line, "include ", 8)) {
c4d1da14
DM
754 if (!parse_keymap(line + 8))
755 return FALSE;
7f3ff8c0 756 } else {
7f3ff8c0 757 char *tok = strtok(line, " ");
c4d1da14
DM
758 if (!tok)
759 continue;
760
078fc735
DM
761 if (tok[0] == 'd' && tok[1] == 'e' && tok[2] == 'a' &&
762 tok[3] == 'd' && tok[4] == '_') {
763 continue;
764 }
765
7f3ff8c0 766 const name2keysym_t *map = lookup_keysym(tok);
c4d1da14
DM
767 if (!map && g_regex_match(uregex, tok, 0, NULL)) {
768 char *hex = tok[1] == '+' ? tok + 2 : tok + 1;
769 long int uc = strtol(hex, NULL, 16);
770 if ((uc >= 0x0020 && uc <= 0x007e) ||
771 (uc >= 0x00a0 && uc <= 0x00ff)) {
772 // Latin 1
773 tmap.keysym = uc;
774 tmap.unicode = uc;
775 map = &tmap;
776
777 } else if (uc >= 0x0100 && uc <= 0x010FFFF) {
778 tmap.keysym = uc + 0x01000000;
779 tmap.unicode = uc;
780 map = &tmap;
781 }
782 }
7f3ff8c0
DM
783 if (!map) {
784 fprintf(stderr, "Warning: unknown keysym '%s'\n", tok);
c4d1da14 785 continue;
7f3ff8c0
DM
786 }
787
788 guint8 mask = 0;
789 guint keycode = 0;
790 gboolean addupper = FALSE;
791
792 while ((tok = strtok(NULL, " "))) {
793 if (!strcmp(tok, "shift")) {
794 mask |= MOD_MASK_SHIFT;
795 } else if (!strcmp(tok, "numlock")) {
796 mask |= MOD_MASK_NUMLOCK;
797 } else if (!strcmp(tok, "altgr")) {
798 mask |= MOD_MASK_ALTGR;
799 } else if (!strcmp(tok, "addupper")) {
800 addupper = TRUE;
801 } else if (!strcmp(tok, "inhibit")) {
c4d1da14 802 // ignore
7f3ff8c0 803 } else if (!strcmp(tok, "localstate")) {
c4d1da14 804 // ignore
7f3ff8c0
DM
805 } else {
806 char *endptr;
807 errno = 0;
808 keycode = strtol(tok, &endptr, 0);
809 if (errno != 0 || *endptr != '\0' || keycode >= 255) {
c4d1da14
DM
810 fprintf(stderr, "got unknown modifier '%s' %d\n",
811 tok, keycode);
812 continue;
7f3ff8c0 813 }
7f3ff8c0
DM
814 }
815 }
816
7f3ff8c0
DM
817 add_keymap_entry(mask, keycode, map->keysym, map->unicode);
818 if (addupper) {
819 gchar uc = g_ascii_toupper(line[0]);
820 if (uc != line[0]) {
821 char ucname[] = { uc, '\0' };
822 if ((map = lookup_keysym(ucname))) {
823 add_keymap_entry(mask|MOD_MASK_SHIFT, keycode,
824 map->keysym, map->unicode);
825 }
826 }
827 }
828 }
829 }
c4d1da14
DM
830
831 return TRUE;
7f3ff8c0
DM
832}
833
f424be98 834spiceTerm *
68c2b067 835spiceterm_create(uint32_t width, uint32_t height, SpiceTermOptions *opts)
f424be98
DM
836{
837 SpiceCoreInterface *core = basic_event_loop_init();
68c2b067 838 SpiceScreen *spice_screen = spice_screen_new(core, width, height, opts);
f424be98 839
ffeedb04
DC
840 keymap = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, g_free);
841
c4d1da14
DM
842 if (!parse_keymap(opts->keymap ? opts->keymap : "en-us")) {
843 return NULL;
844 }
7f3ff8c0 845
f424be98
DM
846 spice_screen->image_cache = g_hash_table_new(g_int_hash, g_int_equal);
847
848 spiceTerm *vt = (spiceTerm *)calloc (sizeof(spiceTerm), 1);
849
850 vt->keyboard_sin.base.sif = &my_keyboard_sif.base;
851 spice_server_add_interface(spice_screen->server, &vt->keyboard_sin.base);
852
853 vt->vdagent_sin.base.sif = &my_vdagent_sif.base;
854 vt->vdagent_sin.subtype = "vdagent";
855 spice_server_add_interface(spice_screen->server, &vt->vdagent_sin.base);
856 vt->screen = spice_screen;
857
68c2b067 858 init_spiceterm(vt, width, height);
f424be98
DM
859
860 return vt;
861}