package main

import (
	"git.sr.ht/~geb/dotool/xkb"
	"github.com/bendahl/uinput"
	"unicode"
)

var super, altgr, ctrl, alt, shift int

var LinuxKeys = map[string]Chord{
	// Linux Only
	"micmute": Chord{Key: uinput.KeyMicmute},
	"unknown": Chord{Key: uinput.KeyUnknown},
	"alterase": Chord{Key: uinput.KeyAlterase},
	"question": Chord{Key: uinput.KeyQuestion},
	"hp": Chord{Key: uinput.KeyHp},
	"bassboost": Chord{Key: uinput.KeyBassboost},
	"play": Chord{Key: uinput.KeyPlay},
	"close": Chord{Key: uinput.KeyClose},
	"edit": Chord{Key: uinput.KeyEdit},
	"move": Chord{Key: uinput.KeyMove},
	"iso": Chord{Key: uinput.KeyIso},
	"closecd": Chord{Key: uinput.KeyClosecd},
	"deletefile": Chord{Key: uinput.KeyDeletefile},
	"setup": Chord{Key: uinput.KeySetup},
	"yen": Chord{Key: uinput.KeyYen},
	"macro": Chord{Key: uinput.KeyMacro},
	"kpjpcomma": Chord{Key: uinput.KeyKpjpcomma},
	"ro": Chord{Key: uinput.KeyRo},
	"zenkakuhankaku": Chord{Key: uinput.KeyZenkakuhankaku},

	// Potentially overwritten by initKeys()
	"esc": Chord{Key: uinput.KeyEsc},
	"1": Chord{Key: uinput.Key1},
	"2": Chord{Key: uinput.Key2},
	"3": Chord{Key: uinput.Key3},
	"4": Chord{Key: uinput.Key4},
	"5": Chord{Key: uinput.Key5},
	"6": Chord{Key: uinput.Key6},
	"7": Chord{Key: uinput.Key7},
	"8": Chord{Key: uinput.Key8},
	"9": Chord{Key: uinput.Key9},
	"0": Chord{Key: uinput.Key0},
	"minus": Chord{Key: uinput.KeyMinus},
	"equal": Chord{Key: uinput.KeyEqual},
	"backspace": Chord{Key: uinput.KeyBackspace},
	"tab": Chord{Key: uinput.KeyTab},
	"q": Chord{Key: uinput.KeyQ},
	"w": Chord{Key: uinput.KeyW},
	"e": Chord{Key: uinput.KeyE},
	"r": Chord{Key: uinput.KeyR},
	"t": Chord{Key: uinput.KeyT},
	"y": Chord{Key: uinput.KeyY},
	"u": Chord{Key: uinput.KeyU},
	"i": Chord{Key: uinput.KeyI},
	"o": Chord{Key: uinput.KeyO},
	"p": Chord{Key: uinput.KeyP},
	"leftbrace": Chord{Key: uinput.KeyLeftbrace},
	"rightbrace": Chord{Key: uinput.KeyRightbrace},
	"enter": Chord{Key: uinput.KeyEnter},
	"leftctrl": Chord{Key: uinput.KeyLeftctrl},
	"a": Chord{Key: uinput.KeyA},
	"s": Chord{Key: uinput.KeyS},
	"d": Chord{Key: uinput.KeyD},
	"f": Chord{Key: uinput.KeyF},
	"g": Chord{Key: uinput.KeyG},
	"h": Chord{Key: uinput.KeyH},
	"j": Chord{Key: uinput.KeyJ},
	"k": Chord{Key: uinput.KeyK},
	"l": Chord{Key: uinput.KeyL},
	"semicolon": Chord{Key: uinput.KeySemicolon},
	"apostrophe": Chord{Key: uinput.KeyApostrophe},
	"grave": Chord{Key: uinput.KeyGrave},
	"leftshift": Chord{Key: uinput.KeyLeftshift},
	"backslash": Chord{Key: uinput.KeyBackslash},
	"z": Chord{Key: uinput.KeyZ},
	"x": Chord{Key: uinput.KeyX},
	"c": Chord{Key: uinput.KeyC},
	"v": Chord{Key: uinput.KeyV},
	"b": Chord{Key: uinput.KeyB},
	"n": Chord{Key: uinput.KeyN},
	"m": Chord{Key: uinput.KeyM},
	"comma": Chord{Key: uinput.KeyComma},
	"dot": Chord{Key: uinput.KeyDot},
	"slash": Chord{Key: uinput.KeySlash},
	"rightshift": Chord{Key: uinput.KeyRightshift},
	"kpasterisk": Chord{Key: uinput.KeyKpasterisk},
	"leftalt": Chord{Key: uinput.KeyLeftalt},
	"space": Chord{Key: uinput.KeySpace},
	"capslock": Chord{Key: uinput.KeyCapslock},
	"f1": Chord{Key: uinput.KeyF1},
	"f2": Chord{Key: uinput.KeyF2},
	"f3": Chord{Key: uinput.KeyF3},
	"f4": Chord{Key: uinput.KeyF4},
	"f5": Chord{Key: uinput.KeyF5},
	"f6": Chord{Key: uinput.KeyF6},
	"f7": Chord{Key: uinput.KeyF7},
	"f8": Chord{Key: uinput.KeyF8},
	"f9": Chord{Key: uinput.KeyF9},
	"f10": Chord{Key: uinput.KeyF10},
	"numlock": Chord{Key: uinput.KeyNumlock},
	"scrolllock": Chord{Key: uinput.KeyScrolllock},
	"kp7": Chord{Key: uinput.KeyKp7},
	"kp8": Chord{Key: uinput.KeyKp8},
	"kp9": Chord{Key: uinput.KeyKp9},
	"kpminus": Chord{Key: uinput.KeyKpminus},
	"kp4": Chord{Key: uinput.KeyKp4},
	"kp5": Chord{Key: uinput.KeyKp5},
	"kp6": Chord{Key: uinput.KeyKp6},
	"kpplus": Chord{Key: uinput.KeyKpplus},
	"kp1": Chord{Key: uinput.KeyKp1},
	"kp2": Chord{Key: uinput.KeyKp2},
	"kp3": Chord{Key: uinput.KeyKp3},
	"kp0": Chord{Key: uinput.KeyKp0},
	"kpdot": Chord{Key: uinput.KeyKpdot},
	"102nd": Chord{Key: uinput.Key102Nd},
	"f11": Chord{Key: uinput.KeyF11},
	"f12": Chord{Key: uinput.KeyF12},
	"katakana": Chord{Key: uinput.KeyKatakana},
	"hiragana": Chord{Key: uinput.KeyHiragana},
	"henkan": Chord{Key: uinput.KeyHenkan},
	"katakanahiragana": Chord{Key: uinput.KeyKatakanahiragana},
	"muhenkan": Chord{Key: uinput.KeyMuhenkan},
	"kpenter": Chord{Key: uinput.KeyKpenter},
	"rightctrl": Chord{Key: uinput.KeyRightctrl},
	"kpslash": Chord{Key: uinput.KeyKpslash},
	"sysrq": Chord{Key: uinput.KeySysrq},
	"rightalt": Chord{Key: uinput.KeyRightalt},
	"linefeed": Chord{Key: uinput.KeyLinefeed},
	"home": Chord{Key: uinput.KeyHome},
	"up": Chord{Key: uinput.KeyUp},
	"pageup": Chord{Key: uinput.KeyPageup},
	"left": Chord{Key: uinput.KeyLeft},
	"right": Chord{Key: uinput.KeyRight},
	"end": Chord{Key: uinput.KeyEnd},
	"down": Chord{Key: uinput.KeyDown},
	"pagedown": Chord{Key: uinput.KeyPagedown},
	"insert": Chord{Key: uinput.KeyInsert},
	"delete": Chord{Key: uinput.KeyDelete},
	"mute": Chord{Key: uinput.KeyMute},
	"volumedown": Chord{Key: uinput.KeyVolumedown},
	"volumeup": Chord{Key: uinput.KeyVolumeup},
	"power": Chord{Key: uinput.KeyPower},
	"kpequal": Chord{Key: uinput.KeyKpequal},
	"kpplusminus": Chord{Key: uinput.KeyKpplusminus},
	"pause": Chord{Key: uinput.KeyPause},
	"scale": Chord{Key: uinput.KeyScale},
	"kpcomma": Chord{Key: uinput.KeyKpcomma},
	"hangeul": Chord{Key: uinput.KeyHangeul},
	"hanja": Chord{Key: uinput.KeyHanja},
	"leftmeta": Chord{Key: uinput.KeyLeftmeta},
	"rightmeta": Chord{Key: uinput.KeyRightmeta},
	"compose": Chord{Key: uinput.KeyCompose},
	"stop": Chord{Key: uinput.KeyStop},
	"again": Chord{Key: uinput.KeyAgain},
	"props": Chord{Key: uinput.KeyProps},
	"undo": Chord{Key: uinput.KeyUndo},
	"front": Chord{Key: uinput.KeyFront},
	"copy": Chord{Key: uinput.KeyCopy},
	"open": Chord{Key: uinput.KeyOpen},
	"paste": Chord{Key: uinput.KeyPaste},
	"find": Chord{Key: uinput.KeyFind},
	"cut": Chord{Key: uinput.KeyCut},
	"help": Chord{Key: uinput.KeyHelp},
	"menu": Chord{Key: uinput.KeyMenu},
	"calc": Chord{Key: uinput.KeyCalc},
	"sleep": Chord{Key: uinput.KeySleep},
	"wakeup": Chord{Key: uinput.KeyWakeup},
	"file": Chord{Key: uinput.KeyFile},
	"sendfile": Chord{Key: uinput.KeySendfile},
	"xfer": Chord{Key: uinput.KeyXfer},
	"prog1": Chord{Key: uinput.KeyProg1},
	"prog2": Chord{Key: uinput.KeyProg2},
	"www": Chord{Key: uinput.KeyWww},
	"msdos": Chord{Key: uinput.KeyMsdos},
	"coffee": Chord{Key: uinput.KeyCoffee},
	"direction": Chord{Key: uinput.KeyDirection},
	"cyclewindows": Chord{Key: uinput.KeyCyclewindows},
	"mail": Chord{Key: uinput.KeyMail},
	"bookmarks": Chord{Key: uinput.KeyBookmarks},
	"computer": Chord{Key: uinput.KeyComputer},
	"back": Chord{Key: uinput.KeyBack},
	"forward": Chord{Key: uinput.KeyForward},
	"ejectcd": Chord{Key: uinput.KeyEjectcd},
	"ejectclosecd": Chord{Key: uinput.KeyEjectclosecd},
	"nextsong": Chord{Key: uinput.KeyNextsong},
	"playpause": Chord{Key: uinput.KeyPlaypause},
	"previoussong": Chord{Key: uinput.KeyPrevioussong},
	"stopcd": Chord{Key: uinput.KeyStopcd},
	"record": Chord{Key: uinput.KeyRecord},
	"rewind": Chord{Key: uinput.KeyRewind},
	"phone": Chord{Key: uinput.KeyPhone},
	"config": Chord{Key: uinput.KeyConfig},
	"homepage": Chord{Key: uinput.KeyHomepage},
	"refresh": Chord{Key: uinput.KeyRefresh},
	"exit": Chord{Key: uinput.KeyExit},
	"scrollup": Chord{Key: uinput.KeyScrollup},
	"scrolldown": Chord{Key: uinput.KeyScrolldown},
	"kpleftparen": Chord{Key: uinput.KeyKpleftparen},
	"kprightparen": Chord{Key: uinput.KeyKprightparen},
	"new": Chord{Key: uinput.KeyNew},
	"redo": Chord{Key: uinput.KeyRedo},
	"f13": Chord{Key: uinput.KeyF13},
	"f14": Chord{Key: uinput.KeyF14},
	"f15": Chord{Key: uinput.KeyF15},
	"f16": Chord{Key: uinput.KeyF16},
	"f17": Chord{Key: uinput.KeyF17},
	"f18": Chord{Key: uinput.KeyF18},
	"f19": Chord{Key: uinput.KeyF19},
	"f20": Chord{Key: uinput.KeyF20},
	"f21": Chord{Key: uinput.KeyF21},
	"f22": Chord{Key: uinput.KeyF22},
	"f23": Chord{Key: uinput.KeyF23},
	"f24": Chord{Key: uinput.KeyF24},
	"playcd": Chord{Key: uinput.KeyPlaycd},
	"pausecd": Chord{Key: uinput.KeyPausecd},
	"prog3": Chord{Key: uinput.KeyProg3},
	"prog4": Chord{Key: uinput.KeyProg4},
	"dashboard": Chord{Key: uinput.KeyDashboard},
	"suspend": Chord{Key: uinput.KeySuspend},
	"fastforward": Chord{Key: uinput.KeyFastforward},
	"print": Chord{Key: uinput.KeyPrint},
	"camera": Chord{Key: uinput.KeyCamera},
	"sound": Chord{Key: uinput.KeySound},
	"email": Chord{Key: uinput.KeyEmail},
	"chat": Chord{Key: uinput.KeyChat},
	"search": Chord{Key: uinput.KeySearch},
	"connect": Chord{Key: uinput.KeyConnect},
	"finance": Chord{Key: uinput.KeyFinance},
	"sport": Chord{Key: uinput.KeySport},
	"shop": Chord{Key: uinput.KeyShop},
	"cancel": Chord{Key: uinput.KeyCancel},
	"brightnessdown": Chord{Key: uinput.KeyBrightnessdown},
	"brightnessup": Chord{Key: uinput.KeyBrightnessup},
	"media": Chord{Key: uinput.KeyMedia},
	"switchvideomode": Chord{Key: uinput.KeySwitchvideomode},
	"kbdillumtoggle": Chord{Key: uinput.KeyKbdillumtoggle},
	"kbdillumdown": Chord{Key: uinput.KeyKbdillumdown},
	"kbdillumup": Chord{Key: uinput.KeyKbdillumup},
	"send": Chord{Key: uinput.KeySend},
	"reply": Chord{Key: uinput.KeyReply},
	"forwardmail": Chord{Key: uinput.KeyForwardmail},
	"save": Chord{Key: uinput.KeySave},
	"documents": Chord{Key: uinput.KeyDocuments},
	"battery": Chord{Key: uinput.KeyBattery},
	"bluetooth": Chord{Key: uinput.KeyBluetooth},
	"wlan": Chord{Key: uinput.KeyWlan},
	"uwb": Chord{Key: uinput.KeyUwb},
	"videonext": Chord{Key: uinput.KeyVideoNext},
	"videoprev": Chord{Key: uinput.KeyVideoPrev},
	"brightnesscycle": Chord{Key: uinput.KeyBrightnessCycle},
	"brightnesszero": Chord{Key: uinput.KeyBrightnessZero},
	"displayoff": Chord{Key: uinput.KeyDisplayOff},
	"wimax": Chord{Key: uinput.KeyWimax},
	"rfkill": Chord{Key: uinput.KeyRfkill},
}

