package x // import "fmt" import "unicode" import "github.com/jezek/xgb/xproto" import "github.com/jezek/xgbutil/keybind" import "git.tebibyte.media/sashakoshka/stone" // when making changes to this file, look at keysymdef.h and // https://tronche.com/gui/x/xlib/input/keyboard-encoding.html var buttonCodeTable = map[xproto.Keysym] stone.Button { 0xFFFFFF: stone.ButtonUnknown, 0xFF63: stone.KeyInsert, 0xFF67: stone.KeyMenu, 0xFF61: stone.KeyPrintScreen, 0xFF6B: stone.KeyPause, 0xFFE5: stone.KeyCapsLock, 0xFF14: stone.KeyScrollLock, 0xFF7F: stone.KeyNumLock, 0xFF08: stone.KeyBackspace, 0xFF09: stone.KeyTab, 0xFF0D: stone.KeyEnter, 0xFF1B: stone.KeyEscape, 0xFF52: stone.KeyUp, 0xFF54: stone.KeyDown, 0xFF51: stone.KeyLeft, 0xFF53: stone.KeyRight, 0xFF55: stone.KeyPageUp, 0xFF56: stone.KeyPageDown, 0xFF50: stone.KeyHome, 0xFF57: stone.KeyEnd, 0xFFE1: stone.KeyLeftShift, 0xFFE2: stone.KeyRightShift, 0xFFE3: stone.KeyLeftControl, 0xFFE4: stone.KeyRightControl, 0xFFE7: stone.KeyLeftMeta, 0xFFE8: stone.KeyRightMeta, 0xFFE9: stone.KeyLeftAlt, 0xFFEA: stone.KeyRightAlt, 0xFFEB: stone.KeyLeftSuper, 0xFFEC: stone.KeyRightSuper, 0xFFED: stone.KeyLeftHyper, 0xFFEE: stone.KeyRightHyper, 0xFFFF: stone.KeyDelete, 0xFFBE: stone.KeyF1, 0xFFBF: stone.KeyF2, 0xFFC0: stone.KeyF3, 0xFFC1: stone.KeyF4, 0xFFC2: stone.KeyF5, 0xFFC3: stone.KeyF6, 0xFFC4: stone.KeyF7, 0xFFC5: stone.KeyF8, 0xFFC6: stone.KeyF9, 0xFFC7: stone.KeyF10, 0xFFC8: stone.KeyF11, 0xFFC9: stone.KeyF12, // TODO: send this whenever a compose key, dead key, etc is pressed, // and then send the resulting character while witholding the key // presses that were used to compose it. As far as the program is // concerned, a magical key with the final character was pressed and the // KeyDead key is just so that the program might provide some visual // feedback to the user while input is being waited for. 0xFF20: stone.KeyDead, } func (backend *Backend) keycodeToButton ( keycode xproto.Keycode, state uint16, ) ( button stone.Button, ) { // FIXME: also set shift to true if the lock modifier is on and the lock // modifier is interpreted as shiftLock shift := state & xproto.ModMaskShift > 0 // FIXME: only set this to true if the lock modifier is on and the lock // modifier is interpreted as capsLock capsLock := state & xproto.ModMaskLock > 0 symbol1 := keybind.KeysymGet(backend.connection, keycode, 0) symbol2 := keybind.KeysymGet(backend.connection, keycode, 1) symbol3 := keybind.KeysymGet(backend.connection, keycode, 2) symbol4 := keybind.KeysymGet(backend.connection, keycode, 3) cased := false // third paragraph switch { case symbol2 == 0 && symbol3 == 0 && symbol4 == 0: symbol3 = symbol1 case symbol3 == 0 && symbol4 == 0: symbol3 = symbol1 symbol4 = symbol2 case symbol4 == 0: symbol4 = 0 } symbol1Rune := keysymToRune(symbol1) symbol2Rune := keysymToRune(symbol2) symbol3Rune := keysymToRune(symbol3) symbol4Rune := keysymToRune(symbol4) // FIXME: we ignore mode switch stuff _ = symbol4Rune // fourth paragraph if symbol2 == 0 { upper := unicode.IsUpper(symbol1Rune) lower := unicode.IsLower(symbol1Rune) if upper || lower { symbol1Rune = unicode.ToLower(symbol1Rune) symbol2Rune = unicode.ToUpper(symbol1Rune) cased = true } else { symbol2 = symbol1 symbol2Rune = symbol1Rune } } if symbol4 == 0 { upper := unicode.IsUpper(symbol3Rune) lower := unicode.IsLower(symbol3Rune) if upper || lower { symbol3Rune = unicode.ToLower(symbol3Rune) symbol4Rune = unicode.ToUpper(symbol3Rune) cased = true } else { symbol4 = symbol3 symbol4Rune = symbol3Rune } } var selectedKeysym xproto.Keysym var selectedRune rune // big ol list in the middle switch { // FIXME: take into account numlock case !shift && !capsLock: selectedKeysym = symbol1 selectedRune = symbol1Rune case !shift && capsLock: if cased && unicode.IsLower(symbol1Rune) { selectedRune = symbol2Rune } else { selectedKeysym = symbol1 selectedRune = symbol1Rune } case shift && capsLock: if cased && unicode.IsLower(symbol2Rune) { selectedRune = unicode.ToUpper(symbol2Rune) } else { selectedKeysym = symbol2 selectedRune = symbol2Rune } case shift: selectedKeysym = symbol2 selectedRune = symbol2Rune } // look up in table var isControl bool button, isControl = buttonCodeTable[selectedKeysym] // if it wasn't found, if !isControl { button = stone.Button(selectedRune) } return } func keysymToRune (keysym xproto.Keysym) (character rune) { // X keysyms like 0xFF.. or 0xFE.. are non-character keys. these cannot // be converted so we return a zero. if (keysym >> 8) == 0xFF || (keysym >> 8) == 0xFE { character = 0 return } // some X keysyms have a single bit set to 1 here. i believe this is to // prevent conflicts with existing codes. if we mask it off we will get // a correct utf-32 code point. if keysym & 0xF000000 == 0x1000000 { character = rune(keysym & 0x0111111) return } // if none of these things happened, we can safely (i think) assume that // the keysym is an exact utf-32 code point. character = rune(keysym) return }