packages feed

termbox-banana 0.2.0 → 0.3.0

raw patch · 6 files changed

+273/−508 lines, 6 filesdep ~basedep ~reactive-bananadep ~termboxPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: base, reactive-banana, termbox

API changes (from Hackage documentation)

- Termbox.Banana: InputModeAlt :: MouseMode -> InputMode
- Termbox.Banana: InputModeEsc :: MouseMode -> InputMode
- Termbox.Banana: KeyBackspace2 :: Key
- Termbox.Banana: KeyCtrl2 :: Key
- Termbox.Banana: KeyCtrl3 :: Key
- Termbox.Banana: KeyCtrl4 :: Key
- Termbox.Banana: KeyCtrl5 :: Key
- Termbox.Banana: KeyCtrl7 :: Key
- Termbox.Banana: KeyCtrlH :: Key
- Termbox.Banana: KeyCtrlI :: Key
- Termbox.Banana: KeyCtrlLsqBracket :: Key
- Termbox.Banana: KeyCtrlM :: Key
- Termbox.Banana: KeyCtrlUnderscore :: Key
- Termbox.Banana: MouseModeNo :: MouseMode
- Termbox.Banana: MouseModeYes :: MouseMode
- Termbox.Banana: OutputMode216 :: OutputMode
- Termbox.Banana: OutputMode256 :: OutputMode
- Termbox.Banana: OutputModeGrayscale :: OutputMode
- Termbox.Banana: OutputModeNormal :: OutputMode
- Termbox.Banana: Scene :: !Cells -> !Cursor -> Scene
- Termbox.Banana: data InputMode
- Termbox.Banana: data MouseMode
- Termbox.Banana: data OutputMode
- Termbox.Banana: data Scene
- Termbox.Banana: instance GHC.Base.Monoid Termbox.Banana.Cells
- Termbox.Banana: instance GHC.Base.Semigroup Termbox.Banana.Cells
- Termbox.Banana: run_ :: InputMode -> OutputMode -> (Event TermboxEvent -> Behavior (Int, Int) -> MomentIO (Behavior Scene, Event a)) -> IO a
+ Termbox.Banana: KeyCtrlBackspace :: Key
+ Termbox.Banana: PollError :: PollError
+ Termbox.Banana: data PollError
+ Termbox.Banana: pattern KeyCtrl2 :: Key
+ Termbox.Banana: pattern KeyCtrl3 :: Key
+ Termbox.Banana: pattern KeyCtrl4 :: Key
+ Termbox.Banana: pattern KeyCtrl5 :: Key
+ Termbox.Banana: pattern KeyCtrl7 :: Key
+ Termbox.Banana: pattern KeyCtrlH :: Key
+ Termbox.Banana: pattern KeyCtrlI :: Key
+ Termbox.Banana: pattern KeyCtrlLsqBracket :: Key
+ Termbox.Banana: pattern KeyCtrlM :: Key
+ Termbox.Banana: pattern KeyCtrlUnderscore :: Key
- Termbox.Banana: EventKey :: !Key -> !Bool -> Event
+ Termbox.Banana: EventKey :: !Key -> Event
- Termbox.Banana: run :: InputMode -> OutputMode -> (Event TermboxEvent -> Behavior (Int, Int) -> MomentIO (Behavior Scene, Event a)) -> IO (Either InitError a)
+ Termbox.Banana: run :: (Event TermboxEvent -> Behavior (Int, Int) -> MomentIO (Behavior (Cells, Cursor), Event a)) -> IO a

Files