var linuxXSyms = map[string]uint32{
	"esc": 0xff1b,
	"1": 0x31,
	"2": 0x32,
	"3": 0x33,
	"4": 0x34,
	"5": 0x35,
	"6": 0x36,
	"7": 0x37,
	"8": 0x38,
	"9": 0x39,
	"0": 0x30,
	"minus": 0x2d,
	"equal": 0x3d,
	"backspace": 0xff08,
	"tab": 0xff09,
	"q": 0x71,
	"w": 0x77,
	"e": 0x65,
	"r": 0x72,
	"t": 0x74,
	"y": 0x79,
	"u": 0x75,
	"i": 0x69,
	"o": 0x6f,
	"p": 0x70,
	"leftbrace": 0x5b,
	"rightbrace": 0x5d,
	"enter": 0xff0d,
	"leftctrl": 0xffe3,
	"a": 0x61,
	"s": 0x73,
	"d": 0x64,
	"f": 0x66,
	"g": 0x67,
	"h": 0x68,
	"j": 0x6a,
	"k": 0x6b,
	"l": 0x6c,
	"semicolon": 0x3b,
	"apostrophe": 0x27,
	"grave": 0x60,
	"leftshift": 0xffe1,
	"backslash": 0x5c,
	"z": 0x7a,
	"x": 0x78,
	"c": 0x63,
	"v": 0x76,
	"b": 0x62,
	"n": 0x6e,
	"m": 0x6d,
	"comma": 0x2c,
	"dot": 0x2e,
	"slash": 0x2f,
	"rightshift": 0xffe2,
	"kpasterisk": 0xffaa,
	"leftalt": 0xffe9,
	"space": 0x20,
	"capslock": 0xffe5,
	"f1": 0xffbe,
	"f2": 0xffbf,
	"f3": 0xffc0,
	"f4": 0xffc1,
	"f5": 0xffc2,
	"f6": 0xffc3,
	"f7": 0xffc4,
	"f8": 0xffc5,
	"f9": 0xffc6,
	"f10": 0xffc7,
	"numlock": 0xff7f,
	"scrolllock": 0xff14,
	"kp7": 0xff95,
	"kp8": 0xff97,
	"kp9": 0xff9a,
	"kpminus": 0xffad,
	"kp4": 0xff96,
	"kp5": 0xff9d,
	"kp6": 0xff98,
	"kpplus": 0xffab,
	"kp1": 0xff9c,
	"kp2": 0xff99,
	"kp3": 0xff9b,
	"kp0": 0xff9e,
	"kpdot": 0xff9f,
	"102nd": 0x3c,
	"f11": 0xffc8,
	"f12": 0xffc9,
	"katakana": 0xff26,
	"hiragana": 0xff25,
	"henkan": 0xff23,
	"katakanahiragana": 0xff27,
	"muhenkan": 0xff22,
	"kpenter": 0xff8d,
	"rightctrl": 0xffe4,
	"kpslash": 0xffaf,
	"sysrq": 0xff61,
	"rightalt": 0xffea,
	"linefeed": 0xff0a,
	"home": 0xff50,
	"up": 0xff52,
	"pageup": 0xff55,
	"left": 0xff51,
	"right": 0xff53,
	"end": 0xff57,
	"down": 0xff54,
	"pagedown": 0xff56,
	"insert": 0xff63,
	"delete": 0xffff,
	"mute": 0x1008ff12,
	"volumedown": 0x1008ff11,
	"volumeup": 0x1008ff13,
	"power": 0x1008ff2a,
	"kpequal": 0xffbd,
	"kpplusminus": 0xb1,
	"pause": 0xff13,
	"scale": 0x1008ff4a,
	"kpcomma": 0xffae,
	"hangeul": 0xff31,
	"hanja": 0xff34,
	"leftmeta": 0xffeb,
	"rightmeta": 0xffec,
	"compose": 0xff67,
	"stop": 0xff69,
	"again": 0xff66,
	"props": 0x1005ff70,
	"undo": 0xff65,
	"front": 0x1005ff71,
	"copy": 0x1008ff57,
	"open": 0x1008ff6b,
	"paste": 0x1008ff6d,
	"find": 0xff68,
	"cut": 0x1008ff58,
	"help": 0xff6a,
	"menu": 0x1008ff65,
	"calc": 0x1008ff1d,
	"sleep": 0x1008ff2f,
	"wakeup": 0x1008ff2b,
	"file": 0x1008ff5d,
	"sendfile": 0x1008ff7b,
	"xfer": 0x1008ff8a,
	"prog1": 0x1008ff41,
	"prog2": 0x1008ff42,
	"www": 0x1008ff2e,
	"msdos": 0x1008ff5a,
	"coffee": 0x1008ff2d,
	"direction": 0x1008ff74,
	"cyclewindows": 0x1008ff7f,
	"mail": 0x1008ff19,
	"bookmarks": 0x1008ff30,
	"computer": 0x1008ff33,
	"back": 0x1008ff26,
	"forward": 0x1008ff27,
	"ejectcd": 0x1008ff2c,
	"ejectclosecd": 0x1008ff2c,
	"nextsong": 0x1008ff17,
	"playpause": 0x1008ff14,
	"previoussong": 0x1008ff16,
	"stopcd": 0x1008ff15,
	"record": 0x1008ff1c,
	"rewind": 0x1008ff3e,
	"phone": 0x1008ff6e,
	"config": 0x1008ff81,
	"homepage": 0x1008ff18,
	"refresh": 0x1008ff73,
	"exit": 0x1008ff56,
	"scrollup": 0x1008ff78,
	"scrolldown": 0x1008ff79,
	"kpleftparen": 0x28,
	"kprightparen": 0x29,
	"new": 0x1008ff68,
	"redo": 0xff66,
	"f13": 0xffca,
	"f14": 0xffcb,
	"f15": 0xffcc,
	"f16": 0xffcd,
	"f17": 0xffce,
	"f18": 0xffcf,
	"f19": 0xffd0,
	"f20": 0xffd1,
	"f21": 0xffd2,
	"f22": 0xffd3,
	"f23": 0xffd4,
	"f24": 0xffd5,
	"playcd": 0x1008ff14,
	"pausecd": 0x1008ff31,
	"prog3": 0x1008ff43,
	"prog4": 0x1008ff44,
	"dashboard": 0x1008ff4b,
	"suspend": 0x1008ffa7,
	"fastforward": 0x1008ff97,
	"print": 0xff61,
	"camera": 0x1008ff8f,
	"sound": 0x1008ffb6,
	"email": 0x1008ff19,
	"chat": 0x1008ff8e,
	"search": 0x1008ff1b,
	"connect": 0x1008ff5f,
	"finance": 0x1008ff3c,
	"sport": 0x1008ff5e,
	"shop": 0x1008ff36,
	"cancel": 0xff69,
	"brightnessdown": 0x1008ff03,
	"brightnessup": 0x1008ff02,
	"media": 0x1008ff32,
	"switchvideomode": 0x1008ff59,
	"kbdillumtoggle": 0x1008ff04,
	"kbdillumdown": 0x1008ff06,
	"kbdillumup": 0x1008ff05,
	"send": 0x1008ff7b,
	"reply": 0x1008ff72,
	"forwardmail": 0x1008ff90,
	"save": 0x1008ff77,
	"documents": 0x1008ff5b,
	"battery": 0x1008ff93,
	"bluetooth": 0x1008ff94,
	"wlan": 0x1008ff95,
	"uwb": 0x1008ff96,
	"videonext": 0x1008fe22,
	"videoprev": 0x1008fe23,
	"brightnesscycle": 0x1008ff07,
	"brightnesszero": 0x100810f4,
	"displayoff": 0x100810f5,
	"wimax": 0x1008ffb4,
	"rfkill": 0x1008ffb5,
}

