diff options
| -rw-r--r-- | common/trans.h | 5 | ||||
| -rw-r--r-- | common/xrdp_client_info.h | 5 | ||||
| -rw-r--r-- | common/xrdp_tls.c | 233 | ||||
| -rw-r--r-- | libxrdp/xrdp_sec.c | 205 | ||||
| -rw-r--r-- | xorg/X11R7.6/rdp/rdp.h | 2 | ||||
| -rw-r--r-- | xorg/X11R7.6/rdp/rdpinput.c | 79 | ||||
| -rw-r--r-- | xorg/X11R7.6/rdp/rdpup.c | 2 | ||||
| -rw-r--r-- | xrdp/Makefile.am | 3 | ||||
| -rw-r--r-- | xrdp/xrdp_keyboard.ini | 105 | 
9 files changed, 481 insertions, 158 deletions
| diff --git a/common/trans.h b/common/trans.h index c28d420b..c5fe49e6 100644 --- a/common/trans.h +++ b/common/trans.h @@ -23,7 +23,6 @@  #include "arch.h"  #include "parse.h" -#include <openssl/ssl.h>  #define TRANS_MODE_TCP 1  #define TRANS_MODE_UNIX 2 @@ -72,8 +71,8 @@ struct trans  /* xrdp_tls */  struct xrdp_tls  { -    SSL *ssl; -    SSL_CTX *ctx; +    void *ssl; /* SSL * */ +    void *ctx; /* SSL_CTX * */      char *cert;      char *key;      struct trans *trans; diff --git a/common/xrdp_client_info.h b/common/xrdp_client_info.h index 59915f37..7e906c92 100644 --- a/common/xrdp_client_info.h +++ b/common/xrdp_client_info.h @@ -128,6 +128,11 @@ struct xrdp_client_info    char certificate[1024];    char key_file[1024]; +  /* X11 keyboard layout - inferred from keyboard type/subtype */ +  char model[16]; +  char layout[16]; +  char variant[16]; +  };  #endif diff --git a/common/xrdp_tls.c b/common/xrdp_tls.c index 48f6b827..589bb598 100644 --- a/common/xrdp_tls.c +++ b/common/xrdp_tls.c @@ -18,9 +18,17 @@   * transport layer security   */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <openssl/ssl.h> + +#include "os_calls.h"  #include "trans.h"  #include "ssl_calls.h" +  /*****************************************************************************/  struct xrdp_tls *  APP_CC @@ -38,6 +46,41 @@ xrdp_tls_create(struct trans *trans, const char *key, const char *cert)      return self;  } + +/*****************************************************************************/ +int APP_CC +xrdp_tls_print_error(char *func, SSL *connection, int value) +{ +    switch (SSL_get_error(connection, value)) +    { +        case SSL_ERROR_ZERO_RETURN: +            g_writeln("xrdp_tls_print_error: %s: Server closed TLS connection", +                      func); +            return 1; + +        case SSL_ERROR_WANT_READ: +            g_writeln("xrdp_tls_print_error: SSL_ERROR_WANT_READ"); +            return 0; + +        case SSL_ERROR_WANT_WRITE: +            g_writeln("xrdp_tls_print_error: SSL_ERROR_WANT_WRITE"); +            return 0; + +        case SSL_ERROR_SYSCALL: +            g_writeln("xrdp_tls_print_error: %s: I/O error", func); +            return 1; + +        case SSL_ERROR_SSL: +            g_writeln("xrdp_tls_print_error: %s: Failure in SSL library (protocol error?)", +                      func); +            return 1; + +        default: +            g_writeln("xrdp_tls_print_error: %s: Unknown error", func); +            return 1; +    } +} +  /*****************************************************************************/  int APP_CC  xrdp_tls_accept(struct xrdp_tls *self) @@ -53,6 +96,7 @@ xrdp_tls_accept(struct xrdp_tls *self)       */      options |= SSL_OP_NO_SSLv2; +#if defined(SSL_OP_NO_COMPRESSION)      /**       * SSL_OP_NO_COMPRESSION:       * @@ -63,6 +107,7 @@ xrdp_tls_accept(struct xrdp_tls *self)       * which is why we're disabling it.       */      options |= SSL_OP_NO_COMPRESSION; +#endif      /**       * SSL_OP_TLS_BLOCK_PADDING_BUG: @@ -137,40 +182,6 @@ xrdp_tls_accept(struct xrdp_tls *self)  }  /*****************************************************************************/  int APP_CC -xrdp_tls_print_error(char *func, SSL *connection, int value) -{ -    switch (SSL_get_error(connection, value)) -    { -    case SSL_ERROR_ZERO_RETURN: -        g_writeln("xrdp_tls_print_error: %s: Server closed TLS connection", -                  func); -        return 1; - -    case SSL_ERROR_WANT_READ: -        g_writeln("xrdp_tls_print_error: SSL_ERROR_WANT_READ"); -        return 0; - -    case SSL_ERROR_WANT_WRITE: -        g_writeln("xrdp_tls_print_error: SSL_ERROR_WANT_WRITE"); -        return 0; - -    case SSL_ERROR_SYSCALL: -        g_writeln("xrdp_tls_print_error: %s: I/O error", func); -        return 1; - -    case SSL_ERROR_SSL: -        g_writeln( -                "xrdp_tls_print_error: %s: Failure in SSL library (protocol error?)", -                func); -        return 1; - -    default: -        g_writeln("xrdp_tls_print_error: %s: Unknown error", func); -        return 1; -    } -} -/*****************************************************************************/ -int APP_CC  xrdp_tls_disconnect(struct xrdp_tls *self)  {      int status = SSL_shutdown(self->ssl); @@ -205,7 +216,7 @@ xrdp_tls_delete(struct xrdp_tls *self)  }  /*****************************************************************************/  int APP_CC -xrdp_tls_read(struct xrdp_tls *tls, unsigned char *data, int length) +xrdp_tls_read(struct xrdp_tls *tls, char *data, int length)  {      int status; @@ -213,25 +224,25 @@ xrdp_tls_read(struct xrdp_tls *tls, unsigned char *data, int length)      switch (SSL_get_error(tls->ssl, status))      { -    case SSL_ERROR_NONE: -        break; - -    case SSL_ERROR_WANT_READ: -    case SSL_ERROR_WANT_WRITE: -        status = 0; -        break; - -    default: -        xrdp_tls_print_error("SSL_read", tls->ssl, status); -        status = -1; -        break; +        case SSL_ERROR_NONE: +            break; + +        case SSL_ERROR_WANT_READ: +        case SSL_ERROR_WANT_WRITE: +            status = 0; +            break; + +        default: +            xrdp_tls_print_error("SSL_read", tls->ssl, status); +            status = -1; +            break;      }      return status;  }  /*****************************************************************************/  int APP_CC -xrdp_tls_write(struct xrdp_tls *tls, unsigned char *data, int length) +xrdp_tls_write(struct xrdp_tls *tls, char *data, int length)  {      int status; @@ -239,18 +250,18 @@ xrdp_tls_write(struct xrdp_tls *tls, unsigned char *data, int length)      switch (SSL_get_error(tls->ssl, status))      { -    case SSL_ERROR_NONE: -        break; - -    case SSL_ERROR_WANT_READ: -    case SSL_ERROR_WANT_WRITE: -        status = 0; -        break; - -    default: -        xrdp_tls_print_error("SSL_write", tls->ssl, status); -        status = -1; -        break; +        case SSL_ERROR_NONE: +            break; + +        case SSL_ERROR_WANT_READ: +        case SSL_ERROR_WANT_WRITE: +            status = 0; +            break; + +        default: +            xrdp_tls_print_error("SSL_write", tls->ssl, status); +            status = -1; +            break;      }      return status; @@ -319,6 +330,58 @@ xrdp_tls_force_read_s(struct trans *self, struct stream *in_s, int size)  /*****************************************************************************/  int APP_CC +xrdp_tls_send_waiting(struct trans *self, int block) +{ +    struct stream *temp_s; +    int bytes; +    int sent; +    int timeout; +    int cont; + +    timeout = block ? 100 : 0; +    cont = 1; +    while (cont) +    { +        if (self->wait_s != 0) +        { +            temp_s = self->wait_s; +            if (g_tcp_can_send(self->sck, timeout)) +            { +                bytes = (int) (temp_s->end - temp_s->p); +                sent = xrdp_tls_write(self->tls, temp_s->p, bytes); +                if (sent > 0) +                { +                    temp_s->p += sent; +                    if (temp_s->p >= temp_s->end) +                    { +                        self->wait_s = (struct stream *) (temp_s->next_packet); +                        free_stream(temp_s); +                    } +                } +                else if (sent == 0) +                { +                    return 1; +                } +                else +                { +                    if (!g_tcp_last_error_would_block(self->sck)) +                    { +                        return 1; +                    } +                } +            } +        } +        else +        { +            break; +        } +        cont = block; +    } +    return 0; +} + +/*****************************************************************************/ +int APP_CC  xrdp_tls_force_write_s(struct trans *self, struct stream *out_s)  {      int size; @@ -382,54 +445,4 @@ xrdp_tls_force_write_s(struct trans *self, struct stream *out_s)      return 0;  } -/*****************************************************************************/ -int APP_CC -xrdp_tls_send_waiting(struct trans *self, int block) -{ -    struct stream *temp_s; -    int bytes; -    int sent; -    int timeout; -    int cont; -    timeout = block ? 100 : 0; -    cont = 1; -    while (cont) -    { -        if (self->wait_s != 0) -        { -            temp_s = self->wait_s; -            if (g_tcp_can_send(self->sck, timeout)) -            { -                bytes = (int) (temp_s->end - temp_s->p); -                sent = xrdp_tls_write(self->tls, temp_s->p, bytes); -                if (sent > 0) -                { -                    temp_s->p += sent; -                    if (temp_s->p >= temp_s->end) -                    { -                        self->wait_s = (struct stream *) (temp_s->next_packet); -                        free_stream(temp_s); -                    } -                } -                else if (sent == 0) -                { -                    return 1; -                } -                else -                { -                    if (!g_tcp_last_error_would_block(self->sck)) -                    { -                        return 1; -                    } -                } -            } -        } -        else -        { -            break; -        } -        cont = block; -    } -    return 0; -} diff --git a/libxrdp/xrdp_sec.c b/libxrdp/xrdp_sec.c index 7ac68b14..5a63afc8 100644 --- a/libxrdp/xrdp_sec.c +++ b/libxrdp/xrdp_sec.c @@ -221,6 +221,210 @@ hex_str_to_bin(char *in, char *out, int out_len)  }  /*****************************************************************************/ +static void APP_CC +xrdp_load_keyboard_layout(struct xrdp_client_info *client_info) +{ +    int fd; +    int index = 0; +    int bytes; +    struct list *names = (struct list *)NULL; +    struct list *items = (struct list *)NULL; +    struct list *values = (struct list *)NULL; +    char *item = (char *)NULL; +    char *value = (char *)NULL; +    char *q = (char *)NULL; +    char keyboard_cfg_file[256] = { 0 }; +    char rdp_layout[256] = { 0 }; + +    LLOGLN(0, ("xrdp_load_keyboard_layout:")); +    /* infer model/variant */ +    /* TODO specify different X11 keyboard models/variants */ +    g_memset(client_info->model, 0, sizeof(client_info->model)); +    g_memset(client_info->variant, 0, sizeof(client_info->variant)); +    g_strncpy(client_info->layout, "us", sizeof(client_info->layout) - 1); +    if (client_info->keyboard_subtype == 3) +    { +        /* macintosh keyboard */ +        bytes = sizeof(client_info->variant); +        g_strncpy(client_info->variant, "mac", bytes - 1); +    } +    else if (client_info->keyboard_subtype == 0) +    { +        /* default - standard subtype */ +        client_info->keyboard_subtype = 1; +    } + +    g_snprintf(keyboard_cfg_file, 255, "%s/xrdp_keyboard.ini", XRDP_CFG_PATH); +    LLOGLN(10, ("keyboard_cfg_file %s", keyboard_cfg_file)); + +    fd = g_file_open(keyboard_cfg_file); + +    if (fd > 0) +    { +        int section_found = -1; +        char section_rdp_layouts[256] = { 0 }; +        char section_layouts_map[256] = { 0 }; + +        names = list_create(); +        names->auto_free = 1; +        items = list_create(); +        items->auto_free = 1; +        values = list_create(); +        values->auto_free = 1; + +        file_read_sections(fd, names); +        for (index = 0; index < names->count; index++) +        { +            q = (char *)list_get_item(names, index); +            if (g_strncasecmp("default", q, 8) != 0) +            { +                int i; + +                file_read_section(fd, q, items, values); + +                for (i = 0; i < items->count; i++) +                { +                    item = (char *)list_get_item(items, i); +                    value = (char *)list_get_item(values, i); +                    LLOGLN(10, ("xrdp_load_keyboard_layout: item %s value %s", +                           item, value)); +                    if (g_strcasecmp(item, "keyboard_type") == 0) +                    { +                        int v = g_atoi(value); +                        if (v == client_info->keyboard_type) +                        { +                            section_found = index; +                        } +                    } +                    else if (g_strcasecmp(item, "keyboard_subtype") == 0) +                    { +                        int v = g_atoi(value); +                        if (v != client_info->keyboard_subtype && +                            section_found == index) +                        { +                            section_found = -1; +                            break; +                        } +                    } +                    else if (g_strcasecmp(item, "rdp_layouts") == 0) +                    { +                        if (section_found != -1 && section_found == index) +                        { +                            g_strncpy(section_rdp_layouts, value, 255); +                        } +                    } +                    else if (g_strcasecmp(item, "layouts_map") == 0) +                    { +                        if (section_found != -1 && section_found == index) +                        { +                            g_strncpy(section_layouts_map, value, 255); +                        } +                    } +                    else if (g_strcasecmp(item, "model") == 0) +                    { +                        if (section_found != -1 && section_found == index) +                        { +                            bytes = sizeof(client_info->model); +                            g_memset(client_info->model, 0, bytes); +                            g_strncpy(client_info->model, value, bytes - 1); +                        } +                    } +                    else if (g_strcasecmp(item, "variant") == 0) +                    { +                        if (section_found != -1 && section_found == index) +                        { +                            bytes = sizeof(client_info->variant); +                            g_memset(client_info->variant, 0, bytes); +                            g_strncpy(client_info->variant, value, bytes - 1); +                        } +                    } +                    else +                    { +                        /* +                         * mixing items from different sections will result in +                         * skipping over current section. +                         */ +                        LLOGLN(10, ("xrdp_load_keyboard_layout: skipping " +                               "configuration item - %s, continuing to next " +                               "section", item)); +                        break; +                    } +                } + +                list_clear(items); +                list_clear(values); +            } +        } + +        if (section_found == -1) +        { +            g_memset(section_rdp_layouts, 0, sizeof(char) * 256); +            g_memset(section_layouts_map, 0, sizeof(char) * 256); +            // read default section +            file_read_section(fd, "default", items, values); +            for (index = 0; index < items->count; index++) +            { +                item = (char *)list_get_item(items, index); +                value = (char *)list_get_item(values, index); +                if (g_strcasecmp(item, "rdp_layouts") == 0) +                { +                    g_strncpy(section_rdp_layouts, value, 255); +                } +                else if (g_strcasecmp(item, "layouts_map") == 0) +                { +                    g_strncpy(section_layouts_map, value, 255); +                } +            } +            list_clear(items); +            list_clear(values); +        } + +        /* load the map */ +        file_read_section(fd, section_rdp_layouts, items, values); +        for (index = 0; index < items->count; index++) +        { +            int rdp_layout_id; +            item = (char *)list_get_item(items, index); +            value = (char *)list_get_item(values, index); +            rdp_layout_id = g_htoi(value); +            if (rdp_layout_id == client_info->keylayout) +            { +                g_strncpy(rdp_layout, item, 255); +                break; +            } +        } +        list_clear(items); +        list_clear(values); +        file_read_section(fd, section_layouts_map, items, values); +        for (index = 0; index < items->count; index++) +        { +            item = (char *)list_get_item(items, index); +            value = (char *)list_get_item(values, index); +            if (g_strcasecmp(item, rdp_layout) == 0) +            { +                bytes = sizeof(client_info->layout); +                g_strncpy(client_info->layout, value, bytes - 1); +                break; +            } +        } + +        list_delete(names); +        list_delete(items); +        list_delete(values); + +        LLOGLN(0, ("xrdp_load_keyboard_layout: model [%s] variant [%s] " +               "layout [%s]", client_info->model, client_info->variant, +               client_info->layout)); +        g_file_close(fd); +    } +    else +    { +        LLOGLN(0, ("xrdp_load_keyboard_layout: error opening %d", +               keyboard_cfg_file)); +    } +} + +/*****************************************************************************/  struct xrdp_sec *APP_CC  xrdp_sec_create(struct xrdp_rdp *owner, struct trans *trans)  { @@ -1853,6 +2057,7 @@ xrdp_sec_in_mcs_data(struct xrdp_sec *self)      in_uint8s(s, 79);      in_uint32_le(s, client_info->keyboard_type);      in_uint32_le(s, client_info->keyboard_subtype); +    xrdp_load_keyboard_layout(client_info);      s->p = s->data;      return 0;  } diff --git a/xorg/X11R7.6/rdp/rdp.h b/xorg/X11R7.6/rdp/rdp.h index 7bc2eb6c..00725d11 100644 --- a/xorg/X11R7.6/rdp/rdp.h +++ b/xorg/X11R7.6/rdp/rdp.h @@ -508,7 +508,7 @@ KbdAddEvent(int down, int param1, int param2, int param3, int param4);  void  KbdSync(int param1);  int -rdpLoadLayout(int keylayout); +rdpLoadLayout(struct xrdp_client_info *client_info);  /* rdpup.c */  int diff --git a/xorg/X11R7.6/rdp/rdpinput.c b/xorg/X11R7.6/rdp/rdpinput.c index 7fb20379..ef0bb929 100644 --- a/xorg/X11R7.6/rdp/rdpinput.c +++ b/xorg/X11R7.6/rdp/rdpinput.c @@ -282,7 +282,7 @@ rdpChangeKeyboardControl(DeviceIntPtr pDev, KeybdCtrl *ctrl)  /******************************************************************************/  int -rdpLoadLayout(int keylayout) +rdpLoadLayout(struct xrdp_client_info *client_info)  {      XkbRMLVOSet set;      XkbSrvInfoPtr xkbi; @@ -292,8 +292,10 @@ rdpLoadLayout(int keylayout)      KeyCode first_key;      CARD8 num_keys; -    LLOGLN(0, ("rdpLoadLayout: keylayout 0x%8.8x display %s", -           keylayout, display)); +    int keylayout = client_info->keylayout; + +    LLOGLN(0, ("rdpLoadLayout: keylayout 0x%8.8x variant %s display %s", +               keylayout, client_info->variant, display));      memset(&set, 0, sizeof(set));      if (g_use_evdev)      { @@ -303,42 +305,26 @@ rdpLoadLayout(int keylayout)      {          set.rules = "base";      } +      set.model = "pc104";      set.layout = "us"; -    switch (keylayout) -    { -        case 0x00000407: /* German */ -            set.layout = "de"; -            break; -        case 0x00000409: /* US */ -            set.layout = "us"; -            break; -        case 0x0000040C: /* French */ -            set.layout = "fr"; -            break; -        case 0x00000410: /* Italian */ -            set.layout = "it"; -            break; -        case 0x00000416: /* Portuguese (Brazilian ABNT) */ -            set.model = "abnt2"; -            set.layout = "br"; -            break; -        case 0x00000419: /* Russian */ -            set.layout = "ru"; -            break; -        case 0x0000041D: /* Swedish */ -            set.layout = "se"; -            break; -        case 0x00000816: /* Portuguese */ -            set.layout = "pt"; -            break; -        default: -            LLOGLN(0, ("rdpLoadLayout: unknown keylayout 0x%8.8x", keylayout)); -            break; -    }      set.variant = "";      set.options = ""; +    if (strlen(client_info->model) > 0) +    { +        set.model = client_info->model; +    } +    if (strlen(client_info->variant) > 0) +    { +        set.variant = client_info->variant; +    } +    if (strlen(client_info->layout) > 0) +    { +        set.layout = client_info->layout; +    } + + retry:      /* free some stuff so we can call InitKeyboardDeviceStruct again */      xkbi = g_keyboard->key->xkbInfo;      xkb = xkbi->desc; @@ -355,21 +341,30 @@ rdpLoadLayout(int keylayout)                                    rdpChangeKeyboardControl))      {          LLOGLN(0, ("rdpLoadLayout: InitKeyboardDeviceStruct failed")); +        return 1;      }      /* notify the X11 clients eg. X_ChangeKeyboardMapping */      keySyms = XkbGetCoreMap(g_keyboard); -    first_key = keySyms->minKeyCode; -    num_keys = (keySyms->maxKeyCode - keySyms->minKeyCode) + 1; -    XkbApplyMappingChange(g_keyboard, keySyms, first_key, num_keys, -                          NULL, serverClient); -    for (pDev = inputInfo.devices; pDev; pDev = pDev->next) +    if (keySyms)      { -        if ((pDev->coreEvents || pDev == inputInfo.keyboard) && pDev->key) +        first_key = keySyms->minKeyCode; +        num_keys = (keySyms->maxKeyCode - keySyms->minKeyCode) + 1; +        XkbApplyMappingChange(g_keyboard, keySyms, first_key, num_keys, +                              NULL, serverClient); +        for (pDev = inputInfo.devices; pDev; pDev = pDev->next)          { -            XkbApplyMappingChange(pDev, keySyms, first_key, num_keys, -                                  NULL, serverClient); +            if ((pDev->coreEvents || pDev == inputInfo.keyboard) && pDev->key) +            { +                XkbApplyMappingChange(pDev, keySyms, first_key, num_keys, +                                      NULL, serverClient); +            }          } +    } else +    { +        /* sometimes, variant doesn't support all layouts */ +        set.variant = ""; +        goto retry;      }      return 0; diff --git a/xorg/X11R7.6/rdp/rdpup.c b/xorg/X11R7.6/rdp/rdpup.c index e7f504ec..f25b390a 100644 --- a/xorg/X11R7.6/rdp/rdpup.c +++ b/xorg/X11R7.6/rdp/rdpup.c @@ -1117,7 +1117,7 @@ rdpup_process_msg(struct stream *s)              g_do_multimon = 0;          } -        rdpLoadLayout(g_rdpScreen.client_info.keylayout); +        rdpLoadLayout(&(g_rdpScreen.client_info));      }      else if (msg_type == 105) diff --git a/xrdp/Makefile.am b/xrdp/Makefile.am index 96ec07f3..4fd9a689 100644 --- a/xrdp/Makefile.am +++ b/xrdp/Makefile.am @@ -65,7 +65,8 @@ xrdp_LDFLAGS = \  xrdpsysconfdir=$(sysconfdir)/xrdp  xrdpsysconf_DATA = \ -  xrdp.ini  +  xrdp.ini \ +  xrdp_keyboard.ini  xrdppkgdatadir=$(datadir)/xrdp diff --git a/xrdp/xrdp_keyboard.ini b/xrdp/xrdp_keyboard.ini new file mode 100644 index 00000000..bd4e4e68 --- /dev/null +++ b/xrdp/xrdp_keyboard.ini @@ -0,0 +1,105 @@ +# +# RDP Keyboard <-> X11 Keyboard layout map +# +# How this file works: +#   1. load the file and scan each section to find matching "keyboard_type" +#      and "keyboard_subtype" based on the values received from the client. +#      If not found, then jump to default section. +#   2. in the selected section, look for "rdp_layouts" and "layouts_map". +#      Based on the "keylayout" value from the client, find the right x11 +#      layout value. +#   3. model/variant are inferred based on the "keyboard_type" and +#      "keyboard_subtype", but they can be overridden.  +# + +# +# RDP Keyboard Type (http://msdn.microsoft.com/en-us/library/cc240563.aspx) +# +# 0 is not a valid value +# +# 1 - IBM PC/XT or compatible (83-key) keyboard +# 2 - Olivetti "ICO" (102-key) keyboard +# 3 - IBM PC/AT (84-key) or similar keyboard +# 4 - IBM enhanced (101- or 102-key) keyboard +# 5 - Nokia 1050 and similar keyboards +# 6 - Nokia 9140 and similar keyboards +# 7 - Japanese keyboard +# +# RDP Keyboard Subtype is vendor dependent. XRDP defines as follows: +# +# 0 is not a valid value +# +# 1 - Standard +# 2 - FreeRDP JP keyboard +# 3 - Macintosh +# ... - < any vendor dependent subtype > +# +# The list can be augmented. +# + + +# default +[default] +# keyboard_type and keyboard_subtype is not readed for default section. It +# is only as a place holder to keep consistency. Default model/variant are +# platform dependent, and could be overridden if needed. +keyboard_type=0 +keyboard_subtype=0 + +# user could override variant and model, but generally they should be inferred +# automatically based on keyboard type and subtype +#variant= +#model= + +# A list of supported RDP keyboard layouts +rdp_layouts=default_rdp_layouts +# The map from RDP keyboard layout to X11 keyboard layout +layouts_map=default_layouts_map + +[default_rdp_layouts] +rdp_layout_us=0x00000409 +rdp_layout_de=0x00000407 +rdp_layout_fr=0x0000040C +rdp_layout_it=0x00000410 +rdp_layout_jp=0x00000411 +rdp_layout_ru=0x00000419 +rdp_layout_se=0x0000041D +rdp_layout_pt=0x00000816 +rdp_layout_br=0x00000416 + +# <rdp layout name> = <X11 keyboard layout value> +[default_layouts_map] +rdp_layout_us=us +rdp_layout_de=de +rdp_layout_fr=fr +rdp_layout_it=it +rdp_layout_jp=jp +rdp_layout_ru=ru +rdp_layout_se=se +rdp_layout_pt=pt +rdp_layout_br=br(abnt2) + +# if two sections have the same keyboard_type and keyboard_subtype, then +# the latter could override the former. +[rdp_keyboard_mac] +keyboard_type=4 +keyboard_subtype=3 +rdp_layouts=default_rdp_layouts +layouts_map=rdp_layouts_map_mac + +[rdp_keyboard_jp] +keyboard_type=7 +keyboard_subtype=2 +rdp_layouts=default_rdp_layouts +layouts_map=default_layouts_map + +[rdp_layouts_map_mac] +rdp_layout_us=us +rdp_layout_de=de +rdp_layout_fr=fr +rdp_layout_jp=jp +rdp_layout_it=it +rdp_layout_ru=ru +rdp_layout_se=se +rdp_layout_pt=pt +rdp_layout_br=br(abnt2) | 
