vty-5.25.1: A simple terminal UI library

Safe Haskell | None
Language | Haskell2010

Graphics.Vty.Input

Description

This module provides the input layer for Vty, including methods for initializing an `Input` structure and reading `Event`s from the terminal.

Note that due to the evolution of terminal emulators, some keys and combinations will not reliably map to the expected events by any terminal program. There is no 1:1 mapping from key events to bytes read from the terminal input device. In very limited cases the terminal and vty's input process can be customized to resolve these issues; see Graphics.Vty.Config for how to configure vty's input processing.

# VTY's Implementation

There are two input modes:

1. 7-bit
2. 8-bit

The 7-bit input mode is the default and the expected mode in most use cases. This is what Vty uses.

## 7-bit input encoding

Control key combinations are represented by masking the two high bits of the 7-bit input. Historically the control key actually grounded the two high bit wires: 6 and 7. This is why control key combos map to single character events: the input bytes are identical. The input byte is the bit encoding of the character with bits 6 and 7 masked. Bit 6 is set by shift. Bit 6 and 7 are masked by control. For example,

- Control-I is `i`, `01101001`, and has bit 6 and 7 masked to become `00001001`, which is the ASCII and UTF-8 encoding of the Tab key.
- Control+Shift-C is `C`, `01000011`, with bit 6 and 7 set to zero which is `0000011` and is the "End of Text" code.
- Hypothesis: This is why capital-A, `A`, has value 65 in ASCII: this is the value 1 with bit 7 set and 6 unset.
- Hypothesis: Bit 6 is unset by upper case letters because, initially, there were only upper case letters used and a 5 bit encoding.

## 8-bit encoding

The 8th bit was originally used for parity checking which is useless for terminal emulators. Some terminal emulators support an 8-bit input encoding. While this provides some advantages, the actual usage is low. Most systems use 7-bit mode but recognize 8-bit control characters when escaped. This is what Vty does.

## Escaped Control Keys

Using 7-bit input encoding, the `ESC` byte can signal the start of an encoded control key. To differentiate a single `ESC` event from a control key, the timing of the input is used.

1. `ESC` individually: `ESC` byte; no bytes following for a period of `VMIN` milliseconds.
2. Control keys that contain `ESC` in their encoding: The @ESC byte is followed by more bytes read within `VMIN` milliseconds. All bytes up until the next valid input block are passed to the classifier.

If the current runtime is the threaded runtime then the terminal's `VMIN` and `VTIME` behavior reliably implement the above rules. If the current runtime does not support `forkOS` then there is currently no implementation.

## Unicode Input and Escaped Control Key Sequences

The input encoding determines how UTF-8 encoded characters are recognized.

- 7-bit mode: UTF-8 can be input unambiguiously. UTF-8 input is a superset of ASCII. UTF-8 does not overlap escaped control key sequences. However, the escape key must be differentiated from escaped control key sequences by the timing of the input bytes.
- 8-bit mode: UTF-8 cannot be input unambiguously. This does not require using the timing of input bytes to differentiate the escape key. Many terminals do not support 8-bit mode.

## Terminfo

The terminfo system is used to determine how some keys are encoded. Terminfo is incomplete and in some cases terminfo is incorrect. Vty assumes terminfo is correct but provides a mechanism to override terminfo; see Graphics.Vty.Config, specifically `inputOverrides`.

## Terminal Input is Broken

Clearly terminal input has fundemental issues. There is no easy way to reliably resolve these issues.

One resolution would be to ditch standard terminal interfaces entirely and just go directly to scancodes. This would be a reasonable option for Vty if everybody used the linux kernel console but for obvious reasons this is not possible.

The Graphics.Vty.Config module supports customizing the input-byte-to-event mapping and xterm supports customizing the scancode-to-input-byte mapping. With a lot of work a user's system can be set up to encode all the key combos in an almost-sane manner.

## See also