var XKeys = map[string]Chord{}

type DeadKeyResult struct {
	DeadKey, Key string
	Result rune
}

// this relies on a conventional keymap
// this isn't exhaustive (e.g. acircumflexacute)
var DeadKeyResults = []DeadKeyResult{
	{"dead_abovedot", "B", 'Ḃ'},
	{"dead_abovedot", "C", 'Ċ'},
	{"dead_abovedot", "D", 'Ḋ'},
	{"dead_abovedot", "E", 'Ė'},
	{"dead_abovedot", "F", 'Ḟ'},
	{"dead_abovedot", "G", 'Ġ'},
	{"dead_abovedot", "I", 'İ'},
	{"dead_abovedot", "M", 'Ṁ'},
	{"dead_abovedot", "P", 'Ṗ'},
	{"dead_abovedot", "S", 'Ṡ'},
	{"dead_abovedot", "T", 'Ṫ'},
	{"dead_abovedot", "X", 'Ẋ'},
	{"dead_abovedot", "Z", 'Ż'},
	{"dead_acute", "A", 'Á'},
	{"dead_acute", "C", 'Ć'},
	{"dead_acute", "E", 'É'},
	{"dead_acute", "I", 'Í'},
	{"dead_acute", "L", 'Ĺ'},
	{"dead_acute", "N", 'Ń'},
	{"dead_acute", "O", 'Ó'},
	{"dead_acute", "R", 'Ŕ'},
	{"dead_acute", "S", 'Ś'},
	{"dead_acute", "U", 'Ú'},
	{"dead_acute", "W", 'Ẃ'},
	{"dead_acute", "Y", 'Ý'},
	{"dead_acute", "Z", 'Ź'},
	{"dead_belowdot", "A", 'Ạ'},
	{"dead_belowdot", "E", 'Ẹ'},
	{"dead_belowdot", "I", 'Ị'},
	{"dead_belowdot", "L", 'Ḷ'},
	{"dead_belowdot", "O", 'Ọ'},
	{"dead_belowdot", "U", 'Ụ'},
	{"dead_belowdot", "Y", 'Ỵ'},
	{"dead_breve", "A", 'Ă'},
	{"dead_breve", "G", 'Ğ'},
	{"dead_breve", "I", 'Ĭ'},
	{"dead_breve", "U", 'Ŭ'},
	{"dead_caron", "C", 'Č'},
	{"dead_caron", "D", 'Ď'},
	{"dead_caron", "E", 'Ě'},
	{"dead_caron", "G", 'Ǧ'},
	{"dead_caron", "L", 'Ľ'},
	{"dead_caron", "N", 'Ň'},
	{"dead_caron", "O", 'Ǒ'},
	{"dead_caron", "R", 'Ř'},
	{"dead_caron", "S", 'Š'},
	{"dead_caron", "T", 'Ť'},
	{"dead_caron", "Z", 'Ž'},
	{"dead_cedilla", "C", 'Ç'},
	{"dead_cedilla", "G", 'Ģ'},
	{"dead_cedilla", "K", 'Ķ'},
	{"dead_cedilla", "L", 'Ļ'},
	{"dead_cedilla", "N", 'Ņ'},
	{"dead_cedilla", "R", 'Ŗ'},
	{"dead_cedilla", "S", 'Ş'},
	{"dead_cedilla", "T", 'Ţ'},
	{"dead_circumflex", "A", 'Â'},
	{"dead_circumflex", "C", 'Ĉ'},
	{"dead_circumflex", "E", 'Ê'},
	{"dead_circumflex", "G", 'Ĝ'},
	{"dead_circumflex", "H", 'Ĥ'},
	{"dead_circumflex", "I", 'Î'},
	{"dead_circumflex", "J", 'Ĵ'},
	{"dead_circumflex", "O", 'Ô'},
	{"dead_circumflex", "S", 'Ŝ'},
	{"dead_circumflex", "U", 'Û'},
	{"dead_circumflex", "W", 'Ŵ'},
	{"dead_circumflex", "Y", 'Ŷ'},
	{"dead_diaeresis", "A", 'Ä'},
	{"dead_diaeresis", "E", 'Ë'},
	{"dead_diaeresis", "I", 'Ï'},
	{"dead_diaeresis", "O", 'Ö'},
	{"dead_diaeresis", "U", 'Ü'},
	{"dead_diaeresis", "W", 'Ẅ'},
	{"dead_diaeresis", "Y", 'Ÿ'},
	{"dead_doubleacute", "O", 'Ő'},
	{"dead_doubleacute", "U", 'Ű'},
	{"dead_grave", "A", 'À'},
	{"dead_grave", "E", 'È'},
	{"dead_grave", "I", 'Ì'},
	{"dead_grave", "O", 'Ò'},
	{"dead_grave", "U", 'Ù'},
	{"dead_grave", "W", 'Ẁ'},
	{"dead_grave", "Y", 'Ỳ'},
	{"dead_hook", "A", 'Ả'},
	{"dead_hook", "E", 'Ẻ'},
	{"dead_hook", "I", 'Ỉ'},
	{"dead_hook", "O", 'Ỏ'},
	{"dead_hook", "U", 'Ủ'},
	{"dead_hook", "Y", 'Ỷ'},
	{"dead_horn", "O", 'Ơ'},
	{"dead_horn", "T", 'Þ'},
	{"dead_horn", "U", 'Ư'},
	{"dead_macron", "A", 'Ā'},
	{"dead_macron", "E", 'Ē'},
	{"dead_macron", "I", 'Ī'},
	{"dead_macron", "O", 'Ō'},
	{"dead_macron", "U", 'Ū'},
	{"dead_ogonek", "A", 'Ą'},
	{"dead_ogonek", "E", 'Ę'},
	{"dead_ogonek", "I", 'Į'},
	{"dead_ogonek", "U", 'Ų'},
	{"dead_stroke", "D", 'Đ'},
	{"dead_stroke", "H", 'Ħ'},
	{"dead_stroke", "L", 'Ł'},
	{"dead_stroke", "Z", 'Ƶ'},
	{"dead_tilde", "A", 'Ã'},
	{"dead_tilde", "E", 'Ẽ'},
	{"dead_tilde", "I", 'Ĩ'},
	{"dead_tilde", "N", 'Ñ'},
	{"dead_tilde", "O", 'Õ'},
	{"dead_tilde", "U", 'Ũ'},
	{"dead_tilde", "Y", 'Ỹ'},
}

