/*
 * synergy -- mouse and keyboard sharing utility
 * Copyright (C) 2012-2016 Symless Ltd.
 * Copyright (C) 2004 Chris Schoeneman
 * 
 * This package is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * found in the file LICENSE that should have accompanied this file.
 * 
 * This package 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "synergy/KeyState.h"
#include "base/Log.h"
#include "synergy/ClientApp.h"
#include "synergy/ClientArgs.h"

#include <cstring>
#include <algorithm>
#include <iterator>
#include <list>

static const KeyButton kButtonMask = (KeyButton)(IKeyState::kNumButtons - 1);

static const KeyID s_decomposeTable[] = {
    // spacing version of dead keys
    0x0060, 0x0300, 0x0020, 0, // grave,        dead_grave,       space
    0x00b4, 0x0301, 0x0020, 0, // acute,        dead_acute,       space
    0x005e, 0x0302, 0x0020, 0, // asciicircum,  dead_circumflex,  space
    0x007e, 0x0303, 0x0020, 0, // asciitilde,   dead_tilde,       space
    0x00a8, 0x0308, 0x0020, 0, // diaeresis,    dead_diaeresis,   space
    0x00b0, 0x030a, 0x0020, 0, // degree,       dead_abovering,   space
    0x00b8, 0x0327, 0x0020, 0, // cedilla,      dead_cedilla,     space
    0x02db, 0x0328, 0x0020, 0, // ogonek,       dead_ogonek,      space
    0x02c7, 0x030c, 0x0020, 0, // caron,        dead_caron,       space
    0x02d9, 0x0307, 0x0020, 0, // abovedot,     dead_abovedot,    space
    0x02dd, 0x030b, 0x0020, 0, // doubleacute,  dead_doubleacute, space
    0x02d8, 0x0306, 0x0020, 0, // breve,        dead_breve,       space
    0x00af, 0x0304, 0x0020, 0, // macron,       dead_macron,      space

    // Latin-1 (ISO 8859-1)
    0x00c0, 0x0300, 0x0041, 0, // Agrave,       dead_grave,       A
    0x00c1, 0x0301, 0x0041, 0, // Aacute,       dead_acute,       A
    0x00c2, 0x0302, 0x0041, 0, // Acircumflex,  dead_circumflex,  A
    0x00c3, 0x0303, 0x0041, 0, // Atilde,       dead_tilde,       A
    0x00c4, 0x0308, 0x0041, 0, // Adiaeresis,   dead_diaeresis,   A
    0x00c5, 0x030a, 0x0041, 0, // Aring,        dead_abovering,   A
    0x00c7, 0x0327, 0x0043, 0, // Ccedilla,     dead_cedilla,     C
    0x00c8, 0x0300, 0x0045, 0, // Egrave,       dead_grave,       E
    0x00c9, 0x0301, 0x0045, 0, // Eacute,       dead_acute,       E
    0x00ca, 0x0302, 0x0045, 0, // Ecircumflex,  dead_circumflex,  E
    0x00cb, 0x0308, 0x0045, 0, // Ediaeresis,   dead_diaeresis,   E
    0x00cc, 0x0300, 0x0049, 0, // Igrave,       dead_grave,       I
    0x00cd, 0x0301, 0x0049, 0, // Iacute,       dead_acute,       I
    0x00ce, 0x0302, 0x0049, 0, // Icircumflex,  dead_circumflex,  I
    0x00cf, 0x0308, 0x0049, 0, // Idiaeresis,   dead_diaeresis,   I
    0x00d1, 0x0303, 0x004e, 0, // Ntilde,       dead_tilde,       N
    0x00d2, 0x0300, 0x004f, 0, // Ograve,       dead_grave,       O
    0x00d3, 0x0301, 0x004f, 0, // Oacute,       dead_acute,       O
    0x00d4, 0x0302, 0x004f, 0, // Ocircumflex,  dead_circumflex,  O
    0x00d5, 0x0303, 0x004f, 0, // Otilde,       dead_tilde,       O
    0x00d6, 0x0308, 0x004f, 0, // Odiaeresis,   dead_diaeresis,   O
    0x00d9, 0x0300, 0x0055, 0, // Ugrave,       dead_grave,       U
    0x00da, 0x0301, 0x0055, 0, // Uacute,       dead_acute,       U
    0x00db, 0x0302, 0x0055, 0, // Ucircumflex,  dead_circumflex,  U
    0x00dc, 0x0308, 0x0055, 0, // Udiaeresis,   dead_diaeresis,   U
    0x00dd, 0x0301, 0x0059, 0, // Yacute,       dead_acute,       Y
    0x00e0, 0x0300, 0x0061, 0, // agrave,       dead_grave,       a
    0x00e1, 0x0301, 0x0061, 0, // aacute,       dead_acute,       a
    0x00e2, 0x0302, 0x0061, 0, // acircumflex,  dead_circumflex,  a
    0x00e3, 0x0303, 0x0061, 0, // atilde,       dead_tilde,       a
    0x00e4, 0x0308, 0x0061, 0, // adiaeresis,   dead_diaeresis,   a
    0x00e5, 0x030a, 0x0061, 0, // aring,        dead_abovering,   a
    0x00e7, 0x0327, 0x0063, 0, // ccedilla,     dead_cedilla,     c
    0x00e8, 0x0300, 0x0065, 0, // egrave,       dead_grave,       e
    0x00e9, 0x0301, 0x0065, 0, // eacute,       dead_acute,       e
    0x00ea, 0x0302, 0x0065, 0, // ecircumflex,  dead_circumflex,  e
    0x00eb, 0x0308, 0x0065, 0, // ediaeresis,   dead_diaeresis,   e
    0x00ec, 0x0300, 0x0069, 0, // igrave,       dead_grave,       i
    0x00ed, 0x0301, 0x0069, 0, // iacute,       dead_acute,       i
    0x00ee, 0x0302, 0x0069, 0, // icircumflex,  dead_circumflex,  i
    0x00ef, 0x0308, 0x0069, 0, // idiaeresis,   dead_diaeresis,   i
    0x00f1, 0x0303, 0x006e, 0, // ntilde,       dead_tilde,       n
    0x00f2, 0x0300, 0x006f, 0, // ograve,       dead_grave,       o
    0x00f3, 0x0301, 0x006f, 0, // oacute,       dead_acute,       o
    0x00f4, 0x0302, 0x006f, 0, // ocircumflex,  dead_circumflex,  o
    0x00f5, 0x0303, 0x006f, 0, // otilde,       dead_tilde,       o
    0x00f6, 0x0308, 0x006f, 0, // odiaeresis,   dead_diaeresis,   o
    0x00f9, 0x0300, 0x0075, 0, // ugrave,       dead_grave,       u
    0x00fa, 0x0301, 0x0075, 0, // uacute,       dead_acute,       u
    0x00fb, 0x0302, 0x0075, 0, // ucircumflex,  dead_circumflex,  u
    0x00fc, 0x0308, 0x0075, 0, // udiaeresis,   dead_diaeresis,   u
    0x00fd, 0x0301, 0x0079, 0, // yacute,       dead_acute,       y
    0x00ff, 0x0308, 0x0079, 0, // ydiaeresis,   dead_diaeresis,   y

    // Latin-2 (ISO 8859-2)
    0x0104, 0x0328, 0x0041, 0, // Aogonek,      dead_ogonek,      A
    0x013d, 0x030c, 0x004c, 0, // Lcaron,       dead_caron,       L
    0x015a, 0x0301, 0x0053, 0, // Sacute,       dead_acute,       S
    0x0160, 0x030c, 0x0053, 0, // Scaron,       dead_caron,       S
    0x015e, 0x0327, 0x0053, 0, // Scedilla,     dead_cedilla,     S
    0x0164, 0x030c, 0x0054, 0, // Tcaron,       dead_caron,       T
    0x0179, 0x0301, 0x005a, 0, // Zacute,       dead_acute,       Z
    0x017d, 0x030c, 0x005a, 0, // Zcaron,       dead_caron,       Z
    0x017b, 0x0307, 0x005a, 0, // Zabovedot,    dead_abovedot,    Z
    0x0105, 0x0328, 0x0061, 0, // aogonek,      dead_ogonek,      a
    0x013e, 0x030c, 0x006c, 0, // lcaron,       dead_caron,       l
    0x015b, 0x0301, 0x0073, 0, // sacute,       dead_acute,       s
    0x0161, 0x030c, 0x0073, 0, // scaron,       dead_caron,       s
    0x015f, 0x0327, 0x0073, 0, // scedilla,     dead_cedilla,     s
    0x0165, 0x030c, 0x0074, 0, // tcaron,       dead_caron,       t
    0x017a, 0x0301, 0x007a, 0, // zacute,       dead_acute,       z
    0x017e, 0x030c, 0x007a, 0, // zcaron,       dead_caron,       z
    0x017c, 0x0307, 0x007a, 0, // zabovedot,    dead_abovedot,    z
    0x0154, 0x0301, 0x0052, 0, // Racute,       dead_acute,       R
    0x0102, 0x0306, 0x0041, 0, // Abreve,       dead_breve,       A
    0x0139, 0x0301, 0x004c, 0, // Lacute,       dead_acute,       L
    0x0106, 0x0301, 0x0043, 0, // Cacute,       dead_acute,       C
    0x010c, 0x030c, 0x0043, 0, // Ccaron,       dead_caron,       C
    0x0118, 0x0328, 0x0045, 0, // Eogonek,      dead_ogonek,      E
    0x011a, 0x030c, 0x0045, 0, // Ecaron,       dead_caron,       E
    0x010e, 0x030c, 0x0044, 0, // Dcaron,       dead_caron,       D
    0x0143, 0x0301, 0x004e, 0, // Nacute,       dead_acute,       N
    0x0147, 0x030c, 0x004e, 0, // Ncaron,       dead_caron,       N
    0x0150, 0x030b, 0x004f, 0, // Odoubleacute, dead_doubleacute, O
    0x0158, 0x030c, 0x0052, 0, // Rcaron,       dead_caron,       R
    0x016e, 0x030a, 0x0055, 0, // Uring,        dead_abovering,   U
    0x0170, 0x030b, 0x0055, 0, // Udoubleacute, dead_doubleacute, U
    0x0162, 0x0327, 0x0054, 0, // Tcedilla,     dead_cedilla,     T
    0x0155, 0x0301, 0x0072, 0, // racute,       dead_acute,       r
    0x0103, 0x0306, 0x0061, 0, // abreve,       dead_breve,       a
    0x013a, 0x0301, 0x006c, 0, // lacute,       dead_acute,       l
    0x0107, 0x0301, 0x0063, 0, // cacute,       dead_acute,       c
    0x010d, 0x030c, 0x0063, 0, // ccaron,       dead_caron,       c
    0x0119, 0x0328, 0x0065, 0, // eogonek,      dead_ogonek,      e
    0x011b, 0x030c, 0x0065, 0, // ecaron,       dead_caron,       e
    0x010f, 0x030c, 0x0064, 0, // dcaron,       dead_caron,       d
    0x0144, 0x0301, 0x006e, 0, // nacute,       dead_acute,       n
    0x0148, 0x030c, 0x006e, 0, // ncaron,       dead_caron,       n
    0x0151, 0x030b, 0x006f, 0, // odoubleacute, dead_doubleacute, o
    0x0159, 0x030c, 0x0072, 0, // rcaron,       dead_caron,       r
    0x016f, 0x030a, 0x0075, 0, // uring,        dead_abovering,   u
    0x0171, 0x030b, 0x0075, 0, // udoubleacute, dead_doubleacute, u
    0x0163, 0x0327, 0x0074, 0, // tcedilla,     dead_cedilla,     t

    // Latin-3 (ISO 8859-3)
    0x0124, 0x0302, 0x0048, 0, // Hcircumflex,  dead_circumflex,  H
    0x0130, 0x0307, 0x0049, 0, // Iabovedot,    dead_abovedot,    I
    0x011e, 0x0306, 0x0047, 0, // Gbreve,        dead_breve,       G
    0x0134, 0x0302, 0x004a, 0, // Jcircumflex,  dead_circumflex,  J
    0x0125, 0x0302, 0x0068, 0, // hcircumflex,  dead_circumflex,  h
    0x011f, 0x0306, 0x0067, 0, // gbreve,        dead_breve,       g
    0x0135, 0x0302, 0x006a, 0, // jcircumflex,  dead_circumflex,  j
    0x010a, 0x0307, 0x0043, 0, // Cabovedot,    dead_abovedot,    C
    0x0108, 0x0302, 0x0043, 0, // Ccircumflex,  dead_circumflex,  C
    0x0120, 0x0307, 0x0047, 0, // Gabovedot,    dead_abovedot,    G
    0x011c, 0x0302, 0x0047, 0, // Gcircumflex,  dead_circumflex,  G
    0x016c, 0x0306, 0x0055, 0, // Ubreve,        dead_breve,       U
    0x015c, 0x0302, 0x0053, 0, // Scircumflex,  dead_circumflex,  S
    0x010b, 0x0307, 0x0063, 0, // cabovedot,    dead_abovedot,    c
    0x0109, 0x0302, 0x0063, 0, // ccircumflex,  dead_circumflex,  c
    0x0121, 0x0307, 0x0067, 0, // gabovedot,    dead_abovedot,    g
    0x011d, 0x0302, 0x0067, 0, // gcircumflex,  dead_circumflex,  g
    0x016d, 0x0306, 0x0075, 0, // ubreve,        dead_breve,       u
    0x015d, 0x0302, 0x0073, 0, // scircumflex,  dead_circumflex,  s

    // Latin-4 (ISO 8859-4)
    0x0156, 0x0327, 0x0052, 0, // Rcedilla,     dead_cedilla,      R
    0x0128, 0x0303, 0x0049, 0, // Itilde,        dead_tilde,       I
    0x013b, 0x0327, 0x004c, 0, // Lcedilla,     dead_cedilla,      L
    0x0112, 0x0304, 0x0045, 0, // Emacron,      dead_macron,      E
    0x0122, 0x0327, 0x0047, 0, // Gcedilla,     dead_cedilla,      G
    0x0157, 0x0327, 0x0072, 0, // rcedilla,     dead_cedilla,      r
    0x0129, 0x0303, 0x0069, 0, // itilde,        dead_tilde,       i
    0x013c, 0x0327, 0x006c, 0, // lcedilla,     dead_cedilla,      l
    0x0113, 0x0304, 0x0065, 0, // emacron,      dead_macron,      e
    0x0123, 0x0327, 0x0067, 0, // gcedilla,     dead_cedilla,      g
    0x0100, 0x0304, 0x0041, 0, // Amacron,      dead_macron,      A
    0x012e, 0x0328, 0x0049, 0, // Iogonek,      dead_ogonek,      I
    0x0116, 0x0307, 0x0045, 0, // Eabovedot,    dead_abovedot,    E
    0x012a, 0x0304, 0x0049, 0, // Imacron,      dead_macron,      I
    0x0145, 0x0327, 0x004e, 0, // Ncedilla,     dead_cedilla,      N
    0x014c, 0x0304, 0x004f, 0, // Omacron,      dead_macron,      O
    0x0136, 0x0327, 0x004b, 0, // Kcedilla,     dead_cedilla,      K
    0x0172, 0x0328, 0x0055, 0, // Uogonek,      dead_ogonek,      U
    0x0168, 0x0303, 0x0055, 0, // Utilde,        dead_tilde,       U
    0x016a, 0x0304, 0x0055, 0, // Umacron,      dead_macron,      U
    0x0101, 0x0304, 0x0061, 0, // amacron,      dead_macron,      a
    0x012f, 0x0328, 0x0069, 0, // iogonek,      dead_ogonek,      i
    0x0117, 0x0307, 0x0065, 0, // eabovedot,    dead_abovedot,    e
    0x012b, 0x0304, 0x0069, 0, // imacron,      dead_macron,      i
    0x0146, 0x0327, 0x006e, 0, // ncedilla,     dead_cedilla,      n
    0x014d, 0x0304, 0x006f, 0, // omacron,      dead_macron,      o
    0x0137, 0x0327, 0x006b, 0, // kcedilla,     dead_cedilla,      k
    0x0173, 0x0328, 0x0075, 0, // uogonek,      dead_ogonek,      u
    0x0169, 0x0303, 0x0075, 0, // utilde,        dead_tilde,       u
    0x016b, 0x0304, 0x0075, 0, // umacron,      dead_macron,      u

    // Latin-8 (ISO 8859-14)
    0x1e02, 0x0307, 0x0042, 0, // Babovedot,    dead_abovedot,    B
    0x1e03, 0x0307, 0x0062, 0, // babovedot,    dead_abovedot,    b
    0x1e0a, 0x0307, 0x0044, 0, // Dabovedot,    dead_abovedot,    D
    0x1e80, 0x0300, 0x0057, 0, // Wgrave,        dead_grave,       W
    0x1e82, 0x0301, 0x0057, 0, // Wacute,        dead_acute,       W
    0x1e0b, 0x0307, 0x0064, 0, // dabovedot,    dead_abovedot,    d
    0x1ef2, 0x0300, 0x0059, 0, // Ygrave,        dead_grave,       Y
    0x1e1e, 0x0307, 0x0046, 0, // Fabovedot,    dead_abovedot,    F
    0x1e1f, 0x0307, 0x0066, 0, // fabovedot,    dead_abovedot,    f
    0x1e40, 0x0307, 0x004d, 0, // Mabovedot,    dead_abovedot,    M
    0x1e41, 0x0307, 0x006d, 0, // mabovedot,    dead_abovedot,    m
    0x1e56, 0x0307, 0x0050, 0, // Pabovedot,    dead_abovedot,    P
    0x1e81, 0x0300, 0x0077, 0, // wgrave,        dead_grave,       w
    0x1e57, 0x0307, 0x0070, 0, // pabovedot,    dead_abovedot,    p
    0x1e83, 0x0301, 0x0077, 0, // wacute,        dead_acute,       w
    0x1e60, 0x0307, 0x0053, 0, // Sabovedot,    dead_abovedot,    S
    0x1ef3, 0x0300, 0x0079, 0, // ygrave,        dead_grave,       y
    0x1e84, 0x0308, 0x0057, 0, // Wdiaeresis,    dead_diaeresis,   W
    0x1e85, 0x0308, 0x0077, 0, // wdiaeresis,    dead_diaeresis,   w
    0x1e61, 0x0307, 0x0073, 0, // sabovedot,    dead_abovedot,    s
    0x0174, 0x0302, 0x0057, 0, // Wcircumflex,  dead_circumflex,  W
    0x1e6a, 0x0307, 0x0054, 0, // Tabovedot,    dead_abovedot,    T
    0x0176, 0x0302, 0x0059, 0, // Ycircumflex,  dead_circumflex,  Y
    0x0175, 0x0302, 0x0077, 0, // wcircumflex,  dead_circumflex,  w
    0x1e6b, 0x0307, 0x0074, 0, // tabovedot,    dead_abovedot,    t
    0x0177, 0x0302, 0x0079, 0, // ycircumflex,  dead_circumflex,  y

    // Latin-9 (ISO 8859-15)
    0x0178, 0x0308, 0x0059, 0, // Ydiaeresis,   dead_diaeresis,   Y

    // Compose key sequences
    0x00c6, kKeyCompose, 0x0041, 0x0045, 0, // AE,             A,           E
    0x00c1, kKeyCompose, 0x0041, 0x0027, 0, // Aacute,         A,           apostrophe
    0x00c2, kKeyCompose, 0x0041, 0x0053, 0, // Acircumflex,    A,           asciicircum
    0x00c3, kKeyCompose, 0x0041, 0x0022, 0, // Adiaeresis,     A,           quotedbl
    0x00c0, kKeyCompose, 0x0041, 0x0060, 0, // Agrave,         A,           grave
    0x00c5, kKeyCompose, 0x0041, 0x002a, 0, // Aring,           A,           asterisk
    0x00c3, kKeyCompose, 0x0041, 0x007e, 0, // Atilde,         A,           asciitilde
    0x00c7, kKeyCompose, 0x0043, 0x002c, 0, // Ccedilla,       C,           comma
    0x00d0, kKeyCompose, 0x0044, 0x002d, 0, // ETH,            D,           minus
    0x00c9, kKeyCompose, 0x0045, 0x0027, 0, // Eacute,         E,           apostrophe
    0x00ca, kKeyCompose, 0x0045, 0x0053, 0, // Ecircumflex,    E,           asciicircum
    0x00cb, kKeyCompose, 0x0045, 0x0022, 0, // Ediaeresis,     E,           quotedbl
    0x00c8, kKeyCompose, 0x0045, 0x0060, 0, // Egrave,         E,           grave
    0x00cd, kKeyCompose, 0x0049, 0x0027, 0, // Iacute,         I,           apostrophe
    0x00ce, kKeyCompose, 0x0049, 0x0053, 0, // Icircumflex,    I,           asciicircum
    0x00cf, kKeyCompose, 0x0049, 0x0022, 0, // Idiaeresis,     I,           quotedbl
    0x00cc, kKeyCompose, 0x0049, 0x0060, 0, // Igrave,         I,           grave
    0x00d1, kKeyCompose, 0x004e, 0x007e, 0, // Ntilde,         N,           asciitilde
    0x00d3, kKeyCompose, 0x004f, 0x0027, 0, // Oacute,         O,           apostrophe
    0x00d4, kKeyCompose, 0x004f, 0x0053, 0, // Ocircumflex,    O,           asciicircum
    0x00d6, kKeyCompose, 0x004f, 0x0022, 0, // Odiaeresis,     O,           quotedbl
    0x00d2, kKeyCompose, 0x004f, 0x0060, 0, // Ograve,         O,           grave
    0x00d8, kKeyCompose, 0x004f, 0x002f, 0, // Ooblique,       O,           slash
    0x00d5, kKeyCompose, 0x004f, 0x007e, 0, // Otilde,         O,           asciitilde
    0x00de, kKeyCompose, 0x0054, 0x0048, 0, // THORN,           T,           H
    0x00da, kKeyCompose, 0x0055, 0x0027, 0, // Uacute,         U,           apostrophe
    0x00db, kKeyCompose, 0x0055, 0x0053, 0, // Ucircumflex,    U,           asciicircum
    0x00dc, kKeyCompose, 0x0055, 0x0022, 0, // Udiaeresis,     U,           quotedbl
    0x00d9, kKeyCompose, 0x0055, 0x0060, 0, // Ugrave,         U,           grave
    0x00dd, kKeyCompose, 0x0059, 0x0027, 0, // Yacute,         Y,           apostrophe
    0x00e1, kKeyCompose, 0x0061, 0x0027, 0, // aacute,         a,           apostrophe
    0x00e2, kKeyCompose, 0x0061, 0x0053, 0, // acircumflex,    a,           asciicircum
    0x00b4, kKeyCompose, 0x0027, 0x0027, 0, // acute,           apostrophe, apostrophe
    0x00e4, kKeyCompose, 0x0061, 0x0022, 0, // adiaeresis,     a,           quotedbl
    0x00e6, kKeyCompose, 0x0061, 0x0065, 0, // ae,             a,           e
    0x00e0, kKeyCompose, 0x0061, 0x0060, 0, // agrave,         a,           grave
    0x00e5, kKeyCompose, 0x0061, 0x002a, 0, // aring,           a,           asterisk
    0x0040, kKeyCompose, 0x0041, 0x0054, 0, // at,             A,           T
    0x00e3, kKeyCompose, 0x0061, 0x007e, 0, // atilde,         a,           asciitilde
    0x005c, kKeyCompose, 0x002f, 0x002f, 0, // backslash,       slash,       slash
    0x007c, kKeyCompose, 0x004c, 0x0056, 0, // bar,            L,           V
    0x007b, kKeyCompose, 0x0028, 0x002d, 0, // braceleft,       parenleft,  minus
    0x007d, kKeyCompose, 0x0029, 0x002d, 0, // braceright,     parenright, minus
    0x005b, kKeyCompose, 0x0028, 0x0028, 0, // bracketleft,    parenleft,  parenleft
    0x005d, kKeyCompose, 0x0029, 0x0029, 0, // bracketright,   parenright, parenright
    0x00a6, kKeyCompose, 0x0042, 0x0056, 0, // brokenbar,       B,           V
    0x00e7, kKeyCompose, 0x0063, 0x002c, 0, // ccedilla,       c,           comma
    0x00b8, kKeyCompose, 0x002c, 0x002c, 0, // cedilla,        comma,       comma
    0x00a2, kKeyCompose, 0x0063, 0x002f, 0, // cent,           c,           slash
    0x00a9, kKeyCompose, 0x0028, 0x0063, 0, // copyright,       parenleft,  c
    0x00a4, kKeyCompose, 0x006f, 0x0078, 0, // currency,       o,           x
    0x00b0, kKeyCompose, 0x0030, 0x0053, 0, // degree,         0,           asciicircum
    0x00a8, kKeyCompose, 0x0022, 0x0022, 0, // diaeresis,       quotedbl,   quotedbl
    0x00f7, kKeyCompose, 0x003a, 0x002d, 0, // division,       colon,       minus
    0x00e9, kKeyCompose, 0x0065, 0x0027, 0, // eacute,         e,           apostrophe
    0x00ea, kKeyCompose, 0x0065, 0x0053, 0, // ecircumflex,    e,           asciicircum
    0x00eb, kKeyCompose, 0x0065, 0x0022, 0, // ediaeresis,     e,           quotedbl
    0x00e8, kKeyCompose, 0x0065, 0x0060, 0, // egrave,         e,           grave
    0x00f0, kKeyCompose, 0x0064, 0x002d, 0, // eth,            d,           minus
    0x00a1, kKeyCompose, 0x0021, 0x0021, 0, // exclamdown,     exclam,     exclam
    0x00ab, kKeyCompose, 0x003c, 0x003c, 0, // guillemotleft,  less,       less
    0x00bb, kKeyCompose, 0x003e, 0x003e, 0, // guillemotright, greater,    greater
    0x0023, kKeyCompose, 0x002b, 0x002b, 0, // numbersign,     plus,       plus
    0x00ad, kKeyCompose, 0x002d, 0x002d, 0, // hyphen,         minus,       minus
    0x00ed, kKeyCompose, 0x0069, 0x0027, 0, // iacute,         i,           apostrophe
    0x00ee, kKeyCompose, 0x0069, 0x0053, 0, // icircumflex,    i,           asciicircum
    0x00ef, kKeyCompose, 0x0069, 0x0022, 0, // idiaeresis,     i,           quotedbl
    0x00ec, kKeyCompose, 0x0069, 0x0060, 0, // igrave,         i,           grave
    0x00af, kKeyCompose, 0x002d, 0x0053, 0, // macron,         minus,       asciicircum
    0x00ba, kKeyCompose, 0x006f, 0x005f, 0, // masculine,       o,           underscore
    0x00b5, kKeyCompose, 0x0075, 0x002f, 0, // mu,             u,           slash
    0x00d7, kKeyCompose, 0x0078, 0x0078, 0, // multiply,       x,           x
    0x00a0, kKeyCompose, 0x0020, 0x0020, 0, // nobreakspace,   space,       space
    0x00ac, kKeyCompose, 0x002c, 0x002d, 0, // notsign,        comma,       minus
    0x00f1, kKeyCompose, 0x006e, 0x007e, 0, // ntilde,         n,           asciitilde
    0x00f3, kKeyCompose, 0x006f, 0x0027, 0, // oacute,         o,           apostrophe
    0x00f4, kKeyCompose, 0x006f, 0x0053, 0, // ocircumflex,    o,           asciicircum
    0x00f6, kKeyCompose, 0x006f, 0x0022, 0, // odiaeresis,     o,           quotedbl
    0x00f2, kKeyCompose, 0x006f, 0x0060, 0, // ograve,         o,           grave
    0x00bd, kKeyCompose, 0x0031, 0x0032, 0, // onehalf,        1,           2
    0x00bc, kKeyCompose, 0x0031, 0x0034, 0, // onequarter,     1,           4
    0x00b9, kKeyCompose, 0x0031, 0x0053, 0, // onesuperior,    1,           asciicircum
    0x00aa, kKeyCompose, 0x0061, 0x005f, 0, // ordfeminine,    a,           underscore
    0x00f8, kKeyCompose, 0x006f, 0x002f, 0, // oslash,         o,           slash
    0x00f5, kKeyCompose, 0x006f, 0x007e, 0, // otilde,         o,           asciitilde
    0x00b6, kKeyCompose, 0x0070, 0x0021, 0, // paragraph,       p,           exclam
    0x00b7, kKeyCompose, 0x002e, 0x002e, 0, // periodcentered, period,     period
    0x00b1, kKeyCompose, 0x002b, 0x002d, 0, // plusminus,       plus,       minus
    0x00bf, kKeyCompose, 0x003f, 0x003f, 0, // questiondown,   question,   question
    0x00ae, kKeyCompose, 0x0028, 0x0072, 0, // registered,     parenleft,  r
    0x00a7, kKeyCompose, 0x0073, 0x006f, 0, // section,        s,           o
    0x00df, kKeyCompose, 0x0073, 0x0073, 0, // ssharp,         s,           s
    0x00a3, kKeyCompose, 0x004c, 0x002d, 0, // sterling,       L,           minus
    0x00fe, kKeyCompose, 0x0074, 0x0068, 0, // thorn,           t,           h
    0x00be, kKeyCompose, 0x0033, 0x0034, 0, // threequarters,  3,           4
    0x00b3, kKeyCompose, 0x0033, 0x0053, 0, // threesuperior,  3,           asciicircum
    0x00b2, kKeyCompose, 0x0032, 0x0053, 0, // twosuperior,    2,           asciicircum
    0x00fa, kKeyCompose, 0x0075, 0x0027, 0, // uacute,         u,           apostrophe
    0x00fb, kKeyCompose, 0x0075, 0x0053, 0, // ucircumflex,    u,           asciicircum
    0x00fc, kKeyCompose, 0x0075, 0x0022, 0, // udiaeresis,     u,           quotedbl
    0x00f9, kKeyCompose, 0x0075, 0x0060, 0, // ugrave,         u,           grave
    0x00fd, kKeyCompose, 0x0079, 0x0027, 0, // yacute,         y,           apostrophe
    0x00ff, kKeyCompose, 0x0079, 0x0022, 0, // ydiaeresis,     y,           quotedbl
    0x00a5, kKeyCompose, 0x0079, 0x003d, 0, // yen,            y,           equal

    // end of table
    0
};

static const KeyID s_numpadTable[] = {
    kKeyKP_Space,        0x0020,
    kKeyKP_Tab,            kKeyTab,
    kKeyKP_Enter,        kKeyReturn,
    kKeyKP_F1,            kKeyF1,
    kKeyKP_F2,            kKeyF2,
    kKeyKP_F3,            kKeyF3,
    kKeyKP_F4,            kKeyF4,
    kKeyKP_Home,        kKeyHome,
    kKeyKP_Left,        kKeyLeft,
    kKeyKP_Up,            kKeyUp,
    kKeyKP_Right,        kKeyRight,
    kKeyKP_Down,        kKeyDown,
    kKeyKP_PageUp,        kKeyPageUp,
    kKeyKP_PageDown,    kKeyPageDown,
    kKeyKP_End,            kKeyEnd,
    kKeyKP_Begin,        kKeyBegin,
    kKeyKP_Insert,        kKeyInsert,
    kKeyKP_Delete,        kKeyDelete,
    kKeyKP_Equal,        0x003d,
    kKeyKP_Multiply,    0x002a,
    kKeyKP_Add,            0x002b,
    kKeyKP_Separator,    0x002c,
    kKeyKP_Subtract,    0x002d,
    kKeyKP_Decimal,        0x002e,
    kKeyKP_Divide,        0x002f,
    kKeyKP_0,            0x0030,
    kKeyKP_1,            0x0031,
    kKeyKP_2,            0x0032,
    kKeyKP_3,            0x0033,
    kKeyKP_4,            0x0034,
    kKeyKP_5,            0x0035,
    kKeyKP_6,            0x0036,
    kKeyKP_7,            0x0037,
    kKeyKP_8,            0x0038,
    kKeyKP_9,            0x0039
};

//
// KeyState
//

KeyState::KeyState(IEventQueue* events, std::vector<String> layouts, bool isLangSyncEnabled) :
    IKeyState(events),
    m_keyMapPtr(new synergy::KeyMap()),
    m_keyMap(*m_keyMapPtr),
    m_mask(0),
    m_events(events),
    m_isLangSyncEnabled(isLangSyncEnabled)
{
    m_keyMap.setLanguageData(std::move(layouts));
    init();
}

KeyState::KeyState(IEventQueue* events, synergy::KeyMap& keyMap, std::vector<String> layouts, bool isLangSyncEnabled) :
    IKeyState(events),
    m_keyMapPtr(0),
    m_keyMap(keyMap),
    m_mask(0),
    m_events(events),
    m_isLangSyncEnabled(isLangSyncEnabled)
{
    m_keyMap.setLanguageData(std::move(layouts));
    init();
}

KeyState::~KeyState()
{
    if (m_keyMapPtr)
        delete m_keyMapPtr;
}

void
KeyState::init()
{
    memset(&m_keys, 0, sizeof(m_keys));
    memset(&m_syntheticKeys, 0, sizeof(m_syntheticKeys));
    memset(&m_keyClientData, 0, sizeof(m_keyClientData));
    memset(&m_serverKeys, 0, sizeof(m_serverKeys));
}

void
KeyState::onKey(KeyButton button, bool down, KeyModifierMask newState)
{
    // update modifier state
    m_mask = newState;
    LOG((CLOG_DEBUG1 "new mask: 0x%04x", m_mask));

    // ignore bogus buttons
    button &= kButtonMask;
    if (button == 0) {
        return;
    }

    // update key state
    if (down) {
        m_keys[button]          = 1;
        m_syntheticKeys[button] = 1;
    }
    else {
        m_keys[button]          = 0;
        m_syntheticKeys[button] = 0;
    }
}

void
KeyState::sendKeyEvent(
                void* target, bool press, bool isAutoRepeat,
                KeyID key, KeyModifierMask mask,
                SInt32 count, KeyButton button)
{
    if (m_keyMap.isHalfDuplex(key, button)) {
        if (isAutoRepeat) {
            // ignore auto-repeat on half-duplex keys
        }
        else {
            m_events->addEvent(Event(m_events->forIKeyState().keyDown(), target,
                            KeyInfo::alloc(key, mask, button, 1)));
            m_events->addEvent(Event(m_events->forIKeyState().keyUp(), target,
                            KeyInfo::alloc(key, mask, button, 1)));
        }
    }
    else {
        if (isAutoRepeat) {
            m_events->addEvent(Event(m_events->forIKeyState().keyRepeat(), target,
                             KeyInfo::alloc(key, mask, button, count)));
        }
        else if (press) {
            m_events->addEvent(Event(m_events->forIKeyState().keyDown(), target,
                            KeyInfo::alloc(key, mask, button, 1)));
        }
        else {
            m_events->addEvent(Event(m_events->forIKeyState().keyUp(), target,
                            KeyInfo::alloc(key, mask, button, 1)));
        }
    }
}

void
KeyState::updateKeyMap(synergy::KeyMap* existing)
{
    if (existing) {
        m_keyMap.swap(*existing);
    }
    else {
        // get the current keyboard map
        synergy::KeyMap keyMap;
        getKeyMap(keyMap);
        m_keyMap.swap(keyMap);
        m_keyMap.finish();
    }

    // add special keys
    addCombinationEntries();
    addKeypadEntries();
    addAliasEntries();
}

void
KeyState::updateKeyState()
{
    // reset our state
    memset(&m_keys, 0, sizeof(m_keys));
    memset(&m_syntheticKeys, 0, sizeof(m_syntheticKeys));
    memset(&m_keyClientData, 0, sizeof(m_keyClientData));
    memset(&m_serverKeys, 0, sizeof(m_serverKeys));
    m_activeModifiers.clear();

    // get the current keyboard state
    KeyButtonSet keysDown;
    pollPressedKeys(keysDown);
    for (KeyButtonSet::const_iterator i = keysDown.begin();
                                i != keysDown.end(); ++i) {
        m_keys[*i] = 1;
    }

    // get the current modifier state
    m_mask = pollActiveModifiers();

    // set active modifiers
    AddActiveModifierContext addModifierContext(pollActiveGroup(), m_mask,
                                                m_activeModifiers);
    m_keyMap.foreachKey(&KeyState::addActiveModifierCB, &addModifierContext);

    LOG((CLOG_DEBUG1 "modifiers on update: 0x%04x", m_mask));
}

void
KeyState::addActiveModifierCB(KeyID, SInt32 group,
                synergy::KeyMap::KeyItem& keyItem, void* vcontext)
{
    AddActiveModifierContext* context =
        static_cast<AddActiveModifierContext*>(vcontext);
    if (group == context->m_activeGroup &&
        (keyItem.m_generates & context->m_mask) != 0) {
        context->m_activeModifiers.insert(std::make_pair(
                                keyItem.m_generates, keyItem));
    }    
}

void
KeyState::setHalfDuplexMask(KeyModifierMask mask)
{
    m_keyMap.clearHalfDuplexModifiers();
    if ((mask & KeyModifierCapsLock) != 0) {
        m_keyMap.addHalfDuplexModifier(kKeyCapsLock);
    }
    if ((mask & KeyModifierNumLock) != 0) {
        m_keyMap.addHalfDuplexModifier(kKeyNumLock);
    }
    if ((mask & KeyModifierScrollLock) != 0) {
        m_keyMap.addHalfDuplexModifier(kKeyScrollLock);
    }
}

void
KeyState::fakeKeyDown(KeyID id, KeyModifierMask mask, KeyButton serverID, const String& lang)
{
    // if this server key is already down then this is probably a
    // mis-reported autorepeat.
    serverID &= kButtonMask;
    if (m_serverKeys[serverID] != 0) {
        fakeKeyRepeat(id, mask, 1, serverID, lang);
        return;
    }

    // ignore certain keys
    if (isIgnoredKey(id, mask)) {
        LOG((CLOG_DEBUG1 "ignored key %04x %04x", id, mask));
        return;
    }

    Keystrokes keys;
    ModifierToKeys oldActiveModifiers = m_activeModifiers;
    const synergy::KeyMap::KeyItem* keyItem =
            m_keyMap.mapKey(keys, id, pollActiveGroup(), m_activeModifiers,
                                getActiveModifiersRValue(), mask, false, lang);

    if (keyItem == nullptr) {
        // a media key won't be mapped on mac, so we need to fake it in a
        // special way
        if (id == kKeyAudioDown || id == kKeyAudioUp ||
            id == kKeyAudioMute || id == kKeyAudioPlay ||
            id == kKeyAudioPrev || id == kKeyAudioNext ||
            id == kKeyBrightnessDown || id == kKeyBrightnessUp
            ) {
            LOG((CLOG_DEBUG1 "emulating media key"));
            fakeMediaKey(id);
        }

        return;
    }

    KeyButton localID = (KeyButton)(keyItem->m_button & kButtonMask);
    updateModifierKeyState(localID, oldActiveModifiers, m_activeModifiers);
    if (localID != 0) {
        // note keys down
        ++m_keys[localID];
        ++m_syntheticKeys[localID];
        m_keyClientData[localID] = keyItem->m_client;
        m_serverKeys[serverID]   = localID;
    }

    // generate key events
    fakeKeys(keys, 1);
}

bool
KeyState::fakeKeyRepeat(
                KeyID id, KeyModifierMask mask,
                SInt32 count, KeyButton serverID, const String& lang)
{
    LOG((CLOG_DEBUG2 "fakeKeyRepeat"));
    serverID &= kButtonMask;

    // if we haven't seen this button go down then ignore it
    KeyButton oldLocalID = m_serverKeys[serverID];
    if (oldLocalID == 0) {
        return false;
    }

    // get keys for key repeat
    Keystrokes keys;
    ModifierToKeys oldActiveModifiers = m_activeModifiers;
    const synergy::KeyMap::KeyItem* keyItem =
        m_keyMap.mapKey(keys, id, pollActiveGroup(), m_activeModifiers,
                                getActiveModifiersRValue(), mask, true, lang);
    if (keyItem == NULL) {
        return false;
    }
    KeyButton localID = (KeyButton)(keyItem->m_button & kButtonMask);
    if (localID == 0) {
        return false;
    }

    // if the KeyButton for the auto-repeat is not the same as for the
    // initial press then mark the initial key as released and the new
    // key as pressed.  this can happen when we auto-repeat after a
    // dead key.  for example, a dead accent followed by 'a' will
    // generate an 'a with accent' followed by a repeating 'a'.  the
    // KeyButtons for the two KeyIDs might be different.
    if (localID != oldLocalID) {
        // replace key up with previous KeyButton but leave key down
        // alone so it uses the new KeyButton.
        for (Keystrokes::iterator index = keys.begin();
                                index != keys.end(); ++index) {
            if (index->m_type == Keystroke::kButton &&
                index->m_data.m_button.m_button == localID) {
                index->m_data.m_button.m_button = oldLocalID;
                break;
            }
        }

        // note that old key is now up
        --m_keys[oldLocalID];
        --m_syntheticKeys[oldLocalID];

        // note keys down
        updateModifierKeyState(localID, oldActiveModifiers, m_activeModifiers);
        ++m_keys[localID];
        ++m_syntheticKeys[localID];
        m_keyClientData[localID] = keyItem->m_client;
        m_serverKeys[serverID]   = localID;
    }

    // generate key events
    fakeKeys(keys, count);
    return true;
}

bool
KeyState::fakeKeyUp(KeyButton serverID)
{
    // if we haven't seen this button go down then ignore it
    KeyButton localID = m_serverKeys[serverID & kButtonMask];
    if (localID == 0) {
        return false;
    }

    // get the sequence of keys to simulate key release
    Keystrokes keys;
    keys.push_back(Keystroke(localID, false, false, m_keyClientData[localID]));

    // note keys down
    --m_keys[localID];
    --m_syntheticKeys[localID];
    m_serverKeys[serverID] = 0;

    // check if this is a modifier
    ModifierToKeys::iterator i = m_activeModifiers.begin();
    while (i != m_activeModifiers.end()) {
        if (i->second.m_button == localID && !i->second.m_lock) {
            // modifier is no longer down
            KeyModifierMask mask = i->first;

            ModifierToKeys::iterator tmp = i;
            ++i;
            m_activeModifiers.erase(tmp);

            if (m_activeModifiers.count(mask) == 0) {
                // no key for modifier is down so deactivate modifier
                m_mask &= ~mask;
                LOG((CLOG_DEBUG1 "new state %04x", m_mask));
            }
        }
        else {
            ++i;
        }
    }

    // generate key events
    fakeKeys(keys, 1);
    return true;
}

void
KeyState::fakeAllKeysUp()
{
    Keystrokes keys;
    for (KeyButton i = 0; i < IKeyState::kNumButtons; ++i) {
        if (m_syntheticKeys[i] > 0) {
            keys.push_back(Keystroke(i, false, false, m_keyClientData[i]));
            m_keys[i]          = 0;
            m_syntheticKeys[i] = 0;
        }
    }
    fakeKeys(keys, 1);
    memset(&m_serverKeys, 0, sizeof(m_serverKeys));
    m_activeModifiers.clear();
    m_mask = pollActiveModifiers();
}

bool
KeyState::fakeMediaKey(KeyID id)
{
    return false;
}

bool
KeyState::isKeyDown(KeyButton button) const
{
    return (m_keys[button & kButtonMask] > 0);
}

KeyModifierMask
KeyState::getActiveModifiers() const
{
    return m_mask;
}

KeyModifierMask&
KeyState::getActiveModifiersRValue()
{
    return m_mask;
}

SInt32
KeyState::getEffectiveGroup(SInt32 group, SInt32 offset) const
{
    return m_keyMap.getEffectiveGroup(group, offset);
}

bool
KeyState::isIgnoredKey(KeyID key, KeyModifierMask) const
{
    switch (key) {
    case kKeyCapsLock:
    case kKeyNumLock:
    case kKeyScrollLock:
        return true;

    default:
        return false;
    }
}

KeyButton
KeyState::getButton(KeyID id, SInt32 group) const
{
    const synergy::KeyMap::KeyItemList* items =
        m_keyMap.findCompatibleKey(id, group, 0, 0);
    if (items == NULL) {
        return 0;
    }
    else {
        return items->back().m_button;
    }
}

void
KeyState::addAliasEntries()
{
    for (SInt32 g = 0, n = m_keyMap.getNumGroups(); g < n; ++g) {
        // if we can't shift any kKeyTab key in a particular group but we can
        // shift kKeyLeftTab then add a shifted kKeyTab entry that matches a
        // shifted kKeyLeftTab entry.
        m_keyMap.addKeyAliasEntry(kKeyTab, g,
                                KeyModifierShift, KeyModifierShift,
                                kKeyLeftTab,
                                KeyModifierShift, KeyModifierShift);

        // if we have no kKeyLeftTab but we do have a kKeyTab that can be
        // shifted then add kKeyLeftTab that matches a kKeyTab.
        m_keyMap.addKeyAliasEntry(kKeyLeftTab, g,
                                KeyModifierShift, KeyModifierShift,
                                kKeyTab,
                                0, KeyModifierShift);

        // map non-breaking space to space
        m_keyMap.addKeyAliasEntry(0x20, g, 0, 0, 0xa0, 0, 0);
    }
}

void
KeyState::addKeypadEntries()
{
    // map every numpad key to its equivalent non-numpad key if it's not
    // on the keyboard.
    for (SInt32 g = 0, n = m_keyMap.getNumGroups(); g < n; ++g) {
        for (size_t i = 0; i < sizeof(s_numpadTable) /
                                sizeof(s_numpadTable[0]); i += 2) {
            m_keyMap.addKeyCombinationEntry(s_numpadTable[i], g,
                                s_numpadTable + i + 1, 1);
        }
    }
}

void
KeyState::addCombinationEntries()
{
    for (SInt32 g = 0, n = m_keyMap.getNumGroups(); g < n; ++g) {
        // add dead and compose key composition sequences
        const KeyID* i = s_decomposeTable;
        while (*i != 0) {
            // count the decomposed keys for this key
            UInt32 numKeys = 0;
            const KeyID* j = i;
            while (*++j != 0) {
                ++numKeys;
            }

            // add an entry for this key
            m_keyMap.addKeyCombinationEntry(*i, g, i + 1, numKeys);

            // next key
            i += numKeys + 1;
            ++i;
        }
    }
}

void
KeyState::fakeKeys(const Keystrokes& keys, UInt32 count)
{
    // do nothing if no keys or no repeats
    if (count == 0 || keys.empty()) {
        return;
    }

    // generate key events
    LOG((CLOG_DEBUG1 "keystrokes:"));
    for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ) {
        if (k->m_type == Keystroke::kButton && k->m_data.m_button.m_repeat) {
            // repeat from here up to but not including the next key
            // with m_repeat == false count times.
            Keystrokes::const_iterator start = k;
            while (count-- > 0) {
                // send repeating events
                for (k = start; k != keys.end() &&
                                k->m_type == Keystroke::kButton &&
                                k->m_data.m_button.m_repeat; ++k) {
                    fakeKey(*k);
                }
            }

            // note -- k is now on the first non-repeat key after the
            // repeat keys, exactly where we'd like to continue from.
        }
        else if (k->m_type != Keystroke::kGroup ||
                 (!k->m_data.m_group.m_restore && m_isLangSyncEnabled)) {
            // send event
            fakeKey(*k);

            // next key
            ++k;
        }
        else {
            LOG((CLOG_DEBUG1 "Skipping keystroke (because language sync is disabled)"));
            ++k;
        }
    }
}

void
KeyState::updateModifierKeyState(KeyButton button,
                const ModifierToKeys& oldModifiers,
                const ModifierToKeys& newModifiers)
{
    // get the pressed modifier buttons before and after
    synergy::KeyMap::ButtonToKeyMap oldKeys, newKeys;
    for (ModifierToKeys::const_iterator i = oldModifiers.begin();
                                i != oldModifiers.end(); ++i) {
        oldKeys.insert(std::make_pair(i->second.m_button, &i->second));
    }
    for (ModifierToKeys::const_iterator i = newModifiers.begin();
                                i != newModifiers.end(); ++i) {
        newKeys.insert(std::make_pair(i->second.m_button, &i->second));
    }

    // get the modifier buttons that were pressed or released
    synergy::KeyMap::ButtonToKeyMap pressed, released;
    std::set_difference(oldKeys.begin(), oldKeys.end(),
                        newKeys.begin(), newKeys.end(),
                        std::inserter(released, released.end()),
                        ButtonToKeyLess());
    std::set_difference(newKeys.begin(), newKeys.end(),
                        oldKeys.begin(), oldKeys.end(),
                        std::inserter(pressed, pressed.end()),
                        ButtonToKeyLess());

    // update state
    for (synergy::KeyMap::ButtonToKeyMap::const_iterator i = released.begin();
                                i != released.end(); ++i) {
        if (i->first != button) {
            m_keys[i->first]          = 0;
            m_syntheticKeys[i->first] = 0;
        }
    }
    for (synergy::KeyMap::ButtonToKeyMap::const_iterator i = pressed.begin();
                                i != pressed.end(); ++i) {
        if (i->first != button) {
            m_keys[i->first]          = 1;
            m_syntheticKeys[i->first] = 1;
            m_keyClientData[i->first] = i->second->m_client;
        }
    }
}

//
// KeyState::AddActiveModifierContext
//

KeyState::AddActiveModifierContext::AddActiveModifierContext(
                SInt32 group, KeyModifierMask mask,
                ModifierToKeys&    activeModifiers) :
    m_activeGroup(group),
    m_mask(mask),
    m_activeModifiers(activeModifiers)
{
    // do nothing
}