− .travis.yml
@@ -1,137 +0,0 @@-# This Travis job script has been generated by a script via-#-#   haskell-ci 'termbox-banana.cabal'-#-# For more information, see https://github.com/haskell-CI/haskell-ci-#-# version: 0.3.20190521-#-language: c-dist: xenial-git:-  # whether to recursively clone submodules-  submodules: false-cache:-  directories:-    - $HOME/.cabal/packages-    - $HOME/.cabal/store-before_cache:-  - rm -fv $CABALHOME/packages/hackage.haskell.org/build-reports.log-  # remove files that are regenerated by 'cabal update'-  - rm -fv $CABALHOME/packages/hackage.haskell.org/00-index.*-  - rm -fv $CABALHOME/packages/hackage.haskell.org/*.json-  - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.cache-  - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar-  - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar.idx-  - rm -rfv $CABALHOME/packages/head.hackage-matrix:-  include:-    - compiler: ghc-8.6.5-      addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.6.5","cabal-install-2.4"]}}-    - compiler: ghc-8.4.4-      addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.4.4","cabal-install-2.4"]}}-before_install:-  - HC=$(echo "/opt/$CC/bin/ghc" | sed 's/-/\//')-  - HCPKG="$HC-pkg"-  - unset CC-  - CABAL=/opt/ghc/bin/cabal-  - CABALHOME=$HOME/.cabal-  - export PATH="$CABALHOME/bin:$PATH"-  - TOP=$(pwd)-  - HCNUMVER=$(( $(${HC} --numeric-version|sed -E 's/([0-9]+)\.([0-9]+)\.([0-9]+).*/\1 * 10000 + \2 * 100 + \3/') ))-  - echo $HCNUMVER-  - CABAL="$CABAL -vnormal+nowrap+markoutput"-  - set -o pipefail-  - |-    echo 'function blue(s) { printf "\033[0;34m" s "\033[0m " }'           >> .colorful.awk-    echo 'BEGIN { state = "output"; }'                                     >> .colorful.awk-    echo '/^-----BEGIN CABAL OUTPUT-----$/ { state = "cabal" }'            >> .colorful.awk-    echo '/^-----END CABAL OUTPUT-----$/ { state = "output" }'             >> .colorful.awk-    echo '!/^(-----BEGIN CABAL OUTPUT-----|-----END CABAL OUTPUT-----)/ {' >> .colorful.awk-    echo '  if (state == "cabal") {'                                       >> .colorful.awk-    echo '    print blue($0)'                                              >> .colorful.awk-    echo '  } else {'                                                      >> .colorful.awk-    echo '    print $0'                                                    >> .colorful.awk-    echo '  }'                                                             >> .colorful.awk-    echo '}'                                                               >> .colorful.awk-  - cat .colorful.awk-  - |-    color_cabal_output () {-      awk -f $TOP/.colorful.awk-    }-  - echo text | color_cabal_output-install:-  - ${CABAL} --version-  - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"-  - TEST=--enable-tests-  - BENCH=--enable-benchmarks-  - GHCHEAD=${GHCHEAD-false}-  - rm -f $CABALHOME/config-  - |-    echo "verbose: normal +nowrap +markoutput"          >> $CABALHOME/config-    echo "remote-build-reporting: anonymous"            >> $CABALHOME/config-    echo "remote-repo-cache: $CABALHOME/packages"       >> $CABALHOME/config-    echo "logs-dir:          $CABALHOME/logs"           >> $CABALHOME/config-    echo "world-file:        $CABALHOME/world"          >> $CABALHOME/config-    echo "extra-prog-path:   $CABALHOME/bin"            >> $CABALHOME/config-    echo "symlink-bindir:    $CABALHOME/bin"            >> $CABALHOME/config-    echo "installdir:        $CABALHOME/bin"            >> $CABALHOME/config-    echo "build-summary:     $CABALHOME/logs/build.log" >> $CABALHOME/config-    echo "store-dir:         $CABALHOME/store"          >> $CABALHOME/config-    echo "install-dirs user"                            >> $CABALHOME/config-    echo "  prefix: $CABALHOME"                         >> $CABALHOME/config-    echo "repository hackage.haskell.org"               >> $CABALHOME/config-    echo "  url: http://hackage.haskell.org/"           >> $CABALHOME/config-  - cat $CABALHOME/config-  - rm -fv cabal.project cabal.project.local cabal.project.freeze-  - travis_retry ${CABAL} v2-update -v-  # Generate cabal.project-  - rm -rf cabal.project cabal.project.local cabal.project.freeze-  - touch cabal.project-  - |-    echo 'packages: "."' >> cabal.project-  - |-    echo "write-ghc-environment-files: always" >> cabal.project-  - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(termbox-banana)$' || true) | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done"-  - cat cabal.project || true-  - cat cabal.project.local || true-  - if [ -f "./configure.ac" ]; then (cd "." && autoreconf -i); fi-  - ${CABAL} v2-freeze -w ${HC} ${TEST} ${BENCH} | color_cabal_output-  - "cat cabal.project.freeze | sed -E 's/^(constraints: *| *)//' | sed 's/any.//'"-  - rm  cabal.project.freeze-  - ${CABAL} v2-build -w ${HC} ${TEST} ${BENCH} --dep -j2 all | color_cabal_output-  - ${CABAL} v2-build -w ${HC} --disable-tests --disable-benchmarks --dep -j2 all | color_cabal_output-script:-  - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)-  # Packaging...-  - ${CABAL} v2-sdist all | color_cabal_output-  # Unpacking...-  - mv dist-newstyle/sdist/*.tar.gz ${DISTDIR}/-  - cd ${DISTDIR} || false-  - find . -maxdepth 1 -name '*.tar.gz' -exec tar -xvf '{}' \;-  # Generate cabal.project-  - rm -rf cabal.project cabal.project.local cabal.project.freeze-  - touch cabal.project-  - |-    echo 'packages: "termbox-banana-*/*.cabal"' >> cabal.project-  - |-    echo "write-ghc-environment-files: always" >> cabal.project-  - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(termbox-banana)$' || true) | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done"-  - cat cabal.project || true-  - cat cabal.project.local || true-  # Building...-  # this builds all libraries and executables (without tests/benchmarks)-  - ${CABAL} v2-build -w ${HC} --disable-tests --disable-benchmarks all | color_cabal_output-  # Building with tests and benchmarks...-  # build & run tests, build benchmarks-  - ${CABAL} v2-build -w ${HC} ${TEST} ${BENCH} all | color_cabal_output-  # cabal check...-  - (cd termbox-banana-* && ${CABAL} -vnormal check)-  # haddock...-  - ${CABAL} v2-haddock -w ${HC} ${TEST} ${BENCH} all | color_cabal_output-  # Building without installed constraints for packages in global-db...-  - rm -f cabal.project.local-  - ${CABAL} v2-build -w ${HC} --disable-tests --disable-benchmarks all | color_cabal_output--# REGENDATA ["termbox-banana.cabal"]-# EOF
CHANGELOG.md view
@@ -5,6 +5,15 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to the [Haskell Package Versioning Policy](https://pvp.haskell.org/). +## [0.3.0] - 2020-09-20++### Changed+- Support GHC 8.8, GHC 8.10+- Depend on `termbox ==0.3`++### Removed+- Removed `Scene`+ ## [0.2.0] - 2019-06-28  ### Added
examples/Echo.hs view
@@ -1,56 +1,49 @@-{-# LANGUAGE LambdaCase          #-}+{-# LANGUAGE LambdaCase #-} {-# LANGUAGE ScopedTypeVariables #-} -module Main where+module Main (main) where  import Reactive.Banana import Reactive.Banana.Frameworks- import qualified Termbox.Banana as Termbox  main :: IO () main =-  Termbox.run_-    (Termbox.InputModeEsc Termbox.MouseModeNo)-    Termbox.OutputModeNormal-    moment+  Termbox.run moment -moment-  :: Event Termbox.Event-  -> Behavior (Int, Int)-  -> MomentIO (Behavior Termbox.Scene, Event ())+moment ::+  Event Termbox.Event ->+  Behavior (Int, Int) ->+  MomentIO (Behavior (Termbox.Cells, Termbox.Cursor), Event ()) moment eEvent _bSize = do-  let-    eQuit :: Event ()-    eQuit =-      () <$ filterE isKeyEsc eEvent+  let eQuit :: Event ()+      eQuit =+        () <$ filterE isKeyEsc eEvent    bLatestEvent :: Behavior (Maybe Termbox.Event) <-     stepper       Nothing       (Just <$> eEvent) -  let-    bCells :: Behavior Termbox.Cells-    bCells =-      maybe mempty renderEvent <$> bLatestEvent+  let bCells :: Behavior Termbox.Cells+      bCells =+        maybe mempty renderEvent <$> bLatestEvent -  let-    bScene :: Behavior Termbox.Scene-    bScene =-      Termbox.Scene-        <$> bCells-        <*> pure Termbox.NoCursor+  let bScene :: Behavior (Termbox.Cells, Termbox.Cursor)+      bScene =+        (,)+          <$> bCells+          <*> pure Termbox.NoCursor    pure (bScene, eQuit)  renderEvent :: Termbox.Event -> Termbox.Cells renderEvent =   foldMap (\(i, c) -> Termbox.set i 0 (Termbox.Cell c mempty mempty))-    . zip [0..]+    . zip [0 ..]     . show  isKeyEsc :: Termbox.Event -> Bool isKeyEsc = \case-  Termbox.EventKey Termbox.KeyEsc _ -> True+  Termbox.EventKey Termbox.KeyEsc -> True   _ -> False
examples/Hoogle.hs view
@@ -1,7 +1,7 @@-{-# language LambdaCase          #-}-{-# language OverloadedStrings   #-}-{-# language RecursiveDo         #-}-{-# language ScopedTypeVariables #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecursiveDo #-}+{-# LANGUAGE ScopedTypeVariables #-}  module Main where @@ -22,21 +22,17 @@ import Network.HTTP.Simple import Reactive.Banana import Reactive.Banana.Frameworks-import Text.HTML.TagSoup- import qualified Termbox.Banana as Termbox+import Text.HTML.TagSoup  main :: IO () main =-  Termbox.run_-    (Termbox.InputModeEsc Termbox.MouseModeNo)-    Termbox.OutputModeNormal-    moment+  Termbox.run moment -moment-  :: Event Termbox.Event-  -> Behavior (Int, Int)-  -> MomentIO (Behavior Termbox.Scene, Event ())+moment ::+  Event Termbox.Event ->+  Behavior (Int, Int) ->+  MomentIO (Behavior (Termbox.Cells, Termbox.Cursor), Event ()) moment eEvent bSize = mdo   eTick :: Event () <-     makeTickEvent@@ -47,13 +43,11 @@   eSearchBox :: Event String <-     makeSearchBoxEvent eKey -  reactimate-    ((atomically . writeTQueue requestQueue . reverse) <$> eSearchBox)+  reactimate ((atomically . writeTQueue requestQueue . reverse) <$> eSearchBox) -  let-    eEmptySearchBox :: Event String-    eEmptySearchBox =-      filterE null eSearchBox+  let eEmptySearchBox :: Event String+      eEmptySearchBox =+        filterE null eSearchBox    bSearchBox :: Behavior String <-     stepper "" eSearchBox@@ -69,53 +63,54 @@   bSearchResults :: Behavior (Either SomeException [Value]) <-     stepper       (Right [])-      (unionWith-        const-        (Right [] <$ eEmptySearchBox)-        eSearchResults)+      ( unionWith+          const+          (Right [] <$ eEmptySearchBox)+          eSearchResults+      )    bSpinnerFrame :: Behavior (Maybe Int) <--    accumB Nothing (unions-      [ ((\query ->-            if null query-              then const Nothing-              else const (Just 1))-          <$> eSearchBox)-      , const Nothing <$ eSearchResults-      , const Nothing <$ eEmptySearchBox-      , fmap (+1) <$ whenE (isJust <$> bSpinnerFrame) eTick-      ])+    accumB+      Nothing+      ( unions+          [ ( ( \query ->+                  if null query+                    then const Nothing+                    else const (Just 1)+              )+                <$> eSearchBox+            ),+            const Nothing <$ eSearchResults,+            const Nothing <$ eEmptySearchBox,+            fmap (+ 1) <$ whenE (isJust <$> bSpinnerFrame) eTick+          ]+      ) -  let-    bCells :: Behavior Termbox.Cells-    bCells =-      render-        <$> bHeight-        <*> bSearchBox-        <*> bSearchResults-        <*> bSpinnerFrame+  let bCells :: Behavior Termbox.Cells+      bCells =+        render+          <$> bHeight+          <*> bSearchBox+          <*> bSearchResults+          <*> bSpinnerFrame -  let-    bCursor :: Behavior Termbox.Cursor-    bCursor =-      (\height searchBox -> Termbox.Cursor (length searchBox + 2) height)-        <$> bHeight-        <*> bSearchBox+  let bCursor :: Behavior Termbox.Cursor+      bCursor =+        (\height searchBox -> Termbox.Cursor (length searchBox + 2) height)+          <$> bHeight+          <*> bSearchBox -  let-    bScene :: Behavior Termbox.Scene-    bScene =-      Termbox.Scene-        <$> bCells-        <*> bCursor+  let bScene :: Behavior (Termbox.Cells, Termbox.Cursor)+      bScene =+        (,)+          <$> bCells+          <*> bCursor    pure (bScene, () <$ filterE (== Termbox.KeyEsc) eKey)-   where     eKey :: Event Termbox.Key     eKey =       filterJust (eventAsKey <$> eEvent)-     bHeight :: Behavior Int     bHeight =       snd <$> bSize@@ -128,56 +123,59 @@     threadDelay 100000   pure e -makeSearchBoxEvent-  :: MonadMoment m-  => Event Termbox.Key-  -> m (Event String)+makeSearchBoxEvent ::+  MonadMoment m =>+  Event Termbox.Key ->+  m (Event String) makeSearchBoxEvent eKey =   accumE     ""-    (unions-      [ (:) <$> filterJust (keyAsChar <$> eKey)-      , (' ' :) <$  filterE (== Termbox.KeySpace) eKey-      , safeTail <$ filterE (== Termbox.KeyBackspace2) eKey-      ])+    ( unions+        [ (:) <$> filterJust (keyAsChar <$> eKey),+          (' ' :) <$ filterE (== Termbox.KeySpace) eKey,+          safeTail <$ filterE (== Termbox.KeyCtrl8) eKey+        ]+    ) -runHttpRequestThread-  :: TQueue String -- ^ Request queue-  -> (Either SomeException [Value] -> IO ()) -- ^ Response callback-  -> IO ()+runHttpRequestThread ::+  -- | Request queue+  TQueue String ->+  -- | Response callback+  (Either SomeException [Value] -> IO ()) ->+  IO () runHttpRequestThread requestQueue respond =   loop Nothing   where     loop :: Maybe (Async [Value]) -> IO ()     loop maybeInFlightRequest =       join . atomically $-        (do-          query <- readTQueue requestQueue-          pure $ do-            for_ maybeInFlightRequest $ \inFlightRequest ->-              forkIO (cancel inFlightRequest)-            if null query-              then-                loop Nothing-              else do-                inFlightRequest <- async (performHoogleSearch query)-                loop (Just inFlightRequest))-        <|>-        (case maybeInFlightRequest of-          Nothing ->-            retry-          Just inFlightRequest -> do-            response <- waitCatchSTM inFlightRequest+        ( do+            query <- readTQueue requestQueue             pure $ do-              respond response-              loop Nothing)+              for_ maybeInFlightRequest $ \inFlightRequest ->+                forkIO (cancel inFlightRequest)+              if null query+                then loop Nothing+                else do+                  inFlightRequest <- async (performHoogleSearch query)+                  loop (Just inFlightRequest)+        )+          <|> ( case maybeInFlightRequest of+                  Nothing ->+                    retry+                  Just inFlightRequest -> do+                    response <- waitCatchSTM inFlightRequest+                    pure $ do+                      respond response+                      loop Nothing+              ) -render-  :: Int-  -> String-  -> Either SomeException [Value]-  -> Maybe Int-  -> Termbox.Cells+render ::+  Int ->+  String ->+  Either SomeException [Value] ->+  Maybe Int ->+  Termbox.Cells render height searchBox searchResults spinnerFrame =   fold     [ case searchResults of@@ -186,56 +184,52 @@         Right results ->           renderSearchResults height results             & execWriterT-            & (`evalState` 0)-    , renderSearchBox height searchBox spinnerFrame+            & (`evalState` 0),+      renderSearchBox height searchBox spinnerFrame     ]  renderSearchBox :: Int -> String -> Maybe Int -> Termbox.Cells renderSearchBox height searchBox spinnerFrame =-  renderString 0 (height-1) (promptChar : ' ' : reverse searchBox)+  renderString 0 (height -1) (promptChar : ' ' : reverse searchBox)   where     promptChar :: Char     promptChar =       case spinnerFrame of         Nothing ->           'λ'-        Just n  ->-          let-            cs =-              "⣧⣏⡟⠿⢻⣹⣼⣶"-          in-            cs !! (n `mod` length cs)+        Just n ->+          let cs =+                "⣧⣏⡟⠿⢻⣹⣼⣶"+           in cs !! (n `mod` length cs) -renderSearchResults-  :: Int-  -> [Value]-  -> WriterT Termbox.Cells (State Int) ()+renderSearchResults ::+  Int ->+  [Value] ->+  WriterT Termbox.Cells (State Int) () renderSearchResults height = \case   [] ->     pure ()-   result : results -> do     row <- get -    let-      ss = searchResultToLines result+    let ss = searchResultToLines result -    when (row + length ss < height-1) $ do-      for_ (zip [row..] ss) $ \(r, s) ->+    when (row + length ss < height -1) $ do+      for_ (zip [row ..] ss) $ \(r, s) ->         tell (renderString 0 r s)       modify' (+ (length ss + 1))       renderSearchResults height results -renderSearchResultsError-  :: Int-  -> SomeException-  -> Termbox.Cells+renderSearchResultsError ::+  Int ->+  SomeException ->+  Termbox.Cells renderSearchResultsError height ex =   ex     & show     & lines     & take height-    & zip [0..]+    & zip [0 ..]     & foldMap (\(row, line) -> renderString 0 row line)  searchResultToLines :: Value -> [String]@@ -243,23 +237,20 @@   case result ^?! key "type" of     "" ->       unwords-        [ "[" ++ resultPackage result ++ "]"-        , "[" ++ resultModule result ++ "]"-        , resultItem result+        [ "[" ++ resultPackage result ++ "]",+          "[" ++ resultModule result ++ "]",+          resultItem result         ]         : map ("  " ++) (resultDocs result)-     "module" ->       unwords-        [ "[" ++ resultPackage result ++ "]"-        , "[" ++ drop 7 (resultItem result) ++ "]"+        [ "[" ++ resultPackage result ++ "]",+          "[" ++ drop 7 (resultItem result) ++ "]"         ]         : map ("  " ++) (resultDocs result)-     "package" ->       ("[" ++ drop 8 (resultItem result) ++ "]")         : map ("  " ++) (resultDocs result)-     _ ->       error (show result) @@ -296,21 +287,17 @@       . to htmlToText       . to lines       . to collapseLines-   where     collapseLines :: [String] -> [String]     collapseLines = \case       [] ->         []-       [""] ->         []-       "" : ss ->         case collapseLines ss of           "" : ts -> "" : ts-          ts      -> "" : ts-+          ts -> "" : ts       s : ss ->         s : collapseLines ss @@ -320,11 +307,10 @@     TagText text <- parseTags html     pure text - renderString :: Int -> Int -> String -> Termbox.Cells renderString col row =-  foldMap (\(i, c) -> Termbox.set i row (Termbox.Cell c mempty mempty)) .-    zip [col..]+  foldMap (\(i, c) -> Termbox.set i row (Termbox.Cell c mempty mempty))+    . zip [col ..]  performHoogleSearch :: String -> IO [Value] performHoogleSearch query =@@ -336,7 +322,7 @@  eventAsKey :: Termbox.Event -> Maybe Termbox.Key eventAsKey = \case-  Termbox.EventKey k _ -> Just k+  Termbox.EventKey k -> Just k   _ -> Nothing  keyAsChar :: Termbox.Key -> Maybe Char@@ -347,4 +333,4 @@ safeTail :: [a] -> [a] safeTail = \case   [] -> []-  _:xs -> xs+  _ : xs -> xs
src/Termbox/Banana.hs view
@@ -1,62 +1,58 @@-{-# language DerivingStrategies         #-}-{-# language GeneralizedNewtypeDeriving #-}-{-# language LambdaCase                 #-}-{-# language ScopedTypeVariables        #-}-{-# language TypeFamilies               #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE ScopedTypeVariables #-}  module Termbox.Banana   ( -- $intro-    TermboxEvent-  , run-  , run_-  , set-  , Scene(..)-  , Cells-  , Cursor(..)+    TermboxEvent,+    run,+     -- * Re-exports-  , Termbox.black-  , Termbox.red-  , Termbox.green-  , Termbox.yellow-  , Termbox.blue-  , Termbox.magenta-  , Termbox.cyan-  , Termbox.white-  , Termbox.bold-  , Termbox.underline-  , Termbox.reverse-  , Termbox.Attr-  , Termbox.Cell(..)-  , Termbox.Event(..)-  , Termbox.InitError(..)-  , Termbox.InputMode(..)-  , Termbox.Key(..)-  , Termbox.Mouse(..)-  , Termbox.MouseMode(..)-  , Termbox.OutputMode(..)+    Termbox.black,+    Termbox.blue,+    Termbox.bold,+    Termbox.cyan,+    Termbox.green,+    Termbox.magenta,+    Termbox.red,+    Termbox.reverse,+    Termbox.underline,+    Termbox.white,+    Termbox.yellow,+    Termbox.set,+    Termbox.Attr,+    Termbox.Cell (..),+    Termbox.Cells,+    Termbox.Cursor (..),+    Termbox.Event (..),+    Termbox.InitError (..),+    Termbox.Key (..),+    pattern Termbox.KeyCtrl2,+    pattern Termbox.KeyCtrl3,+    pattern Termbox.KeyCtrl4,+    pattern Termbox.KeyCtrl5,+    pattern Termbox.KeyCtrl7,+    pattern Termbox.KeyCtrlH,+    pattern Termbox.KeyCtrlI,+    pattern Termbox.KeyCtrlLsqBracket,+    pattern Termbox.KeyCtrlM,+    pattern Termbox.KeyCtrlUnderscore,+    Termbox.Mouse (..),+    Termbox.PollError (..),+     -- * Example     -- $example-  ) where+  )+where  import Control.Concurrent.MVar-import Control.Exception (throwIO) import Data.Function (fix) import Reactive.Banana import Reactive.Banana.Frameworks- import qualified Termbox - -- $intro -- See the bottom of this module for a simple, runnable example to get started. ----- Here's how to run the examples with @cabal@:------ @--- cabal v2-run --constraint "termbox-banana +build-examples" termbox-banana-example-echo--- cabal v2-run --constraint "termbox-banana +build-examples" termbox-banana-example-hoogle--- @--- -- This module is intended to be imported qualified. -- -- @@@ -68,42 +64,11 @@ -- -- * @Event@ for @reactive-banana@ events -- * @Termbox.Event@ for @termbox@ events------ @since 0.1.0-type TermboxEvent-  = Termbox.Event---- | A scene to render; a grid of cells and a cursor.------ @since 0.1.0-data Scene-  = Scene !Cells !Cursor---- | A grid of cells.------ Create a 'Cells' with 'set' or 'mempty' and combine them with ('<>').------ @since 0.1.0-newtype Cells-  = Cells (IO ())-  deriving newtype (Monoid, Semigroup)---- | A cursor.------ @since 0.1.0-data Cursor-  = Cursor !Int !Int -- ^ Column, then row-  | NoCursor---- | Set a single cell's value (column, then row).------ @since 0.1.0-set :: Int -> Int -> Termbox.Cell -> Cells-set x y z =-  Cells (Termbox.set x y z)+type TermboxEvent =+  Termbox.Event -type EventSource a-  = (AddHandler a, a -> IO ())+type EventSource a =+  (AddHandler a, a -> IO ())  -- | Run a @termbox@ program with the specified input and output modes. --@@ -117,20 +82,14 @@ -- * a time-varying scene to render -- * an event stream of arbitrary values, only the first of which is relevant, --   which ends the @termbox@ program and returns from the @main@ action.------ @since 0.2.0-run-  :: Termbox.InputMode -- ^-  -> Termbox.OutputMode -- ^-  -> (  Event TermboxEvent-     -> Behavior (Int, Int)-     -> MomentIO (Behavior Scene, Event a))-  -> IO (Either Termbox.InitError a)-run imode omode program =-  Termbox.run $ do-    Termbox.setInputMode imode-    Termbox.setOutputMode omode-+run ::+  ( Event TermboxEvent ->+    Behavior (Int, Int) ->+    MomentIO (Behavior (Termbox.Cells, Termbox.Cursor), Event a)+  ) ->+  IO a+run program =+  Termbox.run $ \width height render poll -> do     doneVar :: MVar a <-       newEmptyMVar @@ -142,69 +101,47 @@         eEvent :: Event TermboxEvent <-           fromAddHandler eventAddHandler -        let-          eResize :: Event (Int, Int)-          eResize =-            filterJust-              ((\case-                Termbox.EventResize w h -> Just (w, h)-                _ -> Nothing)-              <$> eEvent)+        let eResize :: Event (Int, Int)+            eResize =+              filterJust+                ( ( \case+                      Termbox.EventResize w h -> Just (w, h)+                      _ -> Nothing+                  )+                    <$> eEvent+                )          bSize :: Behavior (Int, Int) <--          flip stepper eResize =<<-            liftIO Termbox.getSize+          flip stepper eResize (width, height) -        moment program eEvent bSize (putMVar doneVar)+        moment (uncurry render) program eEvent bSize (putMVar doneVar)      actuate network      fix $ \loop -> do-      Termbox.poll >>= fireEvent-      tryReadMVar doneVar >>=-        maybe loop pure---- | Like 'run', but throws 'Termbox.InitError's as @IO@ exceptions.------ @since 0.2.0-run_-  :: Termbox.InputMode -- ^-  -> Termbox.OutputMode -- ^-  -> (  Event TermboxEvent-     -> Behavior (Int, Int)-     -> MomentIO (Behavior Scene, Event a))-  -> IO a-run_ imode omode program =-  run imode omode program >>= either throwIO pure+      poll >>= fireEvent+      tryReadMVar doneVar >>= maybe loop pure -moment-  :: (  Event TermboxEvent-     -> Behavior (Int, Int)-     -> MomentIO (Behavior Scene, Event a))-  -> Event TermboxEvent-  -> Behavior (Int, Int)-  -> (a -> IO ())-  -> MomentIO ()-moment program eEvent bSize abort = do-  (bScene, eDone) :: (Behavior Scene, Event a) <-+moment ::+  ((Termbox.Cells, Termbox.Cursor) -> IO ()) ->+  ( Event TermboxEvent ->+    Behavior (Int, Int) ->+    MomentIO (Behavior (Termbox.Cells, Termbox.Cursor), Event a)+  ) ->+  Event TermboxEvent ->+  Behavior (Int, Int) ->+  (a -> IO ()) ->+  MomentIO ()+moment render program eEvent bSize abort = do+  (bScene, eDone) :: (Behavior (Termbox.Cells, Termbox.Cursor), Event a) <-     program eEvent bSize -  eScene :: Event (Future Scene) <-+  eScene :: Event (Future (Termbox.Cells, Termbox.Cursor)) <-     changes bScene -  let-    render :: Scene -> IO ()-    render (Scene (Cells cells) cursor) = do-      Termbox.clear mempty mempty-      cells-      case cursor of-        Cursor c r -> Termbox.setCursor c r-        NoCursor -> Termbox.hideCursor-      Termbox.flush-   liftIO . render =<< valueB bScene   reactimate (abort <$> eDone)-  reactimate' ((fmap.fmap) render eScene)+  reactimate' ((fmap . fmap) render eScene)  -- $example --@@ -224,15 +161,12 @@ -- -- main :: IO () -- main =---   Termbox.'run_'---     (Termbox.'Termbox.InputModeEsc' Termbox.'Termbox.MouseModeNo')---     Termbox.'Termbox.OutputModeNormal'---     moment+--   Termbox.'run' moment -- -- moment --   :: Event Termbox.'Termbox.Event' --   -> Behavior (Int, Int)---   -> MomentIO (Behavior Termbox.'Termbox.Scene', Event ())+--   -> MomentIO (Behavior (Termbox.'Termbox.Cells', Termbox.'Termbox.Cursor'), Event ()) -- moment eEvent _bSize = do --   let --     eQuit :: Event ()@@ -250,9 +184,9 @@ --       maybe mempty renderEvent \<$\> bLatestEvent -- --   let---     bScene :: Behavior Termbox.'Termbox.Scene'+--     bScene :: Behavior (Termbox.'Termbox.Cells', Termbox.'Termbox.Cursor') --     bScene =---       Termbox.'Termbox.Scene'+--       (,) --         \<$\> bCells --         \<*\> pure Termbox.'Termbox.NoCursor' --
termbox-banana.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.4  name: termbox-banana-version: 0.2.0+version: 0.3.0 category: User Interfaces description:   A @reactive-banana@-based interface to writing @termbox@ programs.@@ -23,99 +23,79 @@ maintainer: Mitchell Rosen <mitchellwrosen@gmail.com> homepage: https://github.com/mitchellwrosen/termbox-banana bug-reports: https://github.com/mitchellwrosen/termbox-banana/issues-copyright: (c) 2018-2019, Mitchell Rosen+copyright: (c) 2018-2020, Mitchell Rosen license: BSD-3-Clause license-file: LICENSE build-type: Simple-tested-with: GHC == 8.4.4, GHC == 8.6.5  extra-source-files:-  .travis.yml   CHANGELOG.md   README.md +source-repository head+  type: git+  location: git://github.com/mitchellwrosen/termbox-banana.git+ flag build-examples   description: Build example executables   default: False   manual: True -library-  build-depends:-      base ^>= 4.10 || ^>= 4.11 || ^>= 4.12-    , reactive-banana ^>= 1.2-    , termbox ^>= 0.2--  default-language:-    Haskell2010--  exposed-modules:-    Termbox.Banana-+common component+  default-extensions:+    GeneralizedNewtypeDeriving+    InstanceSigs+    LambdaCase+  default-language: Haskell2010   ghc-options:-    -Wall-    -Wcompat-    -Widentities-    -Wincomplete-record-updates-    -Wincomplete-uni-patterns-    -Wmissing-export-lists-    -Wnoncanonical-monad-instances-    -Wnoncanonical-monadfail-instances-    -Wpartial-fields-    -Wredundant-constraints-    -fprint-expanded-synonyms-    -fprint-explicit-foralls-    -fprint-explicit-kinds-    -fprint-unicode-syntax--  hs-source-dirs:-    src+    -Weverything+    -Wno-implicit-prelude+    -Wno-missing-import-lists+    -Wno-missing-local-signatures+    -Wno-monomorphism-restriction+    -Wno-safe+    -Wno-unsafe+  if impl(ghc >= 8.10)+    ghc-options:+      -Wno-missing-safe-haskell-mode+      -Wno-prepositive-qualified-module -executable termbox-banana-example-echo+common example-executable+  import: component   if !flag(build-examples)     buildable: False-   build-depends:     base,     reactive-banana,     termbox-banana,--  default-language:-    Haskell2010--  ghc-options:-    -Wall -threaded "-with-rtsopts=-N"+  ghc-options: -Wall -threaded "-with-rtsopts=-N"+  hs-source-dirs: examples -  hs-source-dirs:-    examples+library+  import: component+  build-depends:+      base ^>= 4.10 || ^>= 4.11 || ^>= 4.12 || ^>= 4.13 || ^>= 4.14+    , reactive-banana ^>= 1.2+    , termbox ^>= 0.3+  exposed-modules: Termbox.Banana+  hs-source-dirs: src -  main-is:-    Echo.hs+executable termbox-banana-example-echo+  import: example-executable+  ghc-options: -Wall -threaded "-with-rtsopts=-N"+  main-is: Echo.hs  executable termbox-banana-example-hoogle+  import: example-executable   if !flag(build-examples)     buildable: False-   build-depends:     aeson,     async,-    base,     http-conduit,     lens,     lens-aeson,     mtl,-    reactive-banana,     stm,     tagsoup,-    termbox-banana,--  default-language:-    Haskell2010--  ghc-options:-    -Wall -threaded "-with-rtsopts=-N"--  hs-source-dirs:-    examples--  main-is:-    Hoogle.hs+  main-is: Hoogle.hs