3 Copyright (C) 2013 Proxmox Server Solutions GmbH
5 Copyright: spiceterm is under GNU GPL, the GNU General Public License.
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.
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.
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
21 Author: Dietmar Maurer <dietmar@proxmox.com>
29 #include <sys/types.h>
33 #include "spiceterm.h"
38 #include <spice/enums.h>
39 #include <spice/macros.h>
40 #include <spice/qxl_dev.h>
42 #include "event_loop.h"
46 #define DPRINTF(x, format, ...) { \
48 printf("%s: " format "\n" , __FUNCTION__, ## __VA_ARGS__); \
53 my_kbd_get_leds(SpiceKbdInstance
*sin
)
58 #define MOD_MASK_SHIFT (1<<0)
59 #define MOD_MASK_ALTGR (1<<1)
60 #define MOD_MASK_NUMLOCK (1<<2)
63 typedef struct keymap_entry
{
64 gint hkey
; //(mask << 8 || keycode)
65 guint8 mask
; // MOD_MASK_*
71 static GHashTable
*keymap
= NULL
;
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)
77 #define KBD_MOD_ALTGR_FLAG (1<<4)
78 #define KBD_MOD_NUMLOCK (1<<5)
79 #define KBD_MOD_SHIFTLOCK (1<<6)
80 #define KBD_MOD_ALT_FLAG (1<<7)
82 static int kbd_flags
= 0;
84 static const name2keysym_t
*
85 lookup_keysym(const char *name
)
87 const name2keysym_t
*p
;
88 for(p
= name2keysym
; p
->name
!= NULL
; p
++) {
89 if (!strcmp(p
->name
, name
))
96 my_kbd_push_key(SpiceKbdInstance
*sin
, uint8_t frag
)
98 spiceTerm
*vt
= SPICE_CONTAINEROF(sin
, spiceTerm
, keyboard_sin
);
100 char *esc
= NULL
; // used to send special keys
102 static int e0_mode
= 0;
104 DPRINTF(1, "enter frag=%02x flags=%08x", frag
, kbd_flags
);
109 case 0x1d: // press Control_R
110 kbd_flags
|= KBD_MOD_CONTROL_R_FLAG
;
112 case 0x9d: // release Control_R
113 kbd_flags
&= ~KBD_MOD_CONTROL_R_FLAG
;
115 case 0x38: // press ALTGR
116 kbd_flags
|= KBD_MOD_ALTGR_FLAG
;
118 case 0xb8: // release ALTGR
119 kbd_flags
&= ~KBD_MOD_ALTGR_FLAG
;
121 case 0x47: // press Home
124 case 0x4f: // press END
127 case 0x48: // press UP
130 case 0x50: // press DOWN
133 case 0x4b: // press LEFT
136 case 0x4d: // press RIGHT
139 case 0x52: // press INSERT
142 case 0x53: // press Delete
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);
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);
161 case 0x1d: // press Control_L
162 kbd_flags
|= KBD_MOD_CONTROL_L_FLAG
;
164 case 0x9d: // release Control_L
165 kbd_flags
&= ~KBD_MOD_CONTROL_L_FLAG
;
167 case 0x2a: // press Shift_L
168 kbd_flags
|= KBD_MOD_SHIFT_L_FLAG
;
170 case 0xaa: // release Shift_L
171 kbd_flags
&= ~KBD_MOD_SHIFT_L_FLAG
;
173 case 0x36: // press Shift_R
174 kbd_flags
|= KBD_MOD_SHIFT_R_FLAG
;
176 case 0xb6: // release Shift_R
177 kbd_flags
&= ~KBD_MOD_SHIFT_R_FLAG
;
179 case 0x38: // press ALT
180 kbd_flags
|= KBD_MOD_ALT_FLAG
;
182 case 0xb8: // release ALT
183 kbd_flags
&= ~KBD_MOD_ALT_FLAG
;
185 case 0x52: // press KP_INSERT
186 if (!(kbd_flags
& KBD_MOD_NUMLOCK
))
189 case 0x53: // press KP_Delete
190 if (!(kbd_flags
& KBD_MOD_NUMLOCK
))
193 case 0x45: // press Numlock
194 if (kbd_flags
& KBD_MOD_NUMLOCK
) {
195 kbd_flags
&= ~KBD_MOD_NUMLOCK
;
197 kbd_flags
|= KBD_MOD_NUMLOCK
;
200 case 0x3a: // press Shiftlock
201 if (kbd_flags
& KBD_MOD_SHIFTLOCK
) {
202 kbd_flags
&= ~KBD_MOD_SHIFTLOCK
;
204 kbd_flags
|= KBD_MOD_SHIFTLOCK
;
207 case 0x47: // press KP_Home
208 if (!(kbd_flags
& KBD_MOD_NUMLOCK
))
211 case 0x4f: // press KP_END
212 if (!(kbd_flags
& KBD_MOD_NUMLOCK
))
215 case 0x48: // press KP_UP
216 if (!(kbd_flags
& KBD_MOD_NUMLOCK
))
219 case 0x50: // press KP_DOWN
220 if (!(kbd_flags
& KBD_MOD_NUMLOCK
))
223 case 0x4b: // press KP_LEFT
224 if (!(kbd_flags
& KBD_MOD_NUMLOCK
))
227 case 0x4d: // press KP_RIGHT
228 if (!(kbd_flags
& KBD_MOD_NUMLOCK
))
231 case 0x3b: // press F1
234 case 0x3c: // press F2
237 case 0x3d: // press F3
240 case 0x3e: // press F4
243 case 0x3f: // press F5
246 case 0x40: // press F6
249 case 0x41: // press F7
252 case 0x42: // press F8
255 case 0x43: // press F9
258 case 0x44: // press F10
261 case 0x57: // press F11
264 case 0x58: // press F12
271 DPRINTF(1, "escape=%s", esc
);
272 spiceterm_respond_esc(vt
, esc
);
274 if (vt
->y_displ
!= vt
->y_base
) {
275 vt
->y_displ
= vt
->y_base
;
276 spiceterm_refresh(vt
);
279 spiceterm_update_watch_mask(vt
, TRUE
);
280 } else if (frag
< 128) {
283 if (kbd_flags
& (KBD_MOD_SHIFT_L_FLAG
|KBD_MOD_SHIFT_R_FLAG
)) {
284 mask
|= MOD_MASK_SHIFT
;
286 if (kbd_flags
& KBD_MOD_SHIFTLOCK
) {
287 if (mask
& MOD_MASK_SHIFT
) {
288 mask
&= ~MOD_MASK_SHIFT
;
290 mask
|= MOD_MASK_SHIFT
;
293 if (kbd_flags
& KBD_MOD_ALTGR_FLAG
) {
294 mask
|= MOD_MASK_ALTGR
;
296 if (kbd_flags
& KBD_MOD_NUMLOCK
) {
297 mask
|= MOD_MASK_NUMLOCK
;
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
);
309 if (e
&& e
->unicode
) {
310 guint32 uc
= e
->unicode
;
313 if (uc
&& ((len
= g_unichar_to_utf8(uc
, buf
)) > 0)) {
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
)) {
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
);
327 spiceterm_respond_data(vt
, len
, (uint8_t *)buf
);
328 spiceterm_update_watch_mask(vt
, TRUE
);
334 DPRINTF(1, "leave frag=%02x flags=%08x", frag
, kbd_flags
);
338 static 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
,
348 /* vdagent interface - to get mouse/clipboard support */
350 #define VDAGENT_WBUF_SIZE (1024*50)
351 static unsigned char vdagent_write_buffer
[VDAGENT_WBUF_SIZE
];
352 static int vdagent_write_buffer_pos
= 0;
353 static int agent_owns_clipboard
[256] = { 0, };
356 vdagent_reply(spiceTerm
*vt
, uint32_t type
, uint32_t error
)
360 size
= sizeof(VDAgentReply
);
362 int msg_size
= sizeof(VDIChunkHeader
) + sizeof(VDAgentMessage
) + size
;
363 g_assert((vdagent_write_buffer_pos
+ msg_size
) < VDAGENT_WBUF_SIZE
);
365 unsigned char *buf
= vdagent_write_buffer
+ vdagent_write_buffer_pos
;
366 vdagent_write_buffer_pos
+= msg_size
;
368 memset(buf
, 0, msg_size
);
370 VDIChunkHeader
*hdr
= (VDIChunkHeader
*)buf
;
371 VDAgentMessage
*msg
= (VDAgentMessage
*)&hdr
[1];
372 VDAgentReply
*reply
= (VDAgentReply
*)&msg
[1];
374 reply
->error
= error
;
376 hdr
->port
= VDP_CLIENT_PORT
;
377 hdr
->size
= sizeof(VDAgentMessage
) + size
;
379 msg
->protocol
= VD_AGENT_PROTOCOL
;
380 msg
->type
= VD_AGENT_REPLY
;
384 spice_server_char_device_wakeup(&vt
->vdagent_sin
);
388 dump_message(unsigned char *buf
, int size
)
392 for (i
= 0; i
< size
; i
++) {
393 printf("%d %02X\n", i
, buf
[i
]);
400 vdagent_send_capabilities(spiceTerm
*vt
, uint32_t request
)
402 VDAgentAnnounceCapabilities
*caps
;
405 size
= sizeof(*caps
) + VD_AGENT_CAPS_BYTES
;
406 caps
= calloc(1, size
);
407 g_assert(caps
!= NULL
);
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
);
418 int msg_size
= sizeof(VDIChunkHeader
) + sizeof(VDAgentMessage
) + size
;
419 g_assert((vdagent_write_buffer_pos
+ msg_size
) < VDAGENT_WBUF_SIZE
);
421 unsigned char *buf
= vdagent_write_buffer
+ vdagent_write_buffer_pos
;
422 vdagent_write_buffer_pos
+= msg_size
;
424 VDIChunkHeader
*hdr
= (VDIChunkHeader
*)buf
;
425 VDAgentMessage
*msg
= (VDAgentMessage
*)&hdr
[1];
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
;
434 memcpy(buf
+ sizeof(VDIChunkHeader
) + sizeof(VDAgentMessage
), (uint8_t *)caps
, size
);
436 if (0) dump_message(buf
, msg_size
);
438 spice_server_char_device_wakeup(&vt
->vdagent_sin
);
444 vdagent_owns_clipboard(spiceTerm
*vt
)
446 return !!agent_owns_clipboard
[VD_AGENT_CLIPBOARD_SELECTION_PRIMARY
];
450 vdagent_grab_clipboard(spiceTerm
*vt
)
454 uint8_t selection
= VD_AGENT_CLIPBOARD_SELECTION_PRIMARY
;
456 agent_owns_clipboard
[selection
] = 1;
460 int msg_size
= sizeof(VDIChunkHeader
) + sizeof(VDAgentMessage
) + size
;
461 g_assert((vdagent_write_buffer_pos
+ msg_size
) < VDAGENT_WBUF_SIZE
);
463 unsigned char *buf
= vdagent_write_buffer
+ vdagent_write_buffer_pos
;
464 vdagent_write_buffer_pos
+= msg_size
;
466 memset(buf
, 0, msg_size
);
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
;
474 hdr
->port
= VDP_CLIENT_PORT
;
475 hdr
->size
= sizeof(VDAgentMessage
) + size
;
477 msg
->protocol
= VD_AGENT_PROTOCOL
;
478 msg
->type
= VD_AGENT_CLIPBOARD_GRAB
;
482 if (0) dump_message(buf
, msg_size
);
484 spice_server_char_device_wakeup(&vt
->vdagent_sin
);
488 vdagent_request_clipboard(spiceTerm
*vt
)
492 uint8_t selection
= VD_AGENT_CLIPBOARD_SELECTION_PRIMARY
;
494 size
= 4 + sizeof(VDAgentClipboardRequest
);
496 int msg_size
= sizeof(VDIChunkHeader
) + sizeof(VDAgentMessage
) + size
;
497 g_assert((vdagent_write_buffer_pos
+ msg_size
) < VDAGENT_WBUF_SIZE
);
499 unsigned char *buf
= vdagent_write_buffer
+ vdagent_write_buffer_pos
;
500 vdagent_write_buffer_pos
+= msg_size
;
502 memset(buf
, 0, msg_size
);
504 VDIChunkHeader
*hdr
= (VDIChunkHeader
*)buf
;
505 VDAgentMessage
*msg
= (VDAgentMessage
*)&hdr
[1];
506 uint8_t *data
= (uint8_t *)&msg
[1];
507 *((uint32_t *)data
) = 0;
509 ((uint32_t *)data
)[1] = VD_AGENT_CLIPBOARD_UTF8_TEXT
;
511 hdr
->port
= VDP_CLIENT_PORT
;
512 hdr
->size
= sizeof(VDAgentMessage
) + size
;
514 msg
->protocol
= VD_AGENT_PROTOCOL
;
515 msg
->type
= VD_AGENT_CLIPBOARD_REQUEST
;
519 if (0) dump_message(buf
, msg_size
);
521 spice_server_char_device_wakeup(&vt
->vdagent_sin
);
525 vdagent_send_clipboard(spiceTerm
*vt
, uint8_t selection
)
529 if (selection
!= VD_AGENT_CLIPBOARD_SELECTION_PRIMARY
) {
530 fprintf(stderr
, "clipboard select %d is not supported\n", selection
);
537 sel_data
= g_utf16_to_utf8(vt
->selection
, vt
->selection_len
, NULL
, &sel_len
, NULL
);
539 sel_len
= vt
->selection_len
;
540 sel_data
= g_malloc(sel_len
);
542 for (i
= 0; i
< sel_len
; i
++) { sel_data
[i
] = (char)vt
->selection
[i
]; }
543 sel_data
[sel_len
] = 0;
548 int msg_size
= sizeof(VDIChunkHeader
) + sizeof(VDAgentMessage
) + size
;
549 g_assert((vdagent_write_buffer_pos
+ msg_size
) < VDAGENT_WBUF_SIZE
);
551 unsigned char *buf
= vdagent_write_buffer
+ vdagent_write_buffer_pos
;
552 vdagent_write_buffer_pos
+= msg_size
;
554 memset(buf
, 0, msg_size
);
556 VDIChunkHeader
*hdr
= (VDIChunkHeader
*)buf
;
557 VDAgentMessage
*msg
= (VDAgentMessage
*)&hdr
[1];
558 uint8_t *data
= (uint8_t *)&msg
[1];
559 *((uint8_t *)data
) = selection
;
561 *((uint32_t *)data
) = VD_AGENT_CLIPBOARD_UTF8_TEXT
;
564 memcpy(data
, sel_data
, sel_len
);
567 hdr
->port
= VDP_CLIENT_PORT
;
568 hdr
->size
= sizeof(VDAgentMessage
) + size
;
570 msg
->protocol
= VD_AGENT_PROTOCOL
;
571 msg
->type
= VD_AGENT_CLIPBOARD
;
575 spice_server_char_device_wakeup(&vt
->vdagent_sin
);
579 vmc_write(SpiceCharDeviceInstance
*sin
, const uint8_t *buf
, int len
)
581 spiceTerm
*vt
= SPICE_CONTAINEROF(sin
, spiceTerm
, vdagent_sin
);
583 VDIChunkHeader
*hdr
= (VDIChunkHeader
*)buf
;
584 VDAgentMessage
*msg
= (VDAgentMessage
*)&hdr
[1];
586 //g_assert(hdr->port == VDP_SERVER_PORT);
587 g_assert(msg
->protocol
== VD_AGENT_PROTOCOL
);
589 DPRINTF(1, "%d %d %d %d", len
, hdr
->port
, msg
->protocol
, 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
);
597 case VD_AGENT_ANNOUNCE_CAPABILITIES
: {
598 VDAgentAnnounceCapabilities
*caps
= (VDAgentAnnounceCapabilities
*)&msg
[1];
599 DPRINTF(1, "VD_AGENT_ANNOUNCE_CAPABILITIES %d", caps
->request
);
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
));
607 vdagent_send_capabilities(vt
, 0);
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
);
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));
623 DPRINTF(1, "VD_AGENT_CLIPBOARD_REQUEST %d %d", selection
, type
);
625 vdagent_send_clipboard(vt
, selection
);
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
);
636 if (type
== VD_AGENT_CLIPBOARD_UTF8_TEXT
) {
637 spiceterm_respond_data(vt
, size
, data
+ 8);
638 spiceterm_update_watch_mask(vt
, TRUE
);
642 case VD_AGENT_CLIPBOARD_RELEASE
: {
643 uint8_t *data
= (uint8_t *)&msg
[1];
644 uint8_t selection
= data
[0];
646 DPRINTF(1, "VD_AGENT_CLIPBOARD_RELEASE %d", selection
);
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
);
656 spiceterm_resize(vt
, list
->monitors
[0].width
, list
->monitors
[0].height
);
658 vdagent_reply(vt
, VD_AGENT_MONITORS_CONFIG
, VD_AGENT_SUCCESS
);
662 DPRINTF(1, "got uknown vdagent message type %d\n", msg
->type
);
669 vmc_read(SpiceCharDeviceInstance
*sin
, uint8_t *buf
, int len
)
671 DPRINTF(1, "%d %d", len
, vdagent_write_buffer_pos
);
674 if (!vdagent_write_buffer_pos
) {
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
);
684 vdagent_write_buffer_pos
-= size
;
686 DPRINTF(1, "RET %d %d", size
, vdagent_write_buffer_pos
);
691 vmc_state(SpiceCharDeviceInstance
*sin
, int connected
)
696 static 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
,
707 add_keymap_entry(guint8 mask
, guint8 keycode
, guint keysym
, guint unicode
)
709 keymap_entry
*e
= g_new0(keymap_entry
, 1);
712 e
->unicode
= unicode
;
713 e
->keycode
= keycode
;
714 e
->hkey
= mask
<< 8 | (keycode
& 255);
716 g_hash_table_replace(keymap
, &e
->hkey
, e
);
721 parse_keymap(const char *language
)
725 static GRegex
*uregex
= NULL
;
726 name2keysym_t tmap
= { .keysym
= 0, .unicode
= 0 };
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");
735 char *filename
= g_strdup_printf("/usr/share/kvm/keymaps/%s", language
);
736 FILE *f
= fopen(filename
, "r");
739 fprintf(stderr
, "Could not read keymap file: '%s'\n", language
);
744 if (fgets(line
, 1024, f
) == NULL
)
747 if (len
> 0 && line
[len
- 1] == '\n')
748 line
[len
- 1] = '\0';
749 if (line
[0] == '#' || line
[0] == '\0')
751 if (!strncmp(line
, "map ", 4))
753 if (!strncmp(line
, "include ", 8)) {
754 if (!parse_keymap(line
+ 8))
757 char *tok
= strtok(line
, " ");
761 if (tok
[0] == 'd' && tok
[1] == 'e' && tok
[2] == 'a' &&
762 tok
[3] == 'd' && tok
[4] == '_') {
766 const name2keysym_t
*map
= lookup_keysym(tok
);
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)) {
777 } else if (uc
>= 0x0100 && uc
<= 0x010FFFF) {
778 tmap
.keysym
= uc
+ 0x01000000;
784 fprintf(stderr
, "Warning: unknown keysym '%s'\n", tok
);
790 gboolean addupper
= FALSE
;
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")) {
801 } else if (!strcmp(tok
, "inhibit")) {
803 } else if (!strcmp(tok
, "localstate")) {
808 keycode
= strtol(tok
, &endptr
, 0);
809 if (errno
!= 0 || *endptr
!= '\0' || keycode
>= 255) {
810 fprintf(stderr
, "got unknown modifier '%s' %d\n",
817 add_keymap_entry(mask
, keycode
, map
->keysym
, map
->unicode
);
819 gchar uc
= g_ascii_toupper(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
);
835 spiceterm_create(uint32_t width
, uint32_t height
, SpiceTermOptions
*opts
)
837 SpiceCoreInterface
*core
= basic_event_loop_init();
838 SpiceScreen
*spice_screen
= spice_screen_new(core
, width
, height
, opts
);
840 keymap
= g_hash_table_new_full(g_int_hash
, g_int_equal
, NULL
, g_free
);
842 if (!parse_keymap(opts
->keymap
? opts
->keymap
: "en-us")) {
846 spice_screen
->image_cache
= g_hash_table_new(g_int_hash
, g_int_equal
);
848 spiceTerm
*vt
= (spiceTerm
*)calloc (sizeof(spiceTerm
), 1);
850 vt
->keyboard_sin
.base
.sif
= &my_keyboard_sif
.base
;
851 spice_server_add_interface(spice_screen
->server
, &vt
->keyboard_sin
.base
);
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
;
858 init_spiceterm(vt
, width
, height
);