+#ifdef HAVE_LIBVNCSERVER
+
+static const struct keysym_map_ {
+ unsigned int rfb_start;
+ unsigned int rfb_end;
+ DCPU16_WORD dcpu_start;
+} keymap_[] = {
+ { XK_space, XK_asciitilde, 0x20 }, /* ASCII range */
+ { XK_Delete, XK_Delete, 0x7f }, /* ASCII del */
+ { XK_BackSpace, XK_BackSpace, 0x10 }, /* bs */
+ { XK_Return, XK_Return, 0x11 }, /* ret */
+ { XK_Insert, XK_Insert, 0x12 }, /* ins */
+ { XK_Delete, XK_Delete, 0x13 }, /* del */
+ { XK_Up, XK_Up, 0x80 }, /* arrow up */
+ { XK_Down, XK_Down, 0x81 }, /* arrow down */
+ { XK_Left, XK_Left, 0x82 }, /* arrow left */
+ { XK_Right, XK_Right, 0x83 }, /* arrow right */
+ { XK_Shift_L, XK_Shift_R, 0x90 }, /* shift range */
+ { XK_Control_L, XK_Control_R, 0x91 }, /* control range */
+ { 0, 0, 0x0 }
+};
+
+static int keysym_rfbtodcpu(unsigned int rfb, DCPU16_WORD *dcpu) {
+ const struct keysym_map_ *map;
+
+ for (map = keymap_; map->rfb_start; map++) {
+ if (rfb >= map->rfb_start
+ && rfb <= map->rfb_end) {
+ *dcpu = (map->dcpu_start + (rfb - map->rfb_start));
+ return 0;
+ }
+ }
+
+ *dcpu = 0x00;
+ return -1;
+}
+
+static
+void keyboard_rfbevent_(rfbBool down, rfbKeySym key, rfbClientPtr cl) {
+ DCPU16_WORD dcpu_key;
+ struct dcpu16_hw *hw = (struct dcpu16_hw *)cl->screen->screenData;
+ struct keyboard_ *keyboard = (struct keyboard_ *)hw->data;
+
+ hw->vm->trace_cb_("%s>> down:%u rfb_key:0x%04x", down, key);
+
+ if (keysym_rfbtodcpu(key, &dcpu_key)) {
+ /* unhandled key event */
+ return;
+ }
+
+ keyboard->keys_pressed[dcpu_key] = (down ? 1 : 0);
+ if (down) {
+ if ((keyboard->buf_tail + 1) % keyboard->buf_sz == keyboard->buf_head) {
+ hw->vm->warn_cb_("keyboard buffer overflow");
+ return;
+ }
+ keyboard->buf[keyboard->buf_tail] = dcpu_key;
+ keyboard->buf_tail += 1;
+ keyboard->buf_tail %= keyboard->buf_sz;
+ }
+ if (keyboard->interrupt_message) {
+ dcpu16_interrupt(hw->vm, keyboard->interrupt_message);
+ }
+}
+#endif /* HAVE_LIBVNCSERVER */
+