commit 54c91681380332187e0265bd5e492be58afcbd3d
parent 87783226a7ea14f6e1a228621210abef4fc36873
Author: dwrz <dwrz@dwrz.net>
Date: Mon, 19 Dec 2022 20:10:37 +0000
Refactor editor input to terminal input
Diffstat:
8 files changed, 417 insertions(+), 418 deletions(-)
diff --git a/pkg/editor/command/command.go b/pkg/editor/command/command.go
@@ -1,20 +0,0 @@
-package command
-
-type Command int
-
-const (
- Backspace Command = iota
- CursorDown
- CursorLeft
- CursorRight
- CursorUp
- Delete
- End
- Home
- Insert
- Open
- PageDown
- PageUp
- Quit
- Save
-)
diff --git a/pkg/editor/editor.go b/pkg/editor/editor.go
@@ -7,10 +7,10 @@ import (
"code.dwrz.net/src/pkg/editor/buffer"
"code.dwrz.net/src/pkg/editor/canvas"
- "code.dwrz.net/src/pkg/editor/input"
"code.dwrz.net/src/pkg/editor/message"
"code.dwrz.net/src/pkg/log"
"code.dwrz.net/src/pkg/terminal"
+ "code.dwrz.net/src/pkg/terminal/input"
)
type Editor struct {
diff --git a/pkg/editor/input.go b/pkg/editor/input.go
@@ -3,74 +3,80 @@ package editor
import (
"fmt"
- "code.dwrz.net/src/pkg/editor/command"
- "code.dwrz.net/src/pkg/editor/input"
"code.dwrz.net/src/pkg/editor/message"
+ "code.dwrz.net/src/pkg/terminal/input"
)
-func (e *Editor) bufferInput(input *input.Event) error {
+func (e *Editor) bufferInput(event *input.Event) error {
size, err := e.terminal.Size()
if err != nil {
return fmt.Errorf("failed to get terminal size: %w", err)
}
- switch input.Command {
- case command.Backspace:
- e.active.Backspace()
+ if event.Rune != input.Null {
+ switch event.Rune {
+ case input.Delete:
+ e.active.Backspace()
+
+ case 's' & input.Control:
+ // Get the filename.
+ if err := e.active.Save(); err != nil {
+ go func() {
+ e.messages <- message.New(fmt.Sprintf(
+ "failed to save: %v", err,
+ ))
+ }()
+ }
+ go func() {
+ e.messages <- message.New("saved file")
+ }()
+
+ default:
+ e.active.Insert(event.Rune)
+ }
+
+ return nil
+ }
- case command.CursorDown:
+ switch event.Key {
+ case input.Down:
e.active.CursorDown()
- case command.CursorLeft:
+ case input.Left:
e.active.CursorLeft()
- case command.CursorRight:
+ case input.Right:
e.active.CursorRight()
- case command.CursorUp:
+ case input.Up:
e.active.CursorUp()
- case command.Insert:
- e.active.Insert(input.Rune)
+ case input.Insert:
- case command.End:
+ case input.End:
e.active.CursorEnd()
- case command.Home:
+ case input.Home:
e.active.CursorHome()
- case command.PageDown:
+ case input.PageDown:
e.active.PageDown(int(size.Rows))
- case command.PageUp:
+ case input.PageUp:
e.active.PageUp(int(size.Rows))
- case command.Save:
- // Get the filename.
- if err := e.active.Save(); err != nil {
- go func() {
- e.messages <- message.New(fmt.Sprintf(
- "failed to save: %v", err,
- ))
- }()
- }
- go func() {
- e.messages <- message.New("saved file")
- }()
-
default:
- e.log.Debug.Printf("unrecognized input: %#v", input)
+ e.log.Debug.Printf("unrecognized input: %#v", event)
}
return nil
}
-func (e *Editor) promptInput(input *input.Event) error {
- switch input.Command {
- case command.Backspace:
+func (e *Editor) promptInput(event *input.Event) error {
+ switch event.Key {
default:
- e.log.Debug.Printf("unrecognized input: %#v", input)
+ e.log.Debug.Printf("unrecognized input: %#v", event)
}
// If a newline was received, take the input.
diff --git a/pkg/editor/input/input.go b/pkg/editor/input/input.go
@@ -1,220 +0,0 @@
-package input
-
-import (
- "bufio"
- "fmt"
- "os"
- "unicode/utf8"
-
- "code.dwrz.net/src/pkg/editor/command"
- "code.dwrz.net/src/pkg/log"
- "code.dwrz.net/src/pkg/terminal"
-)
-
-type Event struct {
- Command command.Command
- Rune rune
-}
-
-type Reader struct {
- buf *bufio.Reader
- events chan *Event
- log *log.Logger
-}
-
-type Parameters struct {
- Chan chan *Event
- In *os.File
- Log *log.Logger
-}
-
-func New(p Parameters) *Reader {
- return &Reader{
- buf: bufio.NewReader(p.In),
- events: p.Chan,
- log: p.Log,
- }
-}
-
-// TODO: reading one rune at a time is slow, especially when pasting large
-// quantities of text into the active buffer. It would be nice to take more
-// input at once, while still being able to handle escape sequences without
-// too many edge cases.
-func (i *Reader) Run() error {
- for {
- r, size, err := i.buf.ReadRune()
- if err != nil {
- return fmt.Errorf("failed to read: %w", err)
- }
- i.log.Debug.Printf(
- "read rune %s %v (%d)",
- string(r), []byte(string(r)), size,
- )
- switch r {
- case utf8.RuneError:
- i.log.Error.Printf(
- "rune error: %s (%d)", string(r), size,
- )
-
- // Handle escape sequences.
- case terminal.Escape:
- if err := i.parseEscapeSequence(); err != nil {
- return fmt.Errorf("failed to read: %w", err)
- }
-
- case terminal.Delete:
- i.events <- &Event{Command: command.Backspace}
-
- case 'q' & terminal.Control:
- i.events <- &Event{Command: command.Quit}
-
- case 's' & terminal.Control:
- i.events <- &Event{Command: command.Save}
-
- default:
- i.events <- &Event{Command: command.Insert, Rune: r}
- }
- }
-}
-
-func (i *Reader) parseEscapeSequence() error {
- r1, _, err := i.buf.ReadRune()
- if err != nil {
- return fmt.Errorf("failed to read: %w", err)
- }
-
- // Ignore invalid escape sequences.
- if r1 != '[' && r1 != 'O' {
- i.events <- &Event{Command: command.Insert, Rune: r1}
- return nil
- }
-
- // We've received an input of Esc + [ or Esc + O.
- // Determine the escape sequence.
- r2, _, err := i.buf.ReadRune()
- if err != nil {
- return fmt.Errorf("failed to read: %w", err)
-
- }
-
- // Check letter escape sequences.
- switch r2 {
- case 'A':
- i.events <- &Event{Command: command.CursorUp}
- return nil
- case 'B':
- i.events <- &Event{Command: command.CursorDown}
- return nil
- case 'C':
- i.events <- &Event{Command: command.CursorRight}
- return nil
- case 'D':
- i.events <- &Event{Command: command.CursorLeft}
- return nil
-
- case 'O':
- r3, _, err := i.buf.ReadRune()
- if err != nil {
- return fmt.Errorf("failed to read: %w", err)
- }
- switch r3 {
- case 'P': // F1
- return nil
- case 'Q': // F2
- return nil
- case 'R': // F3
- return nil
- case 'S': // F4
- return nil
- default:
- // No match.
- i.events <- &Event{Command: command.Insert, Rune: r1}
- i.events <- &Event{Command: command.Insert, Rune: r2}
- i.events <- &Event{Command: command.Insert, Rune: r3}
- return nil
- }
- }
-
- // Check for single digit numerical escape sequences.
- r3, _, err := i.buf.ReadRune()
- if err != nil {
- return fmt.Errorf("failed to read: %w", err)
- }
- switch {
- case r2 == '1' && r3 == '~':
- i.events <- &Event{Command: command.Home}
- return nil
- case r2 == '2' && r3 == '~':
- i.events <- &Event{Command: command.Insert}
- return nil
- case r2 == '3' && r3 == '~':
- i.events <- &Event{Command: command.Delete}
- return nil
- case r2 == '4' && r3 == '~':
- i.events <- &Event{Command: command.End}
- return nil
- case r2 == '5' && r3 == '~':
- i.events <- &Event{Command: command.PageUp}
- return nil
- case r2 == '6' && r3 == '~':
- i.events <- &Event{Command: command.PageDown}
- return nil
- case r2 == '7' && r3 == '~':
- i.events <- &Event{Command: command.Home}
- return nil
- case r2 == '8' && r3 == '~':
- i.events <- &Event{Command: command.End}
- return nil
- case r2 == '9' && r3 == '~':
- i.events <- &Event{Command: command.End}
- return nil
- }
-
- // Check for double digit numerical escape sequences.
- r4, _, err := i.buf.ReadRune()
- if err != nil {
- return fmt.Errorf("failed to read: %w", err)
- }
- switch {
- case r2 == '1' && r3 == '0' && r4 == '~':
- return nil
- case r2 == '1' && r3 == '1' && r4 == '~':
- return nil
- case r2 == '1' && r3 == '2' && r4 == '~':
- return nil
- case r2 == '1' && r3 == '3' && r4 == '~':
- return nil
- case r2 == '1' && r3 == '4' && r4 == '~':
- return nil
- case r2 == '1' && r3 == '4' && r4 == '~':
- return nil
- case r2 == '1' && r3 == '6' && r4 == '~':
- return nil
- case r2 == '1' && r3 == '7' && r4 == '~':
- return nil
- case r2 == '1' && r3 == '8' && r4 == '~':
- return nil
- case r2 == '1' && r3 == '9' && r4 == '~':
- return nil
- case r2 == '2' && r3 == '0' && r4 == '~':
- return nil
- case r2 == '2' && r3 == '1' && r4 == '~':
- return nil
- case r2 == '2' && r3 == '2' && r4 == '~':
- return nil
- case r2 == '2' && r3 == '3' && r4 == '~':
- return nil
- case r2 == '2' && r3 == '4' && r4 == '~':
- return nil
- case r4 == '~':
- return nil
- }
-
- // No match.
- i.events <- &Event{Command: command.Insert, Rune: r1}
- i.events <- &Event{Command: command.Insert, Rune: r2}
- i.events <- &Event{Command: command.Insert, Rune: r3}
- i.events <- &Event{Command: command.Insert, Rune: r4}
-
- return nil
-}
diff --git a/pkg/editor/run.go b/pkg/editor/run.go
@@ -5,11 +5,13 @@ import (
"fmt"
"code.dwrz.net/src/pkg/build"
- "code.dwrz.net/src/pkg/editor/command"
+ "code.dwrz.net/src/pkg/terminal/input"
)
func (e *Editor) Run(ctx context.Context, files []string) error {
- e.terminal.SetRaw()
+ if err := e.terminal.SetRaw(); err != nil {
+ return fmt.Errorf("failed to set raw mode: %w", err)
+ }
e.canvas.Reset()
// Log build info.
@@ -23,7 +25,7 @@ func (e *Editor) Run(ctx context.Context, files []string) error {
// Start reading user input.
go func() {
- if err := e.reader.Run(); err != nil {
+ if err := e.reader.Run(ctx); err != nil {
e.errs <- err
}
}()
@@ -47,11 +49,11 @@ func (e *Editor) Run(ctx context.Context, files []string) error {
return fmt.Errorf("failed to render: %w", err)
}
- case input := <-e.input:
- if input.Command == command.Quit {
+ case event := <-e.input:
+ if event.Rune == 'q'&input.Control {
return nil
}
- if err := e.bufferInput(input); err != nil {
+ if err := e.bufferInput(event); err != nil {
return fmt.Errorf(
"failed to process input: %w", err,
)
diff --git a/pkg/terminal/input/input.go b/pkg/terminal/input/input.go
@@ -0,0 +1,217 @@
+package input
+
+import (
+ "bufio"
+ "context"
+ "fmt"
+ "os"
+ "unicode/utf8"
+
+ "code.dwrz.net/src/pkg/log"
+)
+
+type Event struct {
+ Key Key
+ Rune rune
+}
+
+type Reader struct {
+ buf *bufio.Reader
+ events chan *Event
+ log *log.Logger
+}
+
+type Parameters struct {
+ Chan chan *Event
+ In *os.File
+ Log *log.Logger
+}
+
+func New(p Parameters) *Reader {
+ return &Reader{
+ buf: bufio.NewReader(p.In),
+ events: p.Chan,
+ log: p.Log,
+ }
+}
+
+// TODO: reading one rune at a time is slow, especially when pasting large
+// quantities of text into the active buffer. It would be nice to take more
+// input at once, while still being able to handle escape sequences without
+// too many edge cases.
+func (i *Reader) Run(ctx context.Context) error {
+ for {
+ select {
+ case <-ctx.Done():
+ return nil
+ default:
+ r, size, err := i.buf.ReadRune()
+ if err != nil {
+ return fmt.Errorf("failed to read: %w", err)
+ }
+ i.log.Debug.Printf(
+ "read rune %s %v (%d)",
+ string(r), []byte(string(r)), size,
+ )
+ switch r {
+ case utf8.RuneError:
+ i.log.Error.Printf(
+ "rune error: %s (%d)", string(r), size,
+ )
+
+ // Handle escape sequences.
+ case Escape:
+ if err := i.parseEscapeSequence(); err != nil {
+ return fmt.Errorf(
+ "failed to read: %w", err,
+ )
+ }
+
+ default:
+ i.events <- &Event{Rune: r}
+ }
+ }
+ }
+}
+
+func (i *Reader) parseEscapeSequence() error {
+ r1, _, err := i.buf.ReadRune()
+ if err != nil {
+ return fmt.Errorf("failed to read: %w", err)
+ }
+
+ // Ignore invalid escape sequences.
+ if r1 != '[' && r1 != 'O' {
+ i.events <- &Event{Rune: r1}
+ return nil
+ }
+
+ // We've received an input of Esc + [ or Esc + O.
+ // Determine the escape sequence.
+ r2, _, err := i.buf.ReadRune()
+ if err != nil {
+ return fmt.Errorf("failed to read: %w", err)
+
+ }
+
+ // Check letter escape sequences.
+ switch r2 {
+ case 'A':
+ i.events <- &Event{Key: Up}
+ return nil
+ case 'B':
+ i.events <- &Event{Key: Down}
+ return nil
+ case 'C':
+ i.events <- &Event{Key: Right}
+ return nil
+ case 'D':
+ i.events <- &Event{Key: Left}
+ return nil
+
+ case 'O':
+ r3, _, err := i.buf.ReadRune()
+ if err != nil {
+ return fmt.Errorf("failed to read: %w", err)
+ }
+ switch r3 {
+ case 'P': // F1
+ return nil
+ case 'Q': // F2
+ return nil
+ case 'R': // F3
+ return nil
+ case 'S': // F4
+ return nil
+ default:
+ // No match.
+ i.events <- &Event{Rune: r1}
+ i.events <- &Event{Rune: r2}
+ i.events <- &Event{Rune: r3}
+ return nil
+ }
+ }
+
+ // Check for single digit numerical escape sequences.
+ r3, _, err := i.buf.ReadRune()
+ if err != nil {
+ return fmt.Errorf("failed to read: %w", err)
+ }
+ switch {
+ case r2 == '1' && r3 == '~':
+ i.events <- &Event{Key: Home}
+ return nil
+ case r2 == '2' && r3 == '~':
+ i.events <- &Event{Key: Insert}
+ return nil
+ case r2 == '3' && r3 == '~':
+ i.events <- &Event{Rune: Delete}
+ return nil
+ case r2 == '4' && r3 == '~':
+ i.events <- &Event{Key: End}
+ return nil
+ case r2 == '5' && r3 == '~':
+ i.events <- &Event{Key: PageUp}
+ return nil
+ case r2 == '6' && r3 == '~':
+ i.events <- &Event{Key: PageDown}
+ return nil
+ case r2 == '7' && r3 == '~':
+ i.events <- &Event{Key: Home}
+ return nil
+ case r2 == '8' && r3 == '~':
+ i.events <- &Event{Key: End}
+ return nil
+ case r2 == '9' && r3 == '~':
+ i.events <- &Event{Key: End}
+ return nil
+ }
+
+ // Check for double digit numerical escape sequences.
+ r4, _, err := i.buf.ReadRune()
+ if err != nil {
+ return fmt.Errorf("failed to read: %w", err)
+ }
+ switch {
+ case r2 == '1' && r3 == '0' && r4 == '~':
+ return nil
+ case r2 == '1' && r3 == '1' && r4 == '~':
+ return nil
+ case r2 == '1' && r3 == '2' && r4 == '~':
+ return nil
+ case r2 == '1' && r3 == '3' && r4 == '~':
+ return nil
+ case r2 == '1' && r3 == '4' && r4 == '~':
+ return nil
+ case r2 == '1' && r3 == '4' && r4 == '~':
+ return nil
+ case r2 == '1' && r3 == '6' && r4 == '~':
+ return nil
+ case r2 == '1' && r3 == '7' && r4 == '~':
+ return nil
+ case r2 == '1' && r3 == '8' && r4 == '~':
+ return nil
+ case r2 == '1' && r3 == '9' && r4 == '~':
+ return nil
+ case r2 == '2' && r3 == '0' && r4 == '~':
+ return nil
+ case r2 == '2' && r3 == '1' && r4 == '~':
+ return nil
+ case r2 == '2' && r3 == '2' && r4 == '~':
+ return nil
+ case r2 == '2' && r3 == '3' && r4 == '~':
+ return nil
+ case r2 == '2' && r3 == '4' && r4 == '~':
+ return nil
+ case r4 == '~':
+ return nil
+ }
+
+ // No match.
+ i.events <- &Event{Rune: r1}
+ i.events <- &Event{Rune: r2}
+ i.events <- &Event{Rune: r3}
+ i.events <- &Event{Rune: r4}
+
+ return nil
+}
diff --git a/pkg/terminal/input/key.go b/pkg/terminal/input/key.go
@@ -0,0 +1,151 @@
+package input
+
+type Key uint8
+
+const (
+ Down Key = iota + 1
+ Left
+ Right
+ Up
+ End
+ Home
+ Insert
+ PageDown
+ PageUp
+)
+
+const Control rune = 0x1f
+
+const (
+ // Control Characters
+ Null rune = 0
+ StartOfHeading rune = 1
+ StartOfText rune = 2
+ EndOfText rune = 3
+ EndOfTransmission rune = 4
+ Enquiry rune = 5
+ Acknowledgment rune = 6
+ Bell rune = 7
+ Backspace rune = 8
+ HorizontalTab rune = 9
+ LineFeed rune = 10
+ VerticalTab rune = 11
+ FormFeed rune = 12
+ CarriageReturn rune = 13
+ ShiftOut rune = 14
+ ShiftIn rune = 15
+ DataLineEscape rune = 16
+ DeviceControl1 rune = 17
+ DeviceControl2 rune = 18
+ DeviceControl3 rune = 19
+ DeviceControl4 rune = 20
+ NegativeAcknowledgment rune = 21
+ SynchronousIdle rune = 22
+ EndOfTransmitBlock rune = 23
+ Cancel rune = 24
+ EndOfMedium rune = 25
+ Substitute rune = 26
+ Escape rune = 27
+ FileSeparator rune = 28
+ GroupSeparator rune = 29
+ RecordSeparator rune = 30
+ UnitSeparator rune = 31
+
+ // Printable Characters
+ Space rune = 32
+ ExclamationMark rune = 33
+ DoubleQuote rune = 34
+ Number rune = 35
+ Dollar rune = 36
+ Percentage rune = 37
+ Ampersand rune = 38
+ SingleQuote rune = 39
+ LeftParenthesis rune = 40
+ RightParenthesis rune = 41
+ Asterisk rune = 42
+ Plus rune = 43
+ Comma rune = 44
+ Hyphen rune = 45
+ Period rune = 46
+ ForwardSlash rune = 47
+ Zero rune = 48
+ One rune = 49
+ Two rune = 50
+ Three rune = 51
+ Four rune = 52
+ Five rune = 53
+ Six rune = 54
+ Seven rune = 55
+ Eight rune = 56
+ Nine rune = 57
+ Colon rune = 58
+ Semicolon rune = 59
+ LessThan rune = 60
+ Equals rune = 61
+ GreaterThan rune = 62
+ QuestionMark rune = 63
+ At rune = 64
+ UpperA rune = 65
+ UpperB rune = 66
+ UpperC rune = 67
+ UpperD rune = 68
+ UpperE rune = 69
+ UpperF rune = 70
+ UpperG rune = 71
+ UpperH rune = 72
+ UpperI rune = 73
+ UpperJ rune = 74
+ UpperK rune = 75
+ UpperL rune = 76
+ UpperM rune = 77
+ UpperN rune = 78
+ UpperO rune = 79
+ UpperP rune = 80
+ UpperQ rune = 81
+ UpperR rune = 82
+ UpperS rune = 83
+ UpperT rune = 84
+ UpperU rune = 85
+ UpperV rune = 86
+ UpperW rune = 87
+ UpperX rune = 88
+ UpperY rune = 89
+ UpperZ rune = 90
+ LeftBracket rune = 91
+ Backslash rune = 92
+ RightBracket rune = 93
+ Caret rune = 94
+ Underscore rune = 95
+ Grave rune = 96
+ LowerA rune = 97
+ LowerB rune = 98
+ LowerC rune = 99
+ LowerD rune = 100
+ LowerE rune = 101
+ LowerF rune = 102
+ LowerG rune = 103
+ LowerH rune = 104
+ LowerI rune = 105
+ LowerJ rune = 106
+ LowerK rune = 107
+ LowerL rune = 108
+ LowerM rune = 109
+ LowerN rune = 110
+ LowerO rune = 111
+ LowerP rune = 112
+ LowerQ rune = 113
+ LowerR rune = 114
+ LowerS rune = 115
+ LowerT rune = 116
+ LowerU rune = 117
+ LowerV rune = 118
+ LowerW rune = 119
+ LowerX rune = 120
+ LowerY rune = 121
+ LowerZ rune = 122
+ LeftBrace rune = 123
+ VerticalBar rune = 124
+ RightBrace rune = 125
+ Tilde rune = 126
+ Delete rune = 127
+)
diff --git a/pkg/terminal/key.go b/pkg/terminal/key.go
@@ -1,137 +0,0 @@
-package terminal
-
-const (
- // Control Characters
- Null rune = 0
- StartOfHeading rune = 1
- StartOfText rune = 2
- EndOfText rune = 3
- EndOfTransmission rune = 4
- Enquiry rune = 5
- Acknowledgment rune = 6
- Bell rune = 7
- Backspace rune = 8
- HorizontalTab rune = 9
- LineFeed rune = 10
- VerticalTab rune = 11
- FormFeed rune = 12
- CarriageReturn rune = 13
- ShiftOut rune = 14
- ShiftIn rune = 15
- DataLineEscape rune = 16
- DeviceControl1 rune = 17
- DeviceControl2 rune = 18
- DeviceControl3 rune = 19
- DeviceControl4 rune = 20
- NegativeAcknowledgment rune = 21
- SynchronousIdle rune = 22
- EndOfTransmitBlock rune = 23
- Cancel rune = 24
- EndOfMedium rune = 25
- Substitute rune = 26
- Escape rune = 27
- FileSeparator rune = 28
- GroupSeparator rune = 29
- RecordSeparator rune = 30
- UnitSeparator rune = 31
-
- // Printable Characters
- Space rune = 32
- ExclamationMark rune = 33
- DoubleQuote rune = 34
- Number rune = 35
- Dollar rune = 36
- Percentage rune = 37
- Ampersand rune = 38
- SingleQuote rune = 39
- LeftParenthesis rune = 40
- RightParenthesis rune = 41
- Asterisk rune = 42
- Plus rune = 43
- Comma rune = 44
- Hyphen rune = 45
- Period rune = 46
- ForwardSlash rune = 47
- Zero rune = 48
- One rune = 49
- Two rune = 50
- Three rune = 51
- Four rune = 52
- Five rune = 53
- Six rune = 54
- Seven rune = 55
- Eight rune = 56
- Nine rune = 57
- Colon rune = 58
- Semicolon rune = 59
- LessThan rune = 60
- Equals rune = 61
- GreaterThan rune = 62
- QuestionMark rune = 63
- At rune = 64
- UpperA rune = 65
- UpperB rune = 66
- UpperC rune = 67
- UpperD rune = 68
- UpperE rune = 69
- UpperF rune = 70
- UpperG rune = 71
- UpperH rune = 72
- UpperI rune = 73
- UpperJ rune = 74
- UpperK rune = 75
- UpperL rune = 76
- UpperM rune = 77
- UpperN rune = 78
- UpperO rune = 79
- UpperP rune = 80
- UpperQ rune = 81
- UpperR rune = 82
- UpperS rune = 83
- UpperT rune = 84
- UpperU rune = 85
- UpperV rune = 86
- UpperW rune = 87
- UpperX rune = 88
- UpperY rune = 89
- UpperZ rune = 90
- LeftBracket rune = 91
- Backslash rune = 92
- RightBracket rune = 93
- Caret rune = 94
- Underscore rune = 95
- Grave rune = 96
- LowerA rune = 97
- LowerB rune = 98
- LowerC rune = 99
- LowerD rune = 100
- LowerE rune = 101
- LowerF rune = 102
- LowerG rune = 103
- LowerH rune = 104
- LowerI rune = 105
- LowerJ rune = 106
- LowerK rune = 107
- LowerL rune = 108
- LowerM rune = 109
- LowerN rune = 110
- LowerO rune = 111
- LowerP rune = 112
- LowerQ rune = 113
- LowerR rune = 114
- LowerS rune = 115
- LowerT rune = 116
- LowerU rune = 117
- LowerV rune = 118
- LowerW rune = 119
- LowerX rune = 120
- LowerY rune = 121
- LowerZ rune = 122
- LeftBrace rune = 123
- VerticalBar rune = 124
- RightBrace rune = 125
- Tilde rune = 126
- Delete rune = 127
-)
-
-const Control = 0x1f