- http://www.leonerd.org.uk/hacks/fixterms/

Synopsis

- data Key
  - = KEsc
  - | KChar Char
  - | KBS
  - | KEnter
  - | KLeft
  - | KRight
  - | KUp
  - | KDown
  - | KUpLeft
  - | KUpRight
  - | KDownLeft
  - | KDownRight
  - | KCenter
  - | KFun Int
  - | KBackTab
  - | KPrtScr
  - | KPause
  - | KIns
  - | KHome
  - | KPageUp
  - | KDel
  - | KEnd
  - | KPageDown
  - | KBegin
  - | KMenu
- data Modifier
  - = MShift
  - | MCtrl
  - | MMeta
  - | MAlt
- data Button
  - = BLeft
  - | BMiddle
  - | BRight
  - | BScrollUp
  - | BScrollDown
- data Event
  - = EvKey Key [Modifier]
  - | EvMouseDown Int Int Button [Modifier]
  - | EvMouseUp Int Int (Maybe Button)
  - | EvResize Int Int
  - | EvPaste ByteString
  - | EvLostFocus
  - | EvGainedFocus
- data Input = Input {
  - _eventChannel :: TChan Event
  - shutdownInput :: IO ()
  - _configRef :: IORef Config
  - _inputDebug :: Maybe Handle
  }
- inputForConfig :: Config -> IO Input

# Documentation

data Key Source #

Representations of non-modifier keys.

- KFun is indexed from 0 to 63. Range of supported FKeys varies by terminal and keyboard.
- KUpLeft, KUpRight, KDownLeft, KDownRight, KCenter support varies by terminal and keyboard.
- Actually, support for most of these but KEsc, KChar, KBS, and KEnter vary by terminal and keyboard.

Constructors

KEsc |
KChar Char |
KBS |
KEnter |
KLeft |
KRight |
KUp |
KDown |
KUpLeft |
KUpRight |
KDownLeft |
KDownRight |
KCenter |
KFun Int |
KBackTab |
KPrtScr |
KPause |
KIns |
KHome |
KPageUp |
KDel |
KEnd |
KPageDown |
KBegin |
KMenu |

Instances

Eq Key Source # |
Instance details Defined in Graphics.Vty.Input.Events Methods (==) :: Key -> Key -> Bool Source # (/=) :: Key -> Key -> Bool Source #
Ord Key Source # |
Instance details Defined in Graphics.Vty.Input.Events Methods compare :: Key -> Key -> Ordering Source # (<) :: Key -> Key -> Bool Source # (<=) :: Key -> Key -> Bool Source # (>) :: Key -> Key -> Bool Source # (>=) :: Key -> Key -> Bool Source # max :: Key -> Key -> Key Source # min :: Key -> Key -> Key Source #
Read Key Source # |
Instance details Defined in Graphics.Vty.Input.Events Methods readsPrec :: Int -> ReadS Key Source # readList :: ReadS [Key] Source # readPrec :: ReadPrec Key Source # readListPrec :: ReadPrec [Key] Source #
Show Key Source # |
Instance details Defined in Graphics.Vty.Input.Events Methods showsPrec :: Int -> Key -> ShowS Source # show :: Key -> String Source # showList :: [Key] -> ShowS Source #
Generic Key Source # |
Instance details Defined in Graphics.Vty.Input.Events Associated Types type Rep Key :: Type -> Type Source # Methods from :: Key -> Rep Key x Source # to :: Rep Key x -> Key Source #
type Rep Key Source # |

Instance details

Defined in Graphics.Vty.Input.Events

