/*
 * Deskflow -- 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 "deskflow/KeyState.h"
#include "base/Log.h"
#include "deskflow/ClientApp.h"
#include "deskflow/ClientArgs.h"

#include <algorithm>
#include <cstring>
#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 deskflow::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, deskflow::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(deskflow::KeyMap *existing)
{
  if (existing) {
    m_keyMap.swap(*existing);
  } else {
    // get the current keyboard map
    deskflow::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, deskflow::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 deskflow::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 deskflow::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 deskflow::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, language sync is disabled"));
      ++k;
    }
  }
}

void KeyState::updateModifierKeyState(
    KeyButton button, const ModifierToKeys &oldModifiers, const ModifierToKeys &newModifiers
)
{
  // get the pressed modifier buttons before and after
  deskflow::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
  deskflow::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 (deskflow::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 (deskflow::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
}
