terminfo-0.1: System/Console/Terminfo/Cursor.hs
-- |
-- Maintainer : judah.jacobson@gmail.com
-- Stability : experimental
-- Portability : portable (FFI)
--
-- | This module provides capabilities for moving the cursor on the terminal. -}
module System.Console.Terminfo.Cursor(
-- * Terminal dimensions
-- | Get the default size of the terminal. For
-- resizeable terminals (e.g., @xterm@), these may not
-- correspond to the actual dimensions.
termLines, termColumns,
-- * Scrolling
carriageReturn,
newline,
scrollForward,
scrollReverse,
-- * Relative cursor movements
-- | The following functions for cursor movement will
-- combine the more primitive capabilities. For example,
-- 'moveDown' may use either 'cursorDown' or
-- 'cursorDown1' depending on the parameter and which of
-- @cud@ and @cud1@ are defined.
moveDown, moveLeft, moveRight, moveUp,
-- ** Primitive movement capabilities
-- | These capabilities correspond directly to @cub@, @cud@,
-- @cub1@, @cud1@, etc.
cursorDown1,
cursorLeft1,
cursorRight1,
cursorUp1,
cursorDown,
cursorLeft,
cursorRight,
cursorUp,
-- * Absolute cursor movements
cursorAddress,
Point(..),
rowAddress,
columnAddress
) where
import System.Console.Terminfo.Base
import Control.Monad
termLines, termColumns :: Capability Int
termLines = tiGetNum "lines"
termColumns = tiGetNum "columns"
{--
On many terminals, the @cud1@ ('cursorDown1') capability is the line feed
character '\n'. However, @stty@ settings may cause that character to have
other effects than intended; e.g. ONLCR turns LF into CRLF, and as a result
@cud1@ will always move the cursor to the first column of the next line.
Looking at the source code of curses (lib_mvcur.c) and other similar programs,
they use @cud@ instead of @cud1@ if it's '\n' and ONLCR is turned on.
Since there's no easy way to check for ONLCR at this point, I've just made
cursorDown1 always use @cud@.
Note the same problems apply to @ind@, but I think there's less of an
expectation that scrolling down will keep the same column.
Suggestions are welcome.
--}
cursorDown1, cursorLeft1,cursorRight1,cursorUp1 :: Capability TermOutput
cursorDown1 = fmap ($1) cursorDown
cursorLeft1 = tiGetOutput1 "cub1"
cursorRight1 = tiGetOutput1 "cuf1"
cursorUp1 = tiGetOutput1 "cuu1"
cursorDown, cursorLeft, cursorRight, cursorUp :: Capability (Int -> TermOutput)
cursorDown = tiGetOutput1 "cud"
cursorLeft = tiGetOutput1 "cub"
cursorRight = tiGetOutput1 "cuf"
cursorUp = tiGetOutput1 "cuu"
cursorHome, cursorToLL :: Capability TermOutput
cursorHome = tiGetOutput1 "home"
cursorToLL = tiGetOutput1 "ll"
-- Movements are built out of parametrized and unparam'd movement
-- capabilities.
-- todo: more complicated logic like ncurses does.
move single param = let
tryBoth = do
s <- single
p <- param
return $ \n -> case n of
0 -> mempty
1 -> s
n -> p n
manySingle = do
s <- single
return $ \n -> mconcat $ replicate n s
in tryBoth `mplus` param `mplus` manySingle
moveLeft, moveRight, moveUp, moveDown :: Capability (Int -> TermOutput)
moveLeft = move cursorLeft1 cursorLeft
moveRight = move cursorRight1 cursorRight
moveUp = move cursorUp1 cursorUp
moveDown = cursorDown -- see notes on @cud1@ above
-- | The @cr@ capability, which moves the cursor to the first column of the
-- current line.
carriageReturn :: Capability TermOutput
carriageReturn = tiGetOutput1 "cr"
-- | The @nel@ capability, which moves the cursor to the first column of
-- the next line. It behaves like a carriage return followed by a line feed.
--
-- If @nel@ is not defined, this may be built out of other capabilities.
newline :: Capability TermOutput
newline = tiGetOutput1 "nel"
`mplus` (liftM2 mappend carriageReturn
(scrollForward `mplus` tiGetOutput1 "cud1"))
-- Note it's OK to use cud1 here, despite the stty problem referenced
-- above, because carriageReturn already puts us on the first column.
scrollForward, scrollReverse :: Capability TermOutput
scrollForward = tiGetOutput1 "ind"
scrollReverse = tiGetOutput1 "ri"
data Point = Point {row, col :: Int}
cursorAddress :: Capability (Point -> TermOutput)
cursorAddress = fmap (\g p -> g (row p) (col p)) $ tiGetOutput1 "cup"
columnAddress, rowAddress :: Capability (Int -> TermOutput)
columnAddress = tiGetOutput1 "hpa"
rowAddress = tiGetOutput1 "vpa"