// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/events/keycodes/keyboard_code_conversion_x.h"

#include <stddef.h>

#include <algorithm>
#include <array>

#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "ui/events/keycodes/dom/keycode_converter.h"
#include "ui/events/keycodes/keyboard_code_conversion_xkb.h"
#include "ui/events/keycodes/keyboard_codes_posix.h"
#include "ui/events/keycodes/keysym_to_unicode.h"
#include "ui/events/keycodes/xkb_keysym.h"
#include "ui/gfx/x/keysyms/keysyms.h"
#if !BUILDFLAG(IS_OHOS)
#include "ui/gfx/x/xinput.h"
#include "ui/gfx/x/xproto.h"
#else
#include "ui/gfx/x/generated_protos/xinput.h"
#include "ui/gfx/x/generated_protos/xproto.h"
#endif
#include "ui/gfx/x/xproto_types.h"

#define VKEY_UNSUPPORTED VKEY_UNKNOWN

namespace ui {

namespace {

constexpr int kXkbGroupShift = 13;

// MAP0 - MAP3:
// These are the generated VKEY code maps from these layout datas on Windows:
//   KBDA1.DLL
//   KBDAL.DLL
//   KBDARME.DLL
//   KBDARMW.DLL
//   KBDAZEL.DLL
//   KBDBE.DLL
//   KBDBENE.DLL
//   KBDBR.DLL
//   KBDCA.DLL
//   KBDCAN.DLL
//   KBDCR.DLL
//   KBDCZ.DLL
//   KBDCZ1.DLL
//   KBDCZ2.DLL
//   KBDDA.DLL
//   KBDDV.DLL
//   KBDES.DLL
//   KBDEST.DLL
//   KBDFC.DLL
//   KBDFI.DLL
//   KBDFI1.DLL
//   KBDFO.DLL
//   KBDFR.DLL
//   KBDHEB.DLL
//   KBDGAE.DLL
//   KBDGEO.DLL
//   kbdgeoer.dll
//   kbdgeoqw.dll
//   KBDGR.DLL
//   KBDGR1.DLL
//   KBDGRLND.DLL
//   KBDHAU.DLL
//   KBDHU.DLL
//   KBDHU1.DLL
//   KBDIBO.DLL
//   KBDIC.DLL
//   KBDIR.DLL
//   KBDIT.DLL
//   KBDIT142.DLL
//   KBDLA.DLL
//   KBDLT.DLL
//   KBDLT1.DLL
//   KBDLT2.DLL
//   KBDLV.DLL
//   KBDLV1.DLL
//   KBDMLT47.DLL
//   KBDMLT48.DLL
//   KBDNE.DLL
//   KBDNO.DLL
//   KBDNO1.DLL
//   KBDPL.DLL
//   KBDPL1.DLL
//   KBDPO.DLL
//   KBDRO.DLL
//   KBDROPR.DLL
//   KBDROST.DLL
//   KBDRU.DLL
//   KBDSF.DLL
//   KBDSG.DLL
//   KBDSL.DLL
//   KBDSL1.DLL
//   KBDSOREX.DLL
//   KBDSORS1.DLL
//   KBDSORST.DLL
//   KBDSP.DLL
//   KBDSW.DLL
//   KBDSW09.DLL
//   KBDUK.DLL
//   KBDUKX.DLL
//   KBDUS.DLL
//   KBDUSL.DLL
//   KBDUSR.DLL
//   KBDUSX.DLL
//   KBDVNTC.DLL
//   KBDYBA.DLL
// And the maps are only for special letter keys excluding [a-z] & [0-9].
//
// ch0: the keysym without modifier states.
// ch1: the keysym with shift state.
// ch2: the keysym with altgr state.
// sc: the hardware keycode (in Windows, it's called scan code).
// vk: the VKEY code.
//
// MAP0: maps from ch0 to vk.
// MAP1: maps from ch0+sc to vk.
// MAP2: maps from ch0+ch1+sc to vk.
// MAP3: maps from ch0+ch1+ch2+sc to vk.
// MAP0 - MAP3 are all sorted, so that finding VK can be binary search.
//
// Reason for creating these maps is because a hard-coded mapping in
// KeyboardCodeFromXKeysym() doesn't support non-US keyboard layouts.
// e.g. in UK keyboard, the key between Quote and Enter keys has the VKEY code
// VKEY_OEM_5 instead of VKEY_3.
//
// The key symbols which are not [a-zA-Z0-9] and functional/extend keys (e.g.
// TAB, ENTER, BS, Arrow keys, modifier keys, F1-F12, media/app keys, etc.)
// should go through these maps for correct VKEY codes.
//
// Please refer to crbug.com/386066.
//
constexpr struct MAP0 {
  uint32_t ch0;
  uint8_t vk;
  bool operator()(const MAP0& m1, const MAP0& m2) const {
    return m1.ch0 < m2.ch0;
  }
} map0[] = {
    {0x0025, 0x35},  // XK_percent: VKEY_5
    {0x0026, 0x31},  // XK_ampersand: VKEY_1
    {0x003C, 0xDC},  // XK_less: VKEY_OEM_5
    {0x007B, 0xDE},  // XK_braceleft: VKEY_OEM_7
    {0x007C, 0xDC},  // XK_bar: VKEY_OEM_5
    {0x007D, 0xBF},  // XK_braceright: VKEY_OEM_2
    {0x007E, 0xDC},  // XK_asciitilde: VKEY_OEM_5
    {0x00A1, 0xDD},  // XK_exclamdown: VKEY_OEM_6
    {0x00AD, 0xC0},  // XK_hyphen: VKEY_OEM_3
    {0x00B2, 0xDE},  // XK_twosuperior: VKEY_OEM_7
    {0x00B5, 0xDC},  // XK_mu: VKEY_OEM_5
    {0x00BB, 0x39},  // XK_guillemotright: VKEY_9
    {0x00BD, 0xDC},  // XK_onehalf: VKEY_OEM_5
    {0x00BF, 0xDD},  // XK_questiondown: VKEY_OEM_6
    {0x00DF, 0xDB},  // XK_ssharp: VKEY_OEM_4
    {0x00E5, 0xDD},  // XK_aring: VKEY_OEM_6
    {0x00EA, 0x33},  // XK_ecircumflex: VKEY_3
    {0x00EB, 0xBA},  // XK_ediaeresis: VKEY_OEM_1
    {0x00EC, 0xDD},  // XK_igrave: VKEY_OEM_6
    {0x00EE, 0xDD},  // XK_icircumflex: VKEY_OEM_6
    {0x00F1, 0xC0},  // XK_ntilde: VKEY_OEM_3
    {0x00F2, 0xC0},  // XK_ograve: VKEY_OEM_3
    {0x00F5, 0xDB},  // XK_otilde: VKEY_OEM_4
    {0x00F7, 0xDD},  // XK_division: VKEY_OEM_6
    {0x00FD, 0x37},  // XK_yacute: VKEY_7
    {0x00FE, 0xBD},  // XK_thorn: VKEY_OEM_MINUS
    {0x01A1, 0xDD},  // XK_ohorn: VKEY_OEM_6
    {0x01B0, 0xDB},  // XK_uhorn: VKEY_OEM_4
    {0x01B5, 0x32},  // XK_lcaron: VKEY_2
    {0x01B6, 0xDD},  // XK_zstroke: VKEY_OEM_6
    {0x01BB, 0x35},  // XK_tcaron: VKEY_5
    {0x01E6, 0xDE},  // XK_cacute: VKEY_OEM_7
    {0x01EC, 0x32},  // XK_ecaron: VKEY_2
    {0x01F2, 0xDC},  // XK_ncaron: VKEY_OEM_5
    {0x01F5, 0xDB},  // XK_odoubleacute: VKEY_OEM_4
    {0x01F8, 0x35},  // XK_rcaron: VKEY_5
    {0x01F9, 0xBA},  // XK_uring: VKEY_OEM_1
    {0x01FB, 0xDC},  // XK_udoubleacute: VKEY_OEM_5
    {0x01FE, 0xDE},  // XK_tcedilla: VKEY_OEM_7
    {0x0259, 0xC0},  // XK_schwa: VKEY_OEM_3
    {0x02B1, 0xDD},  // XK_hstroke: VKEY_OEM_6
    {0x02B9, 0xBA},  // XK_idotless: VKEY_OEM_1
    {0x02BB, 0xDD},  // XK_gbreve: VKEY_OEM_6
    {0x02E5, 0xC0},  // XK_cabovedot: VKEY_OEM_3
    {0x02F5, 0xDB},  // XK_gabovedot: VKEY_OEM_4
    {0x03B6, 0xBF},  // XK_lcedilla: VKEY_OEM_2
    {0x03BA, 0x57},  // XK_emacron: VKEY_W
    {0x03E0, 0xDF},  // XK_amacron: VKEY_OEM_8
    {0x03EF, 0xDD},  // XK_imacron: VKEY_OEM_6
    {0x03F1, 0xDB},  // XK_ncedilla: VKEY_OEM_4
    {0x03F3, 0xDC},  // XK_kcedilla: VKEY_OEM_5
};

constexpr struct MAP1 {
  uint32_t ch0;
  unsigned sc;
  uint8_t vk;
  bool operator()(const MAP1& m1, const MAP1& m2) const {
    if (m1.ch0 == m2.ch0)
      return m1.sc < m2.sc;
    return m1.ch0 < m2.ch0;
  }
} map1[] = {
    {0x0021, 0x0A, 0x31},  // XK_exclam+AE01: VKEY_1
    {0x0021, 0x11, 0x38},  // XK_exclam+AE08: VKEY_8
    {0x0021, 0x3D, 0xDF},  // XK_exclam+AB10: VKEY_OEM_8
    {0x0022, 0x0B, 0x32},  // XK_quotedbl+AE02: VKEY_2
    {0x0022, 0x0C, 0x33},  // XK_quotedbl+AE03: VKEY_3
    {0x0023, 0x31, 0xDE},  // XK_numbersign+TLDE: VKEY_OEM_7
    {0x0024, 0x23, 0xBA},  // XK_dollar+AD12: VKEY_OEM_1
    {0x0024, 0x33, 0xDF},  // XK_dollar+BKSL: VKEY_OEM_8
    {0x0027, 0x0D, 0x34},  // XK_quoteright+AE04: VKEY_4
    {0x0027, 0x18, 0xDE},  // XK_quoteright+AD01: VKEY_OEM_7
    {0x0027, 0x19, 0x57},  // XK_quoteright+AD02: VKEY_W
    {0x0027, 0x23, 0xBA},  // XK_quoteright+AD12: VKEY_OEM_1
    {0x0027, 0x3D, 0xDE},  // XK_quoteright+AB10: VKEY_OEM_7
    {0x0028, 0x0E, 0x35},  // XK_parenleft+AE05: VKEY_5
    {0x0028, 0x12, 0x39},  // XK_parenleft+AE09: VKEY_9
    {0x0028, 0x33, 0xDC},  // XK_parenleft+BKSL: VKEY_OEM_5
    {0x0029, 0x13, 0x30},  // XK_parenright+AE10: VKEY_0
    {0x0029, 0x14, 0xDB},  // XK_parenright+AE11: VKEY_OEM_4
    {0x0029, 0x23, 0xDD},  // XK_parenright+AD12: VKEY_OEM_6
    {0x002A, 0x23, 0xBA},  // XK_asterisk+AD12: VKEY_OEM_1
    {0x002A, 0x33, 0xDC},  // XK_asterisk+BKSL: VKEY_OEM_5
    {0x002B, 0x0A, 0x31},  // XK_plus+AE01: VKEY_1
    {0x002B, 0x15, 0xBB},  // XK_plus+AE12: VKEY_OEM_PLUS
    {0x002B, 0x22, 0xBB},  // XK_plus+AD11: VKEY_OEM_PLUS
    {0x002B, 0x23, 0xBB},  // XK_plus+AD12: VKEY_OEM_PLUS
    {0x002B, 0x2F, 0xBB},  // XK_plus+AC10: VKEY_OEM_PLUS
    {0x002B, 0x33, 0xBF},  // XK_plus+BKSL: VKEY_OEM_2
    {0x002C, 0x0C, 0x33},  // XK_comma+AE03: VKEY_3
    {0x002C, 0x0E, 0x35},  // XK_comma+AE05: VKEY_5
    {0x002C, 0x0F, 0x36},  // XK_comma+AE06: VKEY_6
    {0x002C, 0x12, 0x39},  // XK_comma+AE09: VKEY_9
    {0x002C, 0x19, 0xBC},  // XK_comma+AD02: VKEY_OEM_COMMA
    {0x002C, 0x30, 0xDE},  // XK_comma+AC11: VKEY_OEM_7
    {0x002C, 0x37, 0xBC},  // XK_comma+AB04: VKEY_OEM_COMMA
    {0x002C, 0x3A, 0xBC},  // XK_comma+AB07: VKEY_OEM_COMMA
    {0x002C, 0x3B, 0xBC},  // XK_comma+AB08: VKEY_OEM_COMMA
    {0x002D, 0x0B, 0x32},  // XK_minus+AE02: VKEY_2
    {0x002D, 0x0F, 0x36},  // XK_minus+AE06: VKEY_6
    {0x002D, 0x14, 0xBD},  // XK_minus+AE11: VKEY_OEM_MINUS
    {0x002D, 0x26, 0xBD},  // XK_minus+AC01: VKEY_OEM_MINUS
    {0x002D, 0x30, 0xBD},  // XK_minus+AC11: VKEY_OEM_MINUS
    {0x002E, 0x10, 0x37},  // XK_period+AE07: VKEY_7
    {0x002E, 0x11, 0x38},  // XK_period+AE08: VKEY_8
    {0x002E, 0x1A, 0xBE},  // XK_period+AD03: VKEY_OEM_PERIOD
    {0x002E, 0x1B, 0xBE},  // XK_period+AD04: VKEY_OEM_PERIOD
    {0x002E, 0x20, 0xBE},  // XK_period+AD09: VKEY_OEM_PERIOD
    {0x002E, 0x30, 0xDE},  // XK_period+AC11: VKEY_OEM_7
    {0x002E, 0x3C, 0xBE},  // XK_period+AB09: VKEY_OEM_PERIOD
    {0x002E, 0x3D, 0xBF},  // XK_period+AB10: VKEY_OEM_2
    {0x002F, 0x14, 0xDB},  // XK_slash+AE11: VKEY_OEM_4
    {0x002F, 0x18, 0x51},  // XK_slash+AD01: VKEY_Q
    {0x002F, 0x22, 0xBF},  // XK_slash+AD11: VKEY_OEM_2
    {0x002F, 0x31, 0xDE},  // XK_slash+TLDE: VKEY_OEM_7
    {0x002F, 0x33, 0xDC},  // XK_slash+BKSL: VKEY_OEM_5
    {0x002F, 0x3D, 0xBF},  // XK_slash+AB10: VKEY_OEM_2
    {0x003A, 0x0A, 0x31},  // XK_colon+AE01: VKEY_1
    {0x003A, 0x0E, 0x35},  // XK_colon+AE05: VKEY_5
    {0x003A, 0x0F, 0x36},  // XK_colon+AE06: VKEY_6
    {0x003A, 0x3C, 0xBF},  // XK_colon+AB09: VKEY_OEM_2
    {0x003B, 0x0D, 0x34},  // XK_semicolon+AE04: VKEY_4
    {0x003B, 0x11, 0x38},  // XK_semicolon+AE08: VKEY_8
    {0x003B, 0x18, 0xBA},  // XK_semicolon+AD01: VKEY_OEM_1
    {0x003B, 0x22, 0xBA},  // XK_semicolon+AD11: VKEY_OEM_1
    {0x003B, 0x23, 0xDD},  // XK_semicolon+AD12: VKEY_OEM_6
    {0x003B, 0x2F, 0xBA},  // XK_semicolon+AC10: VKEY_OEM_1
    {0x003B, 0x31, 0xC0},  // XK_semicolon+TLDE: VKEY_OEM_3
    {0x003B, 0x34, 0xBA},  // XK_semicolon+AB01: VKEY_OEM_1
    {0x003B, 0x3B, 0xBE},  // XK_semicolon+AB08: VKEY_OEM_PERIOD
    {0x003B, 0x3D, 0xBF},  // XK_semicolon+AB10: VKEY_OEM_2
    {0x003D, 0x11, 0x38},  // XK_equal+AE08: VKEY_8
    {0x003D, 0x15, 0xBB},  // XK_equal+AE12: VKEY_OEM_PLUS
    {0x003D, 0x23, 0xBB},  // XK_equal+AD12: VKEY_OEM_PLUS
    {0x003F, 0x0B, 0x32},  // XK_question+AE02: VKEY_2
    {0x003F, 0x10, 0x37},  // XK_question+AE07: VKEY_7
    {0x003F, 0x11, 0x38},  // XK_question+AE08: VKEY_8
    {0x003F, 0x14, 0xBB},  // XK_question+AE11: VKEY_OEM_PLUS
    {0x0040, 0x23, 0xDD},  // XK_at+AD12: VKEY_OEM_6
    {0x0040, 0x31, 0xDE},  // XK_at+TLDE: VKEY_OEM_7
    {0x005B, 0x0A, 0xDB},  // XK_bracketleft+AE01: VKEY_OEM_4
    {0x005B, 0x14, 0xDB},  // XK_bracketleft+AE11: VKEY_OEM_4
    {0x005B, 0x22, 0xDB},  // XK_bracketleft+AD11: VKEY_OEM_4
    {0x005B, 0x23, 0xDD},  // XK_bracketleft+AD12: VKEY_OEM_6
    {0x005B, 0x30, 0xDE},  // XK_bracketleft+AC11: VKEY_OEM_7
    {0x005C, 0x15, 0xDB},  // XK_backslash+AE12: VKEY_OEM_4
    {0x005D, 0x0B, 0xDD},  // XK_bracketright+AE02: VKEY_OEM_6
    {0x005D, 0x15, 0xDD},  // XK_bracketright+AE12: VKEY_OEM_6
    {0x005D, 0x22, 0xDB},  // XK_bracketright+AD11: VKEY_OEM_4
    {0x005D, 0x23, 0xDD},  // XK_bracketright+AD12: VKEY_OEM_6
    {0x005D, 0x31, 0xC0},  // XK_bracketright+TLDE: VKEY_OEM_3
    {0x005D, 0x33, 0xDC},  // XK_bracketright+BKSL: VKEY_OEM_5
    {0x005F, 0x11, 0x38},  // XK_underscore+AE08: VKEY_8
    {0x005F, 0x14, 0xBD},  // XK_underscore+AE11: VKEY_OEM_MINUS
    {0x00A7, 0x0D, 0x34},  // XK_section+AE04: VKEY_4
    {0x00A7, 0x0F, 0x36},  // XK_section+AE06: VKEY_6
    {0x00A7, 0x30, 0xDE},  // XK_section+AC11: VKEY_OEM_7
    {0x00AB, 0x11, 0x38},  // XK_guillemotleft+AE08: VKEY_8
    {0x00AB, 0x15, 0xDD},  // XK_guillemotleft+AE12: VKEY_OEM_6
    {0x00B0, 0x15, 0xBF},  // XK_degree+AE12: VKEY_OEM_2
    {0x00B0, 0x31, 0xDE},  // XK_degree+TLDE: VKEY_OEM_7
    {0x00BA, 0x30, 0xDE},  // XK_masculine+AC11: VKEY_OEM_7
    {0x00BA, 0x31, 0xDC},  // XK_masculine+TLDE: VKEY_OEM_5
    {0x00E0, 0x13, 0x30},  // XK_agrave+AE10: VKEY_0
    {0x00E0, 0x33, 0xDC},  // XK_agrave+BKSL: VKEY_OEM_5
    {0x00E1, 0x11, 0x38},  // XK_aacute+AE08: VKEY_8
    {0x00E1, 0x30, 0xDE},  // XK_aacute+AC11: VKEY_OEM_7
    {0x00E2, 0x0B, 0x32},  // XK_acircumflex+AE02: VKEY_2
    {0x00E2, 0x33, 0xDC},  // XK_acircumflex+BKSL: VKEY_OEM_5
    {0x00E4, 0x23, 0xDD},  // XK_adiaeresis+AD12: VKEY_OEM_6
    {0x00E6, 0x2F, 0xC0},  // XK_ae+AC10: VKEY_OEM_3
    {0x00E6, 0x30, 0xDE},  // XK_ae+AC11: VKEY_OEM_7
    {0x00E7, 0x12, 0x39},  // XK_ccedilla+AE09: VKEY_9
    {0x00E7, 0x22, 0xDB},  // XK_ccedilla+AD11: VKEY_OEM_4
    {0x00E7, 0x23, 0xDD},  // XK_ccedilla+AD12: VKEY_OEM_6
    {0x00E7, 0x30, 0xDE},  // XK_ccedilla+AC11: VKEY_OEM_7
    {0x00E7, 0x33, 0xBF},  // XK_ccedilla+BKSL: VKEY_OEM_2
    {0x00E7, 0x3B, 0xBC},  // XK_ccedilla+AB08: VKEY_OEM_COMMA
    {0x00E8, 0x10, 0x37},  // XK_egrave+AE07: VKEY_7
    {0x00E8, 0x22, 0xBA},  // XK_egrave+AD11: VKEY_OEM_1
    {0x00E8, 0x30, 0xC0},  // XK_egrave+AC11: VKEY_OEM_3
    {0x00E9, 0x0B, 0x32},  // XK_eacute+AE02: VKEY_2
    {0x00E9, 0x13, 0x30},  // XK_eacute+AE10: VKEY_0
    {0x00E9, 0x3D, 0xBF},  // XK_eacute+AB10: VKEY_OEM_2
    {0x00ED, 0x12, 0x39},  // XK_iacute+AE09: VKEY_9
    {0x00ED, 0x31, 0x30},  // XK_iacute+TLDE: VKEY_0
    {0x00F0, 0x22, 0xDD},  // XK_eth+AD11: VKEY_OEM_6
    {0x00F0, 0x23, 0xBA},  // XK_eth+AD12: VKEY_OEM_1
    {0x00F3, 0x15, 0xBB},  // XK_oacute+AE12: VKEY_OEM_PLUS
    {0x00F3, 0x33, 0xDC},  // XK_oacute+BKSL: VKEY_OEM_5
    {0x00F4, 0x0D, 0x34},  // XK_ocircumflex+AE04: VKEY_4
    {0x00F4, 0x2F, 0xBA},  // XK_ocircumflex+AC10: VKEY_OEM_1
    {0x00F6, 0x13, 0xC0},  // XK_odiaeresis+AE10: VKEY_OEM_3
    {0x00F6, 0x14, 0xBB},  // XK_odiaeresis+AE11: VKEY_OEM_PLUS
    {0x00F6, 0x22, 0xDB},  // XK_odiaeresis+AD11: VKEY_OEM_4
    {0x00F8, 0x2F, 0xC0},  // XK_oslash+AC10: VKEY_OEM_3
    {0x00F8, 0x30, 0xDE},  // XK_oslash+AC11: VKEY_OEM_7
    {0x00F9, 0x30, 0xC0},  // XK_ugrave+AC11: VKEY_OEM_3
    {0x00F9, 0x33, 0xBF},  // XK_ugrave+BKSL: VKEY_OEM_2
    {0x00FA, 0x22, 0xDB},  // XK_uacute+AD11: VKEY_OEM_4
    {0x00FA, 0x23, 0xDD},  // XK_uacute+AD12: VKEY_OEM_6
    {0x00FC, 0x19, 0x57},  // XK_udiaeresis+AD02: VKEY_W
    {0x01B1, 0x0A, 0x31},  // XK_aogonek+AE01: VKEY_1
    {0x01B1, 0x18, 0x51},  // XK_aogonek+AD01: VKEY_Q
    {0x01B1, 0x30, 0xDE},  // XK_aogonek+AC11: VKEY_OEM_7
    {0x01B3, 0x2F, 0xBA},  // XK_lstroke+AC10: VKEY_OEM_1
    {0x01B3, 0x33, 0xBF},  // XK_lstroke+BKSL: VKEY_OEM_2
    {0x01B9, 0x0C, 0x33},  // XK_scaron+AE03: VKEY_3
    {0x01B9, 0x0F, 0x36},  // XK_scaron+AE06: VKEY_6
    {0x01B9, 0x22, 0xDB},  // XK_scaron+AD11: VKEY_OEM_4
    {0x01B9, 0x26, 0xBA},  // XK_scaron+AC01: VKEY_OEM_1
    {0x01B9, 0x29, 0x46},  // XK_scaron+AC04: VKEY_F
    {0x01B9, 0x3C, 0xBE},  // XK_scaron+AB09: VKEY_OEM_PERIOD
    {0x01BA, 0x2F, 0xBA},  // XK_scedilla+AC10: VKEY_OEM_1
    {0x01BA, 0x3C, 0xBE},  // XK_scedilla+AB09: VKEY_OEM_PERIOD
    {0x01BE, 0x0F, 0x36},  // XK_zcaron+AE06: VKEY_6
    {0x01BE, 0x15, 0xBB},  // XK_zcaron+AE12: VKEY_OEM_PLUS
    {0x01BE, 0x19, 0x57},  // XK_zcaron+AD02: VKEY_W
    {0x01BE, 0x22, 0x59},  // XK_zcaron+AD11: VKEY_Y
    {0x01BE, 0x33, 0xDC},  // XK_zcaron+BKSL: VKEY_OEM_5
    {0x01BF, 0x22, 0xDB},  // XK_zabovedot+AD11: VKEY_OEM_4
    {0x01BF, 0x33, 0xDC},  // XK_zabovedot+BKSL: VKEY_OEM_5
    {0x01E3, 0x0A, 0x31},  // XK_abreve+AE01: VKEY_1
    {0x01E3, 0x22, 0xDB},  // XK_abreve+AD11: VKEY_OEM_4
    {0x01E8, 0x0B, 0x32},  // XK_ccaron+AE02: VKEY_2
    {0x01E8, 0x0D, 0x34},  // XK_ccaron+AE04: VKEY_4
    {0x01E8, 0x21, 0x58},  // XK_ccaron+AD10: VKEY_X
    {0x01E8, 0x2F, 0xBA},  // XK_ccaron+AC10: VKEY_OEM_1
    {0x01E8, 0x3B, 0xBC},  // XK_ccaron+AB08: VKEY_OEM_COMMA
    {0x01EA, 0x0C, 0x33},  // XK_eogonek+AE03: VKEY_3
    {0x01F0, 0x13, 0x30},  // XK_dstroke+AE10: VKEY_0
    {0x01F0, 0x23, 0xDD},  // XK_dstroke+AD12: VKEY_OEM_6
    {0x03E7, 0x0E, 0x35},  // XK_iogonek+AE05: VKEY_5
    {0x03EC, 0x0D, 0x34},  // XK_eabovedot+AE04: VKEY_4
    {0x03EC, 0x30, 0xDE},  // XK_eabovedot+AC11: VKEY_OEM_7
    {0x03F9, 0x10, 0x37},  // XK_uogonek+AE07: VKEY_7
    {0x03FE, 0x11, 0x38},  // XK_umacron+AE08: VKEY_8
    {0x03FE, 0x18, 0x51},  // XK_umacron+AD01: VKEY_Q
    {0x03FE, 0x35, 0x58},  // XK_umacron+AB02: VKEY_X
};

constexpr struct MAP2 {
  uint32_t ch0;
  unsigned sc;
  uint32_t ch1;
  uint8_t vk;
  bool operator()(const MAP2& m1, const MAP2& m2) const {
    if (m1.ch0 == m2.ch0 && m1.sc == m2.sc)
      return m1.ch1 < m2.ch1;
    if (m1.ch0 == m2.ch0)
      return m1.sc < m2.sc;
    return m1.ch0 < m2.ch0;
  }
} map2[] = {
    {0x0023, 0x33, 0x0027,
     0xBF},  // XK_numbersign+BKSL+XK_quoteright: VKEY_OEM_2
    {0x0027, 0x30, 0x0022, 0xDE},  // XK_quoteright+AC11+XK_quotedbl: VKEY_OEM_7
    {0x0027, 0x31, 0x0022, 0xC0},  // XK_quoteright+TLDE+XK_quotedbl: VKEY_OEM_3
    {0x0027, 0x31, 0x00B7,
     0xDC},  // XK_quoteright+TLDE+XK_periodcentered: VKEY_OEM_5
    {0x0027, 0x33, 0x0000, 0xDC},  // XK_quoteright+BKSL+NoSymbol: VKEY_OEM_5
    {0x002D, 0x3D, 0x003D, 0xBD},  // XK_minus+AB10+XK_equal: VKEY_OEM_MINUS
    {0x002F, 0x0C, 0x0033, 0x33},  // XK_slash+AE03+XK_3: VKEY_3
    {0x002F, 0x0C, 0x003F, 0xBF},  // XK_slash+AE03+XK_question: VKEY_OEM_2
    {0x002F, 0x13, 0x0030, 0x30},  // XK_slash+AE10+XK_0: VKEY_0
    {0x002F, 0x13, 0x003F, 0xBF},  // XK_slash+AE10+XK_question: VKEY_OEM_2
    {0x003D, 0x3D, 0x0025, 0xDF},  // XK_equal+AB10+XK_percent: VKEY_OEM_8
    {0x003D, 0x3D, 0x002B, 0xBB},  // XK_equal+AB10+XK_plus: VKEY_OEM_PLUS
    {0x005C, 0x33, 0x007C, 0xDC},  // XK_backslash+BKSL+XK_bar: VKEY_OEM_5
    {0x0060, 0x31, 0x0000, 0xC0},  // XK_quoteleft+TLDE+NoSymbol: VKEY_OEM_3
    {0x0060, 0x31, 0x00AC, 0xDF},  // XK_quoteleft+TLDE+XK_notsign: VKEY_OEM_8
    {0x00A7, 0x31, 0x00B0, 0xBF},  // XK_section+TLDE+XK_degree: VKEY_OEM_2
    {0x00A7, 0x31, 0x00BD, 0xDC},  // XK_section+TLDE+XK_onehalf: VKEY_OEM_5
    {0x00E0, 0x30, 0x00B0, 0xDE},  // XK_agrave+AC11+XK_degree: VKEY_OEM_7
    {0x00E0, 0x30, 0x00E4, 0xDC},  // XK_agrave+AC11+XK_adiaeresis: VKEY_OEM_5
    {0x00E4, 0x30, 0x00E0, 0xDC},  // XK_adiaeresis+AC11+XK_agrave: VKEY_OEM_5
    {0x00E9, 0x2F, 0x00C9, 0xBA},  // XK_eacute+AC10+XK_Eacute: VKEY_OEM_1
    {0x00E9, 0x2F, 0x00F6, 0xDE},  // XK_eacute+AC10+XK_odiaeresis: VKEY_OEM_7
    {0x00F6, 0x2F, 0x00E9, 0xDE},  // XK_odiaeresis+AC10+XK_eacute: VKEY_OEM_7
    {0x00FC, 0x22, 0x00E8, 0xBA},  // XK_udiaeresis+AD11+XK_egrave: VKEY_OEM_1
};

constexpr struct MAP3 {
  uint32_t ch0;
  unsigned sc;
  uint32_t ch1;
  uint32_t ch2;
  uint8_t vk;
  bool operator()(const MAP3& m1, const MAP3& m2) const {
    if (m1.ch0 == m2.ch0 && m1.sc == m2.sc && m1.ch1 == m2.ch1)
      return m1.ch2 < m2.ch2;
    if (m1.ch0 == m2.ch0 && m1.sc == m2.sc)
      return m1.ch1 < m2.ch1;
    if (m1.ch0 == m2.ch0)
      return m1.sc < m2.sc;
    return m1.ch0 < m2.ch0;
  }
} map3[] = {
    {0x0023, 0x33, 0x007E, 0x0000,
     0xDE},  // XK_numbersign+BKSL+XK_asciitilde+NoSymbol: VKEY_OEM_7
    {0x0027, 0x14, 0x003F, 0x0000,
     0xDB},  // XK_quoteright+AE11+XK_question+NoSymbol: VKEY_OEM_4
    {0x0027, 0x14, 0x003F, 0x00DD,
     0xDB},  // XK_quoteright+AE11+XK_question+XK_Yacute: VKEY_OEM_4
    {0x0027, 0x15, 0x002A, 0x0000,
     0xBB},  // XK_quoteright+AE12+XK_asterisk+NoSymbol: VKEY_OEM_PLUS
    {0x0027, 0x30, 0x0040, 0x0000,
     0xC0},  // XK_quoteright+AC11+XK_at+NoSymbol: VKEY_OEM_3
    {0x0027, 0x33, 0x002A, 0x0000,
     0xBF},  // XK_quoteright+BKSL+XK_asterisk+NoSymbol: VKEY_OEM_2
    {0x0027, 0x33, 0x002A, 0x00BD,
     0xDC},  // XK_quoteright+BKSL+XK_asterisk+XK_onehalf: VKEY_OEM_5
    {0x0027, 0x33, 0x002A, 0x01A3,
     0xBF},  // XK_quoteright+BKSL+XK_asterisk+XK_Lstroke: VKEY_OEM_2
    {0x0027, 0x34, 0x0022, 0x0000,
     0x5A},  // XK_quoteright+AB01+XK_quotedbl+NoSymbol: VKEY_Z
    {0x0027, 0x34, 0x0022, 0x01D8,
     0xDE},  // XK_quoteright+AB01+XK_quotedbl+XK_Rcaron: VKEY_OEM_7
    {0x002B, 0x14, 0x003F, 0x0000,
     0xBB},  // XK_plus+AE11+XK_question+NoSymbol: VKEY_OEM_PLUS
    {0x002B, 0x14, 0x003F, 0x005C,
     0xBD},  // XK_plus+AE11+XK_question+XK_backslash: VKEY_OEM_MINUS
    {0x002B, 0x14, 0x003F, 0x01F5,
     0xBB},  // XK_plus+AE11+XK_question+XK_odoubleacute: VKEY_OEM_PLUS
    {0x002D, 0x15, 0x005F, 0x0000,
     0xBD},  // XK_minus+AE12+XK_underscore+NoSymbol: VKEY_OEM_MINUS
    {0x002D, 0x15, 0x005F, 0x03B3,
     0xDB},  // XK_minus+AE12+XK_underscore+XK_rcedilla: VKEY_OEM_4
    {0x002D, 0x3D, 0x005F, 0x0000,
     0xBD},  // XK_minus+AB10+XK_underscore+NoSymbol: VKEY_OEM_MINUS
    {0x002D, 0x3D, 0x005F, 0x002A,
     0xBD},  // XK_minus+AB10+XK_underscore+XK_asterisk: VKEY_OEM_MINUS
    {0x002D, 0x3D, 0x005F, 0x002F,
     0xBF},  // XK_minus+AB10+XK_underscore+XK_slash: VKEY_OEM_2
    {0x002D, 0x3D, 0x005F, 0x006E,
     0xBD},  // XK_minus+AB10+XK_underscore+XK_n: VKEY_OEM_MINUS
    {0x003D, 0x14, 0x0025, 0x0000,
     0xBB},  // XK_equal+AE11+XK_percent+NoSymbol: VKEY_OEM_PLUS
    {0x003D, 0x14, 0x0025, 0x002D,
     0xBD},  // XK_equal+AE11+XK_percent+XK_minus: VKEY_OEM_MINUS
    {0x005C, 0x31, 0x007C, 0x0031,
     0xDC},  // XK_backslash+TLDE+XK_bar+XK_1: VKEY_OEM_5
    {0x005C, 0x31, 0x007C, 0x03D1,
     0xC0},  // XK_backslash+TLDE+XK_bar+XK_Ncedilla: VKEY_OEM_3
    {0x005C, 0x33, 0x002F, 0x0000,
     0xDC},  // XK_backslash+BKSL+XK_slash+NoSymbol: VKEY_OEM_5
    {0x005C, 0x33, 0x002F, 0x01A3,
     0xDE},  // XK_backslash+BKSL+XK_slash+XK_Lstroke: VKEY_OEM_7
    {0x0060, 0x31, 0x007E, 0x0000,
     0xC0},  // XK_quoteleft+TLDE+XK_asciitilde+NoSymbol: VKEY_OEM_3
    {0x0060, 0x31, 0x007E, 0x0031,
     0xC0},  // XK_quoteleft+TLDE+XK_asciitilde+XK_1: VKEY_OEM_3
    {0x0060, 0x31, 0x007E, 0x003B,
     0xC0},  // XK_quoteleft+TLDE+XK_asciitilde+XK_semicolon: VKEY_OEM_3
    {0x0060, 0x31, 0x007E, 0x0060,
     0xC0},  // XK_quoteleft+TLDE+XK_asciitilde+XK_quoteleft: VKEY_OEM_3
    {0x0060, 0x31, 0x007E, 0x00BF,
     0xC0},  // XK_quoteleft+TLDE+XK_asciitilde+XK_questiondown: VKEY_OEM_3
    {0x0060, 0x31, 0x007E, 0x01F5,
     0xC0},  // XK_quoteleft+TLDE+XK_asciitilde+XK_odoubleacute: VKEY_OEM_3
    {0x00E4, 0x30, 0x00C4, 0x0000,
     0xDE},  // XK_adiaeresis+AC11+XK_Adiaeresis+NoSymbol: VKEY_OEM_7
    {0x00E4, 0x30, 0x00C4, 0x01A6,
     0xDE},  // XK_adiaeresis+AC11+XK_Adiaeresis+XK_Sacute: VKEY_OEM_7
    {0x00E4, 0x30, 0x00C4, 0x01F8,
     0xDE},  // XK_adiaeresis+AC11+XK_Adiaeresis+XK_rcaron: VKEY_OEM_7
    {0x00E7, 0x2F, 0x00C7, 0x0000,
     0xBA},  // XK_ccedilla+AC10+XK_Ccedilla+NoSymbol: VKEY_OEM_1
    {0x00E7, 0x2F, 0x00C7, 0x00DE,
     0xC0},  // XK_ccedilla+AC10+XK_Ccedilla+XK_Thorn: VKEY_OEM_3
    {0x00F6, 0x2F, 0x00D6, 0x0000,
     0xC0},  // XK_odiaeresis+AC10+XK_Odiaeresis+NoSymbol: VKEY_OEM_3
    {0x00F6, 0x2F, 0x00D6, 0x01DE,
     0xC0},  // XK_odiaeresis+AC10+XK_Odiaeresis+XK_Tcedilla: VKEY_OEM_3
    {0x00FC, 0x14, 0x00DC, 0x0000,
     0xBF},  // XK_udiaeresis+AE11+XK_Udiaeresis+NoSymbol: VKEY_OEM_2
    {0x00FC, 0x22, 0x00DC, 0x0000,
     0xBA},  // XK_udiaeresis+AD11+XK_Udiaeresis+NoSymbol: VKEY_OEM_1
    {0x00FC, 0x22, 0x00DC, 0x01A3,
     0xC0},  // XK_udiaeresis+AD11+XK_Udiaeresis+XK_Lstroke: VKEY_OEM_3
    {0x01EA, 0x3D, 0x01CA, 0x0000,
     0xBD},  // XK_eogonek+AB10+XK_Eogonek+NoSymbol: VKEY_OEM_MINUS
    {0x01EA, 0x3D, 0x01CA, 0x006E,
     0xBF},  // XK_eogonek+AB10+XK_Eogonek+XK_n: VKEY_OEM_2
    {0x03E7, 0x22, 0x03C7, 0x0000,
     0xDB},  // XK_iogonek+AD11+XK_Iogonek+NoSymbol: VKEY_OEM_4
    {0x03F9, 0x2F, 0x03D9, 0x0000,
     0xC0},  // XK_uogonek+AC10+XK_Uogonek+NoSymbol: VKEY_OEM_3
    {0x03F9, 0x2F, 0x03D9, 0x01DE,
     0xBA},  // XK_uogonek+AC10+XK_Uogonek+XK_Tcedilla: VKEY_OEM_1
};

template <class T_MAP, size_t n>
KeyboardCode FindVK(const T_MAP& key, const T_MAP (&map)[n]) {
  T_MAP comp = {0};
  const T_MAP* p = std::ranges::lower_bound(map, key, comp);
  if (p != std::end(map) && !comp(*p, key) && !comp(key, *p)) {
    return static_cast<KeyboardCode>(p->vk);
  }
  return VKEY_UNKNOWN;
}

// Check for TTY function keys or space key which should always be mapped
// based on KeySym, and never fall back to MAP0~MAP3, since some layouts
// generate them by applying the Control/AltGr modifier to some other key.
// e.g. in de(neo), AltGr+V generates XK_Enter.
bool IsTtyFunctionOrSpaceKey(uint32_t keysym) {
  uint32_t keysyms[] = {XK_BackSpace, XK_Tab,    XK_Linefeed,    XK_Clear,
                        XK_Return,    XK_Pause,  XK_Scroll_Lock, XK_Sys_Req,
                        XK_Escape,    XK_Delete, XK_space};

  for (unsigned long i : keysyms) {
    if (i == keysym)
      return true;
  }
  return false;
}

uint32_t TranslateKey(uint32_t keycode, uint32_t modifiers) {
  return x11::Connection::Get()->KeycodeToKeysym(
      static_cast<x11::KeyCode>(keycode), modifiers);
}

void GetKeycodeAndModifiers(const x11::Event& event,
                            uint32_t* keycode,
                            uint32_t* modifiers) {
  if (auto* dev = event.As<x11::Input::DeviceEvent>()) {
    *keycode = dev->detail;
    *modifiers = GetXI2StateFromEvent(*dev);
  } else if (auto* key = event.As<x11::KeyEvent>()) {
    *keycode = static_cast<uint32_t>(key->detail);
    *modifiers = static_cast<uint32_t>(key->state);
  }
}

bool IsKeypadKey(uint32_t keysym) {
  return keysym >= XK_KP_Space && keysym <= XK_KP_Equal;
}

bool IsPrivateKeypadKey(uint32_t keysym) {
  return keysym >= 0x11000000 && keysym <= 0x1100FFFF;
}

bool IsCursorKey(uint32_t keysym) {
  return keysym >= XK_Home && keysym < XK_Select;
}

bool IsPFKey(uint32_t keysym) {
  return keysym >= XK_KP_F1 && keysym <= XK_KP_F4;
}

bool IsFunctionKey(uint32_t keysym) {
  return keysym >= XK_F1 && keysym <= XK_F35;
}

bool IsModifierKey(uint32_t keysym) {
  return ((keysym >= XK_Shift_L) && (keysym <= XK_Hyper_R)) ||
         ((keysym >= XK_ISO_Lock) && (keysym <= XK_ISO_Level5_Lock)) ||
         keysym == XK_Mode_switch || keysym == XK_Num_Lock;
}

}  // namespace

// Get an ui::KeyboardCode from an X keyevent
KeyboardCode KeyboardCodeFromXKeyEvent(const x11::Event& xev) {
  // Gets correct VKEY code from XEvent is performed as the following steps:
  // 1. Gets the keysym without modifier states.
  // 2. For [a-z] & [0-9] cases, returns the VKEY code accordingly.
  // 3. Find keysym in map0.
  // 4. If not found, fallback to find keysym + hardware_code in map1.
  // 5. If not found, fallback to find keysym + keysym_shift + hardware_code
  //    in map2.
  // 6. If not found, fallback to find keysym + keysym_shift + keysym_altgr +
  //    hardware_code in map3.
  // 7. If not found, fallback to find in KeyboardCodeFromXKeysym(), which
  //    mainly for non-letter keys.
  // 8. If not found, fallback to find with the hardware code in US layout.

  uint32_t keysym = 0;
  uint32_t xkeycode = 0;
  uint32_t modifiers = 0;
  GetKeycodeAndModifiers(xev, &xkeycode, &modifiers);
  KeyboardCode keycode = VKEY_UNKNOWN;
  keysym = TranslateKey(xkeycode, modifiers);
  if (IsKeypadKey(keysym) || IsPrivateKeypadKey(keysym) ||
      IsCursorKey(keysym) || IsPFKey(keysym) || IsFunctionKey(keysym) ||
      IsModifierKey(keysym) || IsTtyFunctionOrSpaceKey(keysym)) {
    return KeyboardCodeFromXKeysym(keysym);
  }

  // If |xkey| has modifiers set, other than NumLock, then determine the
  // un-modified KeySym and use that to map, so that e.g. Ctrl+D correctly
  // generates VKEY_D.
  if (modifiers & 0xFF & ~static_cast<int>(x11::KeyButMask::Mod2)) {
    modifiers &= (~0xFF | static_cast<int>(x11::KeyButMask::Mod2));
    keysym = TranslateKey(xkeycode, modifiers);
  }

  // [a-z] cases.
  if (keysym >= XK_a && keysym <= XK_z)
    return static_cast<KeyboardCode>(VKEY_A + keysym - XK_a);

  // [0-9] cases.
  if (keysym >= XK_0 && keysym <= XK_9)
    return static_cast<KeyboardCode>(VKEY_0 + keysym - XK_0);

  if (!IsKeypadKey(keysym) && !IsPrivateKeypadKey(keysym) &&
      !IsCursorKey(keysym) && !IsPFKey(keysym) && !IsFunctionKey(keysym) &&
      !IsModifierKey(keysym)) {
    MAP0 key0 = {keysym & 0xFFFF, 0};
    keycode = FindVK(key0, map0);
    if (keycode != VKEY_UNKNOWN)
      return keycode;

    MAP1 key1 = {keysym & 0xFFFF, xkeycode, 0};
    keycode = FindVK(key1, map1);
    if (keycode != VKEY_UNKNOWN)
      return keycode;

    uint32_t keysym_shift{};
    modifiers |= static_cast<int>(x11::KeyButMask::Shift);
    keysym_shift = TranslateKey(xkeycode, modifiers);
    MAP2 key2 = {keysym & 0xFFFF, xkeycode, keysym_shift & 0xFFFF, 0};
    keycode = FindVK(key2, map2);
    if (keycode != VKEY_UNKNOWN)
      return keycode;

    uint32_t keysym_altgr{};
    modifiers &= ~static_cast<int>(x11::KeyButMask::Shift);
    modifiers |= static_cast<int>(x11::KeyButMask::Mod1);
    keysym_altgr = TranslateKey(xkeycode, modifiers);
    MAP3 key3 = {keysym & 0xFFFF, xkeycode, keysym_shift & 0xFFFF,
                 keysym_altgr & 0xFFFF, 0};
    keycode = FindVK(key3, map3);
    if (keycode != VKEY_UNKNOWN)
      return keycode;

    // On Linux some keys has AltGr char but not on Windows.
    // So if cannot find VKEY with (ch0+sc+ch1+ch2) in map3, tries to fallback
    // to just find VKEY with (ch0+sc+ch1). This is the best we could do.
    MAP3 key4 = {keysym & 0xFFFF, xkeycode, keysym_shift & 0xFFFF, 0, 0};
    const MAP3* p = std::ranges::lower_bound(map3, key4, MAP3());
    if (p != std::end(map3) && p->ch0 == key4.ch0 && p->sc == key4.sc &&
        p->ch1 == key4.ch1) {
      return static_cast<KeyboardCode>(p->vk);
    }
  }

  keycode = KeyboardCodeFromXKeysym(keysym);
  if (keycode == VKEY_UNKNOWN && !IsModifierKey(keysym)) {
    // Modifier keys should not fall back to the hardware-keycode-based US
    // layout.  See crbug.com/402320
    keycode = DefaultKeyboardCodeFromHardwareKeycode(xkeycode);
  }

  return keycode;
}

KeyboardCode KeyboardCodeFromXKeysym(unsigned int keysym) {
  // TODO(sad): Have |keysym| go through the X map list?

  switch (keysym) {
    case XK_BackSpace:
      return VKEY_BACK;
    case XK_Delete:
    case XK_KP_Delete:
      return VKEY_DELETE;
    case XK_Tab:
    case XK_KP_Tab:
    case XK_ISO_Left_Tab:
    case XK_3270_BackTab:
      return VKEY_TAB;
    case XK_Linefeed:
    case XK_Return:
    case XK_KP_Enter:
    case XK_ISO_Enter:
      return VKEY_RETURN;
    case XK_Clear:
    case XK_KP_Begin:  // NumPad 5 without Num Lock, for crosbug.com/29169.
      return VKEY_CLEAR;
    case XK_KP_Space:
    case XK_space:
      return VKEY_SPACE;
    case XK_Home:
    case XK_KP_Home:
      return VKEY_HOME;
    case XK_End:
    case XK_KP_End:
      return VKEY_END;
    case XK_Page_Up:
    case XK_KP_Page_Up:  // aka XK_KP_Prior
      return VKEY_PRIOR;
    case XK_Page_Down:
    case XK_KP_Page_Down:  // aka XK_KP_Next
      return VKEY_NEXT;
    case XK_Left:
    case XK_KP_Left:
      return VKEY_LEFT;
    case XK_Right:
    case XK_KP_Right:
      return VKEY_RIGHT;
    case XK_Down:
    case XK_KP_Down:
      return VKEY_DOWN;
    case XK_Up:
    case XK_KP_Up:
      return VKEY_UP;
    case XK_Escape:
      return VKEY_ESCAPE;
    case XK_Kana_Lock:
    case XK_Kana_Shift:
      return VKEY_KANA;
    case XK_Hangul:
      return VKEY_HANGUL;
    case XK_Hangul_Hanja:
      return VKEY_HANJA;
    case XK_Kanji:
      return VKEY_KANJI;
    case XK_Henkan:
      return VKEY_CONVERT;
    case XK_Muhenkan:
      return VKEY_NONCONVERT;
    case XK_Zenkaku_Hankaku:
      return VKEY_DBE_DBCSCHAR;

    case XK_KP_0:
    case XK_KP_1:
    case XK_KP_2:
    case XK_KP_3:
    case XK_KP_4:
    case XK_KP_5:
    case XK_KP_6:
    case XK_KP_7:
    case XK_KP_8:
    case XK_KP_9:
      return static_cast<KeyboardCode>(VKEY_NUMPAD0 + (keysym - XK_KP_0));

    case XK_multiply:
    case XK_KP_Multiply:
      return VKEY_MULTIPLY;
    case XK_KP_Add:
      return VKEY_ADD;
    case XK_KP_Separator:
      return VKEY_SEPARATOR;
    case XK_KP_Subtract:
      return VKEY_SUBTRACT;
    case XK_KP_Decimal:
      return VKEY_DECIMAL;
    case XK_KP_Divide:
      return VKEY_DIVIDE;
    case XK_KP_Equal:
    case XK_equal:
    case XK_plus:
      return VKEY_OEM_PLUS;
    case XK_comma:
    case XK_less:
      return VKEY_OEM_COMMA;
    case XK_minus:
    case XK_underscore:
      return VKEY_OEM_MINUS;
    case XK_greater:
    case XK_period:
      return VKEY_OEM_PERIOD;
    case XK_colon:
    case XK_semicolon:
      return VKEY_OEM_1;
    case XK_question:
    case XK_slash:
      return VKEY_OEM_2;
    case XK_asciitilde:
    case XK_quoteleft:
      return VKEY_OEM_3;
    case XK_bracketleft:
    case XK_braceleft:
      return VKEY_OEM_4;
    case XK_backslash:
    case XK_bar:
      return VKEY_OEM_5;
    case XK_bracketright:
    case XK_braceright:
      return VKEY_OEM_6;
    case XK_quoteright:
    case XK_quotedbl:
      return VKEY_OEM_7;
    case XK_ISO_Level5_Shift:
      return VKEY_OEM_8;
    case XK_Shift_L:
    case XK_Shift_R:
      return VKEY_SHIFT;
    case XK_Control_L:
    case XK_Control_R:
      return VKEY_CONTROL;
    case XK_Meta_L:
    case XK_Meta_R:
    case XK_Alt_L:
    case XK_Alt_R:
      return VKEY_MENU;
    case XK_ISO_Level3_Shift:
    case XK_Mode_switch:
      return VKEY_ALTGR;
    case XK_Multi_key:
      return VKEY_COMPOSE;
    case XK_Pause:
      return VKEY_PAUSE;
    case XK_Caps_Lock:
      return VKEY_CAPITAL;
    case XK_Num_Lock:
      return VKEY_NUMLOCK;
    case XK_Scroll_Lock:
      return VKEY_SCROLL;
    case XK_Select:
      return VKEY_SELECT;
    case XK_Print:
      return VKEY_SNAPSHOT;
    case XK_Execute:
      return VKEY_EXECUTE;
    case XK_Insert:
    case XK_KP_Insert:
      return VKEY_INSERT;
    case XK_Help:
      return VKEY_HELP;
    case XK_Super_L:
      return VKEY_LWIN;
    case XK_Super_R:
      return VKEY_RWIN;
    case XK_Menu:
      return VKEY_APPS;
    case XK_F1:
    case XK_F2:
    case XK_F3:
    case XK_F4:
    case XK_F5:
    case XK_F6:
    case XK_F7:
    case XK_F8:
    case XK_F9:
    case XK_F10:
    case XK_F11:
    case XK_F12:
    case XK_F13:
    case XK_F14:
    case XK_F15:
    case XK_F16:
    case XK_F17:
    case XK_F18:
    case XK_F19:
    case XK_F20:
    case XK_F21:
    case XK_F22:
    case XK_F23:
    case XK_F24:
      return static_cast<KeyboardCode>(VKEY_F1 + (keysym - XK_F1));
    case XK_KP_F1:
    case XK_KP_F2:
    case XK_KP_F3:
    case XK_KP_F4:
      return static_cast<KeyboardCode>(VKEY_F1 + (keysym - XK_KP_F1));

    case XK_guillemotleft:
    case XK_guillemotright:
    case XK_degree:
    // In the case of canadian multilingual keyboard layout, VKEY_OEM_102 is
    // assigned to ugrave key.
    case XK_ugrave:
    case XK_Ugrave:
    case XK_brokenbar:
      return VKEY_OEM_102;  // international backslash key in 102 keyboard.

    // When evdev is in use, /usr/share/X11/xkb/symbols/inet maps F13-18 keys
    // to the special XF86XK symbols to support Microsoft Ergonomic keyboards:
    // https://bugs.freedesktop.org/show_bug.cgi?id=5783
    // In Chrome, we map these X key symbols back to F13-18 since we don't have
    // VKEYs for these XF86XK symbols.
    case XF86XK_Tools:
      return VKEY_F13;
    case XF86XK_Launch5:
      return VKEY_F14;
    case XF86XK_Launch6:
      return VKEY_F15;
    case XF86XK_Launch7:
      return VKEY_F16;
    case XF86XK_Launch8:
      return VKEY_F17;
    case XF86XK_Launch9:
      return VKEY_F18;

    // For supporting multimedia buttons on a USB keyboard.
    case XF86XK_Back:
      return VKEY_BROWSER_BACK;
    case XF86XK_Forward:
      return VKEY_BROWSER_FORWARD;
    case XF86XK_Reload:
      return VKEY_BROWSER_REFRESH;
    case XF86XK_Stop:
      return VKEY_BROWSER_STOP;
    case XF86XK_Search:
      return VKEY_BROWSER_SEARCH;
    case XF86XK_Favorites:
      return VKEY_BROWSER_FAVORITES;
    case XF86XK_HomePage:
      return VKEY_BROWSER_HOME;
    case XF86XK_AudioMute:
      return VKEY_VOLUME_MUTE;
    case XF86XK_AudioLowerVolume:
      return VKEY_VOLUME_DOWN;
    case XF86XK_AudioRaiseVolume:
      return VKEY_VOLUME_UP;
    case XF86XK_AudioNext:
      return VKEY_MEDIA_NEXT_TRACK;
    case XF86XK_AudioPrev:
      return VKEY_MEDIA_PREV_TRACK;
    case XF86XK_AudioStop:
      return VKEY_MEDIA_STOP;
    case XF86XK_AudioPlay:
      return VKEY_MEDIA_PLAY_PAUSE;
    case XF86XK_Mail:
      return VKEY_MEDIA_LAUNCH_MAIL;
    case XF86XK_LaunchA:  // F3 on an Apple keyboard.
      return VKEY_MEDIA_LAUNCH_APP1;
    case XF86XK_LaunchB:  // F4 on an Apple keyboard.
    case XF86XK_Calculator:
      return VKEY_MEDIA_LAUNCH_APP2;
    case XF86XK_WLAN:
      return VKEY_WLAN;
    case XF86XK_PowerOff:
      return VKEY_POWER;
    case XF86XK_Sleep:
      return VKEY_SLEEP;
    case XF86XK_MonBrightnessDown:
      return VKEY_BRIGHTNESS_DOWN;
    case XF86XK_MonBrightnessUp:
      return VKEY_BRIGHTNESS_UP;
    case XF86XK_KbdBrightnessDown:
      return VKEY_KBD_BRIGHTNESS_DOWN;
    case XF86XK_KbdBrightnessUp:
      return VKEY_KBD_BRIGHTNESS_UP;

      // TODO(sad): some keycodes are still missing.
  }
  DVLOG(1) << "Unknown keysym: " << base::StringPrintf("0x%x", keysym);
  return VKEY_UNKNOWN;
}

DomCode CodeFromXEvent(const x11::Event& xev) {
  auto* device = xev.As<x11::Input::DeviceEvent>();
  int keycode = 0;
  if (device) {
    keycode = device->detail;
  } else {
    auto* key = xev.As<x11::KeyEvent>();
    DCHECK(key);
    keycode = static_cast<uint8_t>(key->detail);
  }
  return ui::KeycodeConverter::NativeKeycodeToDomCode(keycode);
}

uint16_t GetCharacterFromXEvent(const x11::Event& xev) {
  uint32_t xkeycode = 0;
  uint32_t modifiers = 0;
  GetKeycodeAndModifiers(xev, &xkeycode, &modifiers);
  uint32_t keysym = TranslateKey(xkeycode, modifiers);
  return GetUnicodeCharacterFromXKeySym(keysym);
}

DomKey GetDomKeyFromXEvent(const x11::Event& xev) {
  uint32_t xkeycode = 0;
  uint32_t modifiers = 0;
  GetKeycodeAndModifiers(xev, &xkeycode, &modifiers);
  // There is no good way to check whether a key combination will print a
  // character on screen.
  // e.g. On Linux US keyboard with French layout, |XLookupString()|
  //        * Returns '?' for ctrl-shift-/
  //        * Returns '§' for shift-/
  //      According to spec the DomKey for ctrl-shift-/ should also be '§'.
  // The solution is to take out ctrl modifier directly, as according to XKB map
  // no keyboard combinations with ctrl key are mapped to printable character.
  // https://crbug.com/633838
  modifiers &= ~static_cast<int>(x11::KeyButMask::Control);
  uint32_t keysym = TranslateKey(xkeycode, modifiers);
  char16_t ch = GetUnicodeCharacterFromXKeySym(keysym);
  return XKeySymToDomKey(keysym, ch);
}

KeyboardCode DefaultKeyboardCodeFromHardwareKeycode(
    unsigned int hardware_code) {
  // This function assumes that X11 is using evdev-based keycodes.
  static constexpr auto kHardwareKeycodeMap = std::to_array<KeyboardCode>({
      // Please refer to below links for the table content:
      // http://www.w3.org/TR/DOM-Level-3-Events-code/#keyboard-101
      // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.keyCode
      // http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/translate.pdf
      VKEY_UNKNOWN,       // 0x00:
      VKEY_UNKNOWN,       // 0x01:
      VKEY_UNKNOWN,       // 0x02:
      VKEY_UNKNOWN,       // 0x03:
      VKEY_UNKNOWN,       // 0x04:
      VKEY_UNKNOWN,       // 0x05:
      VKEY_UNKNOWN,       // 0x06:
      VKEY_UNKNOWN,       // XKB   evdev (XKB - 8)      X KeySym
      VKEY_UNKNOWN,       // ===   ===============      ======
      VKEY_ESCAPE,        // 0x09: KEY_ESC              Escape
      VKEY_1,             // 0x0A: KEY_1                1
      VKEY_2,             // 0x0B: KEY_2                2
      VKEY_3,             // 0x0C: KEY_3                3
      VKEY_4,             // 0x0D: KEY_4                4
      VKEY_5,             // 0x0E: KEY_5                5
      VKEY_6,             // 0x0F: KEY_6                6
      VKEY_7,             // 0x10: KEY_7                7
      VKEY_8,             // 0x11: KEY_8                8
      VKEY_9,             // 0x12: KEY_9                9
      VKEY_0,             // 0x13: KEY_0                0
      VKEY_OEM_MINUS,     // 0x14: KEY_MINUS            minus
      VKEY_OEM_PLUS,      // 0x15: KEY_EQUAL            equal
      VKEY_BACK,          // 0x16: KEY_BACKSPACE        BackSpace
      VKEY_TAB,           // 0x17: KEY_TAB              Tab
      VKEY_Q,             // 0x18: KEY_Q                q
      VKEY_W,             // 0x19: KEY_W                w
      VKEY_E,             // 0x1A: KEY_E                e
      VKEY_R,             // 0x1B: KEY_R                r
      VKEY_T,             // 0x1C: KEY_T                t
      VKEY_Y,             // 0x1D: KEY_Y                y
      VKEY_U,             // 0x1E: KEY_U                u
      VKEY_I,             // 0x1F: KEY_I                i
      VKEY_O,             // 0x20: KEY_O                o
      VKEY_P,             // 0x21: KEY_P                p
      VKEY_OEM_4,         // 0x22: KEY_LEFTBRACE        bracketleft
      VKEY_OEM_6,         // 0x23: KEY_RIGHTBRACE       bracketright
      VKEY_RETURN,        // 0x24: KEY_ENTER            Return
      VKEY_LCONTROL,      // 0x25: KEY_LEFTCTRL         Control_L
      VKEY_A,             // 0x26: KEY_A                a
      VKEY_S,             // 0x27: KEY_S                s
      VKEY_D,             // 0x28: KEY_D                d
      VKEY_F,             // 0x29: KEY_F                f
      VKEY_G,             // 0x2A: KEY_G                g
      VKEY_H,             // 0x2B: KEY_H                h
      VKEY_J,             // 0x2C: KEY_J                j
      VKEY_K,             // 0x2D: KEY_K                k
      VKEY_L,             // 0x2E: KEY_L                l
      VKEY_OEM_1,         // 0x2F: KEY_SEMICOLON        semicolon
      VKEY_OEM_7,         // 0x30: KEY_APOSTROPHE       apostrophe
      VKEY_OEM_3,         // 0x31: KEY_GRAVE            grave
      VKEY_LSHIFT,        // 0x32: KEY_LEFTSHIFT        Shift_L
      VKEY_OEM_5,         // 0x33: KEY_BACKSLASH        backslash
      VKEY_Z,             // 0x34: KEY_Z                z
      VKEY_X,             // 0x35: KEY_X                x
      VKEY_C,             // 0x36: KEY_C                c
      VKEY_V,             // 0x37: KEY_V                v
      VKEY_B,             // 0x38: KEY_B                b
      VKEY_N,             // 0x39: KEY_N                n
      VKEY_M,             // 0x3A: KEY_M                m
      VKEY_OEM_COMMA,     // 0x3B: KEY_COMMA            comma
      VKEY_OEM_PERIOD,    // 0x3C: KEY_DOT              period
      VKEY_OEM_2,         // 0x3D: KEY_SLASH            slash
      VKEY_RSHIFT,        // 0x3E: KEY_RIGHTSHIFT       Shift_R
      VKEY_MULTIPLY,      // 0x3F: KEY_KPASTERISK       KP_Multiply
      VKEY_LMENU,         // 0x40: KEY_LEFTALT          Alt_L
      VKEY_SPACE,         // 0x41: KEY_SPACE            space
      VKEY_CAPITAL,       // 0x42: KEY_CAPSLOCK         Caps_Lock
      VKEY_F1,            // 0x43: KEY_F1               F1
      VKEY_F2,            // 0x44: KEY_F2               F2
      VKEY_F3,            // 0x45: KEY_F3               F3
      VKEY_F4,            // 0x46: KEY_F4               F4
      VKEY_F5,            // 0x47: KEY_F5               F5
      VKEY_F6,            // 0x48: KEY_F6               F6
      VKEY_F7,            // 0x49: KEY_F7               F7
      VKEY_F8,            // 0x4A: KEY_F8               F8
      VKEY_F9,            // 0x4B: KEY_F9               F9
      VKEY_F10,           // 0x4C: KEY_F10              F10
      VKEY_NUMLOCK,       // 0x4D: KEY_NUMLOCK          Num_Lock
      VKEY_SCROLL,        // 0x4E: KEY_SCROLLLOCK       Scroll_Lock
      VKEY_NUMPAD7,       // 0x4F: KEY_KP7              KP_7
      VKEY_NUMPAD8,       // 0x50: KEY_KP8              KP_8
      VKEY_NUMPAD9,       // 0x51: KEY_KP9              KP_9
      VKEY_SUBTRACT,      // 0x52: KEY_KPMINUS          KP_Subtract
      VKEY_NUMPAD4,       // 0x53: KEY_KP4              KP_4
      VKEY_NUMPAD5,       // 0x54: KEY_KP5              KP_5
      VKEY_NUMPAD6,       // 0x55: KEY_KP6              KP_6
      VKEY_ADD,           // 0x56: KEY_KPPLUS           KP_Add
      VKEY_NUMPAD1,       // 0x57: KEY_KP1              KP_1
      VKEY_NUMPAD2,       // 0x58: KEY_KP2              KP_2
      VKEY_NUMPAD3,       // 0x59: KEY_KP3              KP_3
      VKEY_NUMPAD0,       // 0x5A: KEY_KP0              KP_0
      VKEY_DECIMAL,       // 0x5B: KEY_KPDOT            KP_Decimal
      VKEY_UNKNOWN,       // 0x5C:
      VKEY_DBE_DBCSCHAR,  // 0x5D: KEY_ZENKAKUHANKAKU   Zenkaku_Hankaku
      VKEY_OEM_5,         // 0x5E: KEY_102ND            backslash
      VKEY_F11,           // 0x5F: KEY_F11              F11
      VKEY_F12,           // 0x60: KEY_F12              F12
      VKEY_OEM_102,       // 0x61: KEY_RO               Romaji
      VKEY_UNSUPPORTED,   // 0x62: KEY_KATAKANA         Katakana
      VKEY_UNSUPPORTED,   // 0x63: KEY_HIRAGANA         Hiragana
      VKEY_CONVERT,       // 0x64: KEY_HENKAN           Henkan
      VKEY_UNSUPPORTED,   // 0x65: KEY_KATAKANAHIRAGANA Hiragana_Katakana
      VKEY_NONCONVERT,    // 0x66: KEY_MUHENKAN         Muhenkan
      VKEY_SEPARATOR,     // 0x67: KEY_KPJPCOMMA        KP_Separator
      VKEY_RETURN,        // 0x68: KEY_KPENTER          KP_Enter
      VKEY_RCONTROL,      // 0x69: KEY_RIGHTCTRL        Control_R
      VKEY_DIVIDE,        // 0x6A: KEY_KPSLASH          KP_Divide
      VKEY_SNAPSHOT,      // 0x6B: KEY_SYSRQ            Print
      VKEY_RMENU,         // 0x6C: KEY_RIGHTALT         Alt_R
      VKEY_RETURN,        // 0x6D: KEY_LINEFEED         Linefeed
      VKEY_HOME,          // 0x6E: KEY_HOME             Home
      VKEY_UP,            // 0x6F: KEY_UP               Up
      VKEY_PRIOR,         // 0x70: KEY_PAGEUP           Page_Up
      VKEY_LEFT,          // 0x71: KEY_LEFT             Left
      VKEY_RIGHT,         // 0x72: KEY_RIGHT            Right
      VKEY_END,           // 0x73: KEY_END              End
      VKEY_DOWN,          // 0x74: KEY_DOWN             Down
      VKEY_NEXT,          // 0x75: KEY_PAGEDOWN         Page_Down
      VKEY_INSERT,        // 0x76: KEY_INSERT           Insert
      VKEY_DELETE,        // 0x77: KEY_DELETE           Delete
      VKEY_UNSUPPORTED,   // 0x78: KEY_MACRO
      VKEY_VOLUME_MUTE,   // 0x79: KEY_MUTE             XF86AudioMute
      VKEY_VOLUME_DOWN,   // 0x7A: KEY_VOLUMEDOWN       XF86AudioLowerVolume
      VKEY_VOLUME_UP,     // 0x7B: KEY_VOLUMEUP         XF86AudioRaiseVolume
      VKEY_POWER,         // 0x7C: KEY_POWER            XF86PowerOff
      VKEY_OEM_PLUS,      // 0x7D: KEY_KPEQUAL          KP_Equal
      VKEY_UNSUPPORTED,   // 0x7E: KEY_KPPLUSMINUS      plusminus
      VKEY_PAUSE,         // 0x7F: KEY_PAUSE            Pause
      VKEY_MEDIA_LAUNCH_APP1,  // 0x80: KEY_SCALE            XF86LaunchA
      VKEY_DECIMAL,            // 0x81: KEY_KPCOMMA          KP_Decimal
      VKEY_HANGUL,             // 0x82: KEY_HANGUEL          Hangul
      VKEY_HANJA,              // 0x83: KEY_HANJA            Hangul_Hanja
      VKEY_OEM_5,              // 0x84: KEY_YEN              yen
      VKEY_LWIN,               // 0x85: KEY_LEFTMETA         Super_L
      VKEY_RWIN,               // 0x86: KEY_RIGHTMETA        Super_R
      VKEY_COMPOSE,            // 0x87: KEY_COMPOSE          Menu
  });

  if (hardware_code >= std::size(kHardwareKeycodeMap)) {
    // Additional keycodes used by the Chrome OS top row special function keys.
    switch (hardware_code) {
      case 0xA6:  // KEY_BACK
        return VKEY_BACK;
      case 0xA7:  // KEY_FORWARD
        return VKEY_BROWSER_FORWARD;
      case 0xB5:  // KEY_REFRESH
        return VKEY_BROWSER_REFRESH;
      case 0xD4:  // KEY_DASHBOARD
        // This was changed from VKEY_MEDIA_LAUNCH_APP2 when the full screen
        // key was moved to VKEY_ZOOM with crbug.com/1204710. In order to
        // maintain existing behavior this was kept consistent but it
        // seems like this should have been VKEY_MEDIA_LAUNCH_APP1 aka
        // overview in the first place.
        return VKEY_ZOOM;
      case 0xE8:  // KEY_BRIGHTNESSDOWN
        return VKEY_BRIGHTNESS_DOWN;
      case 0xE9:  // KEY_BRIGHTNESSUP
        return VKEY_BRIGHTNESS_UP;
    }
    return VKEY_UNKNOWN;
  }
  return kHardwareKeycodeMap[hardware_code];
}

// TODO(jcampan): this method might be incomplete.
#if BUILDFLAG(ARKWEB_INPUT_EVENTS)
int XKeysymForWindowsKeyCode(KeyboardCode keycode, bool shift, bool capslock) {
#else
int XKeysymForWindowsKeyCode(KeyboardCode keycode, bool shift) {
#endif
  switch (keycode) {
    case VKEY_NUMPAD0:
      return XK_KP_0;
    case VKEY_NUMPAD1:
      return XK_KP_1;
    case VKEY_NUMPAD2:
      return XK_KP_2;
    case VKEY_NUMPAD3:
      return XK_KP_3;
    case VKEY_NUMPAD4:
      return XK_KP_4;
    case VKEY_NUMPAD5:
      return XK_KP_5;
    case VKEY_NUMPAD6:
      return XK_KP_6;
    case VKEY_NUMPAD7:
      return XK_KP_7;
    case VKEY_NUMPAD8:
      return XK_KP_8;
    case VKEY_NUMPAD9:
      return XK_KP_9;
    case VKEY_MULTIPLY:
      return XK_KP_Multiply;
    case VKEY_ADD:
      return XK_KP_Add;
    case VKEY_SUBTRACT:
      return XK_KP_Subtract;
    case VKEY_DECIMAL:
      return XK_KP_Decimal;
    case VKEY_DIVIDE:
      return XK_KP_Divide;

    case VKEY_BACK:
      return XK_BackSpace;
    case VKEY_TAB:
      return shift ? XK_ISO_Left_Tab : XK_Tab;
    case VKEY_CLEAR:
      return XK_Clear;
    case VKEY_RETURN:
      return XK_Return;
    case VKEY_SHIFT:
    case VKEY_LSHIFT:
      return XK_Shift_L;
    case VKEY_RSHIFT:
      return XK_Shift_R;
    case VKEY_CONTROL:
      return XK_Control_L;
    case VKEY_MENU:
      return XK_Alt_L;
    case VKEY_APPS:
      return XK_Menu;
    case VKEY_ALTGR:
      return XK_ISO_Level3_Shift;
    case VKEY_COMPOSE:
      return XK_Multi_key;

    case VKEY_PAUSE:
      return XK_Pause;
    case VKEY_CAPITAL:
      return XK_Caps_Lock;
    case VKEY_KANA:
      return XK_Kana_Lock;
    case VKEY_HANJA:
      return XK_Hangul_Hanja;
    case VKEY_CONVERT:
      return XK_Henkan;
    case VKEY_NONCONVERT:
      return XK_Muhenkan;
    case VKEY_DBE_SBCSCHAR:
      return XK_Zenkaku_Hankaku;
    case VKEY_DBE_DBCSCHAR:
      return XK_Zenkaku_Hankaku;
    case VKEY_ESCAPE:
      return XK_Escape;
    case VKEY_SPACE:
      return XK_space;
    case VKEY_PRIOR:
      return XK_Page_Up;
    case VKEY_NEXT:
      return XK_Page_Down;
    case VKEY_END:
      return XK_End;
    case VKEY_HOME:
      return XK_Home;
    case VKEY_LEFT:
      return XK_Left;
    case VKEY_UP:
      return XK_Up;
    case VKEY_RIGHT:
      return XK_Right;
    case VKEY_DOWN:
      return XK_Down;
    case VKEY_SELECT:
      return XK_Select;
    case VKEY_PRINT:
      return XK_Print;
    case VKEY_EXECUTE:
      return XK_Execute;
    case VKEY_INSERT:
      return XK_Insert;
    case VKEY_DELETE:
      return XK_Delete;
    case VKEY_HELP:
      return XK_Help;
    case VKEY_0:
      return shift ? XK_parenright : XK_0;
    case VKEY_1:
      return shift ? XK_exclam : XK_1;
    case VKEY_2:
      return shift ? XK_at : XK_2;
    case VKEY_3:
      return shift ? XK_numbersign : XK_3;
    case VKEY_4:
      return shift ? XK_dollar : XK_4;
    case VKEY_5:
      return shift ? XK_percent : XK_5;
    case VKEY_6:
      return shift ? XK_asciicircum : XK_6;
    case VKEY_7:
      return shift ? XK_ampersand : XK_7;
    case VKEY_8:
      return shift ? XK_asterisk : XK_8;
    case VKEY_9:
      return shift ? XK_parenleft : XK_9;

    case VKEY_A:
    case VKEY_B:
    case VKEY_C:
    case VKEY_D:
    case VKEY_E:
    case VKEY_F:
    case VKEY_G:
    case VKEY_H:
    case VKEY_I:
    case VKEY_J:
    case VKEY_K:
    case VKEY_L:
    case VKEY_M:
    case VKEY_N:
    case VKEY_O:
    case VKEY_P:
    case VKEY_Q:
    case VKEY_R:
    case VKEY_S:
    case VKEY_T:
    case VKEY_U:
    case VKEY_V:
    case VKEY_W:
    case VKEY_X:
    case VKEY_Y:
    case VKEY_Z:
#if BUILDFLAG(ARKWEB_INPUT_EVENTS)
      return ((shift ^ capslock) ? XK_A : XK_a) + (keycode - VKEY_A);
#else
      return (shift ? XK_A : XK_a) + (keycode - VKEY_A);
#endif

    case VKEY_LWIN:
      return XK_Super_L;
    case VKEY_RWIN:
      return XK_Super_R;

    case VKEY_NUMLOCK:
      return XK_Num_Lock;

    case VKEY_SCROLL:
      return XK_Scroll_Lock;

    case VKEY_OEM_1:
      return shift ? XK_colon : XK_semicolon;
    case VKEY_OEM_PLUS:
      return shift ? XK_plus : XK_equal;
    case VKEY_OEM_COMMA:
      return shift ? XK_less : XK_comma;
    case VKEY_OEM_MINUS:
      return shift ? XK_underscore : XK_minus;
    case VKEY_OEM_PERIOD:
      return shift ? XK_greater : XK_period;
    case VKEY_OEM_2:
      return shift ? XK_question : XK_slash;
    case VKEY_OEM_3:
      return shift ? XK_asciitilde : XK_quoteleft;
    case VKEY_OEM_4:
      return shift ? XK_braceleft : XK_bracketleft;
    case VKEY_OEM_5:
      return shift ? XK_bar : XK_backslash;
    case VKEY_OEM_6:
      return shift ? XK_braceright : XK_bracketright;
    case VKEY_OEM_7:
      return shift ? XK_quotedbl : XK_quoteright;
    case VKEY_OEM_8:
      return XK_ISO_Level5_Shift;
    case VKEY_OEM_102:
      return shift ? XK_guillemotleft : XK_guillemotright;

    case VKEY_F1:
    case VKEY_F2:
    case VKEY_F3:
    case VKEY_F4:
    case VKEY_F5:
    case VKEY_F6:
    case VKEY_F7:
    case VKEY_F8:
    case VKEY_F9:
    case VKEY_F10:
    case VKEY_F11:
    case VKEY_F12:
    case VKEY_F13:
    case VKEY_F14:
    case VKEY_F15:
    case VKEY_F16:
    case VKEY_F17:
    case VKEY_F18:
    case VKEY_F19:
    case VKEY_F20:
    case VKEY_F21:
    case VKEY_F22:
    case VKEY_F23:
    case VKEY_F24:
      return XK_F1 + (keycode - VKEY_F1);

    case VKEY_BROWSER_BACK:
      return XF86XK_Back;
    case VKEY_BROWSER_FORWARD:
      return XF86XK_Forward;
    case VKEY_BROWSER_REFRESH:
      return XF86XK_Reload;
    case VKEY_BROWSER_STOP:
      return XF86XK_Stop;
    case VKEY_BROWSER_SEARCH:
      return XF86XK_Search;
    case VKEY_BROWSER_FAVORITES:
      return XF86XK_Favorites;
    case VKEY_BROWSER_HOME:
      return XF86XK_HomePage;
    case VKEY_VOLUME_MUTE:
      return XF86XK_AudioMute;
    case VKEY_VOLUME_DOWN:
      return XF86XK_AudioLowerVolume;
    case VKEY_VOLUME_UP:
      return XF86XK_AudioRaiseVolume;
    case VKEY_MEDIA_NEXT_TRACK:
      return XF86XK_AudioNext;
    case VKEY_MEDIA_PREV_TRACK:
      return XF86XK_AudioPrev;
    case VKEY_MEDIA_STOP:
      return XF86XK_AudioStop;
    case VKEY_MEDIA_PLAY_PAUSE:
      return XF86XK_AudioPlay;
    case VKEY_MEDIA_LAUNCH_MAIL:
      return XF86XK_Mail;
    case VKEY_MEDIA_LAUNCH_APP1:
      return XF86XK_LaunchA;
    case VKEY_MEDIA_LAUNCH_APP2:
      return XF86XK_LaunchB;
    case VKEY_WLAN:
      return XF86XK_WLAN;
    case VKEY_POWER:
      return XF86XK_PowerOff;
    case VKEY_BRIGHTNESS_DOWN:
      return XF86XK_MonBrightnessDown;
    case VKEY_BRIGHTNESS_UP:
      return XF86XK_MonBrightnessUp;
    case VKEY_KBD_BRIGHTNESS_DOWN:
      return XF86XK_KbdBrightnessDown;
    case VKEY_KBD_BRIGHTNESS_UP:
      return XF86XK_KbdBrightnessUp;

    default:
      LOG(WARNING) << "Unknown keycode:" << keycode;
      return 0;
  }
}

unsigned int XKeyCodeForWindowsKeyCode(ui::KeyboardCode key_code,
                                       int flags,
                                       x11::Connection* connection) {
  // SHIFT state is ignored in the call to XKeysymForWindowsKeyCode() here
  // because we map the XKeysym back to a keycode, i.e. a physical key position.
  // Using a SHIFT-modified XKeysym would sometimes yield X keycodes that,
  // while technically valid, may be surprising in that they do not match
  // the keycode of the original press, and conflict with assumptions in
  // other code.
  //
  // For example, in a US layout, Shift-9 has the interpretation XK_parenleft,
  // but the keycode KEY_9 alone does not map to XK_parenleft; instead,
  // XKeysymToKeycode() returns KEY_KPLEFTPAREN (keypad left parenthesis)
  // which does map to XK_parenleft -- notwithstanding that keyboards with
  // dedicated number pad parenthesis keys are currently uncommon.
  //
  // Similarly, Shift-Comma has the interpretation XK_less, but KEY_COMMA
  // alone does not map to XK_less; XKeysymToKeycode() returns KEY_102ND
  // (the '<>' key between Shift and Z on 105-key keyboards) which does.
  //
  // crbug.com/386066 and crbug.com/390263 are examples of problems
  // associated with this.
  //
#if BUILDFLAG(ARKWEB_INPUT_EVENTS)
  return static_cast<uint8_t>(
      connection->KeysymToKeycode(XKeysymForWindowsKeyCode(key_code, false, false)));
#else
  return static_cast<uint8_t>(
      connection->KeysymToKeycode(XKeysymForWindowsKeyCode(key_code, false)));
#endif
}

uint32_t GetXI2StateFromEvent(const x11::Input::DeviceEvent& xievent) {
  // XI2 state includes the base modifiers and the effective keyboard group.
  return xievent.mods.effective | (xievent.group.effective << kXkbGroupShift);
}

int XkbGroupForCoreState(int state) {
  return (state >> kXkbGroupShift) & 0x3;
}

}  // namespace ui