diff --git a/terminfo/dynamic/dynamic.go b/terminfo/dynamic/dynamic.go index 5668e5cd..9ff0e482 100644 --- a/terminfo/dynamic/dynamic.go +++ b/terminfo/dynamic/dynamic.go @@ -367,22 +367,11 @@ func LoadTerminfo(name string) (*terminfo.Terminfo, string, error) { // ALL XTerm sequences dynamically if t.KeyShfHome == "\x1b[1;2H" && t.KeyShfEnd == "\x1b[1;2F" && t.KeyShfRight == "\x1b[1;2C" && t.KeyShfLeft == "\x1b[1;2D" { t.Modifiers = terminfo.ModifiersXTerm - } - // And the same thing for rxvt and workalikes (Eterm, aterm, etc.) // It seems that urxvt at least send escaped as ALT prefix for these, // although some places seem to indicate a separate ALT key sesquence. - if t.KeyShfRight == "\x1b[c" && t.KeyShfLeft == "\x1b[d" { - t.KeyShfUp = "\x1b[a" - t.KeyShfDown = "\x1b[b" - t.KeyCtrlUp = "\x1b[Oa" - t.KeyCtrlDown = "\x1b[Ob" - t.KeyCtrlRight = "\x1b[Oc" - t.KeyCtrlLeft = "\x1b[Od" - } - if t.KeyShfHome == "\x1b[7$" && t.KeyShfEnd == "\x1b[8$" { - t.KeyCtrlHome = "\x1b[7^" - t.KeyCtrlEnd = "\x1b[8^" + } else if t.KeyShfHome == "\x1b[7$" && t.KeyShfEnd == "\x1b[8$" && t.KeyShfRight == "\x1b[c" && t.KeyShfLeft == "\x1b[d" { + t.Modifiers = terminfo.ModifiersUrxvt } // If the kmous entry is present, then we need to record the diff --git a/terminfo/mkinfo.go b/terminfo/mkinfo.go index 9696b995..69fec547 100644 --- a/terminfo/mkinfo.go +++ b/terminfo/mkinfo.go @@ -284,6 +284,14 @@ func getinfo(name string) (*terminfo.Terminfo, string, error) { // keys, but it's nicer to match them to modifiers. if tc.getstr("kRIT") == "\x1b[1;2C" { t.Modifiers = terminfo.ModifiersXTerm + // And the same thing for rxvt + // It seems that urxvt at least send ESC as ALT prefix for these, + // although some places seem to indicate a separate ALT key sequence. + // Users are encouraged to update to an emulator that more closely + // matches xterm for better functionality. + } else if tc.getstr("kRIT") == "\x1b[c" && tc.getstr("kLFT") == "\x1b[d" && + tc.getstr("kHOM") == "\x1b[7$" && tc.getstr("kEND") == "\x1b[8$" { + t.Modifiers = terminfo.ModifiersUrxvt } else { // Lookup high level function keys. t.KeyShfInsert = tc.getstr("kIC") @@ -346,24 +354,6 @@ func getinfo(name string) (*terminfo.Terminfo, string, error) { t.KeyF64 = tc.getstr("kf64") } - // And the same thing for rxvt. - // It seems that urxvt at least send ESC as ALT prefix for these, - // although some places seem to indicate a separate ALT key sequence. - // Users are encouraged to update to an emulator that more closely - // matches xterm for better functionality. - if t.KeyShfRight == "\x1b[c" && t.KeyShfLeft == "\x1b[d" { - t.KeyShfUp = "\x1b[a" - t.KeyShfDown = "\x1b[b" - t.KeyCtrlUp = "\x1b[Oa" - t.KeyCtrlDown = "\x1b[Ob" - t.KeyCtrlRight = "\x1b[Oc" - t.KeyCtrlLeft = "\x1b[Od" - } - if t.KeyShfHome == "\x1b[7$" && t.KeyShfEnd == "\x1b[8$" { - t.KeyCtrlHome = "\x1b[7^" - t.KeyCtrlEnd = "\x1b[8^" - } - // Technically the RGB flag that is provided for xterm-direct is not // quite right. The problem is that the -direct flag that was introduced // with ncurses 6.1 requires a parsing for the parameters that we lack. diff --git a/terminfo/r/rxvt/term.go b/terminfo/r/rxvt/term.go index 8b9502fa..f1ac3bb0 100644 --- a/terminfo/r/rxvt/term.go +++ b/terminfo/r/rxvt/term.go @@ -371,31 +371,8 @@ func init() { KeyF8: "\x1b[19~", KeyF9: "\x1b[20~", KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyF13: "\x1b[25~", - KeyF14: "\x1b[26~", - KeyF15: "\x1b[28~", - KeyF16: "\x1b[29~", - KeyF17: "\x1b[31~", - KeyF18: "\x1b[32~", - KeyF19: "\x1b[33~", - KeyF20: "\x1b[34~", KeyBacktab: "\x1b[Z", - KeyShfLeft: "\x1b[d", - KeyShfRight: "\x1b[c", - KeyShfUp: "\x1b[a", - KeyShfDown: "\x1b[b", - KeyShfHome: "\x1b[7$", - KeyShfEnd: "\x1b[8$", - KeyShfInsert: "\x1b[2$", - KeyShfDelete: "\x1b[3$", - KeyCtrlUp: "\x1b[Oa", - KeyCtrlDown: "\x1b[Ob", - KeyCtrlRight: "\x1b[Oc", - KeyCtrlLeft: "\x1b[Od", - KeyCtrlHome: "\x1b[7^", - KeyCtrlEnd: "\x1b[8^", + Modifiers: 3, }) // rxvt-unicode terminal with 256 colors (X Window System) @@ -451,30 +428,7 @@ func init() { KeyF8: "\x1b[19~", KeyF9: "\x1b[20~", KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyF13: "\x1b[25~", - KeyF14: "\x1b[26~", - KeyF15: "\x1b[28~", - KeyF16: "\x1b[29~", - KeyF17: "\x1b[31~", - KeyF18: "\x1b[32~", - KeyF19: "\x1b[33~", - KeyF20: "\x1b[34~", KeyBacktab: "\x1b[Z", - KeyShfLeft: "\x1b[d", - KeyShfRight: "\x1b[c", - KeyShfUp: "\x1b[a", - KeyShfDown: "\x1b[b", - KeyShfHome: "\x1b[7$", - KeyShfEnd: "\x1b[8$", - KeyShfInsert: "\x1b[2$", - KeyShfDelete: "\x1b[3$", - KeyCtrlUp: "\x1b[Oa", - KeyCtrlDown: "\x1b[Ob", - KeyCtrlRight: "\x1b[Oc", - KeyCtrlLeft: "\x1b[Od", - KeyCtrlHome: "\x1b[7^", - KeyCtrlEnd: "\x1b[8^", + Modifiers: 3, }) } diff --git a/terminfo/terminfo.go b/terminfo/terminfo.go index 05080ee1..9a3f3206 100644 --- a/terminfo/terminfo.go +++ b/terminfo/terminfo.go @@ -221,6 +221,7 @@ const ( ModifiersNone = 0 ModifiersXTerm = 1 ModifiersDynamic = 2 + ModifiersUrxvt = 3 ) type stackElem struct { diff --git a/tscreen.go b/tscreen.go index 7e9c410a..f1dd24f1 100644 --- a/tscreen.go +++ b/tscreen.go @@ -334,6 +334,52 @@ func (t *tScreen) prepareXtermModifiers() { t.prepareKeyModXTerm(KeyF12, t.ti.KeyF12) } +func (t *tScreen) prepareKeyModUrxvt(key Key, val string) { + val = val[:len(val)-1] + t.prepareKeyMod(key, ModShift, val+"$") + t.prepareKeyMod(key, ModCtrl, val+"^") + t.prepareKeyMod(key, ModCtrl|ModShift, val+"@") +} + +func (t *tScreen) prepareUrxvtModifiers() { + if t.ti.Modifiers != terminfo.ModifiersUrxvt { + return + } + t.prepareKeyModUrxvt(KeyInsert, t.ti.KeyInsert) + t.prepareKeyModUrxvt(KeyDelete, t.ti.KeyDelete) + t.prepareKeyModUrxvt(KeyPgUp, t.ti.KeyPgUp) + t.prepareKeyModUrxvt(KeyPgDn, t.ti.KeyPgDn) + t.prepareKeyModUrxvt(KeyHome, t.ti.KeyHome) + t.prepareKeyModUrxvt(KeyEnd, t.ti.KeyEnd) + for i := 1; i <= 4; i++ { + key := Key(int(KeyUp)-1+i) + t.prepareKeyMod(key, ModShift, fmt.Sprintf("\x1b[%c", 96+i)) + t.prepareKeyMod(key, ModCtrl, fmt.Sprintf("\x1bO%c", 96+i)) + // we implement Ctrl-Shift as "application mode" for cursor keys + // in reality, urxvt treats Ctrl-Shift like Shift + t.prepareKeyMod(key, ModCtrl|ModShift, fmt.Sprintf("\x1bO%c", 64+i)) + } + for i := 1; i <= 10; i++ { + key := Key(int(KeyF1)-1+i) + j := 10+i + if i >=6 { j++ } + t.prepareKeyModReplace(key, key+22, ModCtrl, fmt.Sprintf("\x1b[%d^", j)) + j = 22+i + if i >=5 { j++ } + if i >=7 { j++ } + t.prepareKeyModReplace(key, key+10, ModShift, fmt.Sprintf("\x1b[%d~", j)) + t.prepareKeyModReplace(key, key+32, ModCtrl|ModShift, fmt.Sprintf("\x1b[%d^", j)) + key = Key(int(KeyF11)-1+i) + // urxvt treats F11 to F20 like Shift-F1 to Shift-F10, ditto plus Ctrl + // t.prepareKeyMod(key, ModNone, fmt.Sprintf("\x1b[%d~", j)) + t.prepareKeyMod(key, ModNone, fmt.Sprintf("\x1b[%d#", j)) // non-standard! + t.prepareKeyModReplace(key, key+10, ModShift, fmt.Sprintf("\x1b[%d$", j)) + // t.prepareKeyMod(key, ModCtrl, fmt.Sprintf("\x1b[%d^", j)) + t.prepareKeyMod(key, ModCtrl, fmt.Sprintf("\x1b[%d&", j)) // non-standard! + t.prepareKeyModReplace(key, key+32, ModCtrl|ModShift, fmt.Sprintf("\x1b[%d@", j)) + } +} + func (t *tScreen) prepareKey(key Key, val string) { t.prepareKeyMod(key, ModNone, val) } @@ -471,6 +517,8 @@ func (t *tScreen) prepareKeys() { t.prepareKeyMod(KeyEnd, ModMeta|ModShift, ti.KeyMetaShfEnd) } + t.prepareUrxvtModifiers() // must be before application mode cursor keys below + // Sadly, xterm handling of keycodes is somewhat erratic. In // particular, different codes are sent depending on application // mode is in use or not, and the entries for many of these are