func newChord(keymap *xkb.Keymap, code, level uint32) Chord {
	altGrMask := uint32(1) << keymap.ModGetIndex("Mod5")
	ctrlMask := uint32(1) << keymap.ModGetIndex(xkb.ModNameCtrl)
	altMask := uint32(1) << keymap.ModGetIndex(xkb.ModNameAlt)
	shiftMask := uint32(1) << keymap.ModGetIndex(xkb.ModNameShift)

	// TODO support layouts other than 0
	mask := keymap.KeyGetMod(code, 0, level)
	return Chord{
		false,
		(mask & altGrMask) != 0,
		(mask & ctrlMask) != 0,
		(mask & altMask) != 0,
		(mask & shiftMask) != 0,
		int(code) - 8,
		level,
	}
}

func initKeys(keymap *xkb.Keymap) {
	maxCode := keymap.MaxKeycode()
	for code := keymap.MinKeycode(); code <= maxCode && code < 256; code++ {
		numLevels := keymap.NumLevelsForKey(code, 0)
		for level := uint32(0); level < numLevels; level++ {
			for _, sym := range keymap.KeyGetSymsByLevel(code, 0, level) {
				chord := newChord(keymap, code, level)
				for name, s := range linuxXSyms {
					if s == sym {
						if l, ok := LinuxKeys[name]; !ok || level < l.level {
							LinuxKeys[name] = chord
						}
					}
				}
				name := xkb.KeysymGetName(sym)
				if x, ok := XKeys[name]; !ok || level < x.level {
					XKeys[name] = chord
				}
			}
		}
	}
	super = XKeys["Super_L"].Key
	altgr = XKeys["ISO_Level3_Shift"].Key
	ctrl = XKeys["Control_L"].Key
	alt = XKeys["Alt_L"].Key
	shift = XKeys["Shift_L"].Key
}

func getChord(keymap *xkb.Keymap, keysym uint32) Chord {
	maxCode := keymap.MaxKeycode()
	for code := keymap.MinKeycode(); code <= maxCode && code < 256; code++ {
		numLevels := keymap.NumLevelsForKey(code, 0)
		for level := uint32(0); level < numLevels; level++ {
			for _, sym := range keymap.KeyGetSymsByLevel(code, 0, level) {
				if sym == keysym {
					return newChord(keymap, code, level)
				}
			}
		}
	}
	return Chord{}
}

func getDeadChords(keymap *xkb.Keymap, result rune) (Chord, Chord) {
	r := unicode.ToUpper(result)
	for _, d := range DeadKeyResults {
		if d.Result == r {
			deadKey := XKeys[d.DeadKey]
			key := XKeys[d.Key]
			if deadKey.Key != 0 && key.Key != 0 {
				key.Shift = d.Result == result
				return deadKey, key
			}
		}
	}
	return Chord{}, Chord{}
}
