0 | module Control.NCurses
3 | import NCurses.Core.Attribute
5 | import Control.Monad.State
12 | import public NCurses.Core.Color as Color
13 | import public NCurses.Core.Input as Input
14 | import public NCurses.Core.SpecialKey as SpecialKey
15 | import public Control.NCurses.State
20 | 0 NextIn : (w : State.Window) -> Type
22 | ifThenElse w.noDelay Maybe id $
23 | ifThenElse w.keypad (Either Char Key) Char
26 | data NCurses : (a : Type) -> CursesState -> CursesState -> Type where
27 | Pure : (x : a) -> NCurses a s s
28 | Bind : NCurses a s1 s2 -> ((x : a) -> NCurses b s2 s3) -> NCurses b s1 s3
30 | Init : IsInactive s => NCurses () s State.initState
31 | DeInit : IsActive s => NCurses () s Inactive
32 | AddWindow : IsActive s => (name : String) -> Position -> Size -> Maybe (Exists (\c => (Border c, HasColor c s))) -> NCurses () s (addWindow s name)
33 | SetWindow : IsActive s => (name : String) -> HasWindow name s => NCurses () s (setWindow s name)
34 | UnsetWindow : IsActive s => HasWindow DefaultWindow s => NCurses () s (setWindow s DefaultWindow)
35 | InWindow : IsActive s => (w : String) -> HasWindow w s => (IsActive (setWindow s w) => HasWindow w (setWindow s w) => InWindow w (setWindow s w) => NCurses () (setWindow s w) (setWindow s w)) -> NCurses () s s
36 | AddColor : IsActive s => (name : String) -> (fg : Color) -> (bg : Color) -> NCurses () s (addColor s name)
37 | ModAttr : IsActive s => AttrCmd s -> NCurses () s s
38 | Clear : IsActive s => NCurses () s s
39 | Erase : IsActive s => NCurses () s s
40 | Refresh : IsActive s => NCurses () s s
41 | RefreshAll : IsActive s => NCurses () s s
42 | Output : IsActive s => OutputCmd s -> NCurses () s s
44 | GetCursPos : IsActive s => NCurses Position s s
45 | SetEcho : IsActive s => (on : Bool) -> NCurses () s (setEcho s on)
46 | SetCBreak : IsActive s => (on : Bool) -> NCurses () s (setCBreak s on)
47 | SetNoDelay : IsActive s => (on : Bool) -> NCurses () s (setNoDelay s on)
48 | SetCursor : IsActive s => CursorVisibility -> NCurses () s s
49 | SetKeypad : IsActive s => (on : Bool) -> NCurses () s (setKeypad s on)
51 | GetWinPos : IsActive s => NCurses Position s s
53 | SetWinPos : IsActive s => Position -> NCurses () s s
56 | GetWinSize : IsActive s => (internal : Bool) -> NCurses Size s s
58 | SetWinSize : IsActive s => Size -> NCurses () s s
59 | GetCh : IsActive s => NCurses (NextIn (currentWindow s)) s s
63 | NIO : IO a -> NCurses a s s
66 | IndexedFunctor CursesState CursesState NCurses where
67 | map f x = Bind x (\y => Pure (f y))
70 | IndexedApplicative CursesState NCurses where
73 | ap f x = Bind f (\f' => Bind x (\y => Pure (f' y)))
76 | IndexedMonad CursesState NCurses where
86 | liftIO : IO a -> NCurses a s s
92 | init : IsInactive s => NCurses () s State.initState
97 | deinit : IsActive s => NCurses () s Inactive
111 | addWindow : IsActive s =>
115 | -> Maybe (Exists (\c => (Border c, HasColor c s)))
116 | -> NCurses () s (addWindow s name)
117 | addWindow = AddWindow
122 | border : IsActive s =>
124 | -> HasColor color s =>
125 | (left : BorderChar)
126 | -> (right : BorderChar)
127 | -> (top : BorderChar)
128 | -> (bottom : BorderChar)
129 | -> (topLeft : BorderChar)
130 | -> (topRight : BorderChar)
131 | -> (bottomLeft : BorderChar)
132 | -> (bottomRight : BorderChar)
133 | -> Maybe (Exists (\c => (Border c, HasColor c s)))
134 | border @{_} color @{hasColor} left right top bottom topLeft topRight bottomLeft bottomRight =
149 | defaultBorder : IsActive s =>
151 | -> HasColor color s =>
152 | Maybe (Exists (\c => (Border c, HasColor c s)))
153 | defaultBorder color =
154 | border color Default Default Default Default
155 | Default Default Default Default
161 | setWindow : IsActive s => (name : String) -> HasWindow name s => NCurses () s (setWindow s name)
162 | setWindow = SetWindow
167 | unsetWindow : IsActive s => HasWindow DefaultWindow s => NCurses () s (setWindow s DefaultWindow)
168 | unsetWindow = UnsetWindow
173 | inWindow : IsActive s => (name : String) -> HasWindow name s => (IsActive (setWindow s name) => HasWindow name (setWindow s name) => InWindow name (setWindow s name) => NCurses () (setWindow s name) (setWindow s name)) -> NCurses () s s
174 | inWindow = InWindow
179 | hasWindowWithin' : InWindow w s -> HasWindow w s
180 | hasWindowWithin' {s = (Active _ _ (
(MkWindow name _ _) ** Here)
_)} IsCurrentWindow = ItHasWindow
181 | hasWindowWithin' {s = (Active i (y :: xs) (
(MkWindow name k d) ** (There x))
c)} p@(IsCurrentWindow) =
182 | let c' : InWindow name (Active i xs (
(MkWindow name k d) ** x)
c) = IsCurrentWindow
183 | in moreWindowsHasWindow @{hasWindowWithin' (assert_smaller p c')}
187 | hasWindowWithin : InWindow w s => HasWindow w s
188 | hasWindowWithin @{p} = hasWindowWithin' p
192 | identifiesCurrentWindow : InWindow w (Active _ ws _ _) => IdentifiesWindow w ws
193 | identifiesCurrentWindow @{p} with (hasWindowWithin @{p})
194 | identifiesCurrentWindow @{p} | (ItHasWindow @{ident}) = ident
200 | setWindowHasWindowStill : HasWindow n s => HasWindow w s => HasWindow n (setWindow s w)
201 | setWindowHasWindowStill {s = (Active _ (MkWindow n _ _ :: ws) _ _)} @{ItHasWindow @{Here}} @{ItHasWindow} = ItHasWindow
202 | setWindowHasWindowStill {s = Active _ [] _ _} @{ItHasWindow @{Here}} impossible
203 | setWindowHasWindowStill {s = (Active _ (y :: ws) _ _)} @{ItHasWindow @{There x}} @{ItHasWindow} = ItHasWindow @{There x}
204 | setWindowHasWindowStill {s = Active _ [] _ _} @{ItHasWindow @{There x}} impossible
208 | setWindowIsActiveStill : IsActive s => HasWindow w s => IsActive (setWindow s w)
209 | setWindowIsActiveStill @{ItIsActive} @{ItHasWindow} = ItIsActive
213 | setWindowHasColorStill : HasColor c s => HasWindow w s => HasColor c (setWindow s w)
214 | setWindowHasColorStill {s = (Active _ _ _ (c :: xs))} @{ItHasColor @{Here}} @{ItHasWindow} = ItHasColor
215 | setWindowHasColorStill {s = (Active _ _ _ (y :: xs))} @{ItHasColor @{(There x)}} @{ItHasWindow} = ItHasColor
221 | addWindowHasWindowStill : IsActive s => HasWindow n s => HasWindow n (addWindow s w)
222 | addWindowHasWindowStill {s = (Active _ (_ :: ws) _ _)} @{ItIsActive} @{ItHasWindow @{Here}} = ItHasWindow
223 | addWindowHasWindowStill {s = (Active _ (_ :: ws) _ _)} @{ItIsActive} @{ItHasWindow @{(There x)}} = ItHasWindow
227 | addWindowIsActiveStill : IsActive s => IsActive (addWindow s w)
228 | addWindowIsActiveStill @{ItIsActive} = ItIsActive
232 | addWindowHasColorStill : IsActive s => HasColor c s => HasColor c (addWindow s w)
233 | addWindowHasColorStill {s = (Active _ _ _ (c :: xs))} @{ItIsActive} @{ItHasColor @{Here}} = ItHasColor
234 | addWindowHasColorStill {s = (Active _ _ _ (y :: xs))} @{ItIsActive} @{ItHasColor @{(There x)}} = ItHasColor
237 | identifiedWindowExists : IdentifiesWindow w ws -> Exists (\k => Exists (\d => lookupWindow w ws = MkWindow w k d))
238 | identifiedWindowExists {ws = (MkWindow _ _ _ :: windows)} Here = Evidence _ (Evidence _ Refl)
239 | identifiedWindowExists {ws = ((MkWindow identifier keypad noDelay) :: windows)} (There x) = identifiedWindowExists x
243 | inWindowNow : HasWindow w s => InWindow w (setWindow s w)
244 | inWindowNow @{ItHasWindow @{p}} with (identifiedWindowExists p)
245 | inWindowNow @{ItHasWindow @{p}} | (Evidence k (Evidence d prf)) = rewrite prf in IsCurrentWindow
259 | clear : IsActive s => NCurses () s s
264 | erase : IsActive s => NCurses () s s
269 | refresh : IsActive s => NCurses () s s
274 | refreshAll : IsActive s => NCurses () s s
275 | refreshAll = RefreshAll
279 | getPos : IsActive s => NCurses Position s s
280 | getPos = GetCursPos
284 | getWindowPos : IsActive s => NCurses Position s s
285 | getWindowPos = GetWinPos
289 | setWindowPos : IsActive s => Position -> NCurses () s s
290 | setWindowPos = SetWinPos
300 | getWindowSize : IsActive s => (internal : Bool) -> NCurses Size s s
301 | getWindowSize = GetWinSize
307 | setWindowSize : IsActive s => Size -> NCurses () s s
308 | setWindowSize = SetWinSize
314 | namespace Attribute
320 | addColor : IsActive s =>
324 | -> NCurses () s (addColor s name)
325 | addColor = AddColor
332 | enableAttr : IsActive s => Attribute s -> NCurses () s s
333 | enableAttr = ModAttr . EnableAttr
340 | disableAttr : IsActive s => Attribute s -> NCurses () s s
341 | disableAttr = ModAttr . DisableAttr
352 | setAttr : IsActive s => Attribute s -> NCurses () s s
353 | setAttr = ModAttr . SetAttr
359 | setAttrs : IsActive s => List (Attribute s) -> NCurses () s s
363 | foldr (\a,nc => nc >> enableAttr a) (setAttr Normal)
371 | updateAttr : IsActive s => Attribute s -> ColorAttr s -> (length : Maybe Nat) -> NCurses () s s
372 | updateAttr attr color length = ModAttr (UpdateAttr attr color length)
376 | setBackground : IsActive s => ColorAttr s -> NCurses () s s
377 | setBackground = ModAttr . SetBg
386 | putCh : IsActive s => Char -> NCurses () s s
387 | putCh = Output . PutCh
391 | putStr : IsActive s => String -> NCurses () s s
392 | putStr = Output . PutStr False
396 | putStrLn : IsActive s => String -> NCurses () s s
397 | putStrLn = Output . PutStr True
402 | drawVerticalLine : IsActive s => Char -> Nat -> NCurses () s s
403 | drawVerticalLine = Output .: VLine
408 | drawHorizontalLine : IsActive s => Char -> Nat -> NCurses () s s
409 | drawHorizontalLine = Output .: HLine
417 | move : IsActive s => Position -> NCurses () s s
418 | move = Output . Move
431 | setCBreak : IsActive s => (on : Bool) -> NCurses () s (setCBreak s on)
432 | setCBreak = SetCBreak
439 | setEcho : IsActive s => (on : Bool) -> NCurses () s (setEcho s on)
454 | setKeypad : IsActive s => (on : Bool) -> NCurses () s (setKeypad s on)
455 | setKeypad = SetKeypad
464 | setNoDelay : IsActive s => (on : Bool) -> NCurses () s (setNoDelay s on)
465 | setNoDelay = SetNoDelay
469 | setCursor : IsActive s => CursorVisibility -> NCurses () s s
470 | setCursor = SetCursor
483 | getInput : IsActive s => NCurses (NextIn (currentWindow s)) s s
493 | getChar : IsActive s =>
497 | getChar @{act} @{ItIsNoKeypad} @{ItIsDelay} = GetCh
499 | namespace NotDelayed
501 | getChar : IsActive s =>
504 | NCurses (Maybe Char) s s
505 | getChar @{act} @{ItIsNoKeypad} @{ItIsNoDelay} = GetCh
509 | getKeyOrChar : IsActive s =>
512 | NCurses (Either Char Key) s s
513 | getKeyOrChar @{act} @{ItIsKeypad} @{ItIsDelay} = GetCh
515 | namespace NotDelayed
517 | getKeyOrChar : IsActive s =>
520 | NCurses (Maybe (Either Char Key)) s s
521 | getKeyOrChar @{act} @{ItIsKeypad} @{ItIsNoDelay} = GetCh
527 | testRoutine : NCurses () Inactive Inactive
528 | testRoutine = Indexed.do
530 | insideWindow DefaultWindow
531 | addColor "alert" White Red
533 | setAttr (Color (Named "alert"))
535 | putStr "Hello World"
536 | setAttr (Color DefaultColors)
537 | putStrLn "back to basics."
538 | addWindow "win1" (MkPosition 10 10) (MkSize 10 20) Nothing
540 | insideWindow "win1"
542 | putChIfPossible inp
544 | addWindow "win2" (MkPosition 0 0) (MkSize 10 10) (defaultBorder "alert")
546 | insideWindow "win2"
548 | inWindow "win2" (insideWindowTwice "win1")
549 | insideWindow DefaultWindow
552 | putChIfPossible : IsActive s => Either Char Key -> NCurses () s s
553 | putChIfPossible (Left x) = putCh x
554 | putChIfPossible (Right _) = pure ()
556 | getAndPut : IsActive s => YesDelay s => YesKeypad s => NCurses () s s
558 | inp <- getKeyOrChar
559 | putChIfPossible inp
561 | insideWindow : IsActive s => (w : String) -> InWindow w s => NCurses () s s
562 | insideWindow _ = do
567 | insideWindowTwice : IsActive s => InWindow "win2" s => (w : _) -> HasWindow w s => NCurses () s s
568 | insideWindowTwice w = do
570 | inWindow w (insideWindow w)
578 | keys : List (key, value) -> List key
580 | keys ((x, y) :: xs) = x :: keys xs
582 | keysInjective : {0 x : _} -> keys (x :: xs) = (y :: ys) -> (Builtin.fst x = y, NCurses.keys xs = ys)
583 | keysInjective {x = (w, z)} Refl = (Refl, Refl)
585 | RuntimeBorder : Type
586 | RuntimeBorder = (ColorPair, Exists (\c => Border c))
588 | record RuntimeWindow where
589 | constructor MkRuntimeWindow
590 | props : State.Window
591 | border : Maybe RuntimeBorder
594 | initRuntimeWindow : String -> Maybe RuntimeBorder -> Core.Window -> RuntimeWindow
595 | initRuntimeWindow name border win = MkRuntimeWindow (initWindow name) border win
597 | setRWKeypad : (on : Bool) -> RuntimeWindow -> RuntimeWindow
598 | setRWKeypad on (MkRuntimeWindow (MkWindow identifier keypad noDelay) border win) =
599 | MkRuntimeWindow (MkWindow identifier on noDelay) border win
601 | setRWNoDelay : (on : Bool) -> RuntimeWindow -> RuntimeWindow
602 | setRWNoDelay on (MkRuntimeWindow (MkWindow identifier keypad noDelay) border win) =
603 | MkRuntimeWindow (MkWindow identifier keypad on) border win
605 | data RuntimeWindows : List State.Window -> Type where
606 | Nil : RuntimeWindows []
607 | (::) : (rw : RuntimeWindow) -> RuntimeWindows ws -> RuntimeWindows (rw.props :: ws)
609 | data CurrentWindow : (rw : RuntimeWindow) -> Elem w ws -> RuntimeWindows ws -> Type where
610 | Here : CurrentWindow rw Here (rw :: rws)
611 | There : CurrentWindow rw e rws -> CurrentWindow rw (There e) (other :: rws)
613 | 0 currentWindowPropsPrf : (e : Elem w ws) -> CurrentWindow rw e rws -> rw.props = w
614 | currentWindowPropsPrf Here Here = Refl
615 | currentWindowPropsPrf (There e) (There x) = currentWindowPropsPrf e x
618 | record CursesActive (0 ws : List State.Window) (0 w : (
w' ** Elem w' ws)
) (0 cs : List String) where
619 | constructor MkCursesActive
620 | windows : RuntimeWindows ws
621 | currentWindow : (
rw ** CurrentWindow rw w.snd windows)
622 | colors : List (String, ColorPair)
623 | {0 csPrf : (keys colors) = cs}
624 | currentColor : Maybe ColorPair
625 | keyMap : List (Char, Key)
627 | data RuntimeCurses : CursesState -> Type where
628 | RInactive : RuntimeCurses Inactive
629 | RActive : CursesActive ws w cs -> RuntimeCurses (Active i ws w cs)
631 | getColor : (colors : List (String, ColorPair))
632 | -> (0 csPrf : NCurses.keys colors = names)
633 | -> (elemPrf : Elem _ names)
635 | getColor (z :: zs) csPrf Here = snd z
636 | getColor [] csPrf (There elemPrf) impossible
637 | getColor (z :: zs) csPrf (There elemPrf) = getColor zs (snd $
keysInjective csPrf) elemPrf
639 | getWindow : IdentifiesWindow n ws =>
640 | (rws : RuntimeWindows ws)
641 | -> (
rw ** CurrentWindow rw (lookupWindowPrf n ws) rws)
642 | getWindow @{Here} (rw@(MkRuntimeWindow (MkWindow n _ _) b win) :: rws') = (
rw ** Here)
643 | getWindow @{There e} (rw'@(MkRuntimeWindow (MkWindow identifier keypad noDelay) border win) :: rws') =
644 | bimap id There $
getWindow @{e} rws'
646 | bumpWindowSameWindow : {0 ws : List State.Window}
647 | -> {0 w : DPair State.Window (flip Elem ws)}
648 | -> {windows : RuntimeWindows ws}
649 | -> {rw : RuntimeWindow}
650 | -> {wPrf : CurrentWindow rw (w.snd) windows}
651 | -> CurrentWindow rw ((State.bumpWindow w).snd) (MkRuntimeWindow (MkWindow n True False) b runtimeWin :: windows)
652 | bumpWindowSameWindow {w = (
w' ** elem)
} = There wPrf
654 | addRuntimeWindow : {0 ws : List State.Window}
655 | -> {0 w : DPair State.Window (flip Elem ws)}
657 | -> (border : Maybe RuntimeBorder)
658 | -> (runtimeWin : Core.Window)
659 | -> CursesActive ws w cs
660 | -> CursesActive (initWindow name :: ws) (bumpWindow w) cs
661 | addRuntimeWindow identifier border runtimeWin (MkCursesActive windows (
rw ** wPrf)
colors currentColor keyMap {csPrf = csPrf}) =
662 | MkCursesActive { windows = initRuntimeWindow identifier border runtimeWin :: windows
663 | , currentWindow = (
rw ** bumpWindowSameWindow {wPrf})
670 | runtimeBorder : RuntimeCurses (Active i ws w cs)
671 | -> Exists (\c => (Border c, HasColor c (Active i ws w cs)))
673 | runtimeBorder rs@(RActive as) (Evidence color (border, (ItHasColor @{elem}))) =
674 | let cp = getColor as.colors as.csPrf elem
675 | in (cp, Evidence color border)
677 | addRuntimeColor : (name : String)
678 | -> (cp : ColorPair)
679 | -> CursesActive ws w cs
680 | -> CursesActive ws w (name :: cs)
681 | addRuntimeColor name cp (MkCursesActive windows currentWindow colors currentColor {csPrf} keyMap) =
682 | MkCursesActive { windows
684 | , colors = (name, cp) :: colors
685 | , csPrf = cong (name ::) csPrf
690 | setRuntimeWindow : IdentifiesWindow name ws
691 | -> CursesActive ws w cs
692 | -> CursesActive ws (
lookupWindow name ws ** lookupWindowPrf name ws)
cs
693 | setRuntimeWindow elem (MkCursesActive windows currentWindow colors {csPrf} currentColor keyMap) =
694 | MkCursesActive { windows
695 | , currentWindow = getWindow @{elem} windows
702 | getCoreWindow : CursesActive ws w cs -> Core.Window
703 | getCoreWindow (MkCursesActive _ (
(MkRuntimeWindow props border win) ** _)
_ _ _) = win
705 | getCoreWindow' : IsActive s => RuntimeCurses s -> Core.Window
706 | getCoreWindow' (RActive as) = getCoreWindow as
708 | swapKeypad' : {0 origW : State.Window}
709 | -> {0 origWs : List State.Window}
710 | -> {0 e : Elem origW origWs}
713 | -> (rws : RuntimeWindows origWs)
714 | -> CurrentWindow rw e rws
715 | -> (rws' : RuntimeWindows (swapKeypad on origWs e) ** CurrentWindow (setRWKeypad on rw) (swapKeypadPrf on origWs e) rws')
716 | swapKeypad' on {rw = (MkRuntimeWindow (MkWindow identifier keypad noDelay) b _)} ((MkRuntimeWindow (MkWindow identifier keypad noDelay) b _) :: rws) Here =
717 | (
(MkRuntimeWindow (MkWindow identifier on noDelay) b _) :: rws ** Here)
718 | swapKeypad' on {rw = (MkRuntimeWindow (MkWindow y z w) b win)} ((MkRuntimeWindow (MkWindow identifier keypad noDelay) border winOther) :: rws') (There {other=(MkRuntimeWindow (MkWindow identifier keypad noDelay) border winOther)} x) =
719 | let (
rws'' ** rw')
= swapKeypad' on {rw=MkRuntimeWindow (MkWindow y z w) b win} rws' x
720 | in (
(MkRuntimeWindow (MkWindow identifier keypad noDelay) border winOther) :: rws'' ** There {other=(MkRuntimeWindow (MkWindow identifier keypad noDelay) border winOther)} rw')
722 | setRuntimeKeypad : (on : Bool)
723 | -> RuntimeCurses (Active i ws w cs)
724 | -> RuntimeCurses (setKeypad (Active i ws w cs) on)
725 | setRuntimeKeypad on (RActive (MkCursesActive windows (
rw ** wPrf)
colors {csPrf} currentColor keyMap)) =
726 | let (
windows' ** rw')
= swapKeypad' on windows wPrf
729 | MkCursesActive { windows = windows'
730 | , currentWindow = (
setRWKeypad on rw ** rw')
737 | swapNoDelay' : {0 origW : State.Window}
738 | -> {0 origWs : List State.Window}
739 | -> {0 e : Elem origW origWs}
742 | -> (rws : RuntimeWindows origWs)
743 | -> CurrentWindow rw e rws
744 | -> (rws' : RuntimeWindows (swapNoDelay on origWs e) ** CurrentWindow (setRWNoDelay on rw) (swapNoDelayPrf on origWs e) rws')
745 | swapNoDelay' on {rw = (MkRuntimeWindow (MkWindow identifier keypad noDelay) b _)} ((MkRuntimeWindow (MkWindow identifier keypad noDelay) b _) :: rws) Here =
746 | (
(MkRuntimeWindow (MkWindow identifier keypad on) b _) :: rws ** Here)
747 | swapNoDelay' on {rw = (MkRuntimeWindow (MkWindow y z w) b win)} ((MkRuntimeWindow (MkWindow identifier keypad noDelay) border winOther) :: rws') (There {other=(MkRuntimeWindow (MkWindow identifier keypad noDelay) border winOther)} x) =
748 | let (
rws'' ** rw')
= swapNoDelay' on {rw=MkRuntimeWindow (MkWindow y z w) b win} rws' x
749 | in (
(MkRuntimeWindow (MkWindow identifier keypad noDelay) border winOther) :: rws'' ** There {other=(MkRuntimeWindow (MkWindow identifier keypad noDelay) border winOther)} rw')
751 | setRuntimeNoDelay : (on : Bool)
752 | -> RuntimeCurses (Active i ws w cs)
753 | -> RuntimeCurses (setNoDelay (Active i ws w cs) on)
754 | setRuntimeNoDelay on (RActive (MkCursesActive windows (
rw ** wPrf)
colors {csPrf} currentColor keyMap)) =
755 | let (
windows' ** rw')
= swapNoDelay' on windows wPrf
758 | MkCursesActive { windows = windows'
759 | , currentWindow = (
setRWNoDelay on rw ** rw')
766 | coreColor : RuntimeCurses s -> (name : String) -> HasColor name s => ColorPair
767 | coreColor (RActive (MkCursesActive _ _ colors {csPrf} _ _)) name @{ItHasColor @{elem}} =
768 | getColor colors csPrf elem
770 | coreColorAttr : RuntimeCurses s -> ColorAttr s -> ColorPair
771 | coreColorAttr _ DefaultColors = defaultColorPair
772 | coreColorAttr rs (Named name) = (coreColor rs name)
776 | coreAttr : RuntimeCurses s -> Attribute s -> Attribute
777 | coreAttr _ Normal = Normal
778 | coreAttr _ Underline = Underline
779 | coreAttr _ Standout = Standout
780 | coreAttr _ Reverse = Reverse
781 | coreAttr _ Blink = Blink
782 | coreAttr _ Dim = Dim
783 | coreAttr _ Bold = Bold
784 | coreAttr _ Protected = Protected
785 | coreAttr _ Invisible = Invisible
786 | coreAttr rs (Color c) = CP $
coreColorAttr rs c
788 | setCurrentColorPair : IsActive s => ColorPair -> RuntimeCurses s -> RuntimeCurses s
789 | setCurrentColorPair cp (RActive (MkCursesActive windows currentWindow colors {csPrf} _ keyMap)) =
790 | RActive (MkCursesActive windows currentWindow colors {csPrf} (Just cp) keyMap)
793 | setCurrentColor : IsActive s => ColorAttr s -> RuntimeCurses s -> RuntimeCurses s
794 | setCurrentColor (Named name @{ItHasColor @{elem}}) rs@(RActive (MkCursesActive windows currentWindow colors {csPrf} _ keyMap)) =
795 | setCurrentColorPair (getColor colors csPrf elem) rs
796 | setCurrentColor DefaultColors (RActive (MkCursesActive windows currentWindow colors {csPrf} _ keyMap)) =
797 | RActive (MkCursesActive windows currentWindow colors {csPrf} (Just defaultColorPair) keyMap)
800 | maybeSetCurrentColor : IsActive s => Attribute s -> RuntimeCurses s -> RuntimeCurses s
801 | maybeSetCurrentColor (Color attr) rs = setCurrentColor attr rs
802 | maybeSetCurrentColor _ rs = rs
805 | maybeUnsetCurrentColor : IsActive s => Attribute s -> RuntimeCurses s -> RuntimeCurses s
806 | maybeUnsetCurrentColor (Color _) (RActive (MkCursesActive windows currentWindow colors {csPrf} _ keyMap)) =
807 | RActive (MkCursesActive windows currentWindow colors {csPrf} Nothing keyMap)
808 | maybeUnsetCurrentColor _ rs = rs
810 | currentWindowHasBorder : CursesActive ws w cs -> Bool
811 | currentWindowHasBorder (MkCursesActive _ (
(MkRuntimeWindow _ border _) ** _)
_ _ _) = isJust border
813 | currentWindowHasBorder' : IsActive s => RuntimeCurses s -> Bool
814 | currentWindowHasBorder' (RActive as) = currentWindowHasBorder as
816 | modNCursesAttr : HasIO io =>
820 | -> io (RuntimeCurses s)
821 | modNCursesAttr (SetAttr attr) rs =
822 | nSetAttr' (getCoreWindow' rs) (coreAttr rs attr) $> (maybeSetCurrentColor attr rs)
823 | modNCursesAttr (EnableAttr attr) rs =
824 | nEnableAttr' (getCoreWindow' rs) (coreAttr rs attr) $> (maybeSetCurrentColor attr rs)
825 | modNCursesAttr (DisableAttr attr) rs =
826 | nDisableAttr' (getCoreWindow' rs) (coreAttr rs attr) $> (maybeUnsetCurrentColor attr rs)
827 | modNCursesAttr (UpdateAttr attr color len) rs =
828 | let cp = case color of
829 | DefaultColors => defaultColorPair
830 | Named name => coreColor rs name
832 | nChangeAttr' (getCoreWindow' rs) len (coreAttr rs attr) cp $> (maybeSetCurrentColor (Color color) rs)
833 | modNCursesAttr (SetBg color) rs =
834 | let cp = case color of
835 | DefaultColors => defaultColorPair
836 | Named name => coreColor rs name
838 | setBackground' (getCoreWindow' rs) cp *> pure rs
840 | printNCurses : HasIO io =>
844 | -> io (RuntimeCurses s)
845 | printNCurses (PutCh ch) rs = nPutCh' (getCoreWindow' rs) ch $> rs
846 | printNCurses (VLine ch n) rs = nVerticalLine' (getCoreWindow' rs) ch n $> rs
847 | printNCurses (HLine ch n) rs = nHorizontalLine' (getCoreWindow' rs) ch n $> rs
849 | printNCurses (Move (MkPosition row col)) rs@(RActive as) =
850 | nMoveCursor' (getCoreWindow as) (offset row) (offset col) $> rs
852 | offset : Nat -> Nat
853 | offset x = if (currentWindowHasBorder as) then (S x) else x
855 | printNCurses (PutStr newline str) rs@(RActive as) = do
856 | let win = getCoreWindow as
857 | (_, fullWidth) <- getMaxSize' win
858 | col <- getXPos' win
859 | let col' = (if currentWindowHasBorder as then (pred col) else col)
860 | nPutStr' win (wrapText fullWidth col' str) $> rs
862 | winWidth : Nat -> (
k ** NonZero k)
864 | let w = if currentWindowHasBorder as then (n `minus` 2) else n
865 | in case w of 0 => (
1 ** %search);
(S w') => (
(S w') ** %search)
868 | lineInfix = if currentWindowHasBorder as then "\n " else "\n"
870 | chunk : (chunkSize : Nat)
871 | -> NonZero chunkSize =>
873 | -> (rem : List Char)
874 | -> (acc : SnocList Char)
875 | -> (acc2 : SnocList String)
877 | chunk z _ [] [<] acc2 = cast acc2
878 | chunk z _ [] (sx :< x) acc2 = cast $
(acc2 :< (pack $
cast (sx :< x)))
879 | chunk z 0 (x :: xs) acc acc2 = assert_total $
chunk z z (x :: xs) [<] (acc2 :< (pack $
cast acc))
880 | chunk z (S k) (x :: xs) acc acc2 = chunk z k xs (acc :< x) acc2
882 | wrapText : (fullWidth : Nat) -> (currentColumn : Nat) -> String -> String
884 | let (
width ** _)
= winWidth w
885 | splitText = chunk width (width `minus` c) (unpack s) [<] [<]
886 | allTxt = concat (intersperse lineInfix splitText)
887 | final = if newline then allTxt ++ lineInfix else allTxt
891 | drawBorder : HasIO io => Core.Window -> (currentColor : Maybe ColorPair) -> Maybe RuntimeBorder -> io ()
892 | drawBorder _ _ Nothing = pure ()
893 | drawBorder win currentColor (Just (cp, (Evidence _ border))) = do
894 | nEnableAttr' win (CP cp)
895 | nWindowBorder win border.left border.right border.top border.bottom
896 | border.topLeft border.topRight border.bottomLeft border.bottomRight
897 | case currentColor of
898 | Just cp' => nEnableAttr' win (CP cp')
899 | Nothing => nDisableAttr' win (CP cp)
902 | drawCurrentBorder : HasIO io => CursesActive ws w cs -> io ()
903 | drawCurrentBorder (MkCursesActive _ (
(MkRuntimeWindow props border win) ** _)
_ currentColor _) =
904 | drawBorder win currentColor border
906 | refreshAllRuntime : HasIO io => RuntimeWindows ws -> (currentColor : Maybe ColorPair) -> io ()
907 | refreshAllRuntime [] _ = pure ()
908 | refreshAllRuntime ((MkRuntimeWindow _ border win) :: ws) currentColor =
909 | drawBorder win currentColor border *> refresh' win *> refreshAllRuntime ws currentColor
912 | runNCurses : HasIO io => NCurses a s1 s2 -> RuntimeCurses s1 -> io (a, RuntimeCurses s2)
914 | runNCurses (Pure x) rs = pure (x, rs)
915 | runNCurses (Bind mod@(ModAttr (DisableAttr x)) f) rs with (f ())
916 | runNCurses (Bind mod@(ModAttr (DisableAttr x)) f) rs | (Bind mod2@(ModAttr (EnableAttr y)) f2) =
919 | then runNCurses (f2 ()) rs
921 | ((), rs') <- runNCurses mod rs
922 | (x, rs'') <- runNCurses mod2 rs'
923 | runNCurses (f2 x) rs''
924 | runNCurses (Bind mod@(ModAttr (DisableAttr x)) f) rs | next = do
925 | ((), rs') <- runNCurses mod rs
926 | runNCurses next rs'
927 | runNCurses (Bind x f) rs = do
928 | (x', rs') <- runNCurses x rs
929 | runNCurses (f x') rs'
930 | runNCurses (NIO ops) rs = do
934 | runNCurses Init RInactive = Prelude.do
941 | keyMap <- SpecialKey.keyMap
942 | pure ((), RActive $
MkCursesActive [initRuntimeWindow DefaultWindow Nothing win]
943 | (
initRuntimeWindow DefaultWindow Nothing win ** Here)
948 | runNCurses DeInit (RActive _) = do
950 | pure ((), RInactive)
952 | runNCurses (AddWindow @{isActive} name pos size border) rs@(RActive as) = Prelude.do
953 | runtimeWin <- newWindow size.rows size.cols pos.row pos.col
954 | keypad' runtimeWin True
955 | noDelay' runtimeWin False
956 | let b = runtimeBorder rs <$> border
957 | drawBorder runtimeWin as.currentColor b
958 | let as' = addRuntimeWindow name b runtimeWin as
960 | nMoveCursor' runtimeWin 1 1
961 | pure ((), RActive as')
962 | runNCurses (SetWindow @{_} name @{ItHasWindow @{elem}}) (RActive as) = pure ((), RActive $
setRuntimeWindow elem as)
963 | runNCurses (UnsetWindow @{_} @{ItHasWindow @{elem}}) (RActive as) = pure ((), RActive $
setRuntimeWindow elem as)
965 | runNCurses Clear rs@(RActive as) = do
966 | let win = getCoreWindow as
968 | when (currentWindowHasBorder as) $
969 | nMoveCursor' win 1 1
971 | runNCurses Erase rs@(RActive as) = do
972 | let win = getCoreWindow as
974 | when (currentWindowHasBorder as) $
975 | nMoveCursor' win 1 1
977 | runNCurses Refresh rs@(RActive as) =
978 | let win = getCoreWindow as
979 | in drawCurrentBorder as *> refresh' win $> ((), rs)
980 | runNCurses RefreshAll rs@(RActive (MkCursesActive windows _ _ currentColor _)) =
984 | refreshAllRuntime windows currentColor $> ((), rs)
986 | runNCurses (AddColor name fg bg) (RActive as) = do
987 | let nextIdx = length as.colors
988 | when (nextIdx == 0) startColor
989 | cp <- initColorPair nextIdx fg bg
990 | let as' = addRuntimeColor name cp as
991 | pure ((), RActive as')
992 | runNCurses (ModAttr cmd) rs = do
993 | rs' <- modNCursesAttr cmd rs
995 | runNCurses (Output cmd) rs = do
996 | rs' <- printNCurses cmd rs
998 | runNCurses GetCursPos rs = do
999 | let win = getCoreWindow' rs
1002 | let offset : Nat -> Nat = (\coord => if currentWindowHasBorder' rs then (pred coord) else coord)
1003 | pure (MkPosition (offset y) (offset x), rs)
1004 | runNCurses (SetWinPos pos) rs = moveWindow (getCoreWindow' rs) pos.row pos.col $> ((), rs)
1005 | runNCurses GetWinPos rs = do
1006 | (y, x) <- getWindowPos' (getCoreWindow' rs)
1007 | pure ((MkPosition y x), rs)
1008 | runNCurses (SetWinSize size) rs = setWindowSize (getCoreWindow' rs) size.rows size.cols $> ((), rs)
1009 | runNCurses (GetWinSize internal) rs = do
1010 | (rows, cols) <- getMaxSize' (getCoreWindow' rs)
1011 | let offset : Nat -> Nat = (\dim => if currentWindowHasBorder' rs && internal then (dim `minus` 2) else dim)
1012 | pure (MkSize (offset rows) (offset cols), rs)
1013 | runNCurses (SetEcho on) (RActive as) = (ifThenElse on echo noEcho) $> ((), RActive as)
1014 | runNCurses (SetCBreak on) (RActive as) = (ifThenElse on cBreak noCBreak) $> ((), RActive as)
1015 | runNCurses (SetKeypad on) (RActive as) = keypad' (getCoreWindow as) on $> ((), setRuntimeKeypad on $
RActive as)
1016 | runNCurses (SetNoDelay on) (RActive as) = noDelay' (getCoreWindow as) on $> ((), setRuntimeNoDelay on $
RActive as)
1017 | runNCurses (SetCursor c) rs = setCursorVisibility c $> ((), rs)
1018 | runNCurses GetCh rs@(RActive as@(MkCursesActive windows {w} (
(MkRuntimeWindow (MkWindow _ keypad noDelay) _ _) ** wPrf)
colors currentColor keyMap)) with 0 (w)
1019 | runNCurses GetCh rs@(RActive as@(MkCursesActive windows {w} (
(MkRuntimeWindow (MkWindow _ keypad noDelay) _ _) ** wPrf)
colors currentColor keyMap)) | (
w' ** e')
=
1020 | rewrite sym $
currentWindowPropsPrf e' wPrf in
1023 | then do Just ch <- safeGetCh' (getCoreWindow as)
1024 | | Nothing => pure (Nothing, rewrite currentWindowPropsPrf e' wPrf in rs)
1025 | let keyOrCh = maybeToEither ch (lookup ch keyMap)
1026 | pure (Just keyOrCh, rewrite currentWindowPropsPrf e' wPrf in rs)
1027 | else do ch <- safeGetCh' (getCoreWindow as)
1028 | pure (ch, rewrite currentWindowPropsPrf e' wPrf in rs)
1030 | then do Just ch <- safeGetCh' (getCoreWindow as)
1031 | | Nothing => pure (Left ' ', rewrite currentWindowPropsPrf e' wPrf in rs)
1033 | let keyOrCh = maybeToEither ch (lookup ch keyMap)
1034 | pure (keyOrCh, rewrite currentWindowPropsPrf e' wPrf in rs)
1035 | else do Just ch <- safeGetCh' (getCoreWindow as)
1036 | | Nothing => pure (' ', rewrite currentWindowPropsPrf e' wPrf in rs)
1038 | pure (ch, rewrite currentWindowPropsPrf e' wPrf in rs)
1039 | runNCurses (InWindow w @{active} @{ihw@(ItHasWindow @{elem})} nc) rs@(RActive as) = do
1040 | let hasWindow = setWindowHasWindowStill @{ihw} @{ihw}
1041 | let isActive = setWindowIsActiveStill @{active} @{ihw}
1042 | let inWindow = inWindowNow @{ihw}
1043 | let nc' = nc @{isActive} @{hasWindow} @{inWindow}
1044 | r <- runNCurses nc' (RActive $
setRuntimeWindow elem as)
1051 | withNCurses : HasIO io => NCurses a Inactive Inactive -> io a
1054 | lift $
fst <$> (runNCurses nc RInactive)
1056 | testProgram : HasIO io => io ()
1057 | testProgram = withNCurses testRoutine