type Rep Key = D1 (MetaData "Key" "Graphics.Vty.Input.Events" "vty-5.25.1-7oKAOTf5ibHBKcogdwktuc" False) ((((C1 (MetaCons "KEsc" PrefixI False) (U1 :: Type -> Type) :+: (C1 (MetaCons "KChar" PrefixI False) (S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 Char)) :+: C1 (MetaCons "KBS" PrefixI False) (U1 :: Type -> Type))) :+: (C1 (MetaCons "KEnter" PrefixI False) (U1 :: Type -> Type) :+: (C1 (MetaCons "KLeft" PrefixI False) (U1 :: Type -> Type) :+: C1 (MetaCons "KRight" PrefixI False) (U1 :: Type -> Type)))) :+: ((C1 (MetaCons "KUp" PrefixI False) (U1 :: Type -> Type) :+: (C1 (MetaCons "KDown" PrefixI False) (U1 :: Type -> Type) :+: C1 (MetaCons "KUpLeft" PrefixI False) (U1 :: Type -> Type))) :+: (C1 (MetaCons "KUpRight" PrefixI False) (U1 :: Type -> Type) :+: (C1 (MetaCons "KDownLeft" PrefixI False) (U1 :: Type -> Type) :+: C1 (MetaCons "KDownRight" PrefixI False) (U1 :: Type -> Type))))) :+: (((C1 (MetaCons "KCenter" PrefixI False) (U1 :: Type -> Type) :+: (C1 (MetaCons "KFun" PrefixI False) (S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 Int)) :+: C1 (MetaCons "KBackTab" PrefixI False) (U1 :: Type -> Type))) :+: (C1 (MetaCons "KPrtScr" PrefixI False) (U1 :: Type -> Type) :+: (C1 (MetaCons "KPause" PrefixI False) (U1 :: Type -> Type) :+: C1 (MetaCons "KIns" PrefixI False) (U1 :: Type -> Type)))) :+: ((C1 (MetaCons "KHome" PrefixI False) (U1 :: Type -> Type) :+: (C1 (MetaCons "KPageUp" PrefixI False) (U1 :: Type -> Type) :+: C1 (MetaCons "KDel" PrefixI False) (U1 :: Type -> Type))) :+: ((C1 (MetaCons "KEnd" PrefixI False) (U1 :: Type -> Type) :+: C1 (MetaCons "KPageDown" PrefixI False) (U1 :: Type -> Type)) :+: (C1 (MetaCons "KBegin" PrefixI False) (U1 :: Type -> Type) :+: C1 (MetaCons "KMenu" PrefixI False) (U1 :: Type -> Type))))))

data Modifier Source #

Modifier keys. Key codes are interpreted such that users are more likely to have Meta than Alt; for instance on the PC Linux console, `MMeta` will generally correspond to the physical Alt key.

Constructors

MShift |
MCtrl |
MMeta |
MAlt |

Instances

Eq Modifier Source # |
Instance details Defined in Graphics.Vty.Input.Events Methods (==) :: Modifier -> Modifier -> Bool Source # (/=) :: Modifier -> Modifier -> Bool Source #
Ord Modifier Source # |
Instance details Defined in Graphics.Vty.Input.Events Methods compare :: Modifier -> Modifier -> Ordering Source # (<) :: Modifier -> Modifier -> Bool Source # (<=) :: Modifier -> Modifier -> Bool Source # (>) :: Modifier -> Modifier -> Bool Source # (>=) :: Modifier -> Modifier -> Bool Source # max :: Modifier -> Modifier -> Modifier Source # min :: Modifier -> Modifier -> Modifier Source #
Read Modifier Source # |
Instance details Defined in Graphics.Vty.Input.Events Methods readsPrec :: Int -> ReadS Modifier Source # readList :: ReadS [Modifier] Source # readPrec :: ReadPrec Modifier Source # readListPrec :: ReadPrec [Modifier] Source #
Show Modifier Source # |
Instance details Defined in Graphics.Vty.Input.Events Methods showsPrec :: Int -> Modifier -> ShowS Source # show :: Modifier -> String Source # showList :: [Modifier] -> ShowS Source #
Generic Modifier Source # |
Instance details Defined in Graphics.Vty.Input.Events Associated Types type Rep Modifier :: Type -> Type Source # Methods from :: Modifier -> Rep Modifier x Source # to :: Rep Modifier x -> Modifier Source #
type Rep Modifier Source # |

Instance details

Defined in Graphics.Vty.Input.Events

type Rep Modifier = D1 (MetaData "Modifier" "Graphics.Vty.Input.Events" "vty-5.25.1-7oKAOTf5ibHBKcogdwktuc" False) ((C1 (MetaCons "MShift" PrefixI False) (U1 :: Type -> Type) :+: C1 (MetaCons "MCtrl" PrefixI False) (U1 :: Type -> Type)) :+: (C1 (MetaCons "MMeta" PrefixI False) (U1 :: Type -> Type) :+: C1 (MetaCons "MAlt" PrefixI False) (U1 :: Type -> Type)))

data Button Source #

Mouse buttons.

Constructors

BLeft |
BMiddle |
BRight |
BScrollUp |
BScrollDown |

Instances

Eq Button Source # |
Instance details Defined in Graphics.Vty.Input.Events Methods (==) :: Button -> Button -> Bool Source # (/=) :: Button -> Button -> Bool Source #
Ord Button Source # |
Instance details Defined in Graphics.Vty.Input.Events Methods compare :: Button -> Button -> Ordering Source # (<) :: Button -> Button -> Bool Source # (<=) :: Button -> Button -> Bool Source # (>) :: Button -> Button -> Bool Source # (>=) :: Button -> Button -> Bool Source # max :: Button -> Button -> Button Source # min :: Button -> Button -> Button Source #
Read Button Source # |
Instance details Defined in Graphics.Vty.Input.Events Methods readsPrec :: Int -> ReadS Button Source # readList :: ReadS [Button] Source # readPrec :: ReadPrec Button Source # readListPrec :: ReadPrec [Button] Source #
Show Button Source # |
Instance details Defined in Graphics.Vty.Input.Events Methods showsPrec :: Int -> Button -> ShowS Source # show :: Button -> String Source # showList :: [Button] -> ShowS Source #
Generic Button Source # |
Instance details Defined in Graphics.Vty.Input.Events Associated Types type Rep Button :: Type -> Type Source # Methods from :: Button -> Rep Button x Source # to :: Rep Button x -> Button Source #
type Rep Button Source # |

Instance details

Defined in Graphics.Vty.Input.Events

type Rep Button = D1 (MetaData "Button" "Graphics.Vty.Input.Events" "vty-5.25.1-7oKAOTf5ibHBKcogdwktuc" False) ((C1 (MetaCons "BLeft" PrefixI False) (U1 :: Type -> Type) :+: C1 (MetaCons "BMiddle" PrefixI False) (U1 :: Type -> Type)) :+: (C1 (MetaCons "BRight" PrefixI False) (U1 :: Type -> Type) :+: (C1 (MetaCons "BScrollUp" PrefixI False) (U1 :: Type -> Type) :+: C1 (MetaCons "BScrollDown" PrefixI False) (U1 :: Type -> Type))))

data Event Source #

Events.

Constructors

EvKey Key [Modifier] | A keyboard key was pressed with the specified modifiers.
EvMouseDown Int Int Button [Modifier] | A mouse button was pressed at the specified column and row. Any modifiers available in the event are also provided.
EvMouseUp Int Int (Maybe Button) | A mouse button was released at the specified column and row. Some terminals report only that a button was released without specifying which one; in that case, Nothing is provided. Otherwise Just the button released is included in the event.
EvResize Int Int | If read from `eventChannel` this is the size at the time of the signal. If read from `nextEvent` this is the size at the time the event was processed by Vty. Typically these are the same, but if somebody is resizing the terminal quickly they can be different.

EvPaste ByteString

A paste event occurs when a bracketed paste input sequence is received. For terminals that support bracketed paste mode, these events will be triggered on a paste event. Terminals that do not support bracketed pastes will send the paste contents as ordinary input (which is probably bad, so beware!) Note that the data is provided in raw form and you'll have to decode (e.g. as UTF-8) if that's what your application expects.

EvLostFocus | The terminal running the application lost input focus.
EvGainedFocus | The terminal running the application gained input focus.

Instances

Eq Event Source # |
Instance details Defined in Graphics.Vty.Input.Events Methods (==) :: Event -> Event -> Bool Source # (/=) :: Event -> Event -> Bool Source #
Ord Event Source # |
Instance details Defined in Graphics.Vty.Input.Events Methods compare :: Event -> Event -> Ordering Source # (<) :: Event -> Event -> Bool Source # (<=) :: Event -> Event -> Bool Source # (>) :: Event -> Event -> Bool Source # (>=) :: Event -> Event -> Bool Source # max :: Event -> Event -> Event Source # min :: Event -> Event -> Event Source #
Read Event Source # |
Instance details Defined in Graphics.Vty.Input.Events Methods readsPrec :: Int -> ReadS Event Source # readList :: ReadS [Event] Source # readPrec :: ReadPrec Event Source # readListPrec :: ReadPrec [Event] Source #
Show Event Source # |
Instance details Defined in Graphics.Vty.Input.Events Methods showsPrec :: Int -> Event -> ShowS Source # show :: Event -> String Source # showList :: [Event] -> ShowS Source #
Generic Event Source # |
Instance details Defined in Graphics.Vty.Input.Events Associated Types type Rep Event :: Type -> Type Source # Methods from :: Event -> Rep Event x Source # to :: Rep Event x -> Event Source #
type Rep Event Source # |

Instance details

Defined in Graphics.Vty.Input.Events

type Rep Event = D1 (MetaData "Event" "Graphics.Vty.Input.Events" "vty-5.25.1-7oKAOTf5ibHBKcogdwktuc" False) ((C1 (MetaCons "EvKey" PrefixI False) (S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 Key) :*: S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 [Modifier])) :+: (C1 (MetaCons "EvMouseDown" PrefixI False) ((S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 Int) :*: S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 Int)) :*: (S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 Button) :*: S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 [Modifier]))) :+: C1 (MetaCons "EvMouseUp" PrefixI False) (S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 Int) :*: (S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 Int) :*: S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 (Maybe Button)))))) :+: ((C1 (MetaCons "EvResize" PrefixI False) (S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 Int) :*: S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 Int)) :+: C1 (MetaCons "EvPaste" PrefixI False) (S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 ByteString))) :+: (C1 (MetaCons "EvLostFocus" PrefixI False) (U1 :: Type -> Type) :+: C1 (MetaCons "EvGainedFocus" PrefixI False) (U1 :: Type -> Type))))

data Input Source #

Constructors

Input |

Fields

- _eventChannel :: TChan Event

  Channel of events direct from input processing. Unlike `nextEvent` this will not refresh the display if the next event is an `EvResize`.

- shutdownInput :: IO ()

  Shuts down the input processing. This should return the terminal input state to before he input initialized.

- _configRef :: IORef Config

  Changes to this value are reflected after the next event.

- _inputDebug :: Maybe Handle

  input debug log

inputForConfig :: Config -> IO Input Source #

Set up the terminal with file descriptor `inputFd` for input. Returns an `Input`.

The table used to determine the `Events` to produce for the input bytes comes from `classifyMapForTerm` which is then overridden by the the applicable entries from the configuration's `inputMap`.

The terminal device is configured with the attributes:

- IXON disabled: disables software flow control on outgoing data. This stops the process from being suspended if the output terminal cannot keep up.
- Raw mode is used for input.
- ISIG disabled (enables keyboard combinations that result in signals)
- ECHO disabled (input is not echoed to the output)
- ICANON disabled (canonical mode (line mode) input is not used)
- IEXTEN disabled (extended functions are disabled)