diff --git a/common/xrdp_constants.h b/common/xrdp_constants.h index 9ab9d179..9cefbcbc 100644 --- a/common/xrdp_constants.h +++ b/common/xrdp_constants.h @@ -290,6 +290,9 @@ #define XR_RDP_SCAN_LSHIFT 42 #define XR_RDP_SCAN_ALT 56 +// scancodes affected by numlock +#define XR_RDP_SCAN_MIN_NUMLOCK 71 // KP7 +#define XR_RDP_SCAN_MAX_NUMLOCK 83 // KPDL // Since we're not guaranteed to have pixman, copy these directives. #define XRDP_PIXMAN_TYPE_ARGB 2 diff --git a/configure.ac b/configure.ac index 9633c500..bbab3756 100644 --- a/configure.ac +++ b/configure.ac @@ -537,6 +537,13 @@ AC_CHECK_HEADER([X11/extensions/Xrandr.h], [], [AC_MSG_ERROR([please install libxrandr-dev or libXrandr-devel])], [#include ]) +# checking for XKB +AC_CHECK_HEADER([X11/extensions/XKBrules.h], [], + [AC_MSG_ERROR([please install libxkbfile-dev or libxkbfile-devel])], + [#include +#include +#include ]) + if test "x$enable_utmp" = "xyes" then AC_CHECK_HEADERS(utmp.h utmpx.h) diff --git a/genkeymap/Makefile.am b/genkeymap/Makefile.am index 8c295170..abf30972 100644 --- a/genkeymap/Makefile.am +++ b/genkeymap/Makefile.am @@ -2,15 +2,19 @@ EXTRA_DIST = \ dump-keymaps.sh \ readme.txt +AM_CPPFLAGS = \ + -I$(top_srcdir)/common + AM_CFLAGS = $(X_CFLAGS) bin_PROGRAMS = \ xrdp-genkeymap -xrdp_genkeymap_SOURCES = genkeymap.c evdev-map.c +xrdp_genkeymap_SOURCES = genkeymap.c xrdp_genkeymap_LDFLAGS = \ $(X_LIBS) xrdp_genkeymap_LDADD = \ - $(X_PRE_LIBS) -lX11 $(X_EXTRA_LIBS) + $(top_builddir)/common/libcommon.la \ + $(X_PRE_LIBS) -lxkbfile -lX11 $(X_EXTRA_LIBS) diff --git a/genkeymap/evdev-map.c b/genkeymap/evdev-map.c deleted file mode 100644 index c86567f1..00000000 --- a/genkeymap/evdev-map.c +++ /dev/null @@ -1,279 +0,0 @@ -/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ -/* - * evdev-map.c - * Copyright (C) Michał Górny 2014 - * - * You may redistribute it and/or modify it under the terms of the - * GNU General Public License, as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * main.cc is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with main.cc. If not, write to: - * The Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor - * Boston, MA 02110-1301, USA - * - * xfree86(base)->evdev keycode mapping - */ - -#if defined(HAVE_CONFIG_H) -#include -#endif - -int xfree86_to_evdev[255 - 8 + 1] = -{ - /* MDSW */ 203, - /* ESC */ 9, - /* AE01 */ 10, - /* AE02 */ 11, - /* AE03 */ 12, - /* AE04 */ 13, - /* AE05 */ 14, - /* AE06 */ 15, - /* AE07 */ 16, - /* AE08 */ 17, - /* AE09 */ 18, - /* AE10 */ 19, - /* AE11 */ 20, - /* AE12 */ 21, - /* BKSP */ 22, - /* TAB */ 23, - /* AD01 */ 24, - /* AD02 */ 25, - /* AD03 */ 26, - /* AD04 */ 27, - /* AD05 */ 28, - /* AD06 */ 29, - /* AD07 */ 30, - /* AD08 */ 31, - /* AD09 */ 32, - /* AD10 */ 33, - /* AD11 */ 34, - /* AD12 */ 35, - /* RTRN */ 36, - /* LCTL */ 37, - /* AC01 */ 38, - /* AC02 */ 39, - /* AC03 */ 40, - /* AC04 */ 41, - /* AC05 */ 42, - /* AC06 */ 43, - /* AC07 */ 44, - /* AC08 */ 45, - /* AC09 */ 46, - /* AC10 */ 47, - /* AC11 */ 48, - /* TLDE */ 49, - /* LFSH */ 50, - /* BKSL */ 51, - /* AB01 */ 52, - /* AB02 */ 53, - /* AB03 */ 54, - /* AB04 */ 55, - /* AB05 */ 56, - /* AB06 */ 57, - /* AB07 */ 58, - /* AB08 */ 59, - /* AB09 */ 60, - /* AB10 */ 61, - /* RTSH */ 62, - /* KPMU */ 63, - /* LALT */ 64, - /* SPCE */ 65, - /* CAPS */ 66, - /* FK01 */ 67, - /* FK02 */ 68, - /* FK03 */ 69, - /* FK04 */ 70, - /* FK05 */ 71, - /* FK06 */ 72, - /* FK07 */ 73, - /* FK08 */ 74, - /* FK09 */ 75, - /* FK10 */ 76, - /* NMLK */ 77, - /* SCLK */ 78, - /* KP7 */ 79, - /* KP8 */ 80, - /* KP9 */ 81, - /* KPSU */ 82, - /* KP4 */ 83, - /* KP5 */ 84, - /* KP6 */ 85, - /* KPAD */ 86, - /* KP1 */ 87, - /* KP2 */ 88, - /* KP3 */ 89, - /* KP0 */ 90, - /* KPDL */ 91, - /* SYRQ */ 107, - /* II5D */ 0, - /* LSGT */ 94, - /* FK11 */ 95, - /* FK12 */ 96, - /* HOME */ 110, - /* UP */ 111, - /* PGUP */ 112, - /* LEFT */ 113, - /* II65 */ 0, - /* RGHT */ 114, - /* END */ 115, - /* DOWN */ 116, - /* PGDN */ 117, - /* INS */ 118, - /* DELE */ 119, - /* KPEN */ 104, - /* RCTL */ 105, - /* PAUS */ 127, - /* PRSC */ 107, - /* KPDV */ 106, - /* RALT */ 108, - /* BRK */ 419, - /* LWIN */ 133, - /* RWIN */ 134, - /* MENU */ 0, - /* FK13 */ 191, - /* FK14 */ 192, - /* FK15 */ 193, - /* XF86AudioMute */ 121, - /* XF86AudioLowerVolume */ 122, - /* XF86AudioRaiseVolume */ 123, - /* LVL3 */ 92, - /* ALT */ 204, - /* KPEQ */ 125, - /* SUPR */ 206, - /* HYPR */ 207, - /* XFER */ 0, - /* I02 */ 0, - /* NFER */ 0, - /* I04 */ 0, - /* AE13 */ 132, - /* I06 */ 0, - /* I07 */ 0, - /* Cancel */ 136, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - /* XF86Launch1 */ 156, - /* XF86Launch2 */ 157, - 0, - 0, - 0, - 0, - 0, - /* XF86Mail */ 163, - /* XF86Favorites */ 164, - 0, - /* XF86Back */ 166, - /* XF86Forward */ 167, - 0, - 0, - 0, - /* XF86AudioNext */ 171, - /* XF86AudioPlay */ 172, - /* XF86AudioPrev */ 173, - /* XF86AudioStop */ 174, - 0, - 0, - 0, - 0, - 0, - /* XF86HomePage */ 180, - /* XF86Reload */ 181, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - /* XF86Search */ 225, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - /* XF86AudioMedia */ 234, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, -}; diff --git a/genkeymap/genkeymap.c b/genkeymap/genkeymap.c index 51c05b11..53b447db 100644 --- a/genkeymap/genkeymap.c +++ b/genkeymap/genkeymap.c @@ -42,135 +42,407 @@ #include #include #include +#include #include +#include -extern int xfree86_to_evdev[137 - 8 + 1]; +#include "scancode.h" +#include "xrdp_constants.h" -int main(int argc, char **argv) +// cppcheck doesn't always set this macro to something in double-quotes +#if defined(__cppcheck__) +#undef PACKAGE_VERSION +#endif + +#if !defined(PACKAGE_VERSION) +#define PACKAGE_VERSION "???" +#endif + +#define NUM_STATES 9 + +#define KEYMAP_FILE_FORMAT_VERSION "2" + +// Scancodes affected by numlock +#define IS_KEYPAD_SCANCODE(s) \ + ((s) >= XR_RDP_SCAN_MIN_NUMLOCK && (s) <= XR_RDP_SCAN_MAX_NUMLOCK) + +#define MAX_COMMENTS 10 + +/** + * Contains info about the current keyboard setting + */ +struct kbd_info { - const char *programname; - char text[256]; - char *displayname = NULL; - char *outfname; - const char *sections[8] = + char *keycode_set; ///< See 'setxkbmap -v' + char *rules; ///< See 'setxkbmap -query' + char *model; ///< See 'setxkbmap -query' + char *layout; ///< See 'setxkbmap -query' + char *variant; ///< See 'setxkbmap -query' + char *options; ///< See 'setxkbmap -query' (comma separated) +}; + +/*****************************************************************************/ +/** + * Print brief info about program usage and exit + * @param programname Unqualified name of program + * @param status Exit status + */ +static void +usage(const char *programname, int status) +{ + fprintf(stderr, "Usage: %s [ -k keycode_set] [-c comment] [-c comment...]" + " out_filename\n", programname); + fprintf(stderr, "Example: %s -r evdev -c \"en-US pc104 keyboard\"" + " /etc/xrdp/km-00000409.toml\n", programname); + exit(status); +} + +/*****************************************************************************/ +/** + * Free a kbd_info struct + * @param kbd_info struct to free. May be incomplete or NULL + */ +static void +free_kbd_info(struct kbd_info *kbd_info) +{ + if (kbd_info != NULL) { - "noshift", "shift", "altgr", "shiftaltgr", - "capslock", "capslockaltgr", "shiftcapslock", "shiftcapslockaltgr" + free(kbd_info->keycode_set); + free(kbd_info->rules); + free(kbd_info->model); + free(kbd_info->layout); + free(kbd_info->variant); + free(kbd_info->options); + free(kbd_info); + } +} + +/*****************************************************************************/ +/** + * Queries the X server to get information about the current keyboard + * @param dpy X11 Display + * @param programname Unqualified name of program + * @return kbd_info struct, or NULL + * + * The structure may be incomplete if some data could not be obtained + */ +static struct kbd_info * +get_kbd_info(Display *dpy, const char *programname) +{ + struct kbd_info *kbd_info; + char *rules; + XkbRF_VarDefsRec vd; + XkbDescPtr kbdesc = NULL; + + if ((kbd_info = (struct kbd_info *)malloc( sizeof(*kbd_info))) == NULL) + { + fprintf(stderr, "%s: Out of memory\n", programname); + } + else if ((kbdesc = XkbAllocKeyboard()) == NULL) + { + fprintf(stderr, "%s: unable to allocate keyboard desc\n", + programname); + free_kbd_info(kbd_info); + kbd_info = NULL; + } + else if (XkbGetNames(dpy, XkbKeycodesNameMask, kbdesc) != Success) + { + fprintf(stderr, "%s: unable to obtain keycode name for keyboard\n", + programname); + free_kbd_info(kbd_info); + kbd_info = NULL; + } + else + { + char *symatom = XGetAtomName(dpy, kbdesc->names->keycodes); + kbd_info->keycode_set = strdup(symatom); + XFree(symatom); + + if (XkbRF_GetNamesProp(dpy, &rules, &vd) == 0 || rules == NULL) + { + fprintf(stderr, "%s: Couldn't interpret %s property\n", + programname, _XKB_RF_NAMES_PROP_ATOM); + kbd_info->rules = NULL; + kbd_info->model = NULL; + kbd_info->layout = NULL; + kbd_info->variant = NULL; + kbd_info->options = NULL; + } + else + { + kbd_info->rules = rules; + kbd_info->model = vd.model; + kbd_info->layout = vd.layout; + kbd_info->variant = vd.variant; + kbd_info->options = vd.options; + } + } + + if (kbdesc != NULL) + { + XkbFreeKeyboard(kbdesc, 0, True); + } + + return kbd_info; +} + +/*****************************************************************************/ +/** + * Outputs a comment containing the last setxkbmap command + * + * @param outf Output file + * @param kbd_info Keyboard info struct + * + */ +static void +output_setxkbmap_comment(FILE *outf, const struct kbd_info *kbd_info) +{ + if (kbd_info->model != NULL || kbd_info->layout != NULL || + kbd_info->variant != NULL || kbd_info->options != NULL) + { + fprintf(outf, "# setxkbmap -rules %s", kbd_info->rules); + if (kbd_info->model != NULL) + { + fprintf(outf, " -model %s", kbd_info->model); + } + if (kbd_info->layout != NULL) + { + fprintf(outf, " -layout %s", kbd_info->layout); + } + if (kbd_info->variant != NULL) + { + fprintf(outf, " -variant %s", kbd_info->variant); + } + if (kbd_info->options != NULL) + { + // Options is comma-separated, but to achieve the same effect + // with the command we need to use multiple '-option' args + char *optionstr = strdup(kbd_info->options); + if (optionstr != NULL) + { + char *p = strtok(optionstr, ","); + fprintf(outf, " -option \"\""); + while (p != NULL) + { + fprintf(outf, " -option %s", p); + p = strtok(NULL, ","); + } + free(optionstr); + } + } + putc('\n', outf); + } +} + +/*****************************************************************************/ +/** + * Output a section of the keymap file + * @param outf Output file + * @param dpy X display + * @param section name Section name (e.g. 'shift') + * @param event_state Modifier state needed for XKeyPressedEvent + */ +static void +output_file_section(FILE *outf, + Display *dpy, + const char *section_name, + unsigned int event_state) +{ + XKeyPressedEvent e = + { + .type = KeyPress, + .serial = 16, + .send_event = True, + .display = dpy, + .state = event_state, + .same_screen = True }; - int states[8] = {0, 1, 0x80, 0x81, 2, 0x82, 3, 0x83}; - int i; - int idx; int char_count; int nbytes = 0; int unicode; - Display *dpy; KeySym ks; - FILE *outf; - XKeyPressedEvent e; + const char *ksstr; + unsigned short scancode; + unsigned int iter = 0; + char text[256]; wchar_t wtext[256]; - XkbDescPtr kbdesc; - char *symatom; - int is_evdev; + + int is_numlock_section = (strcmp(section_name, "numlock") == 0); + + fprintf(outf, "\n[%s]\n", section_name); + + while ((scancode = scancode_get_next(&iter)) != 0) + { + // Numlock state table can be very small + if (is_numlock_section && !IS_KEYPAD_SCANCODE(scancode)) + { + continue; + } + + e.keycode = scancode_to_keycode(scancode); + nbytes = XLookupString(&e, text, 255, &ks, NULL); + if (ks == NoSymbol) + { + continue; + } + ksstr = XKeysymToString(ks); + if (ksstr == NULL) + { + continue; + } + text[nbytes] = 0; + char_count = mbstowcs(wtext, text, 255); + unicode = 0; + + if (char_count == 1) + { + unicode = wtext[0]; + } + + if (scancode > 0xff) + { + fputs("E0_", outf); + } + fprintf(outf, "%02X=\"%d", (scancode & 0xff), (int)ks); + if (unicode != 0) + { + fprintf(outf, ":U+%04X", unicode); + } + fprintf(outf, "\" # %s\n", ksstr); + } +} + + +/*****************************************************************************/ +/** + * Main + * @param argc Argument count + * @param argv Pointers to arguments + * @return 0 for success + */ +int main(int argc, char **argv) +{ + const char *programname; + int opt; + char *displayname = NULL; + char *outfname; + const char *sections[NUM_STATES] = + { + "noshift", "shift", "altgr", "shiftaltgr", + "capslock", "capslockaltgr", "shiftcapslock", "shiftcapslockaltgr", + "numlock" + }; + int states[NUM_STATES] = {0, 1, 0x80, 0x81, 2, 0x82, 3, 0x83, 0x10}; + int idx; + Display *dpy = NULL; + FILE *outf = NULL; + const char *comment[MAX_COMMENTS] = {0}; + int comment_count = 0; + const char *keycode_set = NULL; + struct kbd_info *kbd_info = NULL; + int status = 1; setlocale(LC_CTYPE, ""); - programname = argv[0]; - - if (argc != 2) + if (strrchr(argv[0], '/') != NULL) { - fprintf(stderr, "Usage: %s out_filename\n", programname); - fprintf(stderr, "Example: %s /etc/xrdp/km-00000409.ini\n", programname); - return 1; + programname = strrchr(argv[0], '/') + 1; + } + else + { + programname = argv[0]; } - outfname = argv[1]; - dpy = XOpenDisplay(displayname); + while ((opt = getopt(argc, argv, "c:k:")) != -1) + { + switch (opt) + { + case 'c': + if (comment_count < MAX_COMMENTS) + { + comment[comment_count++] = optarg; + } + break; + case 'k': + keycode_set = optarg; + break; + + default: /* '?' */ + usage(programname, 1); + } + } + + if ((optind + 1) != argc) + { + usage(programname, 1); + } + + outfname = argv[optind]; + + dpy = XOpenDisplay(displayname); if (!dpy) { fprintf(stderr, "%s: unable to open display '%s'\n", programname, XDisplayName(displayname)); - return 1; + goto finish; } - /* check whether evdev is used */ - kbdesc = XkbAllocKeyboard(); - if (!kbdesc) + if ((kbd_info = get_kbd_info(dpy, programname)) == 0) + { + // An error has already been logged + goto finish; + } + + // If the keycode set isn't specified, use the one returned + // by the XKB extension + if (keycode_set == NULL) + { + keycode_set = kbd_info->keycode_set; + } + + if (scancode_set_keycode_set(keycode_set) != 0) + { + fprintf(stderr, "%s: keycode set '%s' is not recognised\n", + programname, keycode_set); + goto finish; + } + + if ((outf = fopen(outfname, "w")) == NULL) + { + fprintf(stderr, "%s: unable to create file '%s'\n", + programname, outfname); + goto finish; + } + + fprintf(outf, "# Created by %s V" PACKAGE_VERSION + "\n# Key code set: %s\n", + programname, keycode_set); + + output_setxkbmap_comment(outf, kbd_info); + + for (idx = 0; idx < comment_count; ++idx) + { + fprintf(outf, "# %s\n", comment[idx]); + } + + fprintf(outf, "\n[General]\nversion=" KEYMAP_FILE_FORMAT_VERSION "\n"); + + for (idx = 0; idx < NUM_STATES; idx++) /* Sections and states */ + { + output_file_section(outf, dpy, sections[idx], states[idx]); + } + + status = 0; // Successful run + +finish: + free_kbd_info(kbd_info); + if (dpy != NULL) { - fprintf(stderr, "%s: unable to allocate keyboard desc\n", - programname); XCloseDisplay(dpy); - return 1; } - - if (XkbGetNames(dpy, XkbKeycodesNameMask, kbdesc) != Success) + if (outf != NULL) { - fprintf(stderr, "%s: unable to obtain keycode name for keyboard\n", - programname); - XkbFreeKeyboard(kbdesc, 0, True); - XCloseDisplay(dpy); - return 1; + fclose(outf); } - - symatom = XGetAtomName(dpy, kbdesc->names->keycodes); - is_evdev = !strncmp(symatom, "evdev", 5); - XFree(symatom); - XkbFreeKeyboard(kbdesc, 0, True); - - outf = fopen(outfname, "w"); - - if (outf == NULL) - { - fprintf(stderr, "%s: unable to create file '%s'\n", programname, outfname); - XCloseDisplay(dpy); - return 1; - } - - memset(&e, 0, sizeof(e)); - e.type = KeyPress; - e.serial = 16; - e.send_event = True; - e.display = dpy; - e.same_screen = True; - - for (idx = 0; idx < 8; idx++) /* Sections and states */ - { - fprintf(outf, "[%s]\n", sections[idx]); - e.state = states[idx]; - - for (i = 8; i < 256; i++) /* Keycodes */ - { - if (is_evdev) - { - e.keycode = xfree86_to_evdev[i - 8]; - } - else - { - e.keycode = i; - } - nbytes = XLookupString(&e, text, 255, &ks, NULL); - if (ks == NoSymbol) - { - continue; - } - text[nbytes] = 0; - char_count = mbstowcs(wtext, text, 255); - unicode = 0; - - if (char_count == 1) - { - unicode = wtext[0]; - } - - fprintf(outf, "Key%d=%d:%d\n", i, (int) ks, unicode); - } - - if (idx != 7) - { - fprintf(outf, "\n"); - } - } - - XCloseDisplay(dpy); - fclose(outf); - return 0; + return status; } diff --git a/genkeymap/readme.txt b/genkeymap/readme.txt index 242fe92e..a5bc1b85 100644 --- a/genkeymap/readme.txt +++ b/genkeymap/readme.txt @@ -1,39 +1,70 @@ -Creating a new keymap file. ---------------------------- +Keymap file description +----------------------- + +The keymap files are used by the xrdp login screen, and also when +sending keyboard input to a VNC server. The names of the files are of the format; -km-xxxxxxxx.ini +km-xxxxxxxx.toml where the xxxxxxxx is replaced by the hex number of the layout of interest. -The files have 8 sections; +The files are TOML compatible, with 10 sections; -[noshift], [shift], [altgr], [shiftaltgr], [capslock], [capslockaltgr], -[shiftcapslock], [shiftcapslockaltgr] +[General], [noshift], [shift], [altgr], [shiftaltgr], [capslock], +[capslockaltgr], [shiftcapslock], [shiftcapslockaltgr], [numlock] -In each section there are multiple lines for each key. +The [General] section contains information about the file. All other +sections contain key mappings corresponding to the state of the modifier +keys when the key is pressed. An example line looks like; -Key10=49:49 +="[:]" # comment -In this line, 10 is the X11 scancode, the first 49 is the keysym value, -the second 49 if the unicode value of the key. This is the definition -for the 'noshift' '1' key on a en-us keyboard. In this case, the keysym -and the unicode value are the same. +RDP scancode +------------ +The RDP scancode is the code received from the RDP client for each +key. RDP scancodes are more-or-less the same as Windows scancodes, +or "Scan Code Set 1" key-down values. -Here is an example where they are not; +Example scancodes might be '1C' for the enter key on most European +keyboards, or 'E0 1C' for the number pad enter key. -This is the definition for the backspace key; -Key22=65288:8 +A good website to consult for scancodes for a wide range of keyboards is +https://kbdlayout.info/ -And this is the star on the keypad; -Key63=65450:42 +KeySym +------ +The KeySym is a value used by the X server as an abstraction of the +engraving on the key being pressed. It is needed to interact with the +VNC server. -To create a new file run "xrdp-genkeymap " +Unicode +------- +Keys which generate a character when pressed have this value added. +This is used by the xrdp login screen. -Example: ./xrdp-genkeymap /etc/xrdp/km-00000409.ini +Comment +------- +For generated keymap files, the comment is the name of the X11 KeySym +for the key. This makes it a lot easier to see what is going on in +the file. + +Creating a new file +------------------- + +To create a new file:- +1) Start an X server +2) Use the 'setxkbmap' command to get the keyboard configured + for the X server. Currently this has to use the 'evdev' rules so + that xrdp and the X server agree on the low-level X11 keycodes to + be used for the keys. +3) Run the 'xrdp-genkeymap' command to extract the keyboard + mappings + + Example: ./xrdp-genkeymap /etc/xrdp/km-00000409.toml Note: You need to have enough rights to be able to write to the /etc/xrdp directory. @@ -41,3 +72,10 @@ Note: You need to have enough rights to be able to write to the Alternatively, create the keymap file in a directory of your choice, then copy or move it over to /etc/xrdp using sudo/su. +Using the X server of your current session may not be a good idea, as +session and window managers can interfere with key bindings. A good option +is to use an 'Xvfb' dummy X server to do this. + +See also the dump_keymaps.sh script which auto-generates many keymap +files for the xrdp distribution. Consider adding your keyboard into this +list.