Ansillary.jl
Ansillary.Ansillary
Ansillary.Cursor
Ansillary.Inputs
Ansillary.Screen
Ansillary.Cursor.Column
Ansillary.Cursor.Coordinate
Ansillary.Cursor.Down
Ansillary.Cursor.Left
Ansillary.Cursor.Movement
Ansillary.Cursor.Right
Ansillary.Cursor.Row
Ansillary.Cursor.Up
Ansillary.Inputs.Character
Ansillary.Inputs.EventLoop
Ansillary.Inputs.Events
Ansillary.Inputs.F
Ansillary.Inputs.Modified
Ansillary.Inputs.PasteEnd
Ansillary.Inputs.PasteStart
Ansillary.Inputs.Tick
Ansillary.Screen.All
Ansillary.Screen.CurrentLine
Ansillary.Screen.FromCursorBack
Ansillary.Screen.FromCursorDown
Ansillary.Screen.FromCursorForward
Ansillary.Screen.FromCursorUp
Ansillary.Cursor.checkpoint
Ansillary.Cursor.hide
Ansillary.Cursor.location
Ansillary.Cursor.move!
Ansillary.Cursor.save
Ansillary.Inputs.paste
Ansillary.Screen.alternative
Ansillary.Screen.clear!
Ansillary.Screen.raw
Ansillary.Screen.size
Ansillary.Ansillary
— ModuleAnsillary is a package for interacting with ANSI compatible terminals.
Ansillary aims to support only the commonly supported capabilities of ANSI terminals, i.e. completely replacing ncurses is not a goal of Ansillary. Explicitly, if a capability is not available in one of the following terminals then it will not be supported in Ansillary:
- LibVTE terminals.
- LibVTerm terminals.
- The Linux console.
- Konsole.
- XTerm.
The reason for this is simple, reliable capability detection is harder than writing a package that wraps ncurses (or similar libraries such as termbox). Do note, however, that if a terminal does not support a capability but the lack of that capability does not cause issues for the user then it will be supported. For example, bracketed paste mode is supported despite the Linux console not actually having a paste feature because the Linux console still parses and accepts the required escape sequences even though they have no effect. Whereas scrolling is not supported because the Linux console does not support scrolling and not having something scroll is something that the user will definitely notice.
The package is currently split into three modules: Cursor
for controlling the position and visibilty of the cursor, Inputs
for reading input events from a terminal, and Screen
for controlling what is displayed.
Ansillary.Cursor
— ModuleThis module deals with controlling the cursor.
The two major pieces of functionality in this module are moving the cursor and hiding the cursor.
Move the cursor using the move!
function:
move!(Up(3))
There are several different movements available, see documentation on the subtypes of Movement
for more details.
It is possible to temporarily move the cursor to a different location using save
or checkpoint
:
move!(Coordinate(1, 1))
println("First line!")
save() do
move!(Down(4))
println("Fifth line!")
checkpoint() do
move!(Down(4))
println("Tenth line!")
checkpoint() do
move!(Down(4))
println("Fifteenth line!")
end
end
end
println("Second line!")
checkpoint() do
move!(Down(4))
println("Sixth line!")
checkpoint() do
move!(Down(4))
println("Eleventh line!")
end
end
The location of the cursor can also be found using location
, though note that this only works in raw mode:
julia> Screen.raw(Cursor.location)
Ansillary.Inputs.Location(0x003c, 0x0001)
Hiding the cursor is done using Julia's support for do
-notation:
julia> Cursor.hide() do
for c in "There is no cursor..."
print(c)
sleep(0.1)
end
end
There is no cursor...
Ansillary.Cursor.Column
— TypeMove the cursor to a given column without changing it's row.
Ansillary.Cursor.Coordinate
— TypeMove the cursor to the given coordinate.
Ansillary.Cursor.Down
— TypeMove the cursor down the given number of rows.
Ansillary.Cursor.Left
— TypeMove the cursor left the given number of columns.
Ansillary.Cursor.Movement
— TypeA way that the cursor can be moved.
See the documentation of it's subtypes for more details:
Ansillary.Cursor.Right
— TypeMove the cursor right the given number of columns.
Ansillary.Cursor.Row
— TypeMove the cursor to a given row without changing it's column.
This movement currently does not work properly with `Inputs.EventLoop` due to it's use of `Cursor.location`.
Ansillary.Cursor.Up
— TypeMove the cursor up the given number of rows.
Ansillary.Cursor.checkpoint
— FunctionA nestable implementation of save
.
This function will not work correctly with [`Inputs.EventLoop`](@ref).
This function will only work correctly when using [`Screen.raw`](@ref).
This function will save the current location of the cursor, run the function, then move the cursor back to it's original location.
Ansillary.Cursor.hide
— FunctionTemporarily hide the cursor for the duration of the provided function.
Ansillary.Cursor.location
— FunctionGet the current location of the cursor.
This function will only work properly in raw mode, e.g. `Screen.raw(Cursor.location)`.
Ansillary.Cursor.move!
— MethodMove the cursor.
See the documentation on subtypes of Movement
for more details.
Ansillary.Cursor.save
— FunctionReturn the cursor to it's current location after the function has finished.
Use this function instead of [`checkpoint`](@ref) if you are not using raw mode or if you are using [`Inputs.EventLoop`](@ref).
This uses the ANSI code for saving the cursor so it can't be nested, use [`checkpoint`](@ref) if these calls need to be nested.
Ansillary.Inputs
— ModuleThis module deals with reading events from the terminal.
Mostly these events will be input from the user, but some of the functionality is about the terminal sending messages as well.
The fundamental piece of functionality that this module offers is being able to a single event from an input stream.
julia> read(IOBuffer("[7;2~"), Event)
Shift+Home
If Ansillary does not recognise an event it will return the bytes that it has read and any remaining bytes in the stream.
julia> read(IOBuffer("[Q[7;2~"), Event)
Ansillary.Inputs.Unknown(UInt8[0x1b, 0x5b, 0x51, 0x1b, 0x5b, 0x37, 0x3b, 0x32, 0x7e])
This is done to minimise the risk of the application receiving a seemingly valid event that is actually part of an unknown event, which could lead to highly undesirable consequences. Note that this is not completely foolproof, for example Konsole sends "@sy"
when Super+y is pressed and Ansillary will happily accept that as four separate events.
julia> let input = IOBuffer("@sy"); [read(input, Event), read(input, Event), read(input, Event), read(input, Event)] end
4-element Array{Ansillary.Inputs.Key,1}:
Ctrl+x
@
s
y
Events
is an iterator that wraps read(::Any, ::Event)
, see the file examples/keylogger.jl
to see how that works. EventLoop
is an iterator that also sends a Tick
event every period
, see the file examples/snake.jl
or tests/interactive.jl
to see how that works (also make sure to pay attention to the warnings on that type's documentation).
This module also contains the paste
function, which allows you to turn on bracketed paste mode. In bracketed paste mode when the user pastes something into the terminal, a PasteStart
event will be sent, followed by the pasted text, then finally a PasteEnd
event.
The functionality of this module will only work properly if the terminal is in raw mode, e.g.
```julia
Screen.raw() do
for key in Events(stdin)
@show key
if key == CTRL_C
break
end
end
end
```
Ansillary.Inputs.Character
— TypeA printable character.
Ansillary.Inputs.EventLoop
— TypeIterate over a simple event loop.
The event loop will send a Tick
event every period (though this isn't guaranteed, for example computationally heavy loop bodies will cause the tick to be sent after the body finishes), and send any received input events between those ticks.
There is currently a bug in this iterator which makes it "eat" the next byte sent to the stream. This shouldn't be an issue in the common use-case of an event loop, where the process exits when the event loop does.
The current implementation is incompatible with the following methods because there is a background task constantly reading from the input stream:
* [`Cursor.location`](@ref).
* [`Cursor.move!`](@ref) methods that take a [`Cursor.Row`](@ref).
* [`Screen.size`](@ref).
Examples
for key in Inputs.EventLoop(Inputs.Second(1))
@show key
if key == CTRL_C
break
end
end
Ansillary.Inputs.Events
— TypeIterate over input events.
Examples
for key in Inputs.Events(stdin)
@show key
if key == Inputs.CTRL_C
break
end
end
Ansillary.Inputs.F
— TypeA function key.
Ansillary.Inputs.Modified
— TypeA key event that has been modified, e.g. Ctrl+c.
A Modified
event can be created using the +
operator:
julia> Ctrl()+'c' == Modified(Character('c'), [Ctrl()])
true
julia> Ctrl()+Super()+Insert() == Modified(Insert(), [Super(), Ctrl()])
true
The order of modifiers given to the constructor is not important:
julia> Modified(Delete(), [Ctrl(), Alt()]) == Modified(Delete(), [Alt(), Ctrl()])
true
Ansillary.Inputs.PasteEnd
— TypeThe event sent when text has finished being pasted.
Only sent when bracketed paste mode (paste
) is enabled.
Ansillary.Inputs.PasteStart
— TypeThe event sent before text is pasted.
Only sent when bracketed paste mode (paste
) is enabled.
Ansillary.Inputs.Tick
— TypeEvent that is sent by the event loop every period.
Ansillary.Inputs.paste
— FunctionTemporarily set bracketed paste mode for the duration of the function.
Ansillary.Screen
— ModuleThis module deals with controlling what is displayed on the screen.
The two major features of this module are switching to the alernative screen and clearing the what is currenlty on the screen.
The alternative screen is a feature of most terminals that allows an application to switch to a screen that has no scrollback, allowing it to draw whatever it wants to the terminal without interfering with the scrollback of the normal screen. This is most useful for full-screen applications, such as vim or emacs. Ansillary allows you to enter the alternative screen with the alternative
function:
julia> Screen.alternative() do
println("No scrollback!")
read(stdin, UInt8)
end
Ansillary will set raw mode (also known as non-canonical mode) when alternative
is called as this is almost always what is wanted, to avoid this use the non-exported alternative!
and standard!
functions.
It also possible to only set raw mode using the raw
function. The main benefits of raw mode are that the input stream is not line buffered allowing one byte to be read at a time (which in turn allows the application to respond to key presses), and that the input is not printed directly to the output allowing the application to handle printable input in it's own way (so that it can implement vim-style keybindings, for example).
To clear the screen Ansillary provides the clear!
function, as well as the Area
types for specifying which parts of the screen need clearing.
This short script
print("Some line...")
move!(Left(3))
clear!(FromCursorForward())
print("!")
will result in Some line!
being printed to the terminal.
This module also provides the size
as a slightly nicer wrapper around displaysize
.
Ansillary.Screen.All
— TypeClear the entire screen.
Ansillary.Screen.CurrentLine
— TypeClear the line that the cursor is currently on.
Ansillary.Screen.FromCursorBack
— TypeClear from the start of the current line up to, and including, the cursor.
Ansillary.Screen.FromCursorDown
— TypeClear from the cursor up to the end of the line and all lines below the cursor.
Ansillary.Screen.FromCursorForward
— TypeClear from the cursor up to the end of the line.
Ansillary.Screen.FromCursorUp
— TypeClear from the start of the current line up to the cursor and any lines above the cursor.
Ansillary.Screen.alternative
— FunctionTemporarily activate the alernative screen for the duration of the function.
This function also sets the terminal to raw mode as it is rare that you'll need the alternative screen but not raw mode. If the alternative screen is needed without setting raw mode, use alternative!
and standard!
directly.
Ansillary.Screen.clear!
— MethodClear an area of the screen.
See subtypes of Area
for more details.
Ansillary.Screen.raw
— FunctionTemporarily set the terminal to raw mode for the duration of the function.
If switching to raw mode permanently is required use REPL.Terminals.raw!
.
Ansillary.Screen.size
— FunctionGet the current size of the terminal.
See also: displaysize
.