okami.mom/ultraviolet
git clone https://git.nkp.pet/okami.mom/ultraviolet
6d3a341
upstream
1package uv 2 3import ( 4"bytes" 5"encoding/base64" 6"encoding/hex" 7"fmt" 8"image/color" 9"math" 10"slices" 11"strings" 12"unicode" 13"unicode/utf8" 14 15"github.com/charmbracelet/x/ansi" 16"github.com/charmbracelet/x/ansi/parser" 17 xwindows"github.com/charmbracelet/x/windows" 18"github.com/rivo/uniseg" 19) 20 21// Flags to control the behavior of the parser. 22const ( 23// When this flag is set, the driver will treat both Ctrl+Space and Ctrl+@ 24// as the same key sequence. 25// 26// Historically, the ANSI specs generate NUL (0x00) on both the Ctrl+Space 27// and Ctrl+@ key sequences. This flag allows the driver to treat both as 28// the same key sequence. 29flagCtrlAt = 1 << iota 30 31// When this flag is set, the driver will treat the Tab key and Ctrl+I as 32// the same key sequence. 33// 34// Historically, the ANSI specs generate HT (0x09) on both the Tab key and 35// Ctrl+I. This flag allows the driver to treat both as the same key 36// sequence. 37flagCtrlI 38 39// When this flag is set, the driver will treat the Enter key and Ctrl+M as 40// the same key sequence. 41// 42// Historically, the ANSI specs generate CR (0x0D) on both the Enter key 43// and Ctrl+M. This flag allows the driver to treat both as the same key. 44flagCtrlM 45 46// When this flag is set, the driver will treat Escape and Ctrl+[ as 47// the same key sequence. 48// 49// Historically, the ANSI specs generate ESC (0x1B) on both the Escape key 50// and Ctrl+[. This flag allows the driver to treat both as the same key 51// sequence. 52flagCtrlOpenBracket 53 54// When this flag is set, the driver will send a BS (0x08 byte) character 55// instead of a DEL (0x7F byte) character when the Backspace key is 56// pressed. 57// 58// The VT100 terminal has both a Backspace and a Delete key. The VT220 59// terminal dropped the Backspace key and replaced it with the Delete key. 60// Both terminals send a DEL character when the Delete key is pressed. 61// Modern terminals and PCs later readded the Delete key but used a 62// different key sequence, and the Backspace key was standardized to send a 63// DEL character. 64flagBackspace 65 66// When this flag is set, the driver will recognize the Find key instead of 67// treating it as a Home key. 68// 69// The Find key was part of the VT220 keyboard, and is no longer used in 70// modern day PCs. 71flagFind 72 73// When this flag is set, the driver will recognize the Select key instead 74// of treating it as a End key. 75// 76// The Symbol key was part of the VT220 keyboard, and is no longer used in 77// modern day PCs. 78flagSelect 79 80// When this flag is set, the driver will preserve function keys (F13-F63) 81// as symbols. 82// 83// Since these keys are not part of today's standard 20th century keyboard, 84// we treat them as F1-F12 modifier keys i.e. ctrl/shift/alt + Fn combos. 85// Key definitions come from Terminfo, this flag is only useful when 86// FlagTerminfo is not set. 87flagFKeys 88) 89 90// LegacyKeyEncoding is a set of flags that control the behavior of legacy 91// terminal key encodings. Historically, ctrl+<key> input events produce 92// control characters (0x00-0x1F) that collide with some special keys like Tab, 93// Enter, and Escape. This controls the expected behavior of encoding these 94// keys. 95// 96// This type has the following default values: 97// - CtrlAt maps 0x00 [ansi.NUL] to ctrl+space instead of ctrl+@. 98// - CtrlI maps 0x09 [ansi.HT] to the tab key instead of ctrl+i. 99// - CtrlM maps 0x0D [ansi.CR] to the enter key instead of ctrl+m. 100// - CtrlOpenBracket maps 0x1B [ansi.ESC] to the escape key instead of ctrl+[. 101// - Backspace maps the backspace key to 0x08 [ansi.BS] instead of 0x7F [ansi.DEL]. 102// - Find maps the legacy find key to the home key. 103// - Select maps the legacy select key to the end key. 104// - FKeys maps high function keys instead of treating them as Function+<modifiers>. 105type LegacyKeyEncoding uint32 106 107// CtrlAt returns a [LegacyKeyEncoding] with whether [ansi.NUL] (0x00) 108// is mapped to ctrl+at instead of ctrl+space. 109func (l LegacyKeyEncoding )CtrlAt (v bool )LegacyKeyEncoding { 110if v { 111l |= flagCtrlAt 112 }else { 113l &^=flagCtrlAt 114 } 115return l 116} 117 118// CtrlI returns a [LegacyKeyEncoding] with whether [ansi.HT] (0x09) 119// is mapped to ctrl+i instead of the tab key. 120func (l LegacyKeyEncoding )CtrlI (v bool )LegacyKeyEncoding { 121if v { 122l |= flagCtrlI 123 }else { 124l &^=flagCtrlI 125 } 126return l 127} 128 129// CtrlM returns a [LegacyKeyEncoding] with whether [ansi.CR] (0x0D) 130// is mapped to ctrl+m instead of the enter key. 131func (l LegacyKeyEncoding )CtrlM (v bool )LegacyKeyEncoding { 132if v { 133l |= flagCtrlM 134 }else { 135l &^=flagCtrlM 136 } 137return l 138} 139 140// CtrlOpenBracket returns a [LegacyKeyEncoding] with whether [ansi.ESC] (0x1B) 141// is mapped to ctrl+[ instead of the escape key. 142func (l LegacyKeyEncoding )CtrlOpenBracket (v bool )LegacyKeyEncoding { 143if v { 144l |= flagCtrlOpenBracket 145 }else { 146l &^=flagCtrlOpenBracket 147 } 148return l 149} 150 151// Backspace returns a [LegacyKeyEncoding] with whether the backspace key is 152// mapped to [ansi.BS] (0x08) instead of [ansi.DEL] (0x7F). 153func (l LegacyKeyEncoding )Backspace (v bool )LegacyKeyEncoding { 154if v { 155l |= flagBackspace 156 }else { 157l &^=flagBackspace 158 } 159return l 160} 161 162// Find returns a [LegacyKeyEncoding] with whether the legacy find key 163// is mapped to the home key. 164func (l LegacyKeyEncoding )Find (v bool )LegacyKeyEncoding { 165if v { 166l |= flagFind 167 }else { 168l &^=flagFind 169 } 170return l 171} 172 173// Select returns a [LegacyKeyEncoding] with whether the legacy select key 174// is mapped to the end key. 175func (l LegacyKeyEncoding )Select (v bool )LegacyKeyEncoding { 176if v { 177l |= flagSelect 178 }else { 179l &^=flagSelect 180 } 181return l 182} 183 184// FKeys returns a [LegacyKeyEncoding] with whether high function keys are 185// mapped to high function keys (beyond F20) instead of treating them as 186// Function+<modifiers> keys. 187func (l LegacyKeyEncoding )FKeys (v bool )LegacyKeyEncoding { 188if v { 189l |= flagFKeys 190 }else { 191l &^=flagFKeys 192 } 193return l 194} 195 196// EventDecoder decodes terminal input events from a byte buffer. Terminal 197// input events are typically encoded as Unicode or ASCII characters, control 198// codes, or ANSI escape sequences. 199type EventDecoder struct { 200// Legacy is the legacy key encoding flags. These flags control the 201// behavior of legacy terminal key encodings. See [LegacyKeyEncoding] for 202// more details. 203Legacy LegacyKeyEncoding 204// UseTerminfo is a flag that controls whether to use the terminal type 205// Terminfo database to map escape sequences to key events. This will 206// override the default key sequences handled by the parser. 207UseTerminfo bool 208 209lastCks uint32 // the last control key state for the previous key event record. 210} 211 212// Decode finds the first recognized event sequence and returns it along 213// with its length. 214// 215// It will return zero and nil no sequence is recognized or when the buffer is 216// empty. If a sequence is not supported, an [UnknownEvent] is returned. 217// 218// Example: 219// 220// ```go 221// var decoder EventDecoder 222// var events []Event 223// buf := []byte("\x00" + // Ctrl+Space 224// "\x1b[A" + // Up arrow 225// "Hello") // Text input 226// for len(buf) > 0 { 227// n, ev := decoder.Decode(buf) 228// if ev != nil { 229// events = append(events, ev) 230// } 231// buf = buf[n:] 232// } 233// ``` 234func (p * EventDecoder )Decode (buf []byte ) (n int ,Event Event ) { 235if len (buf )== 0 { 236return 0 ,nil 237 } 238 239switch b := buf [0 ];b { 240case ansi .ESC : 241if len (buf )== 1 { 242// Escape key 243return 1 ,KeyPressEvent {Code :KeyEscape } 244 } 245 246switch bPrime := buf [1 ];bPrime { 247case 'O' :// Esc-prefixed SS3 248return p .parseSs3 (buf ) 249case 'P' :// Esc-prefixed DCS 250return p .parseDcs (buf ) 251case '[' :// Esc-prefixed CSI 252return p .parseCsi (buf ) 253case ']' :// Esc-prefixed OSC 254return p .parseOsc (buf ) 255case '_' :// Esc-prefixed APC 256return p .parseApc (buf ) 257case '^' :// Esc-prefixed PM 258return p .parseStTerminated (ansi .PM ,'^' ,nil )(buf ) 259case 'X' :// Esc-prefixed SOS 260return p .parseStTerminated (ansi .SOS ,'X' ,nil )(buf ) 261default : 262n ,e := p .Decode (buf [1 :]) 263if k ,ok := e .(KeyPressEvent );ok { 264k .Text = "" 265k .Mod |= ModAlt 266return n + 1 ,k 267 } 268 269// Not a key sequence, nor an alt modified key sequence. In that 270// case, just report a single escape key. 271return 1 ,KeyPressEvent {Code :KeyEscape } 272 } 273case ansi .SS3 : 274return p .parseSs3 (buf ) 275case ansi .DCS : 276return p .parseDcs (buf ) 277case ansi .CSI : 278return p .parseCsi (buf ) 279case ansi .OSC : 280return p .parseOsc (buf ) 281case ansi .APC : 282return p .parseApc (buf ) 283case ansi .PM : 284return p .parseStTerminated (ansi .PM ,'^' ,nil )(buf ) 285case ansi .SOS : 286return p .parseStTerminated (ansi .SOS ,'X' ,nil )(buf ) 287default : 288if b <= ansi .US || b == ansi .DEL || b == ansi .SP { 289return 1 ,p .parseControl (b ) 290 }else if b >= ansi .PAD && b <= ansi .APC { 291// C1 control code 292// UTF-8 never starts with a C1 control code 293// Encode these as Ctrl+Alt+<code - 0x40> 294code := rune (b )- 0x40 295return 1 ,KeyPressEvent {Code :code ,Mod :ModCtrl | ModAlt } 296 } 297return p .parseUtf8 (buf ) 298 } 299} 300 301func (p * EventDecoder )parseCsi (b []byte ) (int ,Event ) { 302if len (b )== 2 && b [0 ]== ansi .ESC { 303// short cut if this is an alt+[ key 304return 2 ,KeyPressEvent {Code :rune (b [1 ]),Mod :ModAlt } 305 } 306 307var cmd ansi.Cmd 308var params [parser .MaxParamsSize ]ansi.Param 309var paramsLen int 310 311var i int 312if b [i ]== ansi .CSI || b [i ]== ansi .ESC { 313i ++ 314 } 315if i < len (b )&& b [i - 1 ]== ansi .ESC && b [i ]== '[' { 316i ++ 317 } 318 319// Initial CSI byte 320if i < len (b )&& b [i ]>= '<' && b [i ]<= '?' { 321cmd |= ansi .Cmd (b [i ])<< parser .PrefixShift 322 } 323 324// Scan parameter bytes in the range 0x30-0x3F 325var j int 326for j = 0 ;i < len (b )&& paramsLen < len (params )&& b [i ]>= 0x30 && b [i ]<= 0x3F ;i ,j = i + 1 ,j + 1 { 327if b [i ]>= '0' && b [i ]<= '9' { 328if params [paramsLen ]== parser .MissingParam { 329params [paramsLen ]= 0 330 } 331params [paramsLen ]*= 10 332params [paramsLen ]+= ansi .Param (b [i ])- '0' 333 } 334if b [i ]== ':' { 335params [paramsLen ]|= parser .HasMoreFlag 336 } 337if b [i ]== ';' || b [i ]== ':' { 338paramsLen ++ 339if paramsLen < len (params ) { 340// Don't overflow the params slice 341params [paramsLen ]= parser .MissingParam 342 } 343 } 344 } 345 346if j > 0 && paramsLen < len (params ) { 347// has parameters 348paramsLen ++ 349 } 350 351// Scan intermediate bytes in the range 0x20-0x2F 352var intermed byte 353for ;i < len (b )&& b [i ]>= 0x20 && b [i ]<= 0x2F ;i ++ { 354intermed = b [i ] 355 } 356 357// Set the intermediate byte 358cmd |= ansi .Cmd (intermed )<< parser .IntermedShift 359 360// Scan final byte in the range 0x40-0x7E 361if i >= len (b )|| b [i ]< 0x40 || b [i ]> 0x7E { 362// Special case for URxvt keys 363// CSI <number> $ is an invalid sequence, but URxvt uses it for 364// shift modified keys. 365if intermed == '$' && b [i - 1 ]== '$' { 366buf := slices .Clone (b [:i - 1 ]) 367n ,ev := p .parseCsi (append (buf ,'~' )) 368if k ,ok := ev .(KeyPressEvent );ok { 369k .Mod |= ModShift 370return n ,k 371 } 372 } 373return i ,UnknownEvent (b [:i ]) 374 } 375 376// Add the final byte 377cmd |= ansi .Cmd (b [i ]) 378i ++ 379 380pa := ansi .Params (params [:paramsLen ]) 381switch cmd { 382case 'y' | '?' << parser .PrefixShift | '$' << parser .IntermedShift : 383// Report Mode (DECRPM) 384mode ,_ ,ok := pa .Param (0 ,- 1 ) 385if ! ok || mode == - 1 { 386break 387 } 388value ,_ ,ok := pa .Param (1 ,0 ) 389if ! ok { 390break 391 } 392return i ,ModeReportEvent {Mode :ansi .DECMode (mode ),Value :ansi .ModeSetting (value )} 393case 'c' | '?' << parser .PrefixShift : 394// Primary Device Attributes 395return i ,parsePrimaryDevAttrs (pa ) 396case 'c' | '>' << parser .PrefixShift : 397// Secondary Device Attributes 398return i ,parseSecondaryDevAttrs (pa ) 399case 'u' | '?' << parser .PrefixShift : 400// Kitty keyboard flags 401flags ,_ ,_ := pa .Param (0 ,- 1 ) 402return i ,KeyboardEnhancementsEvent {flags } 403case 'R' | '?' << parser .PrefixShift : 404// This report may return a third parameter representing the page 405// number, but we don't really need it. 406row ,_ ,_ := pa .Param (0 ,1 ) 407col ,_ ,ok := pa .Param (1 ,1 ) 408if ! ok { 409break 410 } 411return i ,CursorPositionEvent {Y :row - 1 ,X :col - 1 } 412case 'm' | '<' << parser .PrefixShift ,'M' | '<' << parser .PrefixShift : 413// Handle SGR mouse 414if paramsLen == 3 { 415return i ,parseSGRMouseEvent (cmd ,pa ) 416 } 417case 'm' | '>' << parser .PrefixShift : 418// XTerm modifyOtherKeys 419mok ,_ ,ok := pa .Param (0 ,0 ) 420if ! ok || mok != 4 { 421break 422 } 423val ,_ ,ok := pa .Param (1 ,- 1 ) 424if ! ok || val == - 1 { 425break 426 } 427return i ,ModifyOtherKeysEvent {val } 428case 'n' | '?' << parser .PrefixShift : 429report ,_ ,_ := pa .Param (0 ,- 1 ) 430darkLight ,_ ,_ := pa .Param (1 ,- 1 ) 431switch report { 432case 997 :// [ansi.LightDarkReport] 433switch darkLight { 434case 1 : 435return i ,DarkColorSchemeEvent {} 436case 2 : 437return i ,LightColorSchemeEvent {} 438 } 439 } 440case 'I' : 441return i ,FocusEvent {} 442case 'O' : 443return i ,BlurEvent {} 444case 'R' : 445// Cursor position report OR modified F3 446row ,_ ,rok := pa .Param (0 ,1 ) 447col ,_ ,cok := pa .Param (1 ,1 ) 448if paramsLen == 2 && rok && cok { 449m := CursorPositionEvent {Y :row - 1 ,X :col - 1 } 450if row == 1 && col - 1 <= int (ModMeta | ModShift | ModAlt | ModCtrl ) { 451// XXX: We cannot differentiate between cursor position report and 452// CSI 1 ; <mod> R (which is modified F3) when the cursor is at the 453// row 1. In this case, we report both messages. 454// 455// For a non ambiguous cursor position report, use 456// [ansi.RequestExtendedCursorPosition] (DECXCPR) instead. 457return i ,MultiEvent {KeyPressEvent {Code :KeyF3 ,Mod :KeyMod (col - 1 )},m } 458 } 459 460return i ,m 461 } 462 463if paramsLen != 0 { 464break 465 } 466 467// Unmodified key F3 (CSI R) 468fallthrough 469case 'a' ,'b' ,'c' ,'d' ,'A' ,'B' ,'C' ,'D' ,'E' ,'F' ,'H' ,'P' ,'Q' ,'S' ,'Z' : 470var k KeyPressEvent 471switch cmd { 472case 'a' ,'b' ,'c' ,'d' : 473k = KeyPressEvent {Code :KeyUp + rune (cmd - 'a' ),Mod :ModShift } 474case 'A' ,'B' ,'C' ,'D' : 475k = KeyPressEvent {Code :KeyUp + rune (cmd - 'A' )} 476case 'E' : 477k = KeyPressEvent {Code :KeyBegin } 478case 'F' : 479k = KeyPressEvent {Code :KeyEnd } 480case 'H' : 481k = KeyPressEvent {Code :KeyHome } 482case 'P' ,'Q' ,'R' ,'S' : 483k = KeyPressEvent {Code :KeyF1 + rune (cmd - 'P' )} 484case 'Z' : 485k = KeyPressEvent {Code :KeyTab ,Mod :ModShift } 486 } 487id ,_ ,_ := pa .Param (0 ,1 ) 488mod ,_ ,_ := pa .Param (1 ,1 ) 489if paramsLen > 2 && ! pa [1 ].HasMore ()|| id != 1 { 490break 491 } 492if paramsLen > 1 && id == 1 && mod != - 1 { 493// CSI 1 ; <modifiers> A 494k .Mod |= KeyMod (mod - 1 ) 495 } 496// Don't forget to handle Kitty keyboard protocol 497return i ,parseKittyKeyboardExt (pa ,k ) 498case 'M' : 499// Handle X10 mouse 500if i + 3 > len (b ) { 501return i ,UnknownCsiEvent (b [:i ]) 502 } 503return i + 3 ,parseX10MouseEvent (append (b [:i ],b [i :i + 3 ]... )) 504case 'y' | '$' << parser .IntermedShift : 505// Report Mode (DECRPM) 506mode ,_ ,ok := pa .Param (0 ,- 1 ) 507if ! ok || mode == - 1 { 508break 509 } 510val ,_ ,ok := pa .Param (1 ,0 ) 511if ! ok { 512break 513 } 514return i ,ModeReportEvent {Mode :ansi .ANSIMode (mode ),Value :ansi .ModeSetting (val )} 515case 'u' : 516// Kitty keyboard protocol & CSI u (fixterms) 517if paramsLen == 0 { 518return i ,UnknownCsiEvent (b [:i ]) 519 } 520return i ,parseKittyKeyboard (pa ) 521case '_' : 522// Win32 Input Mode 523if paramsLen != 6 { 524return i ,UnknownCsiEvent (b [:i ]) 525 } 526 527vk ,_ ,_ := pa .Param (0 ,0 ) 528sc ,_ ,_ := pa .Param (1 ,0 ) 529uc ,_ ,_ := pa .Param (2 ,0 ) 530kd ,_ ,_ := pa .Param (3 ,0 ) 531cs ,_ ,_ := pa .Param (4 ,0 ) 532rc ,_ ,_ := pa .Param (5 ,0 ) 533event := p .parseWin32InputKeyEvent ( 534uint16 (vk ),//nolint:gosec // Vk wVirtualKeyCode 535uint16 (sc ),//nolint:gosec // Sc wVirtualScanCode 536rune (uc ),// Uc UnicodeChar 537kd == 1 ,// Kd bKeyDown 538uint32 (cs ),//nolint:gosec // Cs dwControlKeyState 539max (1 ,uint16 (rc )),//nolint:gosec // Rc wRepeatCount 540 ) 541 542return i ,event 543case '@' ,'^' ,'~' : 544if paramsLen == 0 { 545return i ,UnknownCsiEvent (b [:i ]) 546 } 547 548param ,_ ,_ := pa .Param (0 ,0 ) 549switch cmd { 550case '~' : 551switch param { 552case 27 : 553// XTerm modifyOtherKeys 2 554if paramsLen != 3 { 555return i ,UnknownCsiEvent (b [:i ]) 556 } 557return i ,parseXTermModifyOtherKeys (pa ) 558case 200 : 559// bracketed-paste start 560return i ,PasteStartEvent {} 561case 201 : 562// bracketed-paste end 563return i ,PasteEndEvent {} 564 } 565 } 566 567switch param { 568case 1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 , 56911 ,12 ,13 ,14 ,15 , 57017 ,18 ,19 ,20 ,21 , 57123 ,24 ,25 ,26 , 57228 ,29 ,31 ,32 ,33 ,34 : 573var k KeyPressEvent 574switch param { 575case 1 : 576if p .Legacy & flagFind != 0 { 577k = KeyPressEvent {Code :KeyFind } 578 }else { 579k = KeyPressEvent {Code :KeyHome } 580 } 581case 2 : 582k = KeyPressEvent {Code :KeyInsert } 583case 3 : 584k = KeyPressEvent {Code :KeyDelete } 585case 4 : 586if p .Legacy & flagSelect != 0 { 587k = KeyPressEvent {Code :KeySelect } 588 }else { 589k = KeyPressEvent {Code :KeyEnd } 590 } 591case 5 : 592k = KeyPressEvent {Code :KeyPgUp } 593case 6 : 594k = KeyPressEvent {Code :KeyPgDown } 595case 7 : 596k = KeyPressEvent {Code :KeyHome } 597case 8 : 598k = KeyPressEvent {Code :KeyEnd } 599case 11 ,12 ,13 ,14 ,15 : 600k = KeyPressEvent {Code :KeyF1 + rune (param - 11 )} 601case 17 ,18 ,19 ,20 ,21 : 602k = KeyPressEvent {Code :KeyF6 + rune (param - 17 )} 603case 23 ,24 ,25 ,26 : 604k = KeyPressEvent {Code :KeyF11 + rune (param - 23 )} 605case 28 ,29 : 606k = KeyPressEvent {Code :KeyF15 + rune (param - 28 )} 607case 31 ,32 ,33 ,34 : 608k = KeyPressEvent {Code :KeyF17 + rune (param - 31 )} 609 } 610 611// modifiers 612mod ,_ ,_ := pa .Param (1 ,- 1 ) 613if paramsLen > 1 && mod != - 1 { 614k .Mod |= KeyMod (mod - 1 ) 615 } 616 617// Handle URxvt weird keys 618switch cmd { 619case '~' : 620// Don't forget to handle Kitty keyboard protocol 621return i ,parseKittyKeyboardExt (pa ,k ) 622case '^' : 623k .Mod |= ModCtrl 624case '@' : 625k .Mod |= ModCtrl | ModShift 626 } 627 628return i ,k 629 } 630 631case 't' : 632param ,_ ,ok := pa .Param (0 ,0 ) 633if ! ok { 634break 635 } 636 637switch param { 638case 4 :// Report Terminal window size in pixels. 639if paramsLen == 3 { 640height ,_ ,hOk := pa .Param (1 ,0 ) 641width ,_ ,wOk := pa .Param (2 ,0 ) 642if ! hOk || ! wOk { 643break 644 } 645return i ,PixelSizeEvent {Width :width ,Height :height } 646 } 647case 6 :// Report Terminal character cell size. 648if paramsLen == 3 { 649height ,_ ,hOk := pa .Param (1 ,0 ) 650width ,_ ,wOk := pa .Param (2 ,0 ) 651if ! hOk || ! wOk { 652break 653 } 654return i ,CellSizeEvent {Width :width ,Height :height } 655 } 656case 8 :// Report Terminal Window size in cells. 657if paramsLen == 3 { 658height ,_ ,hOk := pa .Param (1 ,0 ) 659width ,_ ,wOk := pa .Param (2 ,0 ) 660if ! hOk || ! wOk { 661break 662 } 663return i ,WindowSizeEvent {Width :width ,Height :height } 664 } 665case 48 :// In band terminal size report. 666if paramsLen == 5 { 667cellHeight ,_ ,chOk := pa .Param (1 ,0 ) 668cellWidth ,_ ,cwOk := pa .Param (2 ,0 ) 669pixelHeight ,_ ,phOk := pa .Param (3 ,0 ) 670pixelWidth ,_ ,pwOk := pa .Param (4 ,0 ) 671if ! chOk || ! cwOk || ! phOk || ! pwOk { 672break 673 } 674return i ,MultiEvent { 675WindowSizeEvent {Width :cellWidth ,Height :cellHeight }, 676PixelSizeEvent {Width :pixelWidth ,Height :pixelHeight }, 677 } 678 } 679 } 680 681// Any other window operation event. 682 683var winop WindowOpEvent 684winop .Op = param 685for j := 1 ;j < paramsLen ;j ++ { 686val ,_ ,ok := pa .Param (j ,0 ) 687if ok { 688winop .Args = append (winop .Args ,val ) 689 } 690 } 691 692return i ,winop 693 } 694return i ,UnknownCsiEvent (b [:i ]) 695} 696 697// parseSs3 parses a SS3 sequence. 698// See https://vt100.net/docs/vt220-rm/chapter4.html#S4.4.4.2 699func (p * EventDecoder )parseSs3 (b []byte ) (int ,Event ) { 700if len (b )== 2 && b [0 ]== ansi .ESC { 701// short cut if this is an alt+O key 702return 2 ,KeyPressEvent {Code :unicode .ToLower (rune (b [1 ])),Mod :ModShift | ModAlt } 703 } 704 705var i int 706if b [i ]== ansi .SS3 || b [i ]== ansi .ESC { 707i ++ 708 } 709if i < len (b )&& b [i - 1 ]== ansi .ESC && b [i ]== 'O' { 710i ++ 711 } 712 713// Scan numbers from 0-9 714var mod int 715for ;i < len (b )&& b [i ]>= '0' && b [i ]<= '9' ;i ++ { 716mod *= 10 717mod += int (b [i ])- '0' 718 } 719 720// Scan a GL character 721// A GL character is a single byte in the range 0x21-0x7E 722// See https://vt100.net/docs/vt220-rm/chapter2.html#S2.3.2 723if i >= len (b )|| b [i ]< 0x21 || b [i ]> 0x7E { 724return i ,UnknownEvent (b [:i ]) 725 } 726 727// GL character(s) 728gl := b [i ] 729i ++ 730 731var k KeyPressEvent 732switch gl { 733case 'a' ,'b' ,'c' ,'d' : 734k = KeyPressEvent {Code :KeyUp + rune (gl - 'a' ),Mod :ModCtrl } 735case 'A' ,'B' ,'C' ,'D' : 736k = KeyPressEvent {Code :KeyUp + rune (gl - 'A' )} 737case 'E' : 738k = KeyPressEvent {Code :KeyBegin } 739case 'F' : 740k = KeyPressEvent {Code :KeyEnd } 741case 'H' : 742k = KeyPressEvent {Code :KeyHome } 743case 'P' ,'Q' ,'R' ,'S' : 744k = KeyPressEvent {Code :KeyF1 + rune (gl - 'P' )} 745case 'M' : 746k = KeyPressEvent {Code :KeyKpEnter } 747case 'X' : 748k = KeyPressEvent {Code :KeyKpEqual } 749case 'j' ,'k' ,'l' ,'m' ,'n' ,'o' ,'p' ,'q' ,'r' ,'s' ,'t' ,'u' ,'v' ,'w' ,'x' ,'y' : 750k = KeyPressEvent {Code :KeyKpMultiply + rune (gl - 'j' )} 751default : 752return i ,UnknownSs3Event (b [:i ]) 753 } 754 755// Handle weird SS3 <modifier> Func 756if mod > 0 { 757k .Mod |= KeyMod (mod - 1 ) 758 } 759 760return i ,k 761} 762 763func (p * EventDecoder )parseOsc (b []byte ) (int ,Event ) { 764defaultKey := func ()KeyPressEvent { 765return KeyPressEvent {Code :rune (b [1 ]),Mod :ModAlt } 766 } 767if len (b )== 2 && b [0 ]== ansi .ESC { 768// short cut if this is an alt+] key 769return 2 ,defaultKey () 770 } 771 772var i int 773if b [i ]== ansi .OSC || b [i ]== ansi .ESC { 774i ++ 775 } 776if i < len (b )&& b [i - 1 ]== ansi .ESC && b [i ]== ']' { 777i ++ 778 } 779 780// Parse OSC command 781// An OSC sequence is terminated by a BEL, ESC, or ST character 782var start ,end int 783cmd := - 1 784for ;i < len (b )&& b [i ]>= '0' && b [i ]<= '9' ;i ++ { 785if cmd == - 1 { 786cmd = 0 787 }else { 788cmd *= 10 789 } 790cmd += int (b [i ])- '0' 791 } 792 793if i < len (b )&& b [i ]== ';' { 794// mark the start of the sequence data 795i ++ 796start = i 797 } 798 799for ;i < len (b );i ++ { 800// advance to the end of the sequence 801if slices .Contains ([]byte {ansi .BEL ,ansi .ESC ,ansi .ST ,ansi .CAN ,ansi .SUB },b [i ]) { 802break 803 } 804 } 805 806if i >= len (b ) { 807return i ,UnknownEvent (b [:i ]) 808 } 809 810end = i // end of the sequence data 811i ++ 812 813// Check 7-bit ST (string terminator) character 814switch b [i - 1 ] { 815case ansi .CAN ,ansi .SUB : 816return i ,ignoredEvent (b [:i ]) 817case ansi .ESC : 818if i >= len (b )|| b [i ]!= '\\' { 819if cmd == - 1 || (start == 0 && end == 2 ) { 820return 2 ,defaultKey () 821 } 822 823// If we don't have a valid ST terminator, then this is a 824// cancelled sequence and should be ignored. 825return i ,ignoredEvent (b [:i ]) 826 } 827 828i ++ 829 } 830 831if end <= start { 832return i ,UnknownEvent (b [:i ]) 833 } 834 835data := string (b [start :end ]) 836switch cmd { 837case 10 : 838return i ,ForegroundColorEvent {ansi .XParseColor (data )} 839case 11 : 840return i ,BackgroundColorEvent {ansi .XParseColor (data )} 841case 12 : 842return i ,CursorColorEvent {ansi .XParseColor (data )} 843case 52 : 844parts := strings .Split (data ,";" ) 845if len (parts )!= 2 || len (parts [0 ])< 1 { 846return i ,ClipboardEvent {} 847 } 848 849b64 := parts [1 ] 850bts ,err := base64 .StdEncoding .DecodeString (b64 ) 851if err != nil { 852return i ,ClipboardEvent {Content :parts [1 ]} 853 } 854 855sel := ClipboardSelection (parts [0 ][0 ])//nolint:unconvert 856return i ,ClipboardEvent {Selection :sel ,Content :string (bts )} 857 } 858 859return i ,UnknownOscEvent (b [:i ]) 860} 861 862// parseStTerminated parses a control sequence that gets terminated by a ST character. 863func (p * EventDecoder )parseStTerminated (intro8 ,intro7 byte ,fn func ([]byte )Event )func ([]byte ) (int ,Event ) { 864defaultKey := func (b []byte ) (int ,Event ) { 865switch intro8 { 866case ansi .SOS : 867return 2 ,KeyPressEvent {Code :unicode .ToLower (rune (b [1 ])),Mod :ModShift | ModAlt } 868case ansi .PM ,ansi .APC : 869return 2 ,KeyPressEvent {Code :rune (b [1 ]),Mod :ModAlt } 870 } 871return 0 ,nil 872 } 873return func (b []byte ) (int ,Event ) { 874if len (b )== 2 && b [0 ]== ansi .ESC { 875return defaultKey (b ) 876 } 877 878var i int 879if b [i ]== intro8 || b [i ]== ansi .ESC { 880i ++ 881 } 882if i < len (b )&& b [i - 1 ]== ansi .ESC && b [i ]== intro7 { 883i ++ 884 } 885 886// Scan control sequence 887// Most common control sequence is terminated by a ST character 888// ST is a 7-bit string terminator character is (ESC \) 889start := i 890for ;i < len (b );i ++ { 891if slices .Contains ([]byte {ansi .ESC ,ansi .ST ,ansi .CAN ,ansi .SUB },b [i ]) { 892break 893 } 894 } 895 896if i >= len (b ) { 897return i ,UnknownEvent (b [:i ]) 898 } 899 900end := i // end of the sequence data 901i ++ 902 903// Check 7-bit ST (string terminator) character 904switch b [i - 1 ] { 905case ansi .CAN ,ansi .SUB : 906return i ,ignoredEvent (b [:i ]) 907case ansi .ESC : 908if i >= len (b )|| b [i ]!= '\\' { 909if start == end { 910return defaultKey (b ) 911 } 912 913// If we don't have a valid ST terminator, then this is a 914// cancelled sequence and should be ignored. 915return i ,ignoredEvent (b [:i ]) 916 } 917 918i ++ 919 } 920 921// Call the function to parse the sequence and return the result 922if fn != nil { 923if e := fn (b [start :end ]);e != nil { 924return i ,e 925 } 926 } 927 928switch intro8 { 929case ansi .PM : 930return i ,UnknownPmEvent (b [:i ]) 931case ansi .SOS : 932return i ,UnknownSosEvent (b [:i ]) 933case ansi .APC : 934return i ,UnknownApcEvent (b [:i ]) 935default : 936return i ,UnknownEvent (b [:i ]) 937 } 938 } 939} 940 941func (p * EventDecoder )parseDcs (b []byte ) (int ,Event ) { 942if len (b )== 2 && b [0 ]== ansi .ESC { 943// short cut if this is an alt+P key 944return 2 ,KeyPressEvent {Code :unicode .ToLower (rune (b [1 ])),Mod :ModShift | ModAlt } 945 } 946 947var params [16 ]ansi.Param 948var paramsLen int 949var cmd ansi.Cmd 950 951// DCS sequences are introduced by DCS (0x90) or ESC P (0x1b 0x50) 952var i int 953if b [i ]== ansi .DCS || b [i ]== ansi .ESC { 954i ++ 955 } 956if i < len (b )&& b [i - 1 ]== ansi .ESC && b [i ]== 'P' { 957i ++ 958 } 959 960// initial DCS byte 961if i < len (b )&& b [i ]>= '<' && b [i ]<= '?' { 962cmd |= ansi .Cmd (b [i ])<< parser .PrefixShift 963 } 964 965// Scan parameter bytes in the range 0x30-0x3F 966var j int 967for j = 0 ;i < len (b )&& paramsLen < len (params )&& b [i ]>= 0x30 && b [i ]<= 0x3F ;i ,j = i + 1 ,j + 1 { 968if b [i ]>= '0' && b [i ]<= '9' { 969if params [paramsLen ]== parser .MissingParam { 970params [paramsLen ]= 0 971 } 972params [paramsLen ]*= 10 973params [paramsLen ]+= ansi .Param (b [i ])- '0' 974 } 975if b [i ]== ':' { 976params [paramsLen ]|= parser .HasMoreFlag 977 } 978if b [i ]== ';' || b [i ]== ':' { 979paramsLen ++ 980if paramsLen < len (params ) { 981// Don't overflow the params slice 982params [paramsLen ]= parser .MissingParam 983 } 984 } 985 } 986 987if j > 0 && paramsLen < len (params ) { 988// has parameters 989paramsLen ++ 990 } 991 992// Scan intermediate bytes in the range 0x20-0x2F 993var intermed byte 994for j := 0 ;i < len (b )&& b [i ]>= 0x20 && b [i ]<= 0x2F ;i ,j = i + 1 ,j + 1 { 995intermed = b [i ] 996 } 997 998// set intermediate byte 999cmd |= ansi .Cmd (intermed )<< parser .IntermedShift 1000 1001// Scan final byte in the range 0x40-0x7E 1002if i >= len (b )|| b [i ]< 0x40 || b [i ]> 0x7E { 1003return i ,UnknownEvent (b [:i ]) 1004 } 1005 1006// Add the final byte 1007cmd |= ansi .Cmd (b [i ]) 1008i ++ 1009 1010start := i // start of the sequence data 1011for ;i < len (b );i ++ { 1012if b [i ]== ansi .ST || b [i ]== ansi .ESC { 1013break 1014 } 1015 } 1016 1017if i >= len (b ) { 1018return i ,UnknownEvent (b [:i ]) 1019 } 1020 1021end := i // end of the sequence data 1022i ++ 1023 1024// Check 7-bit ST (string terminator) character 1025if i < len (b )&& b [i - 1 ]== ansi .ESC && b [i ]== '\\' { 1026i ++ 1027 } 1028 1029pa := ansi .Params (params [:paramsLen ]) 1030switch cmd { 1031case 'r' | '+' << parser .IntermedShift : 1032// XTGETTCAP responses 1033param ,_ ,_ := pa .Param (0 ,0 ) 1034switch param { 1035case 1 :// 1 means valid response, 0 means invalid response 1036tc := parseTermcap (b [start :end ]) 1037// XXX: some terminals like KiTTY report invalid responses with 1038// their queries i.e. sending a query for "Tc" using "\x1bP+q5463\x1b\\" 1039// returns "\x1bP0+r5463\x1b\\". 1040// The specs says that invalid responses should be in the form of 1041// DCS 0 + r ST "\x1bP0+r\x1b\\" 1042// We ignore invalid responses and only send valid ones to the program. 1043// 1044// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands 1045return i ,tc 1046 } 1047case '|' | '>' << parser .PrefixShift : 1048// XTVersion response 1049return i ,TerminalVersionEvent {string (b [start :end ])} 1050case '|' | '!' << parser .IntermedShift : 1051// Teritary Device Attributes 1052return i ,parseTertiaryDevAttrs (b [start :end ]) 1053 } 1054 1055return i ,UnknownDcsEvent (b [:i ]) 1056} 1057 1058func (p * EventDecoder )parseApc (b []byte ) (int ,Event ) { 1059if len (b )== 2 && b [0 ]== ansi .ESC { 1060// short cut if this is an alt+_ key 1061return 2 ,KeyPressEvent {Code :rune (b [1 ]),Mod :ModAlt } 1062 } 1063 1064// APC sequences are introduced by APC (0x9f) or ESC _ (0x1b 0x5f) 1065return p .parseStTerminated (ansi .APC ,'_' ,func (b []byte )Event { 1066if len (b )== 0 { 1067return nil 1068 } 1069 1070switch b [0 ] { 1071case 'G' :// Kitty Graphics Protocol 1072var g KittyGraphicsEvent 1073parts := bytes .Split (b [1 :], []byte {';' }) 1074g .Options .UnmarshalText (parts [0 ])//nolint:errcheck,gosec 1075if len (parts )> 1 { 1076g .Payload = parts [1 ] 1077 } 1078return g 1079 } 1080 1081return nil 1082 })(b ) 1083} 1084 1085func (p * EventDecoder )parseUtf8 (b []byte ) (int ,Event ) { 1086if len (b )== 0 { 1087return 0 ,nil 1088 } 1089 1090c := b [0 ] 1091if c <= ansi .US || c == ansi .DEL { 1092// Control codes get handled by parseControl 1093return 1 ,p .parseControl (c ) 1094 }else if c > ansi .US && c < ansi .DEL { 1095// ASCII printable characters 1096code := rune (c ) 1097k := KeyPressEvent {Code :code ,Text :string (code )} 1098if unicode .IsUpper (code ) { 1099// Convert upper case letters to lower case + shift modifier 1100k .Code = unicode .ToLower (code ) 1101k .ShiftedCode = code 1102k .Mod |= ModShift 1103 } 1104 1105return 1 ,k 1106 } 1107 1108code ,_ := utf8 .DecodeRune (b ) 1109if code == utf8 .RuneError { 1110return 1 ,UnknownEvent (b [0 ]) 1111 } 1112 1113cluster ,_ ,_ ,_ := uniseg .FirstGraphemeCluster (b ,- 1 ) 1114text := string (cluster ) 1115for i := range text { 1116if i > 0 { 1117// Use [KeyExtended] for multi-rune graphemes 1118code = KeyExtended 1119break 1120 } 1121 } 1122 1123return len (cluster ),KeyPressEvent {Code :code ,Text :text } 1124} 1125 1126func (p * EventDecoder )parseControl (b byte )Event { 1127switch b { 1128case ansi .NUL : 1129if p .Legacy & flagCtrlAt != 0 { 1130return KeyPressEvent {Code :'@' ,Mod :ModCtrl } 1131 } 1132return KeyPressEvent {Code :KeySpace ,Mod :ModCtrl } 1133case ansi .BS : 1134return KeyPressEvent {Code :'h' ,Mod :ModCtrl } 1135case ansi .HT : 1136if p .Legacy & flagCtrlI != 0 { 1137return KeyPressEvent {Code :'i' ,Mod :ModCtrl } 1138 } 1139return KeyPressEvent {Code :KeyTab } 1140case ansi .CR : 1141if p .Legacy & flagCtrlM != 0 { 1142return KeyPressEvent {Code :'m' ,Mod :ModCtrl } 1143 } 1144return KeyPressEvent {Code :KeyEnter } 1145case ansi .ESC : 1146if p .Legacy & flagCtrlOpenBracket != 0 { 1147return KeyPressEvent {Code :'[' ,Mod :ModCtrl } 1148 } 1149return KeyPressEvent {Code :KeyEscape } 1150case ansi .DEL : 1151if p .Legacy & flagBackspace != 0 { 1152return KeyPressEvent {Code :KeyDelete } 1153 } 1154return KeyPressEvent {Code :KeyBackspace } 1155case ansi .SP : 1156return KeyPressEvent {Code :KeySpace ,Text :" " } 1157default : 1158if b >= ansi .SOH && b <= ansi .SUB { 1159// Use lower case letters for control codes 1160code := rune (b + 0x60 ) 1161return KeyPressEvent {Code :code ,Mod :ModCtrl } 1162 }else if b >= ansi .FS && b <= ansi .US { 1163code := rune (b + 0x40 ) 1164return KeyPressEvent {Code :code ,Mod :ModCtrl } 1165 } 1166return UnknownEvent (b ) 1167 } 1168} 1169 1170func parseXTermModifyOtherKeys (params ansi.Params )Event { 1171// XTerm modify other keys starts with ESC [ 27 ; <modifier> ; <code> ~ 1172xmod ,_ ,_ := params .Param (1 ,1 ) 1173xrune ,_ ,_ := params .Param (2 ,1 ) 1174mod := KeyMod (xmod - 1 ) 1175r := rune (xrune ) 1176 1177switch r { 1178case ansi .BS : 1179return KeyPressEvent {Mod :mod ,Code :KeyBackspace } 1180case ansi .HT : 1181return KeyPressEvent {Mod :mod ,Code :KeyTab } 1182case ansi .CR : 1183return KeyPressEvent {Mod :mod ,Code :KeyEnter } 1184case ansi .ESC : 1185return KeyPressEvent {Mod :mod ,Code :KeyEscape } 1186case ansi .DEL : 1187return KeyPressEvent {Mod :mod ,Code :KeyBackspace } 1188 } 1189 1190// CSI 27 ; <modifier> ; <code> ~ keys defined in XTerm modifyOtherKeys 1191k := KeyPressEvent {Code :r ,Mod :mod } 1192if k .Mod <= ModShift { 1193k .Text = string (r ) 1194 } 1195 1196return k 1197} 1198 1199// Kitty Clipboard Control Sequences. 1200var kittyKeyMap = map [int ]Key { 1201ansi .BS : {Code :KeyBackspace }, 1202ansi .HT : {Code :KeyTab }, 1203ansi .CR : {Code :KeyEnter }, 1204ansi .ESC : {Code :KeyEscape }, 1205ansi .DEL : {Code :KeyBackspace }, 1206 120757344 : {Code :KeyEscape }, 120857345 : {Code :KeyEnter }, 120957346 : {Code :KeyTab }, 121057347 : {Code :KeyBackspace }, 121157348 : {Code :KeyInsert }, 121257349 : {Code :KeyDelete }, 121357350 : {Code :KeyLeft }, 121457351 : {Code :KeyRight }, 121557352 : {Code :KeyUp }, 121657353 : {Code :KeyDown }, 121757354 : {Code :KeyPgUp }, 121857355 : {Code :KeyPgDown }, 121957356 : {Code :KeyHome }, 122057357 : {Code :KeyEnd }, 122157358 : {Code :KeyCapsLock }, 122257359 : {Code :KeyScrollLock }, 122357360 : {Code :KeyNumLock }, 122457361 : {Code :KeyPrintScreen }, 122557362 : {Code :KeyPause }, 122657363 : {Code :KeyMenu }, 122757364 : {Code :KeyF1 }, 122857365 : {Code :KeyF2 }, 122957366 : {Code :KeyF3 }, 123057367 : {Code :KeyF4 }, 123157368 : {Code :KeyF5 }, 123257369 : {Code :KeyF6 }, 123357370 : {Code :KeyF7 }, 123457371 : {Code :KeyF8 }, 123557372 : {Code :KeyF9 }, 123657373 : {Code :KeyF10 }, 123757374 : {Code :KeyF11 }, 123857375 : {Code :KeyF12 }, 123957376 : {Code :KeyF13 }, 124057377 : {Code :KeyF14 }, 124157378 : {Code :KeyF15 }, 124257379 : {Code :KeyF16 }, 124357380 : {Code :KeyF17 }, 124457381 : {Code :KeyF18 }, 124557382 : {Code :KeyF19 }, 124657383 : {Code :KeyF20 }, 124757384 : {Code :KeyF21 }, 124857385 : {Code :KeyF22 }, 124957386 : {Code :KeyF23 }, 125057387 : {Code :KeyF24 }, 125157388 : {Code :KeyF25 }, 125257389 : {Code :KeyF26 }, 125357390 : {Code :KeyF27 }, 125457391 : {Code :KeyF28 }, 125557392 : {Code :KeyF29 }, 125657393 : {Code :KeyF30 }, 125757394 : {Code :KeyF31 }, 125857395 : {Code :KeyF32 }, 125957396 : {Code :KeyF33 }, 126057397 : {Code :KeyF34 }, 126157398 : {Code :KeyF35 }, 126257399 : {Code :KeyKp0 }, 126357400 : {Code :KeyKp1 }, 126457401 : {Code :KeyKp2 }, 126557402 : {Code :KeyKp3 }, 126657403 : {Code :KeyKp4 }, 126757404 : {Code :KeyKp5 }, 126857405 : {Code :KeyKp6 }, 126957406 : {Code :KeyKp7 }, 127057407 : {Code :KeyKp8 }, 127157408 : {Code :KeyKp9 }, 127257409 : {Code :KeyKpDecimal }, 127357410 : {Code :KeyKpDivide }, 127457411 : {Code :KeyKpMultiply }, 127557412 : {Code :KeyKpMinus }, 127657413 : {Code :KeyKpPlus }, 127757414 : {Code :KeyKpEnter }, 127857415 : {Code :KeyKpEqual }, 127957416 : {Code :KeyKpSep }, 128057417 : {Code :KeyKpLeft }, 128157418 : {Code :KeyKpRight }, 128257419 : {Code :KeyKpUp }, 128357420 : {Code :KeyKpDown }, 128457421 : {Code :KeyKpPgUp }, 128557422 : {Code :KeyKpPgDown }, 128657423 : {Code :KeyKpHome }, 128757424 : {Code :KeyKpEnd }, 128857425 : {Code :KeyKpInsert }, 128957426 : {Code :KeyKpDelete }, 129057427 : {Code :KeyKpBegin }, 129157428 : {Code :KeyMediaPlay }, 129257429 : {Code :KeyMediaPause }, 129357430 : {Code :KeyMediaPlayPause }, 129457431 : {Code :KeyMediaReverse }, 129557432 : {Code :KeyMediaStop }, 129657433 : {Code :KeyMediaFastForward }, 129757434 : {Code :KeyMediaRewind }, 129857435 : {Code :KeyMediaNext }, 129957436 : {Code :KeyMediaPrev }, 130057437 : {Code :KeyMediaRecord }, 130157438 : {Code :KeyLowerVol }, 130257439 : {Code :KeyRaiseVol }, 130357440 : {Code :KeyMute }, 130457441 : {Code :KeyLeftShift }, 130557442 : {Code :KeyLeftCtrl }, 130657443 : {Code :KeyLeftAlt }, 130757444 : {Code :KeyLeftSuper }, 130857445 : {Code :KeyLeftHyper }, 130957446 : {Code :KeyLeftMeta }, 131057447 : {Code :KeyRightShift }, 131157448 : {Code :KeyRightCtrl }, 131257449 : {Code :KeyRightAlt }, 131357450 : {Code :KeyRightSuper }, 131457451 : {Code :KeyRightHyper }, 131557452 : {Code :KeyRightMeta }, 131657453 : {Code :KeyIsoLevel3Shift }, 131757454 : {Code :KeyIsoLevel5Shift }, 1318} 1319 1320func init () { 1321// These are some faulty C0 mappings some terminals such as WezTerm have 1322// and doesn't follow the specs. 1323kittyKeyMap [ansi .NUL ]= Key {Code :KeySpace ,Mod :ModCtrl } 1324for i := ansi .SOH ;i <= ansi .SUB ;i ++ { 1325if _ ,ok := kittyKeyMap [i ];! ok { 1326kittyKeyMap [i ]= Key {Code :rune (i + 0x60 ),Mod :ModCtrl } 1327 } 1328 } 1329for i := ansi .FS ;i <= ansi .US ;i ++ { 1330if _ ,ok := kittyKeyMap [i ];! ok { 1331kittyKeyMap [i ]= Key {Code :rune (i + 0x40 ),Mod :ModCtrl } 1332 } 1333 } 1334} 1335 1336const ( 1337kittyShift = 1 << iota 1338kittyAlt 1339kittyCtrl 1340kittySuper 1341kittyHyper 1342kittyMeta 1343kittyCapsLock 1344kittyNumLock 1345) 1346 1347func fromKittyMod (mod int )KeyMod { 1348var m KeyMod 1349if mod & kittyShift != 0 { 1350m |= ModShift 1351 } 1352if mod & kittyAlt != 0 { 1353m |= ModAlt 1354 } 1355if mod & kittyCtrl != 0 { 1356m |= ModCtrl 1357 } 1358if mod & kittySuper != 0 { 1359m |= ModSuper 1360 } 1361if mod & kittyHyper != 0 { 1362m |= ModHyper 1363 } 1364if mod & kittyMeta != 0 { 1365m |= ModMeta 1366 } 1367if mod & kittyCapsLock != 0 { 1368m |= ModCapsLock 1369 } 1370if mod & kittyNumLock != 0 { 1371m |= ModNumLock 1372 } 1373return m 1374} 1375 1376// parseKittyKeyboard parses a Kitty Keyboard Protocol sequence. 1377// 1378// In `CSI u`, this is parsed as: 1379// 1380// CSI codepoint ; modifiers u 1381// codepoint: ASCII Dec value 1382// 1383// The Kitty Keyboard Protocol extends this with optional components that can be 1384// enabled progressively. The full sequence is parsed as: 1385// 1386// CSI unicode-key-code:alternate-key-codes ; modifiers:event-type ; text-as-codepoints u 1387// 1388// See https://sw.kovidgoyal.net/kitty/keyboard-protocol/ 1389func parseKittyKeyboard (params ansi.Params ) (Event Event ) { 1390var isRelease bool 1391var key Key 1392 1393// The index of parameters separated by semicolons ';'. Sub parameters are 1394// separated by colons ':'. 1395var paramIdx int 1396var sudIdx int // The sub parameter index 1397for _ ,p := range params { 1398// Kitty Keyboard Protocol has 3 optional components. 1399switch paramIdx { 1400case 0 : 1401switch sudIdx { 1402case 0 : 1403var foundKey bool 1404code := p .Param (1 )// CSI u has a default value of 1 1405key ,foundKey = kittyKeyMap [code ] 1406if ! foundKey { 1407r := rune (code ) 1408if ! utf8 .ValidRune (r ) { 1409r = utf8 .RuneError 1410 } 1411 1412key .Code = r 1413 } 1414 1415case 2 : 1416// shifted key + base key 1417if b := rune (p .Param (1 ));unicode .IsPrint (b ) { 1418// XXX: When alternate key reporting is enabled, the protocol 1419// can return 3 things, the unicode codepoint of the key, 1420// the shifted codepoint of the key, and the standard 1421// PC-101 key layout codepoint. 1422// This is useful to create an unambiguous mapping of keys 1423// when using a different language layout. 1424key .BaseCode = b 1425 } 1426fallthrough 1427 1428case 1 : 1429// shifted key 1430if s := rune (p .Param (1 ));unicode .IsPrint (s ) { 1431// XXX: We swap keys here because we want the shifted key 1432// to be the Rune that is returned by the event. 1433// For example, shift+a should produce "A" not "a". 1434// In such a case, we set AltRune to the original key "a" 1435// and Rune to "A". 1436key .ShiftedCode = s 1437 } 1438 } 1439case 1 : 1440switch sudIdx { 1441case 0 : 1442mod := p .Param (1 ) 1443if mod > 1 { 1444key .Mod = fromKittyMod (mod - 1 ) 1445if key .Mod > ModShift { 1446// XXX: We need to clear the text if we have a modifier key 1447// other than a [ModShift] key. 1448key .Text = "" 1449 } 1450 } 1451 1452case 1 : 1453switch p .Param (1 ) { 1454case 2 : 1455key .IsRepeat = true 1456case 3 : 1457isRelease = true 1458 } 1459case 2 : 1460 } 1461case 2 : 1462if code := p .Param (0 );code != 0 { 1463key .Text += string (rune (code )) 1464 } 1465 } 1466 1467sudIdx ++ 1468if ! p .HasMore () { 1469paramIdx ++ 1470sudIdx = 0 1471 } 1472 } 1473 1474keyMod := key .Mod 1475 1476// Remove these lock modifiers from now on since they don't affect the text. 1477keyMod &^=ModNumLock 1478// keyMod &^= ModScrollLock // Kitty doesn't support scroll lock 1479 1480printMod := keyMod <= ModShift || keyMod == ModCapsLock || keyMod == (ModShift | ModCapsLock ) 1481printKeyPad := key .Code >= KeyKpEqual && key .Code <= KeyKpSep 1482if len (key .Text )== 0 && printKeyPad && printMod { 1483switch { 1484case key .Code >= KeyKp0 && key .Code <= KeyKp9 : 1485key .Text = string ('0' + key .Code - KeyKp0 ) 1486case key .Code == KeyKpEqual : 1487key .Text = "=" 1488case key .Code == KeyKpMultiply : 1489key .Text = "*" 1490case key .Code == KeyKpPlus : 1491key .Text = "+" 1492case key .Code == KeyKpMinus : 1493key .Text = "-" 1494case key .Code == KeyKpDecimal : 1495key .Text = "." 1496case key .Code == KeyKpDivide : 1497key .Text = "/" 1498case key .Code == KeyKpSep : 1499key .Text = "," 1500 } 1501 } 1502 1503//nolint:nestif 1504if len (key .Text )== 0 && unicode .IsPrint (key .Code )&& printMod { 1505if keyMod == 0 { 1506key .Text = string (key .Code ) 1507 }else { 1508desiredCase := unicode .ToLower 1509if keyMod .Contains (ModShift )|| keyMod .Contains (ModCapsLock ) { 1510desiredCase = unicode .ToUpper 1511 } 1512if key .ShiftedCode != 0 { 1513key .Text = string (key .ShiftedCode ) 1514 }else { 1515key .Text = string (desiredCase (key .Code )) 1516 } 1517 } 1518 } 1519 1520if isRelease { 1521return KeyReleaseEvent (key ) 1522 } 1523 1524return KeyPressEvent (key ) 1525} 1526 1527// parseKittyKeyboardExt parses a Kitty Keyboard Protocol sequence extensions 1528// for non CSI u sequences. This includes things like CSI A, SS3 A and others, 1529// and CSI ~. 1530func parseKittyKeyboardExt (params ansi.Params ,k KeyPressEvent )Event { 1531// Handle Kitty keyboard protocol 1532if len (params )> 2 && // We have at least 3 parameters 1533params [1 ].HasMore () {// The second parameter is a subparameter (separated by a ":") 1534switch params [2 ].Param (1 ) {// The third parameter is the event type (defaults to 1) 1535case 2 : 1536k .IsRepeat = true 1537case 3 : 1538return KeyReleaseEvent (k ) 1539 } 1540 } 1541return k 1542} 1543 1544func parsePrimaryDevAttrs (params ansi.Params )Event { 1545// Primary Device Attributes 1546da1 := make (PrimaryDeviceAttributesEvent ,len (params )) 1547for i ,p := range params { 1548if ! p .HasMore () { 1549da1 [i ]= p .Param (0 ) 1550 } 1551 } 1552return da1 1553} 1554 1555func parseSecondaryDevAttrs (params ansi.Params )Event { 1556// Secondary Device Attributes 1557da2 := make (SecondaryDeviceAttributesEvent ,len (params )) 1558for i ,p := range params { 1559if ! p .HasMore () { 1560da2 [i ]= p .Param (0 ) 1561 } 1562 } 1563return da2 1564} 1565 1566func parseTertiaryDevAttrs (b []byte )Event { 1567// Tertiary Device Attributes 1568// The response is a 4-digit hexadecimal number. 1569bts ,err := hex .DecodeString (string (b )) 1570if err != nil { 1571return UnknownDcsEvent (fmt .Sprintf ("\x1bP!|%s\x1b\\" ,b )) 1572 } 1573return TertiaryDeviceAttributesEvent (bts ) 1574} 1575 1576// Parse SGR-encoded mouse events; SGR extended mouse events. SGR mouse events 1577// look like: 1578// 1579// ESC [ < Cb ; Cx ; Cy (M or m) 1580// 1581// where: 1582// 1583// Cb is the encoded button code 1584// Cx is the x-coordinate of the mouse 1585// Cy is the y-coordinate of the mouse 1586// M is for button press, m is for button release 1587// 1588// https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Extended-coordinates 1589func parseSGRMouseEvent (cmd ansi.Cmd ,params ansi.Params )Event { 1590x ,_ ,ok := params .Param (1 ,1 ) 1591if ! ok { 1592x = 1 1593 } 1594y ,_ ,ok := params .Param (2 ,1 ) 1595if ! ok { 1596y = 1 1597 } 1598release := cmd .Final ()== 'm' 1599b ,_ ,_ := params .Param (0 ,0 ) 1600mod ,btn ,_ ,isMotion := parseMouseButton (b ) 1601 1602// (1,1) is the upper left. We subtract 1 to normalize it to (0,0). 1603x -- 1604y -- 1605 1606m := Mouse {X :x ,Y :y ,Button :btn ,Mod :mod } 1607 1608// Wheel buttons don't have release events 1609// Motion can be reported as a release event in some terminals (Windows Terminal) 1610if isWheel (m .Button ) { 1611return MouseWheelEvent (m ) 1612 }else if ! isMotion && release { 1613return MouseReleaseEvent (m ) 1614 }else if isMotion { 1615return MouseMotionEvent (m ) 1616 } 1617return MouseClickEvent (m ) 1618} 1619 1620const x10MouseByteOffset = 32 1621 1622// Parse X10-encoded mouse events; the simplest kind. The last release of X10 1623// was December 1986, by the way. The original X10 mouse protocol limits the Cx 1624// and Cy coordinates to 223 (=255-032). 1625// 1626// X10 mouse events look like: 1627// 1628// ESC [M Cb Cx Cy 1629// 1630// See: http://www.xfree86.org/current/ctlseqs.html#Mouse%20Tracking 1631func parseX10MouseEvent (buf []byte )Event { 1632v := buf [3 :6 ] 1633b := int (v [0 ]) 1634if b >= x10MouseByteOffset { 1635// XXX: b < 32 should be impossible, but we're being defensive. 1636b -= x10MouseByteOffset 1637 } 1638 1639mod ,btn ,isRelease ,isMotion := parseMouseButton (b ) 1640 1641// (1,1) is the upper left. We subtract 1 to normalize it to (0,0). 1642x := int (v [1 ])- x10MouseByteOffset - 1 1643y := int (v [2 ])- x10MouseByteOffset - 1 1644 1645m := Mouse {X :x ,Y :y ,Button :btn ,Mod :mod } 1646if isWheel (m .Button ) { 1647return MouseWheelEvent (m ) 1648 }else if isMotion { 1649return MouseMotionEvent (m ) 1650 }else if isRelease { 1651return MouseReleaseEvent (m ) 1652 } 1653return MouseClickEvent (m ) 1654} 1655 1656// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Extended-coordinates 1657func parseMouseButton (b int ) (mod KeyMod ,btn MouseButton ,isRelease bool ,isMotion bool ) { 1658// mouse bit shifts 1659const ( 1660bitShift = 0b0000_0100 1661bitAlt = 0b0000_1000 1662bitCtrl = 0b0001_0000 1663bitMotion = 0b0010_0000 1664bitWheel = 0b0100_0000 1665bitAdd = 0b1000_0000 // additional buttons 8-11 1666 1667bitsMask = 0b0000_0011 1668 ) 1669 1670// Modifiers 1671if b & bitAlt != 0 { 1672mod |= ModAlt 1673 } 1674if b & bitCtrl != 0 { 1675mod |= ModCtrl 1676 } 1677if b & bitShift != 0 { 1678mod |= ModShift 1679 } 1680 1681if b & bitAdd != 0 { 1682btn = MouseBackward + MouseButton (b & bitsMask ) 1683 }else if b & bitWheel != 0 { 1684btn = MouseWheelUp + MouseButton (b & bitsMask ) 1685 }else { 1686btn = MouseLeft + MouseButton (b & bitsMask ) 1687// X10 reports a button release as 0b0000_0011 (3) 1688if b & bitsMask == bitsMask { 1689btn = MouseNone 1690isRelease = true 1691 } 1692 } 1693 1694// Motion bit doesn't get reported for wheel events. 1695if b & bitMotion != 0 && ! isWheel (btn ) { 1696isMotion = true 1697 } 1698 1699return //nolint:nakedret 1700} 1701 1702// isWheel returns true if the mouse event is a wheel event. 1703func isWheel (btn MouseButton )bool { 1704return btn >= MouseWheelUp && btn <= MouseWheelRight 1705} 1706 1707type shiftable interface { 1708~ uint | ~ uint16 | ~ uint32 | ~ uint64 1709} 1710 1711func shift [T shiftable ](x T )T { 1712if x > 0xff { 1713x >>= 8 1714 } 1715return x 1716} 1717 1718func colorToHex (c color.Color )string { 1719if c == nil { 1720return "" 1721 } 1722r ,g ,b ,_ := c .RGBA () 1723return fmt .Sprintf ("#%02x%02x%02x" ,shift (r ),shift (g ),shift (b )) 1724} 1725 1726func getMaxMin (a ,b ,c float64 ) (ma ,mi float64 ) { 1727if a > b { 1728ma = a 1729mi = b 1730 }else { 1731ma = b 1732mi = a 1733 } 1734if c > ma { 1735ma = c 1736 }else if c < mi { 1737mi = c 1738 } 1739return ma ,mi 1740} 1741 1742func round (x float64 )float64 { 1743return math .Round (x * 1000 )/ 1000 1744} 1745 1746// rgbToHSL converts an RGB triple to an HSL triple. 1747func rgbToHSL (r ,g ,b uint8 ) (h ,s ,l float64 ) { 1748// convert uint32 pre-multiplied value to uint8 1749// The r,g,b values are divided by 255 to change the range from 0..255 to 0..1: 1750Rnot := float64 (r )/ 255 1751Gnot := float64 (g )/ 255 1752Bnot := float64 (b )/ 255 1753Cmax ,Cmin := getMaxMin (Rnot ,Gnot ,Bnot ) 1754Δ := Cmax - Cmin 1755// Lightness calculation: 1756l = (Cmax + Cmin )/ 2 1757// Hue and Saturation Calculation: 1758if Δ == 0 { 1759h = 0 1760s = 0 1761 }else { 1762switch Cmax { 1763case Rnot : 1764h = 60 * math .Mod ((Gnot - Bnot )/ Δ ,6 ) 1765case Gnot : 1766h = 60 * (((Bnot - Rnot )/ Δ )+ 2 ) 1767case Bnot : 1768h = 60 * (((Rnot - Gnot )/ Δ )+ 4 ) 1769 } 1770if h < 0 { 1771h += 360 1772 } 1773 1774s = Δ / (1 - math .Abs ((2 * l )- 1 )) 1775 } 1776 1777return h ,round (s ),round (l ) 1778} 1779 1780// isDarkColor returns whether the given color is dark. 1781func isDarkColor (c color.Color )bool { 1782if c == nil { 1783return true 1784 } 1785 1786r ,g ,b ,_ := c .RGBA () 1787_ ,_ ,l := rgbToHSL (uint8 (r >> 8 ),uint8 (g >> 8 ),uint8 (b >> 8 ))//nolint:gosec 1788return l < 0.5 1789} 1790 1791func parseTermcap (data []byte )CapabilityEvent { 1792// XTGETTCAP 1793if len (data )== 0 { 1794return CapabilityEvent {"" } 1795 } 1796 1797var tc strings.Builder 1798split := bytes .Split (data , []byte {';' }) 1799for _ ,s := range split { 1800parts := bytes .SplitN (s , []byte {'=' },2 ) 1801if len (parts )== 0 { 1802return CapabilityEvent {"" } 1803 } 1804 1805name ,err := hex .DecodeString (string (parts [0 ])) 1806if err != nil || len (name )== 0 { 1807continue 1808 } 1809 1810var value []byte 1811if len (parts )> 1 { 1812value ,err = hex .DecodeString (string (parts [1 ])) 1813if err != nil { 1814continue 1815 } 1816 } 1817 1818if tc .Len ()> 0 { 1819tc .WriteByte (';' ) 1820 } 1821tc .WriteString (string (name )) 1822if len (value )> 0 { 1823tc .WriteByte ('=' ) 1824tc .WriteString (string (value )) 1825 } 1826 } 1827 1828return CapabilityEvent {tc .String ()} 1829} 1830 1831// parseWin32InputKeyEvent converts a Windows Input Record Key Event into a 1832// KeyPressEvent, KeyReleaseEvent, or MultiEvent including multiple of the 1833// former type. 1834// A special case is when the key is either part of VtInputMode which produces 1835// vkc == 0, or when a key is a UTF-16 surrogate pair. In both of these cases, 1836// we need to handle key encoding and properly parse the key event. 1837func (p * EventDecoder )parseWin32InputKeyEvent (vkc uint16 ,_ uint16 ,r rune ,keyDown bool ,cks uint32 ,repeatCount uint16 ) (event Event ) { 1838defer func () { 1839// Respect the repeat count. 1840if repeatCount > 1 { 1841var multi MultiEvent 1842for i := 0 ;i < int (repeatCount );i ++ { 1843multi = append (multi ,event ) 1844 } 1845event = multi 1846 } 1847 }() 1848defer func () { 1849if vkc != 0 { 1850p .lastCks = cks 1851 } 1852 }() 1853 1854var key Key 1855switch { 1856case vkc == 0 : 1857// This is either a UTF-16 encoded pair, or an escape sequence waiting 1858// to be decoded. 1859if keyDown { 1860return KeyPressEvent {Code :0 ,BaseCode :r ,Mod :translateControlKeyState (cks )} 1861 } 1862return KeyReleaseEvent {Code :0 ,BaseCode :r ,Mod :translateControlKeyState (cks )} 1863case vkc == xwindows .VK_BACK : 1864key .BaseCode = KeyBackspace 1865case vkc == xwindows .VK_TAB : 1866key .BaseCode = KeyTab 1867case vkc == xwindows .VK_RETURN : 1868key .BaseCode = KeyEnter 1869case vkc == xwindows .VK_SHIFT : 1870//nolint:nestif 1871if cks & xwindows .SHIFT_PRESSED != 0 { 1872if cks & xwindows .ENHANCED_KEY != 0 { 1873key .BaseCode = KeyRightShift 1874 }else { 1875key .BaseCode = KeyLeftShift 1876 } 1877 }else if p .lastCks & xwindows .SHIFT_PRESSED != 0 { 1878if p .lastCks & xwindows .ENHANCED_KEY != 0 { 1879key .BaseCode = KeyRightShift 1880 }else { 1881key .BaseCode = KeyLeftShift 1882 } 1883 } 1884case vkc == xwindows .VK_CONTROL : 1885if cks & xwindows .LEFT_CTRL_PRESSED != 0 { 1886key .BaseCode = KeyLeftCtrl 1887 }else if cks & xwindows .RIGHT_CTRL_PRESSED != 0 { 1888key .BaseCode = KeyRightCtrl 1889 }else if p .lastCks & xwindows .LEFT_CTRL_PRESSED != 0 { 1890key .BaseCode = KeyLeftCtrl 1891 }else if p .lastCks & xwindows .RIGHT_CTRL_PRESSED != 0 { 1892key .BaseCode = KeyRightCtrl 1893 } 1894case vkc == xwindows .VK_MENU : 1895if cks & xwindows .LEFT_ALT_PRESSED != 0 { 1896key .BaseCode = KeyLeftAlt 1897 }else if cks & xwindows .RIGHT_ALT_PRESSED != 0 { 1898key .BaseCode = KeyRightAlt 1899 }else if p .lastCks & xwindows .LEFT_ALT_PRESSED != 0 { 1900key .BaseCode = KeyLeftAlt 1901 }else if p .lastCks & xwindows .RIGHT_ALT_PRESSED != 0 { 1902key .BaseCode = KeyRightAlt 1903 } 1904case vkc == xwindows .VK_PAUSE : 1905key .BaseCode = KeyPause 1906case vkc == xwindows .VK_CAPITAL : 1907key .BaseCode = KeyCapsLock 1908case vkc == xwindows .VK_ESCAPE : 1909key .BaseCode = KeyEscape 1910case vkc == xwindows .VK_SPACE : 1911key .BaseCode = KeySpace 1912case vkc == xwindows .VK_PRIOR : 1913key .BaseCode = KeyPgUp 1914case vkc == xwindows .VK_NEXT : 1915key .BaseCode = KeyPgDown 1916case vkc == xwindows .VK_END : 1917key .BaseCode = KeyEnd 1918case vkc == xwindows .VK_HOME : 1919key .BaseCode = KeyHome 1920case vkc == xwindows .VK_LEFT : 1921key .BaseCode = KeyLeft 1922case vkc == xwindows .VK_UP : 1923key .BaseCode = KeyUp 1924case vkc == xwindows .VK_RIGHT : 1925key .BaseCode = KeyRight 1926case vkc == xwindows .VK_DOWN : 1927key .BaseCode = KeyDown 1928case vkc == xwindows .VK_SELECT : 1929key .BaseCode = KeySelect 1930case vkc == xwindows .VK_SNAPSHOT : 1931key .BaseCode = KeyPrintScreen 1932case vkc == xwindows .VK_INSERT : 1933key .BaseCode = KeyInsert 1934case vkc == xwindows .VK_DELETE : 1935key .BaseCode = KeyDelete 1936case vkc >= '0' && vkc <= '9' : 1937key .BaseCode = rune (vkc ) 1938case vkc >= 'A' && vkc <= 'Z' : 1939// Convert to lowercase. 1940key .BaseCode = rune (vkc )+ 32 1941case vkc == xwindows .VK_LWIN : 1942key .BaseCode = KeyLeftSuper 1943case vkc == xwindows .VK_RWIN : 1944key .BaseCode = KeyRightSuper 1945case vkc == xwindows .VK_APPS : 1946key .BaseCode = KeyMenu 1947case vkc >= xwindows .VK_NUMPAD0 && vkc <= xwindows .VK_NUMPAD9 : 1948key .BaseCode = rune (vkc - xwindows .VK_NUMPAD0 )+ KeyKp0 1949key .Text = string ('0' + (rune (vkc )- xwindows .VK_NUMPAD0 )) 1950case vkc == xwindows .VK_MULTIPLY : 1951key .BaseCode = KeyKpMultiply 1952key .Text = "*" 1953case vkc == xwindows .VK_ADD : 1954key .BaseCode = KeyKpPlus 1955key .Text = "+" 1956case vkc == xwindows .VK_SEPARATOR : 1957key .BaseCode = KeyKpComma 1958key .Text = "," 1959case vkc == xwindows .VK_SUBTRACT : 1960key .BaseCode = KeyKpMinus 1961key .Text = "-" 1962case vkc == xwindows .VK_DECIMAL : 1963key .BaseCode = KeyKpDecimal 1964key .Text = "." 1965case vkc == xwindows .VK_DIVIDE : 1966key .BaseCode = KeyKpDivide 1967key .Text = "/" 1968case vkc >= xwindows .VK_F1 && vkc <= xwindows .VK_F24 : 1969key .BaseCode = rune (vkc - xwindows .VK_F1 )+ KeyF1 1970case vkc == xwindows .VK_NUMLOCK : 1971key .BaseCode = KeyNumLock 1972case vkc == xwindows .VK_SCROLL : 1973key .BaseCode = KeyScrollLock 1974case vkc == xwindows .VK_LSHIFT : 1975key .BaseCode = KeyLeftShift 1976case vkc == xwindows .VK_RSHIFT : 1977key .BaseCode = KeyRightShift 1978case vkc == xwindows .VK_LCONTROL : 1979key .BaseCode = KeyLeftCtrl 1980case vkc == xwindows .VK_RCONTROL : 1981key .BaseCode = KeyRightCtrl 1982case vkc == xwindows .VK_LMENU : 1983key .BaseCode = KeyLeftAlt 1984case vkc == xwindows .VK_RMENU : 1985key .BaseCode = KeyRightAlt 1986case vkc == xwindows .VK_VOLUME_MUTE : 1987key .BaseCode = KeyMute 1988case vkc == xwindows .VK_VOLUME_DOWN : 1989key .BaseCode = KeyLowerVol 1990case vkc == xwindows .VK_VOLUME_UP : 1991key .BaseCode = KeyRaiseVol 1992case vkc == xwindows .VK_MEDIA_NEXT_TRACK : 1993key .BaseCode = KeyMediaNext 1994case vkc == xwindows .VK_MEDIA_PREV_TRACK : 1995key .BaseCode = KeyMediaPrev 1996case vkc == xwindows .VK_MEDIA_STOP : 1997key .BaseCode = KeyMediaStop 1998case vkc == xwindows .VK_MEDIA_PLAY_PAUSE : 1999key .BaseCode = KeyMediaPlayPause 2000case vkc == xwindows .VK_OEM_1 : 2001key .BaseCode = ';' 2002case vkc == xwindows .VK_OEM_PLUS : 2003key .BaseCode = '+' 2004case vkc == xwindows .VK_OEM_COMMA : 2005key .BaseCode = ',' 2006case vkc == xwindows .VK_OEM_MINUS : 2007key .BaseCode = '-' 2008case vkc == xwindows .VK_OEM_PERIOD : 2009key .BaseCode = '.' 2010case vkc == xwindows .VK_OEM_2 : 2011key .BaseCode = '/' 2012case vkc == xwindows .VK_OEM_3 : 2013key .BaseCode = '`' 2014case vkc == xwindows .VK_OEM_4 : 2015key .BaseCode = '[' 2016case vkc == xwindows .VK_OEM_5 : 2017key .BaseCode = '\\' 2018case vkc == xwindows .VK_OEM_6 : 2019key .BaseCode = ']' 2020case vkc == xwindows .VK_OEM_7 : 2021key .BaseCode = '\'' 2022 } 2023 2024// AltGr is left ctrl + right alt. On non-US keyboards, this is used to type 2025// special characters and produce printable events. 2026// XXX: Should this be a KeyMod? 2027const altGrPressed = xwindows .LEFT_CTRL_PRESSED | xwindows .RIGHT_ALT_PRESSED 2028altGr := cks & altGrPressed == altGrPressed 2029 2030// Remove these lock keys from the control key state from now on. 2031cks &^=xwindows .NUMLOCK_ON 2032cks &^=xwindows .SCROLLLOCK_ON 2033key .Code = key .BaseCode 2034if ! unicode .IsControl (r ) { 2035key .Code = r 2036if unicode .IsPrint (key .Code )&& (cks == 0 || // no modifiers pressed 2037cks == xwindows .SHIFT_PRESSED || // shift pressed 2038cks == xwindows .CAPSLOCK_ON || // caps lock on 2039cks == (xwindows .SHIFT_PRESSED | xwindows .CAPSLOCK_ON )|| // Shift + caps lock pressed 2040altGr ) {// AltGr pressed 2041// If the control key state is 0, shift is pressed, or caps lock 2042// then the key event is a printable event i.e. [text] is not empty. 2043key .Text = string (key .Code ) 2044 } 2045 } 2046 2047key .Mod = translateControlKeyState (cks ) 2048key = ensureKeyCase (key ,cks ) 2049if keyDown { 2050return KeyPressEvent (key ) 2051 } 2052 2053return KeyReleaseEvent (key ) 2054} 2055 2056// ensureKeyCase ensures that the key's text is in the correct case based on the 2057// control key state. 2058func ensureKeyCase (key Key ,cks uint32 )Key { 2059if len (key .Text )== 0 { 2060return key 2061 } 2062 2063hasShift := cks & xwindows .SHIFT_PRESSED != 0 2064hasCaps := cks & xwindows .CAPSLOCK_ON != 0 2065if hasShift || hasCaps { 2066if unicode .IsLower (key .Code ) { 2067key .ShiftedCode = unicode .ToUpper (key .Code ) 2068key .Text = string (key .ShiftedCode ) 2069 } 2070 }else { 2071if unicode .IsUpper (key .Code ) { 2072key .ShiftedCode = unicode .ToLower (key .Code ) 2073key .Text = string (key .ShiftedCode ) 2074 } 2075 } 2076 2077return key 2078} 2079 2080// translateControlKeyState translates the control key state from the Windows 2081// Console API into a Mod bitmask. 2082func translateControlKeyState (cks uint32 ) (m KeyMod ) { 2083if cks & xwindows .LEFT_CTRL_PRESSED != 0 || cks & xwindows .RIGHT_CTRL_PRESSED != 0 { 2084m |= ModCtrl 2085 } 2086if cks & xwindows .LEFT_ALT_PRESSED != 0 || cks & xwindows .RIGHT_ALT_PRESSED != 0 { 2087m |= ModAlt 2088 } 2089if cks & xwindows .SHIFT_PRESSED != 0 { 2090m |= ModShift 2091 } 2092if cks & xwindows .CAPSLOCK_ON != 0 { 2093m |= ModCapsLock 2094 } 2095if cks & xwindows .NUMLOCK_ON != 0 { 2096m |= ModNumLock 2097 } 2098if cks & xwindows .SCROLLLOCK_ON != 0 { 2099m |= ModScrollLock 2100 } 2101return 2102}