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