okami.mom/ultraviolet
git clone https://git.nkp.pet/okami.mom/ultraviolet
bf44e60
main
1package uv 2 3import ( 4"strings" 5"unicode" 6"unicode/utf8" 7 8"github.com/charmbracelet/x/ansi" 9) 10 11// KeyMod represents modifier keys. 12type KeyMod int 13 14// Modifier keys. 15const ( 16ModShift KeyMod = 1 << iota 17ModAlt 18ModCtrl 19ModMeta 20 21// These modifiers are used with the Kitty protocol. 22// XXX: Meta and Super are swapped in the Kitty protocol, 23// this is to preserve compatibility with XTerm modifiers. 24 25ModHyper 26ModSuper // Windows/Command keys 27 28// These are key lock states. 29 30ModCapsLock 31ModNumLock 32ModScrollLock // Defined in Windows API only 33) 34 35// Contains reports whether m contains the given modifiers. 36// 37// Example: 38// 39// m := ModAlt | ModCtrl 40// m.Contains(ModCtrl) // true 41// m.Contains(ModAlt | ModCtrl) // true 42// m.Contains(ModAlt | ModCtrl | ModShift) // false 43func (m KeyMod )Contains (mods KeyMod )bool { 44return m & mods == mods 45} 46 47const ( 48// KeyExtended is a special key code used to signify that a key event 49// contains multiple runes. 50KeyExtended = unicode .MaxRune + 1 51) 52 53// Special key symbols. 54const ( 55 56// Special keys. 57 58KeyUp rune = KeyExtended + iota + 1 59KeyDown 60KeyRight 61KeyLeft 62KeyBegin 63KeyFind 64KeyInsert 65KeyDelete 66KeySelect 67KeyPgUp 68KeyPgDown 69KeyHome 70KeyEnd 71 72// Keypad keys. 73 74KeyKpEnter 75KeyKpEqual 76KeyKpMultiply 77KeyKpPlus 78KeyKpComma 79KeyKpMinus 80KeyKpDecimal 81KeyKpDivide 82KeyKp0 83KeyKp1 84KeyKp2 85KeyKp3 86KeyKp4 87KeyKp5 88KeyKp6 89KeyKp7 90KeyKp8 91KeyKp9 92 93//nolint:godox 94// The following are keys defined in the Kitty keyboard protocol. 95// TODO: Investigate the names of these keys. 96 97KeyKpSep 98KeyKpUp 99KeyKpDown 100KeyKpLeft 101KeyKpRight 102KeyKpPgUp 103KeyKpPgDown 104KeyKpHome 105KeyKpEnd 106KeyKpInsert 107KeyKpDelete 108KeyKpBegin 109 110// Function keys. 111 112KeyF1 113KeyF2 114KeyF3 115KeyF4 116KeyF5 117KeyF6 118KeyF7 119KeyF8 120KeyF9 121KeyF10 122KeyF11 123KeyF12 124KeyF13 125KeyF14 126KeyF15 127KeyF16 128KeyF17 129KeyF18 130KeyF19 131KeyF20 132KeyF21 133KeyF22 134KeyF23 135KeyF24 136KeyF25 137KeyF26 138KeyF27 139KeyF28 140KeyF29 141KeyF30 142KeyF31 143KeyF32 144KeyF33 145KeyF34 146KeyF35 147KeyF36 148KeyF37 149KeyF38 150KeyF39 151KeyF40 152KeyF41 153KeyF42 154KeyF43 155KeyF44 156KeyF45 157KeyF46 158KeyF47 159KeyF48 160KeyF49 161KeyF50 162KeyF51 163KeyF52 164KeyF53 165KeyF54 166KeyF55 167KeyF56 168KeyF57 169KeyF58 170KeyF59 171KeyF60 172KeyF61 173KeyF62 174KeyF63 175 176//nolint:godox 177// The following are keys defined in the Kitty keyboard protocol. 178// TODO: Investigate the names of these keys. 179 180KeyCapsLock 181KeyScrollLock 182KeyNumLock 183KeyPrintScreen 184KeyPause 185KeyMenu 186 187KeyMediaPlay 188KeyMediaPause 189KeyMediaPlayPause 190KeyMediaReverse 191KeyMediaStop 192KeyMediaFastForward 193KeyMediaRewind 194KeyMediaNext 195KeyMediaPrev 196KeyMediaRecord 197 198KeyLowerVol 199KeyRaiseVol 200KeyMute 201 202KeyLeftShift 203KeyLeftAlt 204KeyLeftCtrl 205KeyLeftSuper 206KeyLeftHyper 207KeyLeftMeta 208KeyRightShift 209KeyRightAlt 210KeyRightCtrl 211KeyRightSuper 212KeyRightHyper 213KeyRightMeta 214KeyIsoLevel3Shift 215KeyIsoLevel5Shift 216 217// Special names in C0. 218 219KeyBackspace = rune (ansi .DEL ) 220KeyTab = rune (ansi .HT ) 221KeyEnter = rune (ansi .CR ) 222KeyReturn = KeyEnter 223KeyEscape = rune (ansi .ESC ) 224KeyEsc = KeyEscape 225 226// Special names in G0. 227 228KeySpace = rune (ansi .SP ) 229) 230 231// Key represents a Key press or release event. It contains information about 232// the Key pressed, like the runes, the type of Key, and the modifiers pressed. 233// There are a couple general patterns you could use to check for key presses 234// or releases: 235// 236// // Switch on the string representation of the key (shorter) 237// switch ev := ev.(type) { 238// case KeyPressEvent: 239// switch ev.String() { 240// case "enter": 241// fmt.Println("you pressed enter!") 242// case "a": 243// fmt.Println("you pressed a!") 244// } 245// } 246// 247// // Switch on the key type (more foolproof) 248// switch ev := ev.(type) { 249// case KeyEvent: 250// // catch both KeyPressEvent and KeyReleaseEvent 251// switch key := ev.Key(); key.Code { 252// case KeyEnter: 253// fmt.Println("you pressed enter!") 254// default: 255// switch key.Text { 256// case "a": 257// fmt.Println("you pressed a!") 258// } 259// } 260// } 261// 262// Note that [Key.Text] will be empty for special keys like [KeyEnter], 263// [KeyTab], and for keys that don't represent printable characters like key 264// combos with modifier keys. In other words, [Key.Text] is populated only for 265// keys that represent printable characters shifted or unshifted (like 'a', 266// 'A', '1', '!', etc.). 267type Key struct { 268// Text contains the actual characters received. This usually the same as 269// [Key.Code]. When [Key.Text] is non-empty, it indicates that the key 270// pressed represents printable character(s). 271Text string 272 273// Mod represents modifier keys, like [ModCtrl], [ModAlt], and so on. 274Mod KeyMod 275 276// Code represents the key pressed. This is usually a special key like 277// [KeyTab], [KeyEnter], [KeyF1], or a printable character like 'a'. 278Code rune 279 280// ShiftedCode is the actual, shifted key pressed by the user. For example, 281// if the user presses shift+a, or caps lock is on, [Key.ShiftedCode] will 282// be 'A' and [Key.Code] will be 'a'. 283// 284// In the case of non-latin keyboards, like Arabic, [Key.ShiftedCode] is the 285// unshifted key on the keyboard. 286// 287// This is only available with the Kitty Keyboard Protocol or the Windows 288// Console API. 289ShiftedCode rune 290 291// BaseCode is the key pressed according to the standard PC-101 key layout. 292// On international keyboards, this is the key that would be pressed if the 293// keyboard was set to US PC-101 layout. 294// 295// For example, if the user presses 'q' on a French AZERTY keyboard, 296// [Key.BaseCode] will be 'q'. 297// 298// This is only available with the Kitty Keyboard Protocol or the Windows 299// Console API. 300BaseCode rune 301 302// IsRepeat indicates whether the key is being held down and sending events 303// repeatedly. 304// 305// This is only available with the Kitty Keyboard Protocol or the Windows 306// Console API. 307IsRepeat bool 308} 309 310// MatchString returns true if the [Key] matches one of the given strings. 311// 312// A string can be a key name like "enter", "tab", "a", or a printable 313// character like "1" or " ". It can also have combinations of modifiers like 314// "ctrl+a", "shift+enter", "alt+tab", "ctrl+shift+enter", etc. 315func (k Key )MatchString (s ... string )bool { 316for _ ,s := range s { 317if keyMatchString (k ,s ) { 318return true 319 } 320 } 321return false 322} 323 324func keyMatchString (k Key ,s string )bool { 325var ( 326mod KeyMod 327code rune 328text string 329 ) 330parts := strings .Split (s ,"+" ) 331for _ ,part := range parts { 332switch part { 333case "ctrl" : 334mod |= ModCtrl 335case "alt" : 336mod |= ModAlt 337case "shift" : 338mod |= ModShift 339case "meta" : 340mod |= ModMeta 341case "hyper" : 342mod |= ModHyper 343case "super" : 344mod |= ModSuper 345case "capslock" : 346mod |= ModCapsLock 347case "scrolllock" : 348mod |= ModScrollLock 349case "numlock" : 350mod |= ModNumLock 351default : 352// Check if the part is a key name. 353if k ,ok := stringKeyType [part ];ok { 354code = k 355 }else { 356// Check if the part is a printable character. 357if utf8 .RuneCountInString (part )== 1 { 358code ,_ = utf8 .DecodeRuneInString (part ) 359 }else { 360// Multi-rune key. 361code = KeyExtended 362text = part 363 } 364 } 365 } 366 } 367 368// Check if we have a printable character. 369smod := mod &^ (ModShift | ModCapsLock ) 370if smod == 0 && text == "" && unicode .IsPrint (code ) { 371if mod & ModShift != 0 || mod & ModCapsLock != 0 { 372// Shifted code we need to use uppercase. 373text = string (unicode .ToUpper (code )) 374 }else { 375// Otherwise, use the code as is. 376text = string (code ) 377 } 378 } 379 380// Check if we have a match. 381return (k .Mod == mod && k .Code == code )|| 382 (k .Text != "" && k .Text == text ) 383} 384 385// String implements [fmt.Stringer] and is quite useful for matching key 386// events. It will return the textual representation of the [Key] if there is 387// one, otherwise, it will fallback to [Key.Keystroke]. 388// 389// For example, you'll always get "?" and instead of "shift+/" on a US ANSI 390// keyboard. 391func (k Key )String ()string { 392if len (k .Text )> 0 && k .Text != " " { 393return k .Text 394 } 395return k .Keystroke () 396} 397 398// Keystroke returns the keystroke representation of the [Key]. While less type 399// safe than looking at the individual fields, it will usually be more 400// convenient and readable to use this method when matching against keys. 401// 402// Note that modifier keys are always printed in the following order: 403// - ctrl 404// - alt 405// - shift 406// - meta 407// - hyper 408// - super 409// 410// For example, you'll always see "ctrl+shift+alt+a" and never 411// "shift+ctrl+alt+a". 412func (k Key )Keystroke ()string { 413var sb strings.Builder 414if k .Mod .Contains (ModCtrl )&& k .Code != KeyLeftCtrl && k .Code != KeyRightCtrl { 415sb .WriteString ("ctrl+" ) 416 } 417if k .Mod .Contains (ModAlt )&& k .Code != KeyLeftAlt && k .Code != KeyRightAlt { 418sb .WriteString ("alt+" ) 419 } 420if k .Mod .Contains (ModShift )&& k .Code != KeyLeftShift && k .Code != KeyRightShift { 421sb .WriteString ("shift+" ) 422 } 423if k .Mod .Contains (ModMeta )&& k .Code != KeyLeftMeta && k .Code != KeyRightMeta { 424sb .WriteString ("meta+" ) 425 } 426if k .Mod .Contains (ModHyper )&& k .Code != KeyLeftHyper && k .Code != KeyRightHyper { 427sb .WriteString ("hyper+" ) 428 } 429if k .Mod .Contains (ModSuper )&& k .Code != KeyLeftSuper && k .Code != KeyRightSuper { 430sb .WriteString ("super+" ) 431 } 432 433if kt ,ok := keyTypeString [k .Code ];ok { 434sb .WriteString (kt ) 435 }else { 436code := k .Code 437if k .BaseCode != 0 { 438// If a [Key.BaseCode] is present, use it to represent a key using the standard 439// PC-101 key layout. 440code = k .BaseCode 441 } 442 443switch code { 444case KeySpace : 445// Space is the only invisible printable character. 446sb .WriteString ("space" ) 447case KeyExtended : 448// Write the actual text of the key when the key contains multiple 449// runes. 450sb .WriteString (k .Text ) 451default : 452sb .WriteRune (code ) 453 } 454 } 455 456return sb .String () 457} 458 459var keyTypeString = map [rune ]string { 460KeyEnter :"enter" , 461KeyTab :"tab" , 462KeyBackspace :"backspace" , 463KeyEscape :"esc" , 464KeySpace :"space" , 465KeyUp :"up" , 466KeyDown :"down" , 467KeyLeft :"left" , 468KeyRight :"right" , 469KeyBegin :"begin" , 470KeyFind :"find" , 471KeyInsert :"insert" , 472KeyDelete :"delete" , 473KeySelect :"select" , 474KeyPgUp :"pgup" , 475KeyPgDown :"pgdown" , 476KeyHome :"home" , 477KeyEnd :"end" , 478KeyKpEnter :"enter" , 479KeyKpEqual :"equal" , 480KeyKpMultiply :"mul" , 481KeyKpPlus :"plus" , 482KeyKpComma :"comma" , 483KeyKpMinus :"minus" , 484KeyKpDecimal :"period" , 485KeyKpDivide :"div" , 486KeyKp0 :"0" , 487KeyKp1 :"1" , 488KeyKp2 :"2" , 489KeyKp3 :"3" , 490KeyKp4 :"4" , 491KeyKp5 :"5" , 492KeyKp6 :"6" , 493KeyKp7 :"7" , 494KeyKp8 :"8" , 495KeyKp9 :"9" , 496 497// Kitty keyboard extension 498KeyKpSep :"sep" , 499KeyKpUp :"up" , 500KeyKpDown :"down" , 501KeyKpLeft :"left" , 502KeyKpRight :"right" , 503KeyKpPgUp :"pgup" , 504KeyKpPgDown :"pgdown" , 505KeyKpHome :"home" , 506KeyKpEnd :"end" , 507KeyKpInsert :"insert" , 508KeyKpDelete :"delete" , 509KeyKpBegin :"begin" , 510 511KeyF1 :"f1" , 512KeyF2 :"f2" , 513KeyF3 :"f3" , 514KeyF4 :"f4" , 515KeyF5 :"f5" , 516KeyF6 :"f6" , 517KeyF7 :"f7" , 518KeyF8 :"f8" , 519KeyF9 :"f9" , 520KeyF10 :"f10" , 521KeyF11 :"f11" , 522KeyF12 :"f12" , 523KeyF13 :"f13" , 524KeyF14 :"f14" , 525KeyF15 :"f15" , 526KeyF16 :"f16" , 527KeyF17 :"f17" , 528KeyF18 :"f18" , 529KeyF19 :"f19" , 530KeyF20 :"f20" , 531KeyF21 :"f21" , 532KeyF22 :"f22" , 533KeyF23 :"f23" , 534KeyF24 :"f24" , 535KeyF25 :"f25" , 536KeyF26 :"f26" , 537KeyF27 :"f27" , 538KeyF28 :"f28" , 539KeyF29 :"f29" , 540KeyF30 :"f30" , 541KeyF31 :"f31" , 542KeyF32 :"f32" , 543KeyF33 :"f33" , 544KeyF34 :"f34" , 545KeyF35 :"f35" , 546KeyF36 :"f36" , 547KeyF37 :"f37" , 548KeyF38 :"f38" , 549KeyF39 :"f39" , 550KeyF40 :"f40" , 551KeyF41 :"f41" , 552KeyF42 :"f42" , 553KeyF43 :"f43" , 554KeyF44 :"f44" , 555KeyF45 :"f45" , 556KeyF46 :"f46" , 557KeyF47 :"f47" , 558KeyF48 :"f48" , 559KeyF49 :"f49" , 560KeyF50 :"f50" , 561KeyF51 :"f51" , 562KeyF52 :"f52" , 563KeyF53 :"f53" , 564KeyF54 :"f54" , 565KeyF55 :"f55" , 566KeyF56 :"f56" , 567KeyF57 :"f57" , 568KeyF58 :"f58" , 569KeyF59 :"f59" , 570KeyF60 :"f60" , 571KeyF61 :"f61" , 572KeyF62 :"f62" , 573KeyF63 :"f63" , 574 575// Kitty keyboard extension 576KeyCapsLock :"capslock" , 577KeyScrollLock :"scrolllock" , 578KeyNumLock :"numlock" , 579KeyPrintScreen :"printscreen" , 580KeyPause :"pause" , 581KeyMenu :"menu" , 582KeyMediaPlay :"mediaplay" , 583KeyMediaPause :"mediapause" , 584KeyMediaPlayPause :"mediaplaypause" , 585KeyMediaReverse :"mediareverse" , 586KeyMediaStop :"mediastop" , 587KeyMediaFastForward :"mediafastforward" , 588KeyMediaRewind :"mediarewind" , 589KeyMediaNext :"medianext" , 590KeyMediaPrev :"mediaprev" , 591KeyMediaRecord :"mediarecord" , 592KeyLowerVol :"lowervol" , 593KeyRaiseVol :"raisevol" , 594KeyMute :"mute" , 595KeyLeftShift :"leftshift" , 596KeyLeftAlt :"leftalt" , 597KeyLeftCtrl :"leftctrl" , 598KeyLeftSuper :"leftsuper" , 599KeyLeftHyper :"lefthyper" , 600KeyLeftMeta :"leftmeta" , 601KeyRightShift :"rightshift" , 602KeyRightAlt :"rightalt" , 603KeyRightCtrl :"rightctrl" , 604KeyRightSuper :"rightsuper" , 605KeyRightHyper :"righthyper" , 606KeyRightMeta :"rightmeta" , 607KeyIsoLevel3Shift :"isolevel3shift" , 608KeyIsoLevel5Shift :"isolevel5shift" , 609} 610 611var stringKeyType = map [string ]rune { 612"enter" :KeyEnter , 613"tab" :KeyTab , 614"backspace" :KeyBackspace , 615"escape" :KeyEscape , 616"esc" :KeyEscape , 617"space" :KeySpace , 618"up" :KeyUp , 619"down" :KeyDown , 620"left" :KeyLeft , 621"right" :KeyRight , 622"begin" :KeyBegin , 623"find" :KeyFind , 624"insert" :KeyInsert , 625"delete" :KeyDelete , 626"select" :KeySelect , 627"pgup" :KeyPgUp , 628"pgdown" :KeyPgDown , 629"home" :KeyHome , 630"end" :KeyEnd , 631"kpenter" :KeyKpEnter , 632"kpequal" :KeyKpEqual , 633"kpmul" :KeyKpMultiply , 634"kpplus" :KeyKpPlus , 635"kpcomma" :KeyKpComma , 636"kpminus" :KeyKpMinus , 637"kpperiod" :KeyKpDecimal , 638"kpdiv" :KeyKpDivide , 639"kp0" :KeyKp0 , 640"kp1" :KeyKp1 , 641"kp2" :KeyKp2 , 642"kp3" :KeyKp3 , 643"kp4" :KeyKp4 , 644"kp5" :KeyKp5 , 645"kp6" :KeyKp6 , 646"kp7" :KeyKp7 , 647"kp8" :KeyKp8 , 648"kp9" :KeyKp9 , 649 650// Kitty keyboard extension 651"kpsep" :KeyKpSep , 652"kpup" :KeyKpUp , 653"kpdown" :KeyKpDown , 654"kpleft" :KeyKpLeft , 655"kpright" :KeyKpRight , 656"kppgup" :KeyKpPgUp , 657"kppgdown" :KeyKpPgDown , 658"kphome" :KeyKpHome , 659"kpend" :KeyKpEnd , 660"kpinsert" :KeyKpInsert , 661"kpdelete" :KeyKpDelete , 662"kpbegin" :KeyKpBegin , 663 664"f1" :KeyF1 , 665"f2" :KeyF2 , 666"f3" :KeyF3 , 667"f4" :KeyF4 , 668"f5" :KeyF5 , 669"f6" :KeyF6 , 670"f7" :KeyF7 , 671"f8" :KeyF8 , 672"f9" :KeyF9 , 673"f10" :KeyF10 , 674"f11" :KeyF11 , 675"f12" :KeyF12 , 676"f13" :KeyF13 , 677"f14" :KeyF14 , 678"f15" :KeyF15 , 679"f16" :KeyF16 , 680"f17" :KeyF17 , 681"f18" :KeyF18 , 682"f19" :KeyF19 , 683"f20" :KeyF20 , 684"f21" :KeyF21 , 685"f22" :KeyF22 , 686"f23" :KeyF23 , 687"f24" :KeyF24 , 688"f25" :KeyF25 , 689"f26" :KeyF26 , 690"f27" :KeyF27 , 691"f28" :KeyF28 , 692"f29" :KeyF29 , 693"f30" :KeyF30 , 694"f31" :KeyF31 , 695"f32" :KeyF32 , 696"f33" :KeyF33 , 697"f34" :KeyF34 , 698"f35" :KeyF35 , 699"f36" :KeyF36 , 700"f37" :KeyF37 , 701"f38" :KeyF38 , 702"f39" :KeyF39 , 703"f40" :KeyF40 , 704"f41" :KeyF41 , 705"f42" :KeyF42 , 706"f43" :KeyF43 , 707"f44" :KeyF44 , 708"f45" :KeyF45 , 709"f46" :KeyF46 , 710"f47" :KeyF47 , 711"f48" :KeyF48 , 712"f49" :KeyF49 , 713"f50" :KeyF50 , 714"f51" :KeyF51 , 715"f52" :KeyF52 , 716"f53" :KeyF53 , 717"f54" :KeyF54 , 718"f55" :KeyF55 , 719"f56" :KeyF56 , 720"f57" :KeyF57 , 721"f58" :KeyF58 , 722"f59" :KeyF59 , 723"f60" :KeyF60 , 724"f61" :KeyF61 , 725"f62" :KeyF62 , 726"f63" :KeyF63 , 727 728// Kitty keyboard extension 729"capslock" :KeyCapsLock , 730"scrolllock" :KeyScrollLock , 731"numlock" :KeyNumLock , 732"printscreen" :KeyPrintScreen , 733"pause" :KeyPause , 734"menu" :KeyMenu , 735"mediaplay" :KeyMediaPlay , 736"mediapause" :KeyMediaPause , 737"mediaplaypause" :KeyMediaPlayPause , 738"mediareverse" :KeyMediaReverse , 739"mediastop" :KeyMediaStop , 740"mediafastforward" :KeyMediaFastForward , 741"mediarewind" :KeyMediaRewind , 742"medianext" :KeyMediaNext , 743"mediaprev" :KeyMediaPrev , 744"mediarecord" :KeyMediaRecord , 745"lowervol" :KeyLowerVol , 746"raisevol" :KeyRaiseVol , 747"mute" :KeyMute , 748"leftshift" :KeyLeftShift , 749"leftalt" :KeyLeftAlt , 750"leftctrl" :KeyLeftCtrl , 751"leftsuper" :KeyLeftSuper , 752"lefthyper" :KeyLeftHyper , 753"leftmeta" :KeyLeftMeta , 754"rightshift" :KeyRightShift , 755"rightalt" :KeyRightAlt , 756"rightctrl" :KeyRightCtrl , 757"rightsuper" :KeyRightSuper , 758"righthyper" :KeyRightHyper , 759"rightmeta" :KeyRightMeta , 760"isolevel3shift" :KeyIsoLevel3Shift , 761"isolevel5shift" :KeyIsoLevel5Shift , 762}