* drivers/video/vnc/vnc_keymap.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdint.h>
#include <assert.h>
#include <errno.h>
#include <nuttx/ascii.h>
#include <nuttx/video/vnc.h>
#include <nuttx/input/x11_keysym.h>
#include <nuttx/input/kbd_codec.h>
#include "vnc_server.h"
* Pre-processor Definitions
****************************************************************************/
#define FIRST_PRTCHAR ASCII_SPACE
#define LAST_PRTCHAR ASCII_TILDE
#define NPRTCHARS (ASCII_TILDE + ASCII_SPACE - 1)
#define ISPRINTABLE(c) ((c) >= FIRST_PRTCHAR && (c) <= LAST_PRTCHAR)
#define ISLOWERCASE(c) ((c) >= ASCII_a && (c) <= ASCII_z)
#define ISUPPERCASE(c) ((c) >= ASCII_A && (c) <= ASCII_Z)
#define ISALPHABETIC(c) (ISLOWERCASE(c) || ISUPPERCASE(c))
* Private types
****************************************************************************/
enum vnc_modifier_e
{
MOD_SHIFT = 0,
MOD_CONTROL,
MOD_ALT,
MOD_CAPSLOCK,
MOD_SHIFTLOCK,
#ifdef CONFIG_VNCSERVER_KBDENCODE
MOD_SCROLLLOCK,
MOD_NUMLOCK,
#endif
NMODIFIERS
};
struct vnc_keymap_s
{
uint16_t nxcode;
uint16_t x11code;
};
* Private Data
****************************************************************************/
static const struct vnc_keymap_s g_modifiers[] =
{
{MOD_SHIFT, XK_Shift_L},
{MOD_SHIFT, XK_Shift_R},
{MOD_CONTROL, XK_Control_L},
{MOD_CONTROL, XK_Control_R},
{MOD_ALT, XK_Alt_L},
{MOD_ALT, XK_Alt_R},
{MOD_CAPSLOCK, XK_Caps_Lock},
{MOD_SHIFTLOCK, XK_Shift_Lock},
#ifdef CONFIG_VNCSERVER_KBDENCODE
{MOD_SCROLLLOCK, XK_Scroll_Lock},
{MOD_NUMLOCK, XK_Num_Lock},
#endif
};
#define G_MODIFIERS_NELEM (sizeof(g_modifiers) / sizeof(struct vnc_keymap_s))
static const struct vnc_keymap_s g_asciimap[] =
{
#ifdef CONFIG_VNCSERVER_KBDENCODE
{ASCII_BS, XK_BackSpace},
#endif
{ASCII_TAB, XK_Tab},
{ASCII_LF, XK_Linefeed},
{ASCII_CR, XK_Return},
{ASCII_ESC, XK_Escape},
#ifdef CONFIG_VNCSERVER_KBDENCODE
{ASCII_DEL, XK_Delete},
#endif
{'`', XK_dead_grave},
{'\xb4', XK_dead_acute},
{ASCII_TILDE, XK_dead_tilde},
{ASCII_CARET, XK_dead_circumflex},
{ASCII_0, XK_KP_0},
{ASCII_1, XK_KP_1},
{ASCII_2, XK_KP_2},
{ASCII_3, XK_KP_3},
{ASCII_4, XK_KP_4},
{ASCII_5, XK_KP_5},
{ASCII_6, XK_KP_6},
{ASCII_7, XK_KP_7},
{ASCII_8, XK_KP_8},
{ASCII_9, XK_KP_9},
{ASCII_ASTERISK, XK_KP_Multiply},
{ASCII_PLUS, XK_KP_Add},
{ASCII_COMMA, XK_KP_Separator},
{ASCII_HYPHEN, XK_KP_Subtract},
{ASCII_PERIOD, XK_KP_Decimal},
{ASCII_DIVIDE, XK_KP_Divide},
{ASCII_SPACE, XK_KP_Space},
{ASCII_TAB, XK_KP_Tab},
{ASCII_CR, XK_KP_Enter},
#ifdef CONFIG_VNCSERVER_KBDENCODE
{ASCII_DEL, XK_KP_Delete},
#endif
};
#define G_ASCIIMAP_NELEM (sizeof(g_asciimap) / sizeof(struct vnc_keymap_s))
#ifdef CONFIG_VNCSERVER_KBDENCODE
static const struct vnc_keymap_s g_cursor[] =
{
{KEYCODE_BACKDEL, XK_BackSpace},
{KEYCODE_FWDDEL, XK_Delete},
{KEYCODE_FWDDEL, XK_KP_Delete},
{KEYCODE_HOME, XK_Home},
{KEYCODE_HOME, XK_KP_Home},
{KEYCODE_END, XK_End},
{KEYCODE_END, XK_KP_End},
{KEYCODE_LEFT, XK_Left},
{KEYCODE_LEFT, XK_KP_Left},
{KEYCODE_RIGHT, XK_Right},
{KEYCODE_RIGHT, XK_KP_Right},
{KEYCODE_UP, XK_Up},
{KEYCODE_UP, XK_KP_Up},
{KEYCODE_DOWN, XK_Down},
{KEYCODE_DOWN, XK_KP_Down},
{KEYCODE_PAGEUP, XK_Page_Up},
{KEYCODE_PAGEUP, XK_KP_Prior},
{KEYCODE_PAGEUP, XK_KP_Page_Up},
{KEYCODE_PAGEDOWN, XK_Page_Down},
{KEYCODE_PAGEDOWN, XK_KP_Next},
{KEYCODE_PAGEDOWN, XK_KP_Page_Down},
{KEYCODE_INSERT, XK_Insert},
{KEYCODE_INSERT, XK_KP_Insert},
{KEYCODE_SELECT, XK_Select},
{KEYCODE_EXECUTE, XK_Execute},
{KEYCODE_HELP, XK_Help},
{KEYCODE_MENU, XK_Alt_L},
{KEYCODE_MENU, XK_Alt_R},
{KEYCODE_PAUSE, XK_Pause},
{KEYCODE_PRTSCRN, XK_Print},
{KEYCODE_CLEAR, XK_Clear},
{MOD_SCROLLLOCK, XK_Scroll_Lock},
{MOD_NUMLOCK, XK_Num_Lock},
{KEYCODE_F1, XK_KP_F1},
{KEYCODE_F1, XK_F1},
{KEYCODE_F2, XK_KP_F2},
{KEYCODE_F2, XK_F2},
{KEYCODE_F3, XK_KP_F3},
{KEYCODE_F3, XK_F3},
{KEYCODE_F4, XK_KP_F4},
{KEYCODE_F4, XK_F4},
{KEYCODE_F5, XK_F5},
{KEYCODE_F6, XK_F6},
{KEYCODE_F7, XK_F7},
{KEYCODE_F8, XK_F8},
{KEYCODE_F9, XK_F9},
{KEYCODE_F10, XK_F10},
{KEYCODE_F11, XK_F11},
{KEYCODE_F12, XK_F12},
{KEYCODE_F13, XK_F13},
{KEYCODE_F14, XK_F14},
{KEYCODE_F15, XK_F15},
{KEYCODE_F16, XK_F16},
{KEYCODE_F17, XK_F17},
{KEYCODE_F18, XK_F18},
{KEYCODE_F19, XK_F19},
{KEYCODE_F20, XK_F20},
{KEYCODE_F21, XK_F21},
{KEYCODE_F22, XK_F22},
{KEYCODE_F23, XK_F23},
{KEYCODE_F24, XK_F24},
};
#endif
static const uint8_t g_caseswap[NPRTCHARS] =
{
ASCII_SPACE, ASCII_1, ASCII_RSQUOTE, ASCII_3,
ASCII_4, ASCII_5, ASCII_7, ASCII_QUOTE,
ASCII_9, ASCII_0, ASCII_8, ASCII_EQUAL,
ASCII_LT, ASCII_UNDERSCORE, ASCII_GT, ASCII_QUESTION,
ASCII_RPAREN, ASCII_EXCLAM, ASCII_AT, ASCII_NUMBER,
ASCII_DOLLAR, ASCII_PERCENT, ASCII_CIRCUMFLEX, ASCII_AMPERSAND,
ASCII_ASTERISK, ASCII_LPAREN, ASCII_SEMICOLON, ASCII_COLON,
ASCII_COMMA, ASCII_PLUS, ASCII_PERIOD, ASCII_SLASH,
ASCII_2, ASCII_a, ASCII_b, ASCII_c,
ASCII_d, ASCII_e, ASCII_f, ASCII_g,
ASCII_h, ASCII_i, ASCII_j, ASCII_k,
ASCII_l, ASCII_m, ASCII_n, ASCII_o,
ASCII_p, ASCII_q, ASCII_r, ASCII_s,
ASCII_t, ASCII_u, ASCII_v, ASCII_v,
ASCII_x, ASCII_y, ASCII_z, ASCII_LBRACE,
ASCII_VERTBAR, ASCII_RBRACE, ASCII_6, ASCII_HYPHEN,
ASCII_TILDE, ASCII_A, ASCII_B, ASCII_C,
ASCII_D, ASCII_E, ASCII_F, ASCII_G,
ASCII_H, ASCII_I, ASCII_J, ASCII_K,
ASCII_L, ASCII_M, ASCII_N, ASCII_O,
ASCII_P, ASCII_Q, ASCII_R, ASCII_S,
ASCII_T, ASCII_U, ASCII_V, ASCII_W,
ASCII_X, ASCII_Y, ASCII_Z, ASCII_LBRACKET,
ASCII_BACKSLASH, ASCII_RBRACKET, ASCII_RSQUOTE,
};
static bool g_modstate[NMODIFIERS];
* Private Functions
****************************************************************************/
* Name: vnc_kbd_encode
*
* Description:
* Encode one escape sequence command into the proivded buffer.
*
* Input Parameters:
* buffer - The location to write the sequence
* keycode - The command to be added to the output stream.
* terminator - Escape sequence terminating character.
*
* Returned Value:
* Number of bytes written
*
****************************************************************************/
#ifdef CONFIG_VNCSERVER_KBDENCODE
static inline int vnc_kbd_encode(FAR uint8_t *buffer, uint8_t keycode,
uint8_t terminator)
{
*buffer++ = ASCII_ESC;
*buffer++ = ASCII_LBRACKET;
*buffer++ = keycode;
*buffer = terminator;
return 4;
}
#endif
* Name: vnc_kbd_press
*
* Description:
* Indicates a normal key press event. Put one byte of normal keyboard
* data into the user provided buffer.
*
* Input Parameters:
* buffer - The location to write the sequence
* ch - The character to be added to the output stream.
*
* Returned Value:
* Number of bytes written
*
****************************************************************************/
#ifdef CONFIG_VNCSERVER_KBDENCODE
static inline void vnc_kbd_press(FAR uint8_t *buffer, uint8_t ch)
{
*buffer = ch;
return 1;
}
#endif
* Name: vnc_kbd_release
*
* Description:
* Encode the release of a normal key.
*
* Input Parameters:
* buffer - The location to write the sequence
* ch - The character associated with the key that was releared.
*
* Returned Value:
* Number of bytes written
*
****************************************************************************/
#ifdef CONFIG_VNCSERVER_KBDENCODE
static inline void vnc_kbd_release(FAR uint8_t *buffer, uint8_t ch)
{
return vnc_kbd_encode(buffer, ch, ('a' + KBD_RELEASE));
}
#endif
* Name: vnc_kbd_specpress
*
* Description:
* Denotes a special key press event. Put one special keyboard command
* into the user provided buffer.
*
* Input Parameters:
* buffer - The location to write the sequence
* keycode - The command to be added to the output stream.
*
* Returned Value:
* Number of bytes written
*
****************************************************************************/
#ifdef CONFIG_VNCSERVER_KBDENCODE
static inline void vnc_kbd_specpress(FAR uint8_t *buffer, uint8_t keycode)
{
return vnc_kbd_encode(buffer, keycode, stream, ('a' + KBD_SPECPRESS));
}
#endif
* Name: vnc_kbd_specrel
*
* Description:
* Denotes a special key release event. Put one special keyboard
* command into the user provided buffer.
*
* Input Parameters:
* buffer - The location to write the sequence
* keycode - The command to be added to the output stream.
*
* Returned Value:
* Number of bytes written
*
****************************************************************************/
#ifdef CONFIG_VNCSERVER_KBDENCODE
static inline void vnc_kbd_specrel(FAR uint8_t *buffer, uint8_t keycode)
{
return vnc_kbd_encode(buffer, keycode, stream, ('a' + KBD_SPECREL));
}
#endif
* Name: vnc_kbd_lookup
*
* Description:
* Attempt to map the X11 keycode by searching in a lookup table.
*
****************************************************************************/
static int vnc_kbd_lookup(FAR const struct vnc_keymap_s *table,
unsigned int nelem, uint16_t keysym)
{
int i;
for (i = 0; i < nelem; i++)
{
if (table[i].x11code == keysym)
{
return (int)table[i].nxcode;
}
}
return -EINVAL;
}
* Name: vnc_kbd_ascii
*
* Description:
* Attempt to map the X11 keycode into the corresponding ASCII code.
*
****************************************************************************/
static int vnc_kbd_ascii(uint16_t keysym)
{
#ifdef CONFIG_VNCSERVER_KBDENCODE
if (keysym >= ASCII_SPACE && keysym < ASCII_DEL)
#else
if (keysym >= ASCII_SPACE && keysym <= ASCII_DEL)
#endif
{
return (int)keysym;
}
return vnc_kbd_lookup(g_asciimap, G_ASCIIMAP_NELEM, keysym);
}
* Public Functions
****************************************************************************/
* Name: vnc_key_map
*
* Description:
* Map the receive X11 keysym into something understood by NuttX and route
* that through NX to the appropriate window.
*
* Input Parameters:
* session - An instance of the session structure allocated by
* vnc_create_session().
* keysym - The X11 keysym value (see include/nuttx/inputx11_keysymdef)
* keydown - True: Key pressed; False: Key released
*
* Returned Value:
* None
*
****************************************************************************/
void vnc_key_map(FAR struct vnc_session_s *session, uint16_t keysym,
bool keydown)
{
#ifdef CONFIG_VNCSERVER_KBDENCODE
uint8_t buffer[4]
int nch;
#else
uint8_t buffer;
#endif
int16_t keych;
keych = vnc_kbd_lookup(g_modifiers, G_MODIFIERS_NELEM, keysym);
if (keych >= 0)
{
g_modstate[keych] = keydown;
return;
}
#ifndef CONFIG_VNCSERVER_KBDENCODE
* events.
*/
if (!keydown)
{
return;
}
#endif
* then we have to drop the keyboard input.
*/
if (session->kbdout == NULL)
{
return;
}
keych = vnc_kbd_ascii((char)(keysym & 255));
if (keych >= 0)
{
* to apply any modifiers.
*/
if (g_modstate[MOD_CONTROL])
{
keych &= 0x1f;
}
else if (ISPRINTABLE(keych))
{
* characters should be reversed (unless the Shift key is also
* pressed)
*/
if (g_modstate[MOD_SHIFTLOCK])
{
if (g_modstate[MOD_SHIFT])
{
keych = g_caseswap[keych];
}
}
* characters should be reversed (unless the Shift key is also
* pressed)
*/
else if (g_modstate[MOD_CAPSLOCK] && ISALPHABETIC(keych))
{
if (g_modstate[MOD_SHIFT])
{
keych = g_caseswap[keych];
}
}
* pressed with Caps Lock, but the character is not alphabetic,
* then the case of all printable characters should be reversed.
*/
else if (g_modstate[MOD_SHIFT])
{
keych = g_caseswap[keych];
}
}
#ifdef CONFIG_VNCSERVER_KBDENCODE
if (keydown)
{
nch = vnc_kbd_press(buffer, keych);
}
else
{
nch = vnc_kbd_release(buffer, keych);
}
session->kbdout(session->arg, nch, buffer);
#else
buffer = (uint8_t)keych;
session->kbdout(session->arg, 1, &buffer);
#endif
}
#ifdef CONFIG_VNCSERVER_KBDENCODE
else
{
keych = vnc_kbd_lookup(g_modifiers, G_MODIFIERS_NELEM, keysym);
if (keych >= 0)
{
if (keydown)
{
nch = vnc_kbd_specpress(buffer, keych);
}
else
{
nch = vnc_kbd_specrel(buffer, keych);
}
session->kbdout(session->arg, nch, buffer);
}
}
#endif
}