packages feed

termbox 0.3.0 → 1.0.0

raw patch · 26 files changed

+1211/−2726 lines, 26 filesdep +kidep +termbox-bindings-hsdep ~basenew-component:exe:termbox-example-demoPVP ok

version bump matches the API change (PVP)

Dependencies added: ki, termbox-bindings-hs

Dependency ranges changed: base

API changes (from Hackage documentation)

- Termbox: Cell :: !Char -> !Attr -> !Attr -> Cell
- Termbox: Cursor :: !Int -> !Int -> Cursor
- Termbox: MouseLeft :: Mouse
- Termbox: MouseMiddle :: Mouse
- Termbox: MouseRelease :: Mouse
- Termbox: MouseRight :: Mouse
- Termbox: MouseWheelDown :: Mouse
- Termbox: MouseWheelUp :: Mouse
- Termbox: NoCursor :: Cursor
- Termbox: PollError :: PollError
- Termbox: black :: Attr
- Termbox: data Attr
- Termbox: data Cells
- Termbox: data Cursor
- Termbox: data PollError
- Termbox: reverse :: Attr
- Termbox: set :: Int -> Int -> Cell -> Cells
- Termbox.Internal: TbEvent :: !Word8 -> Word8 -> Word16 -> Word32 -> Int32 -> Int32 -> Int32 -> Int32 -> TbEvent
- Termbox.Internal: data TbEvent
- Termbox.Internal: instance Foreign.Storable.Storable Termbox.Internal.TbEvent
- Termbox.Internal: tB_BLACK :: Word16
- Termbox.Internal: tB_BLUE :: Word16
- Termbox.Internal: tB_BOLD :: Word16
- Termbox.Internal: tB_CYAN :: Word16
- Termbox.Internal: tB_DEFAULT :: Word16
- Termbox.Internal: tB_EFAILED_TO_OPEN_TTY :: Int
- Termbox.Internal: tB_EPIPE_TRAP_ERROR :: Int
- Termbox.Internal: tB_EUNSUPPORTED_TERMINAL :: Int
- Termbox.Internal: tB_EVENT_KEY :: Word8
- Termbox.Internal: tB_EVENT_MOUSE :: Word8
- Termbox.Internal: tB_EVENT_RESIZE :: Word8
- Termbox.Internal: tB_GREEN :: Word16
- Termbox.Internal: tB_HIDE_CURSOR :: Int
- Termbox.Internal: tB_INPUT_ALT :: Int
- Termbox.Internal: tB_INPUT_CURRENT :: Int
- Termbox.Internal: tB_INPUT_ESC :: Int
- Termbox.Internal: tB_INPUT_MOUSE :: Int
- Termbox.Internal: tB_KEY_ARROW_DOWN :: Word16
- Termbox.Internal: tB_KEY_ARROW_LEFT :: Word16
- Termbox.Internal: tB_KEY_ARROW_RIGHT :: Word16
- Termbox.Internal: tB_KEY_ARROW_UP :: Word16
- Termbox.Internal: tB_KEY_BACKSPACE :: Word16
- Termbox.Internal: tB_KEY_BACKSPACE2 :: Word16
- Termbox.Internal: tB_KEY_CTRL_2 :: Word16
- Termbox.Internal: tB_KEY_CTRL_3 :: Word16
- Termbox.Internal: tB_KEY_CTRL_4 :: Word16
- Termbox.Internal: tB_KEY_CTRL_5 :: Word16
- Termbox.Internal: tB_KEY_CTRL_6 :: Word16
- Termbox.Internal: tB_KEY_CTRL_7 :: Word16
- Termbox.Internal: tB_KEY_CTRL_8 :: Word16
- Termbox.Internal: tB_KEY_CTRL_A :: Word16
- Termbox.Internal: tB_KEY_CTRL_B :: Word16
- Termbox.Internal: tB_KEY_CTRL_BACKSLASH :: Word16
- Termbox.Internal: tB_KEY_CTRL_C :: Word16
- Termbox.Internal: tB_KEY_CTRL_D :: Word16
- Termbox.Internal: tB_KEY_CTRL_E :: Word16
- Termbox.Internal: tB_KEY_CTRL_F :: Word16
- Termbox.Internal: tB_KEY_CTRL_G :: Word16
- Termbox.Internal: tB_KEY_CTRL_H :: Word16
- Termbox.Internal: tB_KEY_CTRL_I :: Word16
- Termbox.Internal: tB_KEY_CTRL_J :: Word16
- Termbox.Internal: tB_KEY_CTRL_K :: Word16
- Termbox.Internal: tB_KEY_CTRL_L :: Word16
- Termbox.Internal: tB_KEY_CTRL_LSQ_BRACKET :: Word16
- Termbox.Internal: tB_KEY_CTRL_M :: Word16
- Termbox.Internal: tB_KEY_CTRL_N :: Word16
- Termbox.Internal: tB_KEY_CTRL_O :: Word16
- Termbox.Internal: tB_KEY_CTRL_P :: Word16
- Termbox.Internal: tB_KEY_CTRL_Q :: Word16
- Termbox.Internal: tB_KEY_CTRL_R :: Word16
- Termbox.Internal: tB_KEY_CTRL_RSQ_BRACKET :: Word16
- Termbox.Internal: tB_KEY_CTRL_S :: Word16
- Termbox.Internal: tB_KEY_CTRL_SLASH :: Word16
- Termbox.Internal: tB_KEY_CTRL_T :: Word16
- Termbox.Internal: tB_KEY_CTRL_TILDE :: Word16
- Termbox.Internal: tB_KEY_CTRL_U :: Word16
- Termbox.Internal: tB_KEY_CTRL_UNDERSCORE :: Word16
- Termbox.Internal: tB_KEY_CTRL_V :: Word16
- Termbox.Internal: tB_KEY_CTRL_W :: Word16
- Termbox.Internal: tB_KEY_CTRL_X :: Word16
- Termbox.Internal: tB_KEY_CTRL_Y :: Word16
- Termbox.Internal: tB_KEY_CTRL_Z :: Word16
- Termbox.Internal: tB_KEY_DELETE :: Word16
- Termbox.Internal: tB_KEY_END :: Word16
- Termbox.Internal: tB_KEY_ENTER :: Word16
- Termbox.Internal: tB_KEY_ESC :: Word16
- Termbox.Internal: tB_KEY_F1 :: Word16
- Termbox.Internal: tB_KEY_F10 :: Word16
- Termbox.Internal: tB_KEY_F11 :: Word16
- Termbox.Internal: tB_KEY_F12 :: Word16
- Termbox.Internal: tB_KEY_F2 :: Word16
- Termbox.Internal: tB_KEY_F3 :: Word16
- Termbox.Internal: tB_KEY_F4 :: Word16
- Termbox.Internal: tB_KEY_F5 :: Word16
- Termbox.Internal: tB_KEY_F6 :: Word16
- Termbox.Internal: tB_KEY_F7 :: Word16
- Termbox.Internal: tB_KEY_F8 :: Word16
- Termbox.Internal: tB_KEY_F9 :: Word16
- Termbox.Internal: tB_KEY_HOME :: Word16
- Termbox.Internal: tB_KEY_INSERT :: Word16
- Termbox.Internal: tB_KEY_MOUSE_LEFT :: Word16
- Termbox.Internal: tB_KEY_MOUSE_MIDDLE :: Word16
- Termbox.Internal: tB_KEY_MOUSE_RELEASE :: Word16
- Termbox.Internal: tB_KEY_MOUSE_RIGHT :: Word16
- Termbox.Internal: tB_KEY_MOUSE_WHEEL_DOWN :: Word16
- Termbox.Internal: tB_KEY_MOUSE_WHEEL_UP :: Word16
- Termbox.Internal: tB_KEY_PGDN :: Word16
- Termbox.Internal: tB_KEY_PGUP :: Word16
- Termbox.Internal: tB_KEY_SPACE :: Word16
- Termbox.Internal: tB_KEY_TAB :: Word16
- Termbox.Internal: tB_MAGENTA :: Word16
- Termbox.Internal: tB_MOD_ALT :: Word8
- Termbox.Internal: tB_OUTPUT_216 :: Int
- Termbox.Internal: tB_OUTPUT_256 :: Int
- Termbox.Internal: tB_OUTPUT_CURRENT :: Int
- Termbox.Internal: tB_OUTPUT_GRAYSCALE :: Int
- Termbox.Internal: tB_OUTPUT_NORMAL :: Int
- Termbox.Internal: tB_RED :: Word16
- Termbox.Internal: tB_REVERSE :: Word16
- Termbox.Internal: tB_UNDERLINE :: Word16
- Termbox.Internal: tB_WHITE :: Word16
- Termbox.Internal: tB_YELLOW :: Word16
- Termbox.Internal: tb_change_cell :: Int -> Int -> Word32 -> Word16 -> Word16 -> IO ()
- Termbox.Internal: tb_clear :: IO ()
- Termbox.Internal: tb_height :: IO Int
- Termbox.Internal: tb_init :: IO Int
- Termbox.Internal: tb_peek_event :: Ptr TbEvent -> Int -> IO Int
- Termbox.Internal: tb_poll_event :: Ptr TbEvent -> IO Int
- Termbox.Internal: tb_present :: IO ()
- Termbox.Internal: tb_select_input_mode :: Int -> IO Int
- Termbox.Internal: tb_select_output_mode :: Int -> IO Int
- Termbox.Internal: tb_set_clear_attributes :: Word16 -> Word16 -> IO ()
- Termbox.Internal: tb_set_cursor :: Int -> Int -> IO ()
- Termbox.Internal: tb_shutdown :: IO ()
- Termbox.Internal: tb_width :: IO Int
+ Termbox: EventUser :: !e -> Event e
+ Termbox: Pos :: {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> Pos
+ Termbox: Program :: (Size -> s) -> Maybe (IO e) -> (s -> Event e -> IO s) -> (s -> Scene) -> (s -> Bool) -> Program s
+ Termbox: Size :: {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> Size
+ Termbox: [$sel:col:Pos] :: Pos -> {-# UNPACK #-} !Int
+ Termbox: [$sel:finished:Program] :: Program s -> s -> Bool
+ Termbox: [$sel:handleEvent:Program] :: Program s -> s -> Event e -> IO s
+ Termbox: [$sel:height:Size] :: Size -> {-# UNPACK #-} !Int
+ Termbox: [$sel:initialize:Program] :: Program s -> Size -> s
+ Termbox: [$sel:pollEvent:Program] :: Program s -> Maybe (IO e)
+ Termbox: [$sel:render:Program] :: Program s -> s -> Scene
+ Termbox: [$sel:row:Pos] :: Pos -> {-# UNPACK #-} !Int
+ Termbox: [$sel:width:Size] :: Size -> {-# UNPACK #-} !Int
+ Termbox: bg :: Color -> Cell -> Cell
+ Termbox: blink :: Cell -> Cell
+ Termbox: bright :: Color -> Color
+ Termbox: cell :: Pos -> Cell -> Scene
+ Termbox: char :: Char -> Cell
+ Termbox: color :: Int -> Color
+ Termbox: cursor :: Pos -> Scene
+ Termbox: data Color
+ Termbox: data Pos
+ Termbox: data Program s
+ Termbox: data Scene
+ Termbox: data Size
+ Termbox: defaultColor :: Color
+ Termbox: fg :: Color -> Cell -> Cell
+ Termbox: fill :: Color -> Scene
+ Termbox: gray :: Int -> Color
+ Termbox: pattern MouseLeft :: Mouse
+ Termbox: pattern MouseMiddle :: Mouse
+ Termbox: pattern MouseRelease :: Mouse
+ Termbox: pattern MouseRight :: Mouse
+ Termbox: pattern MouseWheelDown :: Mouse
+ Termbox: pattern MouseWheelUp :: Mouse
+ Termbox: posDown :: Int -> Pos -> Pos
+ Termbox: posLeft :: Int -> Pos -> Pos
+ Termbox: posRight :: Int -> Pos -> Pos
+ Termbox: posUp :: Int -> Pos -> Pos
- Termbox: EventKey :: !Key -> Event
+ Termbox: EventKey :: !Key -> Event e
- Termbox: EventMouse :: !Mouse -> !Int -> !Int -> Event
+ Termbox: EventMouse :: !Mouse -> !Pos -> Event e
- Termbox: EventResize :: !Int -> !Int -> Event
+ Termbox: EventResize :: !Size -> Event e
- Termbox: blue :: Attr
+ Termbox: blue :: Color
- Termbox: bold :: Attr
+ Termbox: bold :: Cell -> Cell
- Termbox: cyan :: Attr
+ Termbox: cyan :: Color
- Termbox: data Event
+ Termbox: data Event e
- Termbox: green :: Attr
+ Termbox: green :: Color
- Termbox: magenta :: Attr
+ Termbox: magenta :: Color
- Termbox: red :: Attr
+ Termbox: red :: Color
- Termbox: run :: (Int -> Int -> (Cells -> Cursor -> IO ()) -> IO Event -> IO a) -> IO a
+ Termbox: run :: Program s -> IO (Either InitError s)
- Termbox: underline :: Attr
+ Termbox: underline :: Cell -> Cell
- Termbox: white :: Attr
+ Termbox: white :: Color
- Termbox: yellow :: Attr
+ Termbox: yellow :: Color

Files

CHANGELOG.md view
@@ -1,25 +1,27 @@-# Changelog--All notable changes to this project will be documented in this file.--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/).+## [1.0.0] - Unreleased+- Rework `run`; add `Program` record of callbacks+- Add user events to `Event` type+- Overhaul `Attr`+`Cell`+`Cells`+`Cursor` API; now it's `Cell`+`Color`+`Scene`+- Rename `set` to `cell`+- Add combinator to brighten basic colors (`bright`)+- Add combinator for miscellaneous colors (`color`)+- Add combinator for monochrome colors (`gray`)+- Add `Pos` type+- Add `Size` type+- Remove `Termbox.Internal` module (see packages `termbox-bindings-hs` or `termbox-bindings-c` instead)+- Fix off-by-one error in named colors (black, etc) (thanks @seagreen)+- Support GHC 9.4+- Drop support for GHC < 8.8  ## [0.3.0] - 2020-09-20--### Added - Add `Cells` and `Cursor` types - Export `Termbox.Internal` module that roughly corresponds to the C library--### Changed - Add a few arguments to the action provided to `run` - Make `run` throw `InitError`s as IO exceptions - Reset output mode to "normal" on shutdown to work around a small bug in termbox.c that retains the output mode across   separate invocations of init/shutdown - Change type of `set` to construct a `Cells` rather than an `IO ()` - Change a few keys into pattern synonyms because they overlap--### Removed - Remove the alt modifier field from `KeyEvent` - Remove `setCursor`, `hideCursor`, `clear`, `flush`, `getCells`, `getSize`, `poll`, `run_` - Remove `InputMode`, `MouseMode`, and `OutputMode`, providing sane defaults instead@@ -27,26 +29,16 @@ - Remove support for GHC < 8.2  ## [0.2.0.1] - 2020-06-27--### Changed - Bump `base` upper bound  ## [0.2.0] - 2019-06-21--### Added - Add `getCells` function - Add `run` function--### Changed - Rename `size` to `getSize` - Rename `main` to `run_` and return errors as an `Either` instead of throwing. - Make `Attr`'s `Semigroup` instance right-biased instead of left-biased. - Make `Attr`'s `Num` instance total.--### Removed-- Add `buffer` function+- Remove `buffer` function  ## [0.1.0] - 2018-07-18--### Added - Initial release
LICENSE view
@@ -1,4 +1,4 @@-Copyright 2018 Mitchell Rosen+Copyright 2018-2022 Mitchell Rosen  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 
− README.md
@@ -1,7 +0,0 @@-# `termbox`--[![GitHub CI](https://github.com/mitchellwrosen/termbox/workflows/CI/badge.svg)](https://github.com/mitchellwrosen/termbox/actions)-[![Hackage](https://img.shields.io/hackage/v/termbox.svg)](https://hackage.haskell.org/package/termbox)-[![Stackage LTS](https://stackage.org/package/termbox/badge/lts)](https://www.stackage.org/lts/package/termbox)-[![Stackage Nightly](https://stackage.org/package/termbox/badge/nightly)](https://www.stackage.org/nightly/package/termbox)-[![Dependencies](https://img.shields.io/hackage-deps/v/termbox)](https://packdeps.haskellers.com/reverse/termbox)
− cbits/LICENSE
@@ -1,19 +0,0 @@-Copyright (C) 2010-2013 nsf <no.smile.face@gmail.com>--Permission is hereby granted, free of charge, to any person obtaining a copy-of this software and associated documentation files (the "Software"), to deal-in the Software without restriction, including without limitation the rights-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell-copies of the Software, and to permit persons to whom the Software is-furnished to do so, subject to the following conditions:--The above copyright notice and this permission notice shall be included in-all copies or substantial portions of the Software.--THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN-THE SOFTWARE.
− cbits/termbox.c
@@ -1,1284 +0,0 @@-#include <assert.h>-#include <stdlib.h>-#include <string.h>-#include <errno.h>-#include <fcntl.h>-#include <signal.h>-#include <stdio.h>-#include <stdbool.h>-#include <sys/select.h>-#include <sys/ioctl.h>-#include <sys/time.h>-#include <sys/stat.h>-#include <termios.h>-#include <unistd.h>-#include <wchar.h>--#include "termbox.h"--struct bytebuffer {-	char *buf;-	int len;-	int cap;-};--static void bytebuffer_reserve(struct bytebuffer *b, int cap) {-	if (b->cap >= cap) {-		return;-	}--	// prefer doubling capacity-	if (b->cap * 2 >= cap) {-		cap = b->cap * 2;-	}--	char *newbuf = realloc(b->buf, cap);-	b->buf = newbuf;-	b->cap = cap;-}--static void bytebuffer_init(struct bytebuffer *b, int cap) {-	b->cap = 0;-	b->len = 0;-	b->buf = 0;--	if (cap > 0) {-		b->cap = cap;-		b->buf = malloc(cap); // just assume malloc works always-	}-}--static void bytebuffer_free(struct bytebuffer *b) {-	if (b->buf)-		free(b->buf);-}--static void bytebuffer_clear(struct bytebuffer *b) {-	b->len = 0;-}--static void bytebuffer_append(struct bytebuffer *b, const char *data, int len) {-	bytebuffer_reserve(b, b->len + len);-	memcpy(b->buf + b->len, data, len);-	b->len += len;-}--static void bytebuffer_puts(struct bytebuffer *b, const char *str) {-	bytebuffer_append(b, str, strlen(str));-}--static void bytebuffer_resize(struct bytebuffer *b, int len) {-	bytebuffer_reserve(b, len);-	b->len = len;-}--static void bytebuffer_flush(struct bytebuffer *b, int fd) {-	write(fd, b->buf, b->len);-	bytebuffer_clear(b);-}--static void bytebuffer_truncate(struct bytebuffer *b, int n) {-	if (n <= 0)-		return;-	if (n > b->len)-		n = b->len;-	const int nmove = b->len - n;-	memmove(b->buf, b->buf+n, nmove);-	b->len -= n;-}--enum {-	T_ENTER_CA,-	T_EXIT_CA,-	T_SHOW_CURSOR,-	T_HIDE_CURSOR,-	T_CLEAR_SCREEN,-	T_SGR0,-	T_UNDERLINE,-	T_BOLD,-	T_BLINK,-	T_REVERSE,-	T_ENTER_KEYPAD,-	T_EXIT_KEYPAD,-	T_ENTER_MOUSE,-	T_EXIT_MOUSE,-	T_FUNCS_NUM,-};--#define ENTER_MOUSE_SEQ "\x1b[?1000h\x1b[?1002h\x1b[?1015h\x1b[?1006h"-#define EXIT_MOUSE_SEQ "\x1b[?1006l\x1b[?1015l\x1b[?1002l\x1b[?1000l"--#define EUNSUPPORTED_TERM -1--// rxvt-256color-static const char *rxvt_256color_keys[] = {-	"\033[11~","\033[12~","\033[13~","\033[14~","\033[15~","\033[17~","\033[18~","\033[19~","\033[20~","\033[21~","\033[23~","\033[24~","\033[2~","\033[3~","\033[7~","\033[8~","\033[5~","\033[6~","\033[A","\033[B","\033[D","\033[C", 0-};-static const char *rxvt_256color_funcs[] = {-	"\0337\033[?47h", "\033[2J\033[?47l\0338", "\033[?25h", "\033[?25l", "\033[H\033[2J", "\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "\033=", "\033>", ENTER_MOUSE_SEQ, EXIT_MOUSE_SEQ,-};--// Eterm-static const char *eterm_keys[] = {-	"\033[11~","\033[12~","\033[13~","\033[14~","\033[15~","\033[17~","\033[18~","\033[19~","\033[20~","\033[21~","\033[23~","\033[24~","\033[2~","\033[3~","\033[7~","\033[8~","\033[5~","\033[6~","\033[A","\033[B","\033[D","\033[C", 0-};-static const char *eterm_funcs[] = {-	"\0337\033[?47h", "\033[2J\033[?47l\0338", "\033[?25h", "\033[?25l", "\033[H\033[2J", "\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "", "", "", "",-};--// screen-static const char *screen_keys[] = {-	"\033OP","\033OQ","\033OR","\033OS","\033[15~","\033[17~","\033[18~","\033[19~","\033[20~","\033[21~","\033[23~","\033[24~","\033[2~","\033[3~","\033[1~","\033[4~","\033[5~","\033[6~","\033OA","\033OB","\033OD","\033OC", 0-};-static const char *screen_funcs[] = {-	"\033[?1049h", "\033[?1049l", "\033[34h\033[?25h", "\033[?25l", "\033[H\033[J", "\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "\033[?1h\033=", "\033[?1l\033>", ENTER_MOUSE_SEQ, EXIT_MOUSE_SEQ,-};--// rxvt-unicode-static const char *rxvt_unicode_keys[] = {-	"\033[11~","\033[12~","\033[13~","\033[14~","\033[15~","\033[17~","\033[18~","\033[19~","\033[20~","\033[21~","\033[23~","\033[24~","\033[2~","\033[3~","\033[7~","\033[8~","\033[5~","\033[6~","\033[A","\033[B","\033[D","\033[C", 0-};-static const char *rxvt_unicode_funcs[] = {-	"\033[?1049h", "\033[r\033[?1049l", "\033[?25h", "\033[?25l", "\033[H\033[2J", "\033[m\033(B", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "\033=", "\033>", ENTER_MOUSE_SEQ, EXIT_MOUSE_SEQ,-};--// linux-static const char *linux_keys[] = {-	"\033[[A","\033[[B","\033[[C","\033[[D","\033[[E","\033[17~","\033[18~","\033[19~","\033[20~","\033[21~","\033[23~","\033[24~","\033[2~","\033[3~","\033[1~","\033[4~","\033[5~","\033[6~","\033[A","\033[B","\033[D","\033[C", 0-};-static const char *linux_funcs[] = {-	"", "", "\033[?25h\033[?0c", "\033[?25l\033[?1c", "\033[H\033[J", "\033[0;10m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "", "", "", "",-};--// xterm-static const char *xterm_keys[] = {-	"\033OP","\033OQ","\033OR","\033OS","\033[15~","\033[17~","\033[18~","\033[19~","\033[20~","\033[21~","\033[23~","\033[24~","\033[2~","\033[3~","\033OH","\033OF","\033[5~","\033[6~","\033OA","\033OB","\033OD","\033OC", 0-};-static const char *xterm_funcs[] = {-	"\033[?1049h", "\033[?1049l", "\033[?12l\033[?25h", "\033[?25l", "\033[H\033[2J", "\033(B\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "\033[?1h\033=", "\033[?1l\033>", ENTER_MOUSE_SEQ, EXIT_MOUSE_SEQ,-};--static struct term {-	const char *name;-	const char **keys;-	const char **funcs;-} terms[] = {-	{"rxvt-256color", rxvt_256color_keys, rxvt_256color_funcs},-	{"Eterm", eterm_keys, eterm_funcs},-	{"screen", screen_keys, screen_funcs},-	{"rxvt-unicode", rxvt_unicode_keys, rxvt_unicode_funcs},-	{"linux", linux_keys, linux_funcs},-	{"xterm", xterm_keys, xterm_funcs},-	{0, 0, 0},-};--static bool init_from_terminfo = false;-static const char **keys;-static const char **funcs;--static int try_compatible(const char *term, const char *name,-			  const char **tkeys, const char **tfuncs)-{-	if (strstr(term, name)) {-		keys = tkeys;-		funcs = tfuncs;-		return 0;-	}--	return EUNSUPPORTED_TERM;-}--static int init_term_builtin(void)-{-	int i;-	const char *term = getenv("TERM");--	if (term) {-		for (i = 0; terms[i].name; i++) {-			if (!strcmp(terms[i].name, term)) {-				keys = terms[i].keys;-				funcs = terms[i].funcs;-				return 0;-			}-		}--		/* let's do some heuristic, maybe it's a compatible terminal */-		if (try_compatible(term, "xterm", xterm_keys, xterm_funcs) == 0)-			return 0;-		if (try_compatible(term, "rxvt", rxvt_unicode_keys, rxvt_unicode_funcs) == 0)-			return 0;-		if (try_compatible(term, "linux", linux_keys, linux_funcs) == 0)-			return 0;-		if (try_compatible(term, "Eterm", eterm_keys, eterm_funcs) == 0)-			return 0;-		if (try_compatible(term, "screen", screen_keys, screen_funcs) == 0)-			return 0;-		/* let's assume that 'cygwin' is xterm compatible */-		if (try_compatible(term, "cygwin", xterm_keys, xterm_funcs) == 0)-			return 0;-	}--	return EUNSUPPORTED_TERM;-}--//-----------------------------------------------------------------------// terminfo-//------------------------------------------------------------------------static char *read_file(const char *file) {-	FILE *f = fopen(file, "rb");-	if (!f)-		return 0;--	struct stat st;-	if (fstat(fileno(f), &st) != 0) {-		fclose(f);-		return 0;-	}--	char *data = malloc(st.st_size);-	if (!data) {-		fclose(f);-		return 0;-	}--	if (fread(data, 1, st.st_size, f) != (size_t)st.st_size) {-		fclose(f);-		free(data);-		return 0;-	}--	fclose(f);-	return data;-}--static char *terminfo_try_path(const char *path, const char *term) {-	char tmp[4096];-	snprintf(tmp, sizeof(tmp), "%s/%c/%s", path, term[0], term);-	tmp[sizeof(tmp)-1] = '\0';-	char *data = read_file(tmp);-	if (data) {-		return data;-	}--	// fallback to darwin specific dirs structure-	snprintf(tmp, sizeof(tmp), "%s/%x/%s", path, term[0], term);-	tmp[sizeof(tmp)-1] = '\0';-	return read_file(tmp);-}--static char *load_terminfo(void) {-	char tmp[4096];-	const char *term = getenv("TERM");-	if (!term) {-		return 0;-	}--	// if TERMINFO is set, no other directory should be searched-	const char *terminfo = getenv("TERMINFO");-	if (terminfo) {-		return terminfo_try_path(terminfo, term);-	}--	// next, consider ~/.terminfo-	const char *home = getenv("HOME");-	if (home) {-		snprintf(tmp, sizeof(tmp), "%s/.terminfo", home);-		tmp[sizeof(tmp)-1] = '\0';-		char *data = terminfo_try_path(tmp, term);-		if (data)-			return data;-	}--	// next, TERMINFO_DIRS-	const char *dirs = getenv("TERMINFO_DIRS");-	if (dirs) {-		snprintf(tmp, sizeof(tmp), "%s", dirs);-		tmp[sizeof(tmp)-1] = '\0';-		char *dir = strtok(tmp, ":");-		while (dir) {-			const char *cdir = dir;-			if (strcmp(cdir, "") == 0) {-				cdir = "/usr/share/terminfo";-			}-			char *data = terminfo_try_path(cdir, term);-			if (data)-				return data;-			dir = strtok(0, ":");-		}-	}--	// fallback to /usr/share/terminfo-	return terminfo_try_path("/usr/share/terminfo", term);-}--#define TI_MAGIC 0432-#define TI_ALT_MAGIC 542-#define TI_HEADER_LENGTH 12-#define TB_KEYS_NUM 22--static const char *terminfo_copy_string(char *data, int str, int table) {-	const int16_t off = *(int16_t*)(data + str);-	const char *src = data + table + off;-	int len = strlen(src);-	char *dst = malloc(len+1);-	strcpy(dst, src);-	return dst;-}--static const int16_t ti_funcs[] = {-	28, 40, 16, 13, 5, 39, 36, 27, 26, 34, 89, 88,-};--static const int16_t ti_keys[] = {-	66, 68 /* apparently not a typo; 67 is F10 for whatever reason */, 69,-	70, 71, 72, 73, 74, 75, 67, 216, 217, 77, 59, 76, 164, 82, 81, 87, 61,-	79, 83,-};--static int init_term(void) {-	int i;-	char *data = load_terminfo();-	if (!data) {-		init_from_terminfo = false;-		return init_term_builtin();-	}--	int16_t *header = (int16_t*)data;--	const int number_sec_len = header[0] == TI_ALT_MAGIC ? 4 : 2;--	if ((header[1] + header[2]) % 2) {-		// old quirk to align everything on word boundaries-		header[2] += 1;-	}--	const int str_offset = TI_HEADER_LENGTH +-		header[1] + header[2] +	number_sec_len * header[3];-	const int table_offset = str_offset + 2 * header[4];--	keys = malloc(sizeof(const char*) * (TB_KEYS_NUM+1));-	for (i = 0; i < TB_KEYS_NUM; i++) {-		keys[i] = terminfo_copy_string(data,-			str_offset + 2 * ti_keys[i], table_offset);-	}-	keys[TB_KEYS_NUM] = 0;--	funcs = malloc(sizeof(const char*) * T_FUNCS_NUM);-	// the last two entries are reserved for mouse. because the table offset is-	// not there, the two entries have to fill in manually-	for (i = 0; i < T_FUNCS_NUM-2; i++) {-		funcs[i] = terminfo_copy_string(data,-			str_offset + 2 * ti_funcs[i], table_offset);-	}--	funcs[T_FUNCS_NUM-2] = ENTER_MOUSE_SEQ;-	funcs[T_FUNCS_NUM-1] = EXIT_MOUSE_SEQ;--	init_from_terminfo = true;-	free(data);-	return 0;-}--static void shutdown_term(void) {-	if (init_from_terminfo) {-		int i;-		for (i = 0; i < TB_KEYS_NUM; i++) {-			free((void*)keys[i]);-		}-		// the last two entries are reserved for mouse. because the table offset-		// is not there, the two entries have to fill in manually and do not-		// need to be freed.-		for (i = 0; i < T_FUNCS_NUM-2; i++) {-			free((void*)funcs[i]);-		}-		free(keys);-		free(funcs);-	}-}--// if s1 starts with s2 returns true, else false-// len is the length of s1-// s2 should be null-terminated-static bool starts_with(const char *s1, int len, const char *s2)-{-	int n = 0;-	while (*s2 && n < len) {-		if (*s1++ != *s2++)-			return false;-		n++;-	}-	return *s2 == 0;-}--static int parse_mouse_event(struct tb_event *event, const char *buf, int len) {-	if (len >= 6 && starts_with(buf, len, "\033[M")) {-		// X10 mouse encoding, the simplest one-		// \033 [ M Cb Cx Cy-		int b = buf[3] - 32;-		switch (b & 3) {-		case 0:-			if ((b & 64) != 0)-				event->key = TB_KEY_MOUSE_WHEEL_UP;-			else-				event->key = TB_KEY_MOUSE_LEFT;-			break;-		case 1:-			if ((b & 64) != 0)-				event->key = TB_KEY_MOUSE_WHEEL_DOWN;-			else-				event->key = TB_KEY_MOUSE_MIDDLE;-			break;-		case 2:-			event->key = TB_KEY_MOUSE_RIGHT;-			break;-		case 3:-			event->key = TB_KEY_MOUSE_RELEASE;-			break;-		default:-			return -6;-		}-		event->type = TB_EVENT_MOUSE; // TB_EVENT_KEY by default-		if ((b & 32) != 0)-			event->mod |= TB_MOD_MOTION;--		// the coord is 1,1 for upper left-		event->x = (uint8_t)buf[4] - 1 - 32;-		event->y = (uint8_t)buf[5] - 1 - 32;--		return 6;-	} else if (starts_with(buf, len, "\033[<") || starts_with(buf, len, "\033[")) {-		// xterm 1006 extended mode or urxvt 1015 extended mode-		// xterm: \033 [ < Cb ; Cx ; Cy (M or m)-		// urxvt: \033 [ Cb ; Cx ; Cy M-		int i, mi = -1, starti = -1;-		int isM, isU, s1 = -1, s2 = -1;-		int n1 = 0, n2 = 0, n3 = 0;--		for (i = 0; i < len; i++) {-			// We search the first (s1) and the last (s2) ';'-			if (buf[i] == ';') {-				if (s1 == -1)-					s1 = i;-				s2 = i;-			}--			// We search for the first 'm' or 'M'-			if ((buf[i] == 'm' || buf[i] == 'M') && mi == -1) {-				mi = i;-				break;-			}-		}-		if (mi == -1)-			return 0;--		// whether it's a capital M or not-		isM = (buf[mi] == 'M');--		if (buf[2] == '<') {-			isU = 0;-			starti = 3;-		} else {-			isU = 1;-			starti = 2;-		}--		if (s1 == -1 || s2 == -1 || s1 == s2)-			return 0;--		n1 = strtoul(&buf[starti], NULL, 10);-		n2 = strtoul(&buf[s1 + 1], NULL, 10);-		n3 = strtoul(&buf[s2 + 1], NULL, 10);--		if (isU)-			n1 -= 32;--		switch (n1 & 3) {-		case 0:-			if ((n1&64) != 0) {-				event->key = TB_KEY_MOUSE_WHEEL_UP;-			} else {-				event->key = TB_KEY_MOUSE_LEFT;-			}-			break;-		case 1:-			if ((n1&64) != 0) {-				event->key = TB_KEY_MOUSE_WHEEL_DOWN;-			} else {-				event->key = TB_KEY_MOUSE_MIDDLE;-			}-			break;-		case 2:-			event->key = TB_KEY_MOUSE_RIGHT;-			break;-		case 3:-			event->key = TB_KEY_MOUSE_RELEASE;-			break;-		default:-			return mi + 1;-		}--		if (!isM) {-			// on xterm mouse release is signaled by lowercase m-			event->key = TB_KEY_MOUSE_RELEASE;-		}--		event->type = TB_EVENT_MOUSE; // TB_EVENT_KEY by default-		if ((n1&32) != 0)-			event->mod |= TB_MOD_MOTION;--		event->x = (uint8_t)n2 - 1;-		event->y = (uint8_t)n3 - 1;--		return mi + 1;-	}--	return 0;-}--// convert escape sequence to event, and return consumed bytes on success (failure == 0)-static int parse_escape_seq(struct tb_event *event, const char *buf, int len)-{-	int mouse_parsed = parse_mouse_event(event, buf, len);--	if (mouse_parsed != 0)-		return mouse_parsed;--	// it's pretty simple here, find 'starts_with' match and return-	// success, else return failure-	int i;-	for (i = 0; keys[i]; i++) {-		if (starts_with(buf, len, keys[i])) {-			event->ch = 0;-			event->key = 0xFFFF-i;-			return strlen(keys[i]);-		}-	}-	return 0;-}--static bool extract_event(struct tb_event *event, struct bytebuffer *inbuf, int inputmode)-{-	const char *buf = inbuf->buf;-	const int len = inbuf->len;-	if (len == 0)-		return false;--	if (buf[0] == '\033') {-		int n = parse_escape_seq(event, buf, len);-		if (n != 0) {-			bool success = true;-			if (n < 0) {-				success = false;-				n = -n;-			}-			bytebuffer_truncate(inbuf, n);-			return success;-		} else {-			// it's not escape sequence, then it's ALT or ESC,-			// check inputmode-			if (inputmode&TB_INPUT_ESC) {-				// if we're in escape mode, fill ESC event, pop-				// buffer, return success-				event->ch = 0;-				event->key = TB_KEY_ESC;-				event->mod = 0;-				bytebuffer_truncate(inbuf, 1);-				return true;-			} else if (inputmode&TB_INPUT_ALT) {-				// if we're in alt mode, set ALT modifier to-				// event and redo parsing-				event->mod = TB_MOD_ALT;-				bytebuffer_truncate(inbuf, 1);-				return extract_event(event, inbuf, inputmode);-			}-			assert(!"never got here");-		}-	}--	// if we're here, this is not an escape sequence and not an alt sequence-	// so, it's a FUNCTIONAL KEY or a UNICODE character--	// first of all check if it's a functional key-	if ((unsigned char)buf[0] <= TB_KEY_SPACE ||-	    (unsigned char)buf[0] == TB_KEY_BACKSPACE2)-	{-		// fill event, pop buffer, return success */-		event->ch = 0;-		event->key = (uint16_t)buf[0];-		bytebuffer_truncate(inbuf, 1);-		return true;-	}--	// feh... we got utf8 here--	// check if there is all bytes-	if (len >= tb_utf8_char_length(buf[0])) {-		/* everything ok, fill event, pop buffer, return success */-		tb_utf8_char_to_unicode(&event->ch, buf);-		event->key = 0;-		bytebuffer_truncate(inbuf, tb_utf8_char_length(buf[0]));-		return true;-	}--	// event isn't recognized, perhaps there is not enough bytes in utf8-	// sequence-	return false;-}--struct cellbuf {-	int width;-	int height;-	struct tb_cell *cells;-};--#define CELL(buf, x, y) (buf)->cells[(y) * (buf)->width + (x)]-#define IS_CURSOR_HIDDEN(cx, cy) (cx == -1 || cy == -1)-#define LAST_COORD_INIT -1--static struct termios orig_tios;--static struct cellbuf back_buffer;-static struct cellbuf front_buffer;-static struct bytebuffer output_buffer;-static struct bytebuffer input_buffer;--static int termw = -1;-static int termh = -1;--static int inputmode = TB_INPUT_ESC;-static int outputmode = TB_OUTPUT_NORMAL;--static int inout;-static int winch_fds[2];--static int lastx = LAST_COORD_INIT;-static int lasty = LAST_COORD_INIT;-static int cursor_x = -1;-static int cursor_y = -1;--static uint16_t background = TB_DEFAULT;-static uint16_t foreground = TB_DEFAULT;--static void write_cursor(int x, int y);-static void write_sgr(uint16_t fg, uint16_t bg);--static void cellbuf_init(struct cellbuf *buf, int width, int height);-static void cellbuf_resize(struct cellbuf *buf, int width, int height);-static void cellbuf_clear(struct cellbuf *buf);-static void cellbuf_free(struct cellbuf *buf);--static void update_size(void);-static void update_term_size(void);-static void send_attr(uint16_t fg, uint16_t bg);-static void send_char(int x, int y, uint32_t c);-static void send_clear(void);-static void sigwinch_handler(int xxx);-static int wait_fill_event(struct tb_event *event, struct timeval *timeout);--/* may happen in a different thread */-static volatile int buffer_size_change_request;--/* -------------------------------------------------------- */--int tb_init_fd(int inout_)-{-	inout = inout_;-	if (inout == -1) {-		return TB_EFAILED_TO_OPEN_TTY;-	}--	if (init_term() < 0) {-		close(inout);-		return TB_EUNSUPPORTED_TERMINAL;-	}--	if (pipe(winch_fds) < 0) {-		close(inout);-		return TB_EPIPE_TRAP_ERROR;-	}--	struct sigaction sa;-	memset(&sa, 0, sizeof(sa));-	sa.sa_handler = sigwinch_handler;-	sa.sa_flags = 0;-	sigaction(SIGWINCH, &sa, 0);--	tcgetattr(inout, &orig_tios);--	struct termios tios;-	memcpy(&tios, &orig_tios, sizeof(tios));--	tios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP-                           | INLCR | IGNCR | ICRNL | IXON);-	tios.c_oflag &= ~OPOST;-	tios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);-	tios.c_cflag &= ~(CSIZE | PARENB);-	tios.c_cflag |= CS8;-	tios.c_cc[VMIN] = 0;-	tios.c_cc[VTIME] = 0;-	tcsetattr(inout, TCSAFLUSH, &tios);--	bytebuffer_init(&input_buffer, 128);-	bytebuffer_init(&output_buffer, 32 * 1024);--	bytebuffer_puts(&output_buffer, funcs[T_ENTER_CA]);-	bytebuffer_puts(&output_buffer, funcs[T_ENTER_KEYPAD]);-	bytebuffer_puts(&output_buffer, funcs[T_HIDE_CURSOR]);-	send_clear();--	update_term_size();-	cellbuf_init(&back_buffer, termw, termh);-	cellbuf_init(&front_buffer, termw, termh);-	cellbuf_clear(&back_buffer);-	cellbuf_clear(&front_buffer);--	return 0;-}--int tb_init_file(const char* name){-	return tb_init_fd(open(name, O_RDWR));-}--int tb_init(void)-{-	return tb_init_file("/dev/tty");-}--void tb_shutdown(void)-{-	if (termw == -1) {-		fputs("tb_shutdown() should not be called twice.", stderr);-		abort();-	}--	bytebuffer_puts(&output_buffer, funcs[T_SHOW_CURSOR]);-	bytebuffer_puts(&output_buffer, funcs[T_SGR0]);-	bytebuffer_puts(&output_buffer, funcs[T_CLEAR_SCREEN]);-	bytebuffer_puts(&output_buffer, funcs[T_EXIT_CA]);-	bytebuffer_puts(&output_buffer, funcs[T_EXIT_KEYPAD]);-	bytebuffer_puts(&output_buffer, funcs[T_EXIT_MOUSE]);-	bytebuffer_flush(&output_buffer, inout);-	tcsetattr(inout, TCSAFLUSH, &orig_tios);--	shutdown_term();-	close(inout);-	close(winch_fds[0]);-	close(winch_fds[1]);--	cellbuf_free(&back_buffer);-	cellbuf_free(&front_buffer);-	bytebuffer_free(&output_buffer);-	bytebuffer_free(&input_buffer);-	termw = termh = -1;-}--void tb_present(void)-{-	int x,y,w,i;-	struct tb_cell *back, *front;--	/* invalidate cursor position */-	lastx = LAST_COORD_INIT;-	lasty = LAST_COORD_INIT;--	if (buffer_size_change_request) {-		update_size();-		buffer_size_change_request = 0;-	}--	for (y = 0; y < front_buffer.height; ++y) {-		for (x = 0; x < front_buffer.width; ) {-			back = &CELL(&back_buffer, x, y);-			front = &CELL(&front_buffer, x, y);-			w = wcwidth(back->ch);-			if (w < 1) w = 1;-			if (memcmp(back, front, sizeof(struct tb_cell)) == 0) {-				x += w;-				continue;-			}-			memcpy(front, back, sizeof(struct tb_cell));-			send_attr(back->fg, back->bg);-			if (w > 1 && x >= front_buffer.width - (w - 1)) {-				// Not enough room for wide ch, so send spaces-				for (i = x; i < front_buffer.width; ++i) {-					send_char(i, y, ' ');-				}-			} else {-				send_char(x, y, back->ch);-				for (i = 1; i < w; ++i) {-					front = &CELL(&front_buffer, x + i, y);-					front->ch = 0;-					front->fg = back->fg;-					front->bg = back->bg;-				}-			}-			x += w;-		}-	}-	if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y))-		write_cursor(cursor_x, cursor_y);-	bytebuffer_flush(&output_buffer, inout);-}--void tb_set_cursor(int cx, int cy)-{-	if (IS_CURSOR_HIDDEN(cursor_x, cursor_y) && !IS_CURSOR_HIDDEN(cx, cy))-		bytebuffer_puts(&output_buffer, funcs[T_SHOW_CURSOR]);--	if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y) && IS_CURSOR_HIDDEN(cx, cy))-		bytebuffer_puts(&output_buffer, funcs[T_HIDE_CURSOR]);--	cursor_x = cx;-	cursor_y = cy;-	if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y))-		write_cursor(cursor_x, cursor_y);-}--void tb_put_cell(int x, int y, const struct tb_cell *cell)-{-	if ((unsigned)x >= (unsigned)back_buffer.width)-		return;-	if ((unsigned)y >= (unsigned)back_buffer.height)-		return;-	CELL(&back_buffer, x, y) = *cell;-}--void tb_change_cell(int x, int y, uint32_t ch, uint16_t fg, uint16_t bg)-{-	struct tb_cell c = {ch, fg, bg};-	tb_put_cell(x, y, &c);-}--void tb_blit(int x, int y, int w, int h, const struct tb_cell *cells)-{-	if (x + w < 0 || x >= back_buffer.width)-		return;-	if (y + h < 0 || y >= back_buffer.height)-		return;-	int xo = 0, yo = 0, ww = w, hh = h;-	if (x < 0) {-		xo = -x;-		ww -= xo;-		x = 0;-	}-	if (y < 0) {-		yo = -y;-		hh -= yo;-		y = 0;-	}-	if (ww > back_buffer.width - x)-		ww = back_buffer.width - x;-	if (hh > back_buffer.height - y)-		hh = back_buffer.height - y;--	int sy;-	struct tb_cell *dst = &CELL(&back_buffer, x, y);-	const struct tb_cell *src = cells + yo * w + xo;-	size_t size = sizeof(struct tb_cell) * ww;--	for (sy = 0; sy < hh; ++sy) {-		memcpy(dst, src, size);-		dst += back_buffer.width;-		src += w;-	}-}--struct tb_cell *tb_cell_buffer(void)-{-	return back_buffer.cells;-}--int tb_poll_event(struct tb_event *event)-{-	return wait_fill_event(event, 0);-}--int tb_peek_event(struct tb_event *event, int timeout)-{-	struct timeval tv;-	tv.tv_sec = timeout / 1000;-	tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000;-	return wait_fill_event(event, &tv);-}--int tb_width(void)-{-	return termw;-}--int tb_height(void)-{-	return termh;-}--void tb_clear(void)-{-	if (buffer_size_change_request) {-		update_size();-		buffer_size_change_request = 0;-	}-	cellbuf_clear(&back_buffer);-}--int tb_select_input_mode(int mode)-{-	if (mode) {-		if ((mode & (TB_INPUT_ESC | TB_INPUT_ALT)) == 0)-			mode |= TB_INPUT_ESC;--		/* technically termbox can handle that, but let's be nice and show here-		   what mode is actually used */-		if ((mode & (TB_INPUT_ESC | TB_INPUT_ALT)) == (TB_INPUT_ESC | TB_INPUT_ALT))-			mode &= ~TB_INPUT_ALT;--		inputmode = mode;-		if (mode&TB_INPUT_MOUSE) {-			bytebuffer_puts(&output_buffer, funcs[T_ENTER_MOUSE]);-			bytebuffer_flush(&output_buffer, inout);-		} else {-			bytebuffer_puts(&output_buffer, funcs[T_EXIT_MOUSE]);-			bytebuffer_flush(&output_buffer, inout);-		}-	}-	return inputmode;-}--int tb_select_output_mode(int mode)-{-	if (mode)-		outputmode = mode;-	return outputmode;-}--void tb_set_clear_attributes(uint16_t fg, uint16_t bg)-{-	foreground = fg;-	background = bg;-}--/* -------------------------------------------------------- */--static int convertnum(uint32_t num, char* buf) {-	int i, l = 0;-	int ch;-	do {-		buf[l++] = '0' + (num % 10);-		num /= 10;-	} while (num);-	for(i = 0; i < l / 2; i++) {-		ch = buf[i];-		buf[i] = buf[l - 1 - i];-		buf[l - 1 - i] = ch;-	}-	return l;-}--#define WRITE_LITERAL(X) bytebuffer_append(&output_buffer, (X), sizeof(X)-1)-#define WRITE_INT(X) bytebuffer_append(&output_buffer, buf, convertnum((X), buf))--static void write_cursor(int x, int y) {-	char buf[32];-	WRITE_LITERAL("\033[");-	WRITE_INT(y+1);-	WRITE_LITERAL(";");-	WRITE_INT(x+1);-	WRITE_LITERAL("H");-}--static void write_sgr(uint16_t fg, uint16_t bg) {-	char buf[32];--	if (fg == TB_DEFAULT && bg == TB_DEFAULT)-		return;--	switch (outputmode) {-	case TB_OUTPUT_256:-	case TB_OUTPUT_216:-	case TB_OUTPUT_GRAYSCALE:-		WRITE_LITERAL("\033[");-		if (fg != TB_DEFAULT) {-			WRITE_LITERAL("38;5;");-			WRITE_INT(fg);-			if (bg != TB_DEFAULT) {-				WRITE_LITERAL(";");-			}-		}-		if (bg != TB_DEFAULT) {-			WRITE_LITERAL("48;5;");-			WRITE_INT(bg);-		}-		WRITE_LITERAL("m");-		break;-	case TB_OUTPUT_NORMAL:-	default:-		WRITE_LITERAL("\033[");-		if (fg != TB_DEFAULT) {-			WRITE_LITERAL("3");-			WRITE_INT(fg - 1);-			if (bg != TB_DEFAULT) {-				WRITE_LITERAL(";");-			}-		}-		if (bg != TB_DEFAULT) {-			WRITE_LITERAL("4");-			WRITE_INT(bg - 1);-		}-		WRITE_LITERAL("m");-		break;-	}-}--static void cellbuf_init(struct cellbuf *buf, int width, int height)-{-	buf->cells = (struct tb_cell*)malloc(sizeof(struct tb_cell) * width * height);-	assert(buf->cells);-	buf->width = width;-	buf->height = height;-}--static void cellbuf_resize(struct cellbuf *buf, int width, int height)-{-	if (buf->width == width && buf->height == height)-		return;--	int oldw = buf->width;-	int oldh = buf->height;-	struct tb_cell *oldcells = buf->cells;--	cellbuf_init(buf, width, height);-	cellbuf_clear(buf);--	int minw = (width < oldw) ? width : oldw;-	int minh = (height < oldh) ? height : oldh;-	int i;--	for (i = 0; i < minh; ++i) {-		struct tb_cell *csrc = oldcells + (i * oldw);-		struct tb_cell *cdst = buf->cells + (i * width);-		memcpy(cdst, csrc, sizeof(struct tb_cell) * minw);-	}--	free(oldcells);-}--static void cellbuf_clear(struct cellbuf *buf)-{-	int i;-	int ncells = buf->width * buf->height;--	for (i = 0; i < ncells; ++i) {-		buf->cells[i].ch = ' ';-		buf->cells[i].fg = foreground;-		buf->cells[i].bg = background;-	}-}--static void cellbuf_free(struct cellbuf *buf)-{-	free(buf->cells);-}--static void get_term_size(int *w, int *h)-{-	struct winsize sz;-	memset(&sz, 0, sizeof(sz));--	ioctl(inout, TIOCGWINSZ, &sz);--	if (w) *w = sz.ws_col;-	if (h) *h = sz.ws_row;-}--static void update_term_size(void)-{-	struct winsize sz;-	memset(&sz, 0, sizeof(sz));--	ioctl(inout, TIOCGWINSZ, &sz);--	termw = sz.ws_col;-	termh = sz.ws_row;-}--static void send_attr(uint16_t fg, uint16_t bg)-{-#define LAST_ATTR_INIT 0xFFFF-	static uint16_t lastfg = LAST_ATTR_INIT, lastbg = LAST_ATTR_INIT;-	if (fg != lastfg || bg != lastbg) {-		bytebuffer_puts(&output_buffer, funcs[T_SGR0]);--		uint16_t fgcol;-		uint16_t bgcol;--		switch (outputmode) {-		case TB_OUTPUT_256:-			fgcol = fg & 0xFF;-			bgcol = bg & 0xFF;-			break;--		case TB_OUTPUT_216:-			fgcol = fg & 0xFF; if (fgcol > 215) fgcol = 7;-			bgcol = bg & 0xFF; if (bgcol > 215) bgcol = 0;-			fgcol += 0x10;-			bgcol += 0x10;-			break;--		case TB_OUTPUT_GRAYSCALE:-			fgcol = fg & 0xFF; if (fgcol > 23) fgcol = 23;-			bgcol = bg & 0xFF; if (bgcol > 23) bgcol = 0;-			fgcol += 0xe8;-			bgcol += 0xe8;-			break;--		case TB_OUTPUT_NORMAL:-		default:-			fgcol = fg & 0x0F;-			bgcol = bg & 0x0F;-		}--		if (fg & TB_BOLD)-			bytebuffer_puts(&output_buffer, funcs[T_BOLD]);-		if (bg & TB_BOLD)-			bytebuffer_puts(&output_buffer, funcs[T_BLINK]);-		if (fg & TB_UNDERLINE)-			bytebuffer_puts(&output_buffer, funcs[T_UNDERLINE]);-		if ((fg & TB_REVERSE) || (bg & TB_REVERSE))-			bytebuffer_puts(&output_buffer, funcs[T_REVERSE]);--		write_sgr(fgcol, bgcol);--		lastfg = fg;-		lastbg = bg;-	}-}--static void send_char(int x, int y, uint32_t c)-{-	char buf[7];-	int bw = tb_utf8_unicode_to_char(buf, c);-	if (x-1 != lastx || y != lasty)-		write_cursor(x, y);-	lastx = x; lasty = y;-	if(!c) buf[0] = ' '; // replace 0 with whitespace-	bytebuffer_append(&output_buffer, buf, bw);-}--static void send_clear(void)-{-	send_attr(foreground, background);-	bytebuffer_puts(&output_buffer, funcs[T_CLEAR_SCREEN]);-	if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y))-		write_cursor(cursor_x, cursor_y);-	bytebuffer_flush(&output_buffer, inout);--	/* we need to invalidate cursor position too and these two vars are-	 * used only for simple cursor positioning optimization, cursor-	 * actually may be in the correct place, but we simply discard-	 * optimization once and it gives us simple solution for the case when-	 * cursor moved */-	lastx = LAST_COORD_INIT;-	lasty = LAST_COORD_INIT;-}--static void sigwinch_handler(int xxx)-{-	(void) xxx;-	const int zzz = 1;-	write(winch_fds[1], &zzz, sizeof(int));-}--static void update_size(void)-{-	update_term_size();-	cellbuf_resize(&back_buffer, termw, termh);-	cellbuf_resize(&front_buffer, termw, termh);-	cellbuf_clear(&front_buffer);-	send_clear();-}--static int read_up_to(int n) {-	assert(n > 0);-	const int prevlen = input_buffer.len;-	bytebuffer_resize(&input_buffer, prevlen + n);--	int read_n = 0;-	while (read_n <= n) {-		ssize_t r = 0;-		if (read_n < n) {-			r = read(inout, input_buffer.buf + prevlen + read_n, n - read_n);-		}-#ifdef __CYGWIN__-		// While linux man for tty says when VMIN == 0 && VTIME == 0, read-		// should return 0 when there is nothing to read, cygwin's read returns-		// -1. Not sure why and if it's correct to ignore it, but let's pretend-		// it's zero.-		if (r < 0) r = 0;-#endif-		if (r < 0) {-			// EAGAIN / EWOULDBLOCK shouldn't occur here-			assert(errno != EAGAIN && errno != EWOULDBLOCK);-			return -1;-		} else if (r > 0) {-			read_n += r;-		} else {-			bytebuffer_resize(&input_buffer, prevlen + read_n);-			return read_n;-		}-	}-	assert(!"unreachable");-	return 0;-}--static int wait_fill_event(struct tb_event *event, struct timeval *timeout)-{-	// ;-)-#define ENOUGH_DATA_FOR_PARSING 64-	fd_set events;-	memset(event, 0, sizeof(struct tb_event));--	// try to extract event from input buffer, return on success-	event->type = TB_EVENT_KEY;-	if (extract_event(event, &input_buffer, inputmode))-		return event->type;--	// it looks like input buffer is incomplete, let's try the short path,-	// but first make sure there is enough space-	int n = read_up_to(ENOUGH_DATA_FOR_PARSING);-	if (n < 0)-		return -1;-	if (n > 0 && extract_event(event, &input_buffer, inputmode))-		return event->type;--	// n == 0, or not enough data, let's go to select-	while (1) {-		FD_ZERO(&events);-		FD_SET(inout, &events);-		FD_SET(winch_fds[0], &events);-		int maxfd = (winch_fds[0] > inout) ? winch_fds[0] : inout;-		int result = select(maxfd+1, &events, 0, 0, timeout);-		if (!result)-			return 0;--		if (FD_ISSET(inout, &events)) {-			event->type = TB_EVENT_KEY;-			n = read_up_to(ENOUGH_DATA_FOR_PARSING);-			if (n < 0)-				return -1;--			if (n == 0)-				continue;--			if (extract_event(event, &input_buffer, inputmode))-				return event->type;-		}-		if (FD_ISSET(winch_fds[0], &events)) {-			event->type = TB_EVENT_RESIZE;-			int zzz = 0;-			read(winch_fds[0], &zzz, sizeof(int));-			buffer_size_change_request = 1;-			get_term_size(&event->w, &event->h);-			return TB_EVENT_RESIZE;-		}-	}-}
− cbits/utf8.c
@@ -1,79 +0,0 @@-#include "termbox.h"--static const unsigned char utf8_length[256] = {-  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,-  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,-  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,-  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,-  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,-  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,-  3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1-};--static const unsigned char utf8_mask[6] = {-	0x7F,-	0x1F,-	0x0F,-	0x07,-	0x03,-	0x01-};--int tb_utf8_char_length(char c)-{-	return utf8_length[(unsigned char)c];-}--int tb_utf8_char_to_unicode(uint32_t *out, const char *c)-{-	if (*c == 0)-		return TB_EOF;--	int i;-	unsigned char len = tb_utf8_char_length(*c);-	unsigned char mask = utf8_mask[len-1];-	uint32_t result = c[0] & mask;-	for (i = 1; i < len; ++i) {-		result <<= 6;-		result |= c[i] & 0x3f;-	}--	*out = result;-	return (int)len;-}--int tb_utf8_unicode_to_char(char *out, uint32_t c)-{-	int len = 0;-	int first;-	int i;--	if (c < 0x80) {-		first = 0;-		len = 1;-	} else if (c < 0x800) {-		first = 0xc0;-		len = 2;-	} else if (c < 0x10000) {-		first = 0xe0;-		len = 3;-	} else if (c < 0x200000) {-		first = 0xf0;-		len = 4;-	} else if (c < 0x4000000) {-		first = 0xf8;-		len = 5;-	} else {-		first = 0xfc;-		len = 6;-	}--	for (i = len - 1; i > 0; --i) {-		out[i] = (c & 0x3f) | 0x80;-		c >>= 6;-	}-	out[0] = c | first;--	return len;-}
− examples/Colors.hs
@@ -1,42 +0,0 @@-import Data.Semigroup ((<>))-import qualified Termbox--main :: IO ()-main =-  Termbox.run $ \_w _h render poll -> do-    let rectangles :: [(Int, Int, Int, Int)]-        rectangles = do-          y0 <- [0, 2 ..]-          x0 <- [0, 4 .. 48]-          pure (x0, y0, x0 + 3, y0 + 1)--    let cells :: Maybe Termbox.Event -> Termbox.Cells-        cells lastEvent =-          mconcat-            ( zipWith-                ( \(x0, y0, x1, y1) n ->-                    rectangle x0 y0 x1 y1 (Termbox.Cell ' ' mempty (fromInteger n))-                      <> string x0 y0 mempty (fromInteger n) (show n)-                )-                rectangles-                [0 .. 255]-            )-            <> string 54 1 mempty mempty "Press Esc to quit."-            <> string 54 3 mempty mempty ("Last event: " ++ show lastEvent)--    let loop :: Maybe Termbox.Event -> IO ()-        loop lastEvent = do-          render (cells lastEvent) Termbox.NoCursor-          poll >>= \case-            Termbox.EventKey Termbox.KeyEsc -> pure ()-            event -> loop (Just event)--    loop Nothing--string :: Int -> Int -> Termbox.Attr -> Termbox.Attr -> [Char] -> Termbox.Cells-string x0 y fg bg =-  mconcat . zipWith (\x c -> Termbox.set x y (Termbox.Cell c fg bg)) [x0 ..]--rectangle :: Int -> Int -> Int -> Int -> Termbox.Cell -> Termbox.Cells-rectangle x0 y0 x1 y1 c =-  foldMap (\(x, y) -> Termbox.set x y c) ((,) <$> [x0 .. x1] <*> [y0 .. y1])
+ examples/Demo.hs view
@@ -0,0 +1,292 @@+module Main (main) where++import Control.Concurrent (threadDelay)+import Control.Concurrent.MVar+import Control.Monad (forever)+import GHC.Clock (getMonotonicTime)+import qualified Ki+import qualified Termbox+import Text.Printf (printf)++main :: IO ()+main = do+  t0 <- getMonotonicTime+  result <-+    Ki.scoped \scope -> do+      timeVar <- newEmptyMVar+      Ki.fork_ scope do+        forever do+          threadDelay 100_000+          getMonotonicTime >>= putMVar timeVar+      Termbox.run+        Termbox.Program+          { initialize,+            pollEvent = pollEvent t0 timeVar,+            handleEvent,+            render,+            finished+          }+  case result of+    Left err -> print err+    Right _state -> pure ()++data State = State+  { elapsed :: Double,+    lastKey :: Maybe Termbox.Key,+    bright :: Bool+  }++initialize :: Termbox.Size -> State+initialize _size =+  State+    { elapsed = 0,+      lastKey = Nothing,+      bright = False+    }++pollEvent :: Double -> MVar Double -> Maybe (IO Double)+pollEvent t0 timeVar =+  Just do+    t1 <- takeMVar timeVar+    pure (t1 - t0)++handleEvent :: State -> Termbox.Event Double -> IO State+handleEvent State {elapsed, lastKey, bright} = \case+  Termbox.EventKey key ->+    pure+      State+        { elapsed,+          lastKey = Just key,+          bright =+            case key of+              Termbox.KeyChar '*' -> not bright+              _ -> bright+        }+  Termbox.EventUser elapsed1 ->+    pure+      State+        { elapsed = elapsed1,+          lastKey,+          bright+        }+  _ ->+    pure+      State+        { elapsed,+          lastKey,+          bright+        }++render :: State -> Termbox.Scene+render State {elapsed, lastKey, bright} =+  renderBox Termbox.Pos {row = 1, col = 2} $+    vcat+      [ string "Welcome to Termbox. Try typing, clicking, and resizing the terminal!",+        string " ",+        hcat+          [ string "Elapsed time: ",+            string (map Termbox.char (printf "%.1fs" elapsed))+          ],+        hcat+          [ string "Latest key press: ",+            case lastKey of+              Nothing -> emptyBox+              Just event -> string (map (Termbox.bold . Termbox.char) (show event))+          ],+        string " ",+        string "default red green yellow blue magenta cyan white",+        hcat+          [ rect+              Rect+                { size = Termbox.Size {width = 7, height = 2},+                  color = brighten Termbox.defaultColor+                },+            string " ",+            rect+              Rect+                { size = Termbox.Size {width = 3, height = 2},+                  color = brighten Termbox.red+                },+            string " ",+            rect+              Rect+                { size = Termbox.Size {width = 5, height = 2},+                  color = brighten Termbox.green+                },+            string " ",+            rect+              Rect+                { size = Termbox.Size {width = 6, height = 2},+                  color = brighten Termbox.yellow+                },+            string " ",+            rect+              Rect+                { size = Termbox.Size {width = 4, height = 2},+                  color = brighten Termbox.blue+                },+            string " ",+            rect+              Rect+                { size = Termbox.Size {width = 7, height = 2},+                  color = brighten Termbox.magenta+                },+            string " ",+            rect+              Rect+                { size = Termbox.Size {width = 4, height = 2},+                  color = brighten Termbox.cyan+                },+            string " ",+            rect+              Rect+                { size = Termbox.Size {width = 5, height = 2},+                  color = brighten Termbox.white+                },+            string " ",+            vcat+              [ string ("Press " ++ [Termbox.bold (Termbox.char '*')] ++ " to toggle brightness."),+                string+                  ( let selected = map (Termbox.bg (Termbox.gray 20) . Termbox.fg (Termbox.gray 0))+                     in if bright+                          then "normal " ++ selected "bright"+                          else selected "normal" ++ " bright"+                  )+              ]+          ],+        string " ",+        string "color 0 .. color 215",+        vcat+          ( map+              ( \is ->+                  hcat+                    ( map+                        ( \i ->+                            acat+                              [ rect+                                  Rect+                                    { size = Termbox.Size {width = 4, height = 2},+                                      color = Termbox.color i+                                    },+                                string (map (Termbox.bg (Termbox.color i) . Termbox.char) (show i))+                              ]+                        )+                        is+                    )+              )+              (chunksOf 24 [0 .. 215])+          ),+        string " ",+        string "gray 0 .. gray 23",+        hcat+          ( map+              ( \i ->+                  acat+                    [ rect+                        Rect+                          { size = Termbox.Size {width = 4, height = 2},+                            color = Termbox.gray i+                          },+                      string (map (Termbox.bg (Termbox.gray i) . Termbox.char) (show i))+                    ]+              )+              [0 .. 23]+          ),+        string " ",+        hcat+          [ string (map Termbox.bold "This text is bold."),+            string " ",+            string (map Termbox.underline "This text is underlined."),+            string " ",+            string (map Termbox.blink "This text is blinking (maybe).")+          ],+        string " ",+        hcat+          [ string "Press ",+            string (map Termbox.bold "Esc"),+            string " to quit!"+          ]+      ]+  where+    brighten :: Termbox.Color -> Termbox.Color+    brighten =+      if bright+        then Termbox.bright+        else id++finished :: State -> Bool+finished State {lastKey} =+  case lastKey of+    Just Termbox.KeyEsc -> True+    _ -> False++data Box+  = Box !Termbox.Size Content++data Content+  = E+  | O !Termbox.Cell+  | A !Box !Box+  | H !Box !Box+  | V !Box !Box++emptyBox :: Box+emptyBox =+  Box (Termbox.Size 0 0) E++one :: Termbox.Cell -> Box+one cell =+  Box (Termbox.Size 1 1) (O cell) -- assume it's not an empty cell *shrug*++acat :: [Box] -> Box+acat =+  foldr f emptyBox+  where+    f :: Box -> Box -> Box+    f box1@(Box (Termbox.Size w1 h1) _) box2@(Box (Termbox.Size w2 h2) _) =+      Box (Termbox.Size (max w1 w2) (max h1 h2)) (A box1 box2)++hcat :: [Box] -> Box+hcat =+  foldr f emptyBox+  where+    f :: Box -> Box -> Box+    f box1@(Box (Termbox.Size w1 h1) _) box2@(Box (Termbox.Size w2 h2) _) =+      Box (Termbox.Size (w1 + w2) (max h1 h2)) (H box1 box2)++vcat :: [Box] -> Box+vcat =+  foldr f emptyBox+  where+    f :: Box -> Box -> Box+    f box1@(Box (Termbox.Size w1 h1) _) box2@(Box (Termbox.Size w2 h2) _) =+      Box (Termbox.Size (max w1 w2) (h1 + h2)) (V box1 box2)++renderBox :: Termbox.Pos -> Box -> Termbox.Scene+renderBox pos (Box _ content) =+  case content of+    E -> mempty+    O cell -> Termbox.cell pos cell+    A box1 box2 -> renderBox pos box1 <> renderBox pos box2+    H box1@(Box (Termbox.Size w1 _) _) box2 -> renderBox pos box1 <> renderBox (Termbox.posRight w1 pos) box2+    V box1@(Box (Termbox.Size _ h1) _) box2 -> renderBox pos box1 <> renderBox (Termbox.posDown h1 pos) box2++string :: [Termbox.Cell] -> Box+string =+  hcat . map one++data Rect = Rect+  { size :: Termbox.Size,+    color :: Termbox.Color+  }++rect :: Rect -> Box+rect Rect {size = Termbox.Size {width, height}, color} =+  vcat (replicate height (string (replicate width (Termbox.bg color (Termbox.char ' ')))))++chunksOf :: Int -> [a] -> [[a]]+chunksOf n = \case+  [] -> []+  xs ->+    let (ys, zs) = splitAt n xs+     in ys : chunksOf n zs
− include/termbox.h
@@ -1,320 +0,0 @@-#pragma once--#include <stdint.h>--/* for shared objects */-#if __GNUC__ >= 4- #define SO_IMPORT __attribute__((visibility("default")))-#else- #define SO_IMPORT-#endif--#ifdef __cplusplus-extern "C" {-#endif--/* Key constants. See also struct tb_event's key field.- *- * These are a safe subset of terminfo keys, which exist on all popular- * terminals. Termbox uses only them to stay truly portable.- */-#define TB_KEY_F1               (0xFFFF-0)-#define TB_KEY_F2               (0xFFFF-1)-#define TB_KEY_F3               (0xFFFF-2)-#define TB_KEY_F4               (0xFFFF-3)-#define TB_KEY_F5               (0xFFFF-4)-#define TB_KEY_F6               (0xFFFF-5)-#define TB_KEY_F7               (0xFFFF-6)-#define TB_KEY_F8               (0xFFFF-7)-#define TB_KEY_F9               (0xFFFF-8)-#define TB_KEY_F10              (0xFFFF-9)-#define TB_KEY_F11              (0xFFFF-10)-#define TB_KEY_F12              (0xFFFF-11)-#define TB_KEY_INSERT           (0xFFFF-12)-#define TB_KEY_DELETE           (0xFFFF-13)-#define TB_KEY_HOME             (0xFFFF-14)-#define TB_KEY_END              (0xFFFF-15)-#define TB_KEY_PGUP             (0xFFFF-16)-#define TB_KEY_PGDN             (0xFFFF-17)-#define TB_KEY_ARROW_UP         (0xFFFF-18)-#define TB_KEY_ARROW_DOWN       (0xFFFF-19)-#define TB_KEY_ARROW_LEFT       (0xFFFF-20)-#define TB_KEY_ARROW_RIGHT      (0xFFFF-21)-#define TB_KEY_MOUSE_LEFT       (0xFFFF-22)-#define TB_KEY_MOUSE_RIGHT      (0xFFFF-23)-#define TB_KEY_MOUSE_MIDDLE     (0xFFFF-24)-#define TB_KEY_MOUSE_RELEASE    (0xFFFF-25)-#define TB_KEY_MOUSE_WHEEL_UP   (0xFFFF-26)-#define TB_KEY_MOUSE_WHEEL_DOWN (0xFFFF-27)--/* These are all ASCII code points below SPACE character and a BACKSPACE key. */-#define TB_KEY_CTRL_TILDE       0x00-#define TB_KEY_CTRL_2           0x00 /* clash with 'CTRL_TILDE' */-#define TB_KEY_CTRL_A           0x01-#define TB_KEY_CTRL_B           0x02-#define TB_KEY_CTRL_C           0x03-#define TB_KEY_CTRL_D           0x04-#define TB_KEY_CTRL_E           0x05-#define TB_KEY_CTRL_F           0x06-#define TB_KEY_CTRL_G           0x07-#define TB_KEY_BACKSPACE        0x08-#define TB_KEY_CTRL_H           0x08 /* clash with 'CTRL_BACKSPACE' */-#define TB_KEY_TAB              0x09-#define TB_KEY_CTRL_I           0x09 /* clash with 'TAB' */-#define TB_KEY_CTRL_J           0x0A-#define TB_KEY_CTRL_K           0x0B-#define TB_KEY_CTRL_L           0x0C-#define TB_KEY_ENTER            0x0D-#define TB_KEY_CTRL_M           0x0D /* clash with 'ENTER' */-#define TB_KEY_CTRL_N           0x0E-#define TB_KEY_CTRL_O           0x0F-#define TB_KEY_CTRL_P           0x10-#define TB_KEY_CTRL_Q           0x11-#define TB_KEY_CTRL_R           0x12-#define TB_KEY_CTRL_S           0x13-#define TB_KEY_CTRL_T           0x14-#define TB_KEY_CTRL_U           0x15-#define TB_KEY_CTRL_V           0x16-#define TB_KEY_CTRL_W           0x17-#define TB_KEY_CTRL_X           0x18-#define TB_KEY_CTRL_Y           0x19-#define TB_KEY_CTRL_Z           0x1A-#define TB_KEY_ESC              0x1B-#define TB_KEY_CTRL_LSQ_BRACKET 0x1B /* clash with 'ESC' */-#define TB_KEY_CTRL_3           0x1B /* clash with 'ESC' */-#define TB_KEY_CTRL_4           0x1C-#define TB_KEY_CTRL_BACKSLASH   0x1C /* clash with 'CTRL_4' */-#define TB_KEY_CTRL_5           0x1D-#define TB_KEY_CTRL_RSQ_BRACKET 0x1D /* clash with 'CTRL_5' */-#define TB_KEY_CTRL_6           0x1E-#define TB_KEY_CTRL_7           0x1F-#define TB_KEY_CTRL_SLASH       0x1F /* clash with 'CTRL_7' */-#define TB_KEY_CTRL_UNDERSCORE  0x1F /* clash with 'CTRL_7' */-#define TB_KEY_SPACE            0x20-#define TB_KEY_BACKSPACE2       0x7F-#define TB_KEY_CTRL_8           0x7F /* clash with 'BACKSPACE2' */--/* These are non-existing ones.- *- * #define TB_KEY_CTRL_1 clash with '1'- * #define TB_KEY_CTRL_9 clash with '9'- * #define TB_KEY_CTRL_0 clash with '0'- */--/*- * Alt modifier constant, see tb_event.mod field and tb_select_input_mode function.- * Mouse-motion modifier- */-#define TB_MOD_ALT    0x01-#define TB_MOD_MOTION 0x02--/* Colors (see struct tb_cell's fg and bg fields). */-#define TB_DEFAULT 0x00-#define TB_BLACK   0x01-#define TB_RED     0x02-#define TB_GREEN   0x03-#define TB_YELLOW  0x04-#define TB_BLUE    0x05-#define TB_MAGENTA 0x06-#define TB_CYAN    0x07-#define TB_WHITE   0x08--/* Attributes, it is possible to use multiple attributes by combining them- * using bitwise OR ('|'). Although, colors cannot be combined. But you can- * combine attributes and a single color. See also struct tb_cell's fg and bg- * fields.- */-#define TB_BOLD      0x0100-#define TB_UNDERLINE 0x0200-#define TB_REVERSE   0x0400--/* A cell, single conceptual entity on the terminal screen. The terminal screen- * is basically a 2d array of cells. It has the following fields:- *  - 'ch' is a unicode character- *  - 'fg' foreground color and attributes- *  - 'bg' background color and attributes- */-struct tb_cell {-	uint32_t ch;-	uint16_t fg;-	uint16_t bg;-};--#define TB_EVENT_KEY    1-#define TB_EVENT_RESIZE 2-#define TB_EVENT_MOUSE  3--/* An event, single interaction from the user. The 'mod' and 'ch' fields are- * valid if 'type' is TB_EVENT_KEY. The 'w' and 'h' fields are valid if 'type'- * is TB_EVENT_RESIZE. The 'x' and 'y' fields are valid if 'type' is- * TB_EVENT_MOUSE. The 'key' field is valid if 'type' is either TB_EVENT_KEY- * or TB_EVENT_MOUSE. The fields 'key' and 'ch' are mutually exclusive; only- * one of them can be non-zero at a time.- */-struct tb_event {-	uint8_t type;-	uint8_t mod; /* modifiers to either 'key' or 'ch' below */-	uint16_t key; /* one of the TB_KEY_* constants */-	uint32_t ch; /* unicode character */-	int32_t w;-	int32_t h;-	int32_t x;-	int32_t y;-};--/* Error codes returned by tb_init(). All of them are self-explanatory, except- * the pipe trap error. Termbox uses unix pipes in order to deliver a message- * from a signal handler (SIGWINCH) to the main event reading loop. Honestly in- * most cases you should just check the returned code as < 0.- */-#define TB_EUNSUPPORTED_TERMINAL -1-#define TB_EFAILED_TO_OPEN_TTY   -2-#define TB_EPIPE_TRAP_ERROR      -3--/* Initializes the termbox library. This function should be called before any- * other functions. Function tb_init is same as tb_init_file("/dev/tty").- * After successful initialization, the library must be- * finalized using the tb_shutdown() function.- */-SO_IMPORT int tb_init(void);-SO_IMPORT int tb_init_file(const char* name);-SO_IMPORT int tb_init_fd(int inout);-SO_IMPORT void tb_shutdown(void);--/* Returns the size of the internal back buffer (which is the same as- * terminal's window size in characters). The internal buffer can be resized- * after tb_clear() or tb_present() function calls. Both dimensions have an- * unspecified negative value when called before tb_init() or after- * tb_shutdown().- */-SO_IMPORT int tb_width(void);-SO_IMPORT int tb_height(void);--/* Clears the internal back buffer using TB_DEFAULT color or the- * color/attributes set by tb_set_clear_attributes() function.- */-SO_IMPORT void tb_clear(void);-SO_IMPORT void tb_set_clear_attributes(uint16_t fg, uint16_t bg);--/* Synchronizes the internal back buffer with the terminal. */-SO_IMPORT void tb_present(void);--#define TB_HIDE_CURSOR -1--/* Sets the position of the cursor. Upper-left character is (0, 0). If you pass- * TB_HIDE_CURSOR as both coordinates, then the cursor will be hidden. Cursor- * is hidden by default.- */-SO_IMPORT void tb_set_cursor(int cx, int cy);--/* Changes cell's parameters in the internal back buffer at the specified- * position.- */-SO_IMPORT void tb_put_cell(int x, int y, const struct tb_cell *cell);-SO_IMPORT void tb_change_cell(int x, int y, uint32_t ch, uint16_t fg, uint16_t bg);--/* Copies the buffer from 'cells' at the specified position, assuming the- * buffer is a two-dimensional array of size ('w' x 'h'), represented as a- * one-dimensional buffer containing lines of cells starting from the top.- *- * (DEPRECATED: use tb_cell_buffer() instead and copy memory on your own)- */-SO_IMPORT void tb_blit(int x, int y, int w, int h, const struct tb_cell *cells);--/* Returns a pointer to internal cell back buffer. You can get its dimensions- * using tb_width() and tb_height() functions. The pointer stays valid as long- * as no tb_clear() and tb_present() calls are made. The buffer is- * one-dimensional buffer containing lines of cells starting from the top.- */-SO_IMPORT struct tb_cell *tb_cell_buffer(void);--#define TB_INPUT_CURRENT 0 /* 000 */-#define TB_INPUT_ESC     1 /* 001 */-#define TB_INPUT_ALT     2 /* 010 */-#define TB_INPUT_MOUSE   4 /* 100 */--/* Sets the termbox input mode. Termbox has two input modes:- * 1. Esc input mode.- *    When ESC sequence is in the buffer and it doesn't match any known- *    ESC sequence => ESC means TB_KEY_ESC.- * 2. Alt input mode.- *    When ESC sequence is in the buffer and it doesn't match any known- *    sequence => ESC enables TB_MOD_ALT modifier for the next keyboard event.- *- * You can also apply TB_INPUT_MOUSE via bitwise OR operation to either of the- * modes (e.g. TB_INPUT_ESC | TB_INPUT_MOUSE). If none of the main two modes- * were set, but the mouse mode was, TB_INPUT_ESC mode is used. If for some- * reason you've decided to use (TB_INPUT_ESC | TB_INPUT_ALT) combination, it- * will behave as if only TB_INPUT_ESC was selected.- *- * If 'mode' is TB_INPUT_CURRENT, it returns the current input mode.- *- * Default termbox input mode is TB_INPUT_ESC.- */-SO_IMPORT int tb_select_input_mode(int mode);--#define TB_OUTPUT_CURRENT   0-#define TB_OUTPUT_NORMAL    1-#define TB_OUTPUT_256       2-#define TB_OUTPUT_216       3-#define TB_OUTPUT_GRAYSCALE 4--/* Sets the termbox output mode. Termbox has three output options:- * 1. TB_OUTPUT_NORMAL     => [1..8]- *    This mode provides 8 different colors:- *      black, red, green, yellow, blue, magenta, cyan, white- *    Shortcut: TB_BLACK, TB_RED, ...- *    Attributes: TB_BOLD, TB_UNDERLINE, TB_REVERSE- *- *    Example usage:- *        tb_change_cell(x, y, '@', TB_BLACK | TB_BOLD, TB_RED);- *- * 2. TB_OUTPUT_256        => [0..256]- *    In this mode you can leverage the 256 terminal mode:- *    0x00 - 0x07: the 8 colors as in TB_OUTPUT_NORMAL- *    0x08 - 0x0f: TB_* | TB_BOLD- *    0x10 - 0xe7: 216 different colors- *    0xe8 - 0xff: 24 different shades of grey- *- *    Example usage:- *        tb_change_cell(x, y, '@', 184, 240);- *        tb_change_cell(x, y, '@', 0xb8, 0xf0);- *- * 3. TB_OUTPUT_216        => [0..216]- *    This mode supports the 3rd range of the 256 mode only.- *    But you don't need to provide an offset.- *- * 4. TB_OUTPUT_GRAYSCALE  => [0..23]- *    This mode supports the 4th range of the 256 mode only.- *    But you dont need to provide an offset.- *- * Execute build/src/demo/output to see its impact on your terminal.- *- * If 'mode' is TB_OUTPUT_CURRENT, it returns the current output mode.- *- * Default termbox output mode is TB_OUTPUT_NORMAL.- */-SO_IMPORT int tb_select_output_mode(int mode);--/* Wait for an event up to 'timeout' milliseconds and fill the 'event'- * structure with it, when the event is available. Returns the type of the- * event (one of TB_EVENT_* constants) or -1 if there was an error or 0 in case- * there were no event during 'timeout' period.- */-SO_IMPORT int tb_peek_event(struct tb_event *event, int timeout);--/* Wait for an event forever and fill the 'event' structure with it, when the- * event is available. Returns the type of the event (one of TB_EVENT_*- * constants) or -1 if there was an error.- */-SO_IMPORT int tb_poll_event(struct tb_event *event);--/* Utility utf8 functions. */-#define TB_EOF -1-SO_IMPORT int tb_utf8_char_length(char c);-SO_IMPORT int tb_utf8_char_to_unicode(uint32_t *out, const char *c);-SO_IMPORT int tb_utf8_unicode_to_char(char *out, uint32_t c);--#ifdef __cplusplus-}-#endif
src/Termbox.hs view
@@ -1,71 +1,129 @@-{-# LANGUAGE PatternSynonyms #-}- -- |--- A @termbox@ program is typically constructed as an infinite loop that: ----- 1. Renders a scene.--- 2. Polls for an event.+-- This module provides a high-level wrapper around @termbox@, a simple C library for writing text-based user+-- interfaces: <https://github.com/termbox/termbox> ----- For example, this progam simply displays the number of keys pressed, and--- quits on @Esc@:+-- This module is intended to be imported qualified. --+-- ==== __👉 Quick start example__+--+-- This @termbox@ program displays the number of keys pressed.+-- -- @+-- {-\# LANGUAGE DerivingStrategies \#-}+-- {-\# LANGUAGE DuplicateRecordFields \#-}+-- {-\# LANGUAGE ImportQualifiedPost \#-} -- {-\# LANGUAGE LambdaCase \#-}+-- {-\# LANGUAGE OverloadedRecordDot \#-}+-- {-\# LANGUAGE OverloadedStrings \#-}+-- {-\# LANGUAGE NoFieldSelectors \#-} ----- import qualified Termbox+-- import Data.Foldable (fold)+-- import Data.Void (Void)+-- import Termbox qualified -- -- main :: IO ()--- main =---   Termbox.'run' (\\_width _height render poll -> loop render poll 0)+-- main = do+--   result <-+--     Termbox.'run'+--       Termbox.'Program'+--         { initialize,+--           pollEvent,+--           handleEvent,+--           handleEventError,+--           render,+--           finished+--         }+--   case result of+--     Left err -> putStrLn ("Termbox program failed to initialize: " ++ show err)+--     Right state -> putStrLn ("Final state: " ++ show state) ----- loop :: (Termbox.'Cells' -> Termbox.'Cursor' -> IO ()) -> IO Termbox.'Event' -> Int -> IO ()--- loop render poll n = do---   render (string (show n)) Termbox.'NoCursor'+-- data MyState = MyState+--   { keysPressed :: Int,+--     pressedEsc :: Bool+--   }+--   deriving stock (Show) -----   poll >>= \\case---     Termbox.'EventKey' Termbox.'KeyEsc' -> pure ()---     _ -> loop render poll (n+1)+-- initialize :: Termbox.'Size' -> MyState+-- initialize _size =+--   MyState+--     { keysPressed = 0,+--       pressedEsc = False+--     } ----- string :: Int -> Int -> String -> Termbox.'Cells'--- string col row =---   foldMap (\\(i, c) -> Termbox.'set' (col + i) row (Termbox.'Cell' c 0 0)) . zip [0..]--- @+-- pollEvent :: Maybe (IO Void)+-- pollEvent =+--   Nothing ----- Other termbox features include cell attributes (style, color), cursor--- display, and mouse click handling.+-- handleEvent :: MyState -> Termbox.'Event' Void -> IO MyState+-- handleEvent state = \\case+--   Termbox.'EventKey' key ->+--     pure+--       MyState+--         { keysPressed = state.keysPressed + 1,+--           pressedEsc =+--             case key of+--               Termbox.'KeyEsc' -> True+--               _ -> False+--         }+--   _ -> pure state ----- This module is intended to be imported qualified.+-- handleEventError :: MyState -> IO MyState+-- handleEventError state =+--   pure state+--+-- render :: MyState -> Termbox.'Scene'+-- render state =+--   fold+--     [ string+--         Termbox.'Pos' {row = 2, col = 4}+--         ("Number of keys pressed: " ++ map Termbox.'char' (show state.keysPressed))+--     , string+--         Termbox.'Pos' {row = 4, col = 4}+--         ("Press " ++ map (Termbox.'bold' . Termbox.'char') "Esc" ++ " to quit.")+--     ]+--+-- finished :: MyState -> Bool+-- finished state =+--   state.pressedEsc+--+-- string :: Termbox.'Pos' -> [Termbox.'Cell'] -> Termbox.'Scene'+-- string pos cells =+--   foldMap+--     (\\(i, cell) ->+--       Termbox.'cell'+--         Termbox.'Pos' {row = pos.row, col = pos.col + i}+--         cell)+--     (zip [0 ..] cells)+-- @ module Termbox-  ( -- * Initialization+  ( -- * Termbox+    Program (..),     run,     InitError (..),      -- * Terminal contents-    set,-    Cells,-    Cell (..),-    Cursor (..), -    -- * Event handling-    Event (..),-    Key (..),-    -- $key-aliases-    pattern KeyCtrlH,-    pattern KeyCtrlLsqBracket,-    pattern KeyCtrl2,-    pattern KeyCtrl3,-    pattern KeyCtrl4,-    pattern KeyCtrl5,-    pattern KeyCtrl7,-    pattern KeyCtrlM,-    pattern KeyCtrlI,-    pattern KeyCtrlUnderscore,-    Mouse (..),-    PollError (..),+    -- ** Scene+    Scene,+    cell,+    fill,+    cursor, -    -- * Attributes-    Attr,-    black,+    -- ** Cell+    Cell,+    char,+    fg,+    bg,+    bold,+    underline,+    blink,++    -- ** Colors+    Color,++    -- *** Basic colors+    defaultColor,     red,     green,     yellow,@@ -73,33 +131,63 @@     magenta,     cyan,     white,-    bold,-    underline,-    reverse,+    bright,++    -- *** 216 miscellaneous colors+    color,++    -- *** 24 monochrome colors+    gray,++    -- * Event handling+    Event (..),+    Key+      ( ..,+        KeyCtrlH,+        KeyCtrlLsqBracket,+        KeyCtrl2,+        KeyCtrl3,+        KeyCtrl4,+        KeyCtrl5,+        KeyCtrl7,+        KeyCtrlM,+        KeyCtrlI,+        KeyCtrlUnderscore+      ),+    Mouse (..),++    -- * Miscellaneous types+    Pos (..),+    posUp,+    posDown,+    posLeft,+    posRight,+    Size (..),   ) where +import Control.Concurrent.MVar import Control.Exception-import Data.Semigroup (Semigroup (..))-import Termbox.Attr-  ( Attr,-    black,+import Control.Monad (forever)+import qualified Ki+import qualified Termbox.Bindings.Hs+import Termbox.Internal.Cell (Cell, bg, blink, bold, char, fg, underline)+import Termbox.Internal.Color+  ( Color,     blue,-    bold,+    bright,+    color,     cyan,+    defaultColor,+    gray,     green,     magenta,     red,-    reverse,-    underline,     white,     yellow,   )-import Termbox.Cell (Cell (Cell))-import Termbox.Cells (Cells (Cells), set)-import Termbox.Event (Event (..), PollError (..), poll)-import Termbox.Internal-import Termbox.Key+import Termbox.Internal.Event (Event (..), poll)+import Termbox.Internal.Key   ( Key (..),     pattern KeyCtrl2,     pattern KeyCtrl3,@@ -112,73 +200,106 @@     pattern KeyCtrlM,     pattern KeyCtrlUnderscore,   )-import Termbox.Mouse (Mouse (..))-import Prelude hiding (reverse)---- | A cursor.-data Cursor-  = -- | Column, then row-    Cursor !Int !Int-  | NoCursor---- $key-aliases--- In a few cases, distinct key sequences map to equivalent key events. The pattern synonyms below are provided for an--- alternate syntax in these cases, if desired.+import Termbox.Internal.Mouse+  ( Mouse+      ( MouseLeft,+        MouseMiddle,+        MouseRelease,+        MouseRight,+        MouseWheelDown,+        MouseWheelUp+      ),+  )+import Termbox.Internal.Pos (Pos (..), posDown, posLeft, posRight, posUp)+import Termbox.Internal.Scene (Scene, cell, cursor, drawScene, fill)+import Termbox.Internal.Size (Size (..)) --- | Termbox initialization errors.+-- | @termbox@ initialization errors. data InitError   = FailedToOpenTTY   | PipeTrapError   | UnsupportedTerminal-  deriving (Show)+  deriving stock (Show)  instance Exception InitError --- | Run a @termbox@ program and restore the terminal state afterwards.------ The function provided to @run@ is provided:------   * The initial terminal width---   * The initial terminal height---   * An action that renders a scene---   * An action that polls for an event indefinitely+-- | A @termbox@ program, parameterized by state __@s@__.+data Program s = forall e. Program+  { -- | The initial state, given the initial terminal size.+    initialize :: Size -> s,+    -- | Poll for a user event. Every value that this @IO@ action returns is provided to @handleEvent@.+    pollEvent :: Maybe (IO e),+    -- | Handle an event.+    handleEvent :: s -> Event e -> IO s,+    -- | Render the current state.+    render :: s -> Scene,+    -- | Is the current state finished?+    finished :: s -> Bool+  }++-- | Run a @termbox@ program. ----- /Throws/: 'InitError'-run :: (Int -> Int -> (Cells -> Cursor -> IO ()) -> IO Event -> IO a) -> IO a-run action = do-  mask $ \unmask -> do-    initResult <- tb_init-    case () of-      _ | initResult == 0 -> do-        result <--          unmask-            ( do-                _ <- tb_select_input_mode tB_INPUT_MOUSE-                _ <- tb_select_output_mode tB_OUTPUT_256-                width <- tb_width-                height <- tb_height-                action width height render poll-            )-            `onException` shutdown+-- Either returns immediately with an 'InitError', or once the program state is finished with the final state.+run :: Program s -> IO (Either InitError s)+run program = do+  mask \unmask ->+    Termbox.Bindings.Hs.tb_init >>= \case+      Left err ->+        (pure . Left) case err of+          Termbox.Bindings.Hs.TB_EFAILED_TO_OPEN_TTY -> FailedToOpenTTY+          Termbox.Bindings.Hs.TB_EPIPE_TRAP_ERROR -> PipeTrapError+          Termbox.Bindings.Hs.TB_EUNSUPPORTED_TERMINAL -> UnsupportedTerminal+      Right () -> do+        result <- unmask (runProgram program) `onException` shutdown         shutdown-        pure result-      _ | initResult == tB_EFAILED_TO_OPEN_TTY -> throwIO FailedToOpenTTY-      _ | initResult == tB_EPIPE_TRAP_ERROR -> throwIO PipeTrapError-      _ | initResult == tB_EUNSUPPORTED_TERMINAL -> throwIO UnsupportedTerminal-      _ -> error ("termbox: unknown tb_init error " ++ show initResult)+        pure (Right result) --- | Render a scene.-render :: Cells -> Cursor -> IO ()-render (Cells cells) cursor = do-  tb_set_clear_attributes 0 0-  tb_clear-  cells-  case cursor of-    Cursor col row -> tb_set_cursor col row-    NoCursor -> tb_set_cursor tB_HIDE_CURSOR tB_HIDE_CURSOR-  tb_present+runProgram :: Program s -> IO s+runProgram Program {initialize, pollEvent, handleEvent, render, finished} = do+  _ <- Termbox.Bindings.Hs.tb_select_input_mode Termbox.Bindings.Hs.TB_INPUT_MOUSE+  _ <- Termbox.Bindings.Hs.tb_select_output_mode Termbox.Bindings.Hs.TB_OUTPUT_256+  width <- Termbox.Bindings.Hs.tb_width+  height <- Termbox.Bindings.Hs.tb_height +  let state0 =+        initialize Size {width, height}++  let loop0 doPoll =+        let loop s0 =+              if finished s0+                then pure s0+                else do+                  drawScene (render s0)+                  event <- doPoll+                  s1 <- handleEvent s0 event+                  loop s1+         in loop++  case pollEvent of+    Nothing -> loop0 pollIgnoringErrors state0+    Just pollEvent1 -> do+      eventVar <- newEmptyMVar++      Ki.scoped \scope -> do+        Ki.fork_ scope do+          forever do+            event <- pollEvent1+            putMVar eventVar (EventUser event)++        Ki.fork_ scope do+          forever do+            event <- pollIgnoringErrors+            putMVar eventVar event++        loop0 (takeMVar eventVar) state0++pollIgnoringErrors :: IO (Event e)+pollIgnoringErrors =+  poll >>= \case+    Left () -> pollIgnoringErrors+    Right event -> pure event+ shutdown :: IO () shutdown = do-  _ <- tb_select_output_mode tB_OUTPUT_NORMAL-  tb_shutdown+  _ <- Termbox.Bindings.Hs.tb_select_output_mode Termbox.Bindings.Hs.TB_OUTPUT_NORMAL+  Termbox.Bindings.Hs.tb_shutdown
− src/Termbox/Attr.hs
@@ -1,124 +0,0 @@-module Termbox.Attr-  ( Attr,-    black,-    blue,-    bold,-    cyan,-    green,-    magenta,-    red,-    reverse,-    underline,-    white,-    yellow,-    ---    attrToWord,-    wordToAttr,-  )-where--import Data.Bits ((.&.), (.|.))-import Data.Semigroup (Semigroup (..))-import Data.Word (Word16)-import Termbox.Internal-import Prelude hiding (reverse)---- | A cell attribute, which includes its color, and whether or not it is--- bold, underlined, and/or reversed.------ A cell can only have one color, but may be (for example) bold /and/--- underlined. The 'Monoid' instance combines 'Attr's this way, with a right bias.-data Attr-  = Attr !Word16 {- color -} !Word16 {- attr -}-  deriving (Eq, Show)--instance Monoid Attr where-  mempty :: Attr-  mempty =-    Attr tB_DEFAULT 0--  mappend :: Attr -> Attr -> Attr-  mappend =-    (<>)---- | Provided for numeric literals.-instance Num Attr where-  fromInteger :: Integer -> Attr-  fromInteger n =-    Attr (fromIntegral (n `rem` 256)) 0--  (+) = (<>)-  (*) = (<>)-  (-) = (<>)-  abs = id-  signum = id---- | Right-biased color; attributes are merged.-instance Semigroup Attr where-  (<>) :: Attr -> Attr -> Attr-  Attr 0 ax <> Attr cy ay = Attr cy (ax .|. ay)-  Attr cx ax <> Attr 0 ay = Attr cx (ax .|. ay)-  Attr _ ax <> Attr cy ay = Attr cy (ax .|. ay)--wordToAttr :: Word16 -> Attr-wordToAttr w =-  Attr (w .&. 0x00FF) (w .&. 0xFF00)--attrToWord :: Attr -> Word16-attrToWord (Attr x y) =-  x .|. y---- | @black = 1@.-black :: Attr-black =-  Attr tB_BLACK 0---- | @red = 2@.-red :: Attr-red =-  Attr tB_RED 0---- | @green = 3@.-green :: Attr-green =-  Attr tB_GREEN 0---- | @yellow = 4@.-yellow :: Attr-yellow =-  Attr tB_YELLOW 0---- | @blue = 5@.-blue :: Attr-blue =-  Attr tB_BLUE 0---- | @magenta = 6@.-magenta :: Attr-magenta =-  Attr tB_MAGENTA 0---- | @cyan = 7@.-cyan :: Attr-cyan =-  Attr tB_CYAN 0---- | @white = 8@.-white :: Attr-white =-  Attr tB_WHITE 0---- | Bold modifier attribute.-bold :: Attr-bold =-  Attr tB_DEFAULT tB_BOLD---- | Underline modifier attribute.-underline :: Attr-underline =-  Attr tB_DEFAULT tB_UNDERLINE---- | Reverse modifier attribute.-reverse :: Attr-reverse =-  Attr tB_DEFAULT tB_REVERSE
− src/Termbox/Cell.hs
@@ -1,39 +0,0 @@-{-# LANGUAGE TypeApplications #-}--module Termbox.Cell-  ( Cell (..),-  )-where--import Data.Char (chr, ord)-import Data.Word (Word32)-import Foreign.Ptr (Ptr)-import Foreign.Storable-import Termbox.Attr (Attr, attrToWord, wordToAttr)---- | A cell contains a character, foreground attribute, and background attribute.-data Cell-  = Cell !Char !Attr !Attr-  deriving (Eq, Show)--instance Storable Cell where-  sizeOf :: Cell -> Int-  sizeOf _ =-    8--  alignment :: Cell -> Int-  alignment _ =-    4--  peek :: Ptr Cell -> IO Cell-  peek ptr = do-    Cell-      <$> (chr . fromIntegral @Word32 @Int <$> peekByteOff ptr 0)-      <*> (wordToAttr <$> peekByteOff ptr 4)-      <*> (wordToAttr <$> peekByteOff ptr 6)--  poke :: Ptr Cell -> Cell -> IO ()-  poke ptr (Cell ch fg bg) = do-    pokeByteOff ptr 0 (fromIntegral @Int @Word32 (ord ch))-    pokeByteOff ptr 4 (attrToWord fg)-    pokeByteOff ptr 6 (attrToWord bg)
− src/Termbox/Cells.hs
@@ -1,21 +0,0 @@-module Termbox.Cells-  ( Cells (..),-    set,-  )-where--import Data.Char (ord)-import Data.Semigroup (Semigroup)-import Termbox.Attr (attrToWord)-import Termbox.Cell (Cell (Cell))-import Termbox.Internal (tb_change_cell)---- | A grid of cells. Create with 'set' and combine with ('<>').-newtype Cells-  = Cells (IO ())-  deriving {- newtype -} (Monoid, Semigroup)---- | Set a single cell's value (column, then row).-set :: Int -> Int -> Cell -> Cells-set col row (Cell ch fg bg) =-  Cells (tb_change_cell col row (fromIntegral (ord ch)) (attrToWord fg) (attrToWord bg))
− src/Termbox/Event.hs
@@ -1,57 +0,0 @@-{-# LANGUAGE TypeApplications #-}--module Termbox.Event-  ( Event (..),-    poll,-    PollError (..),-  )-where--import Control.Exception (Exception, throwIO)-import Data.Char (chr)-import Data.Int (Int32)-import Data.Semigroup (Semigroup (..))-import Data.Word (Word32)-import Foreign.Marshal.Alloc (alloca)-import Foreign.Storable (peek)-import Termbox.Internal-import Termbox.Key (Key (KeyChar), parseKey)-import Termbox.Mouse (Mouse, parseMouse)-import Prelude hiding (mod)---- | A input event.-data Event-  = -- | Key event-    EventKey !Key-  | -- | Resize event (width, then height)-    EventResize !Int !Int-  | -- | Mouse event (column, then row)-    EventMouse !Mouse !Int !Int-  deriving (Eq, Show)---- | Block until an 'Event' arrives.------ /Throws/: 'PollError'-poll :: IO Event-poll =-  alloca $ \ptr ->-    tb_poll_event ptr >>= \case-      -1 -> throwIO PollError-      _ -> parseEvent <$> peek ptr---- | An error occurred when polling, due to mysterious circumstances that are not well-documented in the original C--- codebase.-data PollError-  = PollError-  deriving (Show)--instance Exception PollError---- | Parse an 'Event' from a 'TbEvent'.-parseEvent :: TbEvent -> Event-parseEvent (TbEvent typ _mod key ch w h x y)-  | typ == tB_EVENT_KEY =-    EventKey (if ch == 0 then parseKey key else KeyChar (chr (fromIntegral @Word32 @Int ch)))-  | typ == tB_EVENT_RESIZE = EventResize (fromIntegral @Int32 @Int w) (fromIntegral @Int32 @Int h)-  | typ == tB_EVENT_MOUSE = EventMouse (parseMouse key) (fromIntegral @Int32 @Int x) (fromIntegral @Int32 @Int y)-  | otherwise = error ("termbox: unknown event type " ++ show typ)
− src/Termbox/Internal.hs
@@ -1,350 +0,0 @@-module Termbox.Internal where--import Data.Int (Int32)-import Data.Word-import Foreign.Ptr (Ptr)-import Foreign.Storable-import Prelude hiding (mod)--tB_EUNSUPPORTED_TERMINAL, tB_EFAILED_TO_OPEN_TTY, tB_EPIPE_TRAP_ERROR :: Int-tB_EUNSUPPORTED_TERMINAL = -1-tB_EFAILED_TO_OPEN_TTY = -2-tB_EPIPE_TRAP_ERROR = -3--tB_INPUT_CURRENT, tB_INPUT_ESC, tB_INPUT_ALT, tB_INPUT_MOUSE :: Int-tB_INPUT_CURRENT = 0-tB_INPUT_ESC = 1-tB_INPUT_ALT = 2-tB_INPUT_MOUSE = 4--tB_OUTPUT_CURRENT, tB_OUTPUT_NORMAL, tB_OUTPUT_256, tB_OUTPUT_216, tB_OUTPUT_GRAYSCALE :: Int-tB_OUTPUT_CURRENT = 0-tB_OUTPUT_NORMAL = 1-tB_OUTPUT_256 = 2-tB_OUTPUT_216 = 3-tB_OUTPUT_GRAYSCALE = 4--tB_EVENT_KEY, tB_EVENT_RESIZE, tB_EVENT_MOUSE :: Word8-tB_EVENT_KEY = 1-tB_EVENT_RESIZE = 2-tB_EVENT_MOUSE = 3--tB_MOD_ALT :: Word8-tB_MOD_ALT = 0x01---- #define TB_MOD_MOTION 0x02--tB_HIDE_CURSOR :: Int-tB_HIDE_CURSOR = -1--tB_BOLD, tB_UNDERLINE, tB_REVERSE :: Word16-tB_BOLD = 0x0100-tB_UNDERLINE = 0x0200-tB_REVERSE = 0x0400--tB_DEFAULT, tB_BLACK, tB_RED, tB_GREEN, tB_YELLOW, tB_BLUE, tB_MAGENTA, tB_CYAN, tB_WHITE :: Word16-tB_DEFAULT = 0x00-tB_BLACK = 0x01-tB_RED = 0x02-tB_GREEN = 0x03-tB_YELLOW = 0x04-tB_BLUE = 0x05-tB_MAGENTA = 0x06-tB_CYAN = 0x07-tB_WHITE = 0x08--tB_KEY_F1 :: Word16-tB_KEY_F1 = 0xFFFF - 0--tB_KEY_F2 :: Word16-tB_KEY_F2 = 0xFFFF - 1--tB_KEY_F3 :: Word16-tB_KEY_F3 = 0xFFFF - 2--tB_KEY_F4 :: Word16-tB_KEY_F4 = 0xFFFF - 3--tB_KEY_F5 :: Word16-tB_KEY_F5 = 0xFFFF - 4--tB_KEY_F6 :: Word16-tB_KEY_F6 = 0xFFFF - 5--tB_KEY_F7 :: Word16-tB_KEY_F7 = 0xFFFF - 6--tB_KEY_F8 :: Word16-tB_KEY_F8 = 0xFFFF - 7--tB_KEY_F9 :: Word16-tB_KEY_F9 = 0xFFFF - 8--tB_KEY_F10 :: Word16-tB_KEY_F10 = 0xFFFF - 9--tB_KEY_F11 :: Word16-tB_KEY_F11 = 0xFFFF - 10--tB_KEY_F12 :: Word16-tB_KEY_F12 = 0xFFFF - 11--tB_KEY_INSERT :: Word16-tB_KEY_INSERT = 0xFFFF - 12--tB_KEY_DELETE :: Word16-tB_KEY_DELETE = 0xFFFF - 13--tB_KEY_HOME :: Word16-tB_KEY_HOME = 0xFFFF - 14--tB_KEY_END :: Word16-tB_KEY_END = 0xFFFF - 15--tB_KEY_PGUP :: Word16-tB_KEY_PGUP = 0xFFFF - 16--tB_KEY_PGDN :: Word16-tB_KEY_PGDN = 0xFFFF - 17--tB_KEY_ARROW_UP :: Word16-tB_KEY_ARROW_UP = 0xFFFF - 18--tB_KEY_ARROW_DOWN :: Word16-tB_KEY_ARROW_DOWN = 0xFFFF - 19--tB_KEY_ARROW_LEFT :: Word16-tB_KEY_ARROW_LEFT = 0xFFFF - 20--tB_KEY_ARROW_RIGHT :: Word16-tB_KEY_ARROW_RIGHT = 0xFFFF - 21--tB_KEY_MOUSE_LEFT :: Word16-tB_KEY_MOUSE_LEFT = 0xFFFF - 22--tB_KEY_MOUSE_RIGHT :: Word16-tB_KEY_MOUSE_RIGHT = 0xFFFF - 23--tB_KEY_MOUSE_MIDDLE :: Word16-tB_KEY_MOUSE_MIDDLE = 0xFFFF - 24--tB_KEY_MOUSE_RELEASE :: Word16-tB_KEY_MOUSE_RELEASE = 0xFFFF - 25--tB_KEY_MOUSE_WHEEL_UP :: Word16-tB_KEY_MOUSE_WHEEL_UP = 0xFFFF - 26--tB_KEY_MOUSE_WHEEL_DOWN :: Word16-tB_KEY_MOUSE_WHEEL_DOWN = 0xFFFF - 27--tB_KEY_CTRL_TILDE :: Word16-tB_KEY_CTRL_TILDE = 0x00--tB_KEY_CTRL_2 :: Word16-tB_KEY_CTRL_2 = 0x00--tB_KEY_CTRL_A :: Word16-tB_KEY_CTRL_A = 0x01--tB_KEY_CTRL_B :: Word16-tB_KEY_CTRL_B = 0x02--tB_KEY_CTRL_C :: Word16-tB_KEY_CTRL_C = 0x03--tB_KEY_CTRL_D :: Word16-tB_KEY_CTRL_D = 0x04--tB_KEY_CTRL_E :: Word16-tB_KEY_CTRL_E = 0x05--tB_KEY_CTRL_F :: Word16-tB_KEY_CTRL_F = 0x06--tB_KEY_CTRL_G :: Word16-tB_KEY_CTRL_G = 0x07--tB_KEY_BACKSPACE :: Word16-tB_KEY_BACKSPACE = 0x08--tB_KEY_CTRL_H :: Word16-tB_KEY_CTRL_H = 0x08--tB_KEY_TAB :: Word16-tB_KEY_TAB = 0x09--tB_KEY_CTRL_I :: Word16-tB_KEY_CTRL_I = 0x09--tB_KEY_CTRL_J :: Word16-tB_KEY_CTRL_J = 0x0A--tB_KEY_CTRL_K :: Word16-tB_KEY_CTRL_K = 0x0B--tB_KEY_CTRL_L :: Word16-tB_KEY_CTRL_L = 0x0C--tB_KEY_ENTER :: Word16-tB_KEY_ENTER = 0x0D--tB_KEY_CTRL_M :: Word16-tB_KEY_CTRL_M = 0x0D--tB_KEY_CTRL_N :: Word16-tB_KEY_CTRL_N = 0x0E--tB_KEY_CTRL_O :: Word16-tB_KEY_CTRL_O = 0x0F--tB_KEY_CTRL_P :: Word16-tB_KEY_CTRL_P = 0x10--tB_KEY_CTRL_Q :: Word16-tB_KEY_CTRL_Q = 0x11--tB_KEY_CTRL_R :: Word16-tB_KEY_CTRL_R = 0x12--tB_KEY_CTRL_S :: Word16-tB_KEY_CTRL_S = 0x13--tB_KEY_CTRL_T :: Word16-tB_KEY_CTRL_T = 0x14--tB_KEY_CTRL_U :: Word16-tB_KEY_CTRL_U = 0x15--tB_KEY_CTRL_V :: Word16-tB_KEY_CTRL_V = 0x16--tB_KEY_CTRL_W :: Word16-tB_KEY_CTRL_W = 0x17--tB_KEY_CTRL_X :: Word16-tB_KEY_CTRL_X = 0x18--tB_KEY_CTRL_Y :: Word16-tB_KEY_CTRL_Y = 0x19--tB_KEY_CTRL_Z :: Word16-tB_KEY_CTRL_Z = 0x1A--tB_KEY_ESC :: Word16-tB_KEY_ESC = 0x1B--tB_KEY_CTRL_LSQ_BRACKET :: Word16-tB_KEY_CTRL_LSQ_BRACKET = 0x1B--tB_KEY_CTRL_3 :: Word16-tB_KEY_CTRL_3 = 0x1B--tB_KEY_CTRL_4 :: Word16-tB_KEY_CTRL_4 = 0x1C--tB_KEY_CTRL_BACKSLASH :: Word16-tB_KEY_CTRL_BACKSLASH = 0x1C--tB_KEY_CTRL_5 :: Word16-tB_KEY_CTRL_5 = 0x1D--tB_KEY_CTRL_RSQ_BRACKET :: Word16-tB_KEY_CTRL_RSQ_BRACKET = 0x1D--tB_KEY_CTRL_6 :: Word16-tB_KEY_CTRL_6 = 0x1E--tB_KEY_CTRL_7 :: Word16-tB_KEY_CTRL_7 = 0x1F--tB_KEY_CTRL_SLASH :: Word16-tB_KEY_CTRL_SLASH = 0x1F--tB_KEY_CTRL_UNDERSCORE :: Word16-tB_KEY_CTRL_UNDERSCORE = 0x1F--tB_KEY_SPACE :: Word16-tB_KEY_SPACE = 0x20--tB_KEY_BACKSPACE2 :: Word16-tB_KEY_BACKSPACE2 = 0x7F--tB_KEY_CTRL_8 :: Word16-tB_KEY_CTRL_8 = 0x7F--data TbEvent-  = TbEvent !Word8 Word8 Word16 Word32 Int32 Int32 Int32 Int32--instance Storable TbEvent where-  sizeOf :: TbEvent -> Int-  sizeOf _ =-    24--  alignment :: TbEvent -> Int-  alignment _ =-    4--  peek :: Ptr TbEvent -> IO TbEvent-  peek ptr =-    TbEvent-      <$> peekByteOff ptr 0-      <*> peekByteOff ptr 1-      <*> peekByteOff ptr 2-      <*> peekByteOff ptr 4-      <*> peekByteOff ptr 8-      <*> peekByteOff ptr 12-      <*> peekByteOff ptr 16-      <*> peekByteOff ptr 20--  poke :: Ptr TbEvent -> TbEvent -> IO ()-  poke ptr (TbEvent typ mod key ch w h x y) = do-    pokeByteOff ptr 0 typ-    pokeByteOff ptr 1 mod-    pokeByteOff ptr 2 key-    pokeByteOff ptr 4 ch-    pokeByteOff ptr 8 w-    pokeByteOff ptr 12 h-    pokeByteOff ptr 16 x-    pokeByteOff ptr 20 y--foreign import ccall unsafe "tb_change_cell"-  tb_change_cell :: Int -> Int -> Word32 -> Word16 -> Word16 -> IO ()--foreign import ccall unsafe "tb_clear"-  tb_clear :: IO ()--foreign import ccall unsafe "tb_height"-  tb_height :: IO Int--foreign import ccall unsafe "tb_init"-  tb_init :: IO Int--foreign import ccall safe "tb_peek_event"-  tb_peek_event :: Ptr TbEvent -> Int -> IO Int--foreign import ccall safe "tb_poll_event"-  tb_poll_event :: Ptr TbEvent -> IO Int--foreign import ccall unsafe "tb_present"-  tb_present :: IO ()--foreign import ccall unsafe "tb_select_input_mode"-  tb_select_input_mode :: Int -> IO Int--foreign import ccall unsafe "tb_select_output_mode"-  tb_select_output_mode :: Int -> IO Int--foreign import ccall unsafe "tb_set_clear_attributes"-  tb_set_clear_attributes :: Word16 -> Word16 -> IO ()--foreign import ccall unsafe "tb_set_cursor"-  tb_set_cursor :: Int -> Int -> IO ()--foreign import ccall unsafe "tb_shutdown"-  tb_shutdown :: IO ()--foreign import ccall unsafe "tb_width"-  tb_width :: IO Int---- foreign import ccall unsafe "tb_cell_buffer"---   tb_cell_buffer :: IO (Ptr Cell)
+ src/Termbox/Internal/Cell.hs view
@@ -0,0 +1,115 @@+module Termbox.Internal.Cell+  ( -- * Cell+    Cell,+    drawCell,+    char,++    -- ** Color+    fg,+    bg,++    -- ** Style+    bold,+    underline,+    blink,+  )+where++import qualified Data.Char as Char+import Data.String (IsString (..))+import Foreign.C.Types (CInt (CInt), CWchar (CWchar))+import qualified Termbox.Bindings.Hs+import Termbox.Internal.Color (Color (Color))++-- | A single cell.+--+-- * Create a cell with 'char', or with a string literal.+-- * Set a cell's color with 'fg', 'bg'.+-- * Style a cell with 'bold', 'underline', 'blink'.+data Cell+  = CellEmpty+  | CellFg+      {-# UNPACK #-} !Char -- invariant: char is width 1+      {-# UNPACK #-} !Termbox.Bindings.Hs.Tb_color -- fg+  | CellFgBlink+      {-# UNPACK #-} !Char -- invariant: char is width 1+      {-# UNPACK #-} !Termbox.Bindings.Hs.Tb_color -- fg+  | CellFgBg+      {-# UNPACK #-} !Char -- invariant: char is width 1+      {-# UNPACK #-} !Termbox.Bindings.Hs.Tb_color -- fg+      {-# UNPACK #-} !Termbox.Bindings.Hs.Tb_color -- bg++instance {-# OVERLAPS #-} IsString [Cell] where+  fromString =+    map char++drawCell :: Termbox.Bindings.Hs.Tb_color -> Int -> Int -> Cell -> IO ()+drawCell bg0 col row = \case+  CellEmpty -> pure ()+  CellFg ch fg_ -> Termbox.Bindings.Hs.tb_change_cell col row ch fg_ bg0+  CellFgBlink ch fg_ -> Termbox.Bindings.Hs.tb_change_cell col row ch fg_ (makeBold bg0) -- bold background = blink+  CellFgBg ch fg_ bg_ -> Termbox.Bindings.Hs.tb_change_cell col row ch fg_ bg_++-- | Create a cell from a character.+--+-- If the character is not 1 character wide, it will not be displayed.+char :: Char -> Cell+char ch =+  case wcwidth (charToCWchar ch) of+    1 -> CellFg ch Termbox.Bindings.Hs.TB_DEFAULT+    _ -> CellEmpty++-- | Set the foreground color of a cell.+fg :: Color -> Cell -> Cell+fg (Color color) = \case+  CellEmpty -> CellEmpty+  CellFg ch _ -> CellFg ch color+  CellFgBlink ch _ -> CellFgBlink ch color+  CellFgBg ch _ bg_ -> CellFgBg ch color bg_++-- | Set the background color of a cell.+bg :: Color -> Cell -> Cell+bg (Color color) = \case+  CellEmpty -> CellEmpty+  CellFg ch fg_ -> CellFgBg ch fg_ color+  CellFgBlink ch fg_ -> CellFgBg ch fg_ (makeBold color) -- bold background = blink+  CellFgBg ch fg_ _ -> CellFgBg ch fg_ color++-- | Make a cell bold.+bold :: Cell -> Cell+bold = \case+  CellEmpty -> CellEmpty+  CellFg ch fg_ -> CellFg ch (makeBold fg_)+  CellFgBlink ch fg_ -> CellFgBlink ch (makeBold fg_)+  CellFgBg ch fg_ bg_ -> CellFgBg ch (makeBold fg_) bg_++-- | Make a cell underlined.+underline :: Cell -> Cell+underline = \case+  CellEmpty -> CellEmpty+  CellFg ch fg_ -> CellFg ch (makeUnderline fg_)+  CellFgBlink ch fg_ -> CellFgBlink ch (makeUnderline fg_)+  CellFgBg ch fg_ bg_ -> CellFgBg ch (makeUnderline fg_) bg_++-- | Make a cell blink.+blink :: Cell -> Cell+blink = \case+  CellEmpty -> CellEmpty+  CellFg ch fg_ -> CellFgBlink ch fg_+  CellFgBlink ch fg_ -> CellFgBlink ch fg_+  CellFgBg ch fg_ bg_ -> CellFgBg ch fg_ (makeBold bg_) -- bold background = blink++makeBold :: Termbox.Bindings.Hs.Tb_color -> Termbox.Bindings.Hs.Tb_color+makeBold =+  Termbox.Bindings.Hs.tb_attr Termbox.Bindings.Hs.TB_BOLD++makeUnderline :: Termbox.Bindings.Hs.Tb_color -> Termbox.Bindings.Hs.Tb_color+makeUnderline =+  Termbox.Bindings.Hs.tb_attr Termbox.Bindings.Hs.TB_UNDERLINE++charToCWchar :: Char -> CWchar+charToCWchar =+  fromIntegral @Int @CWchar . Char.ord++foreign import capi unsafe "wchar.h wcwidth"+  wcwidth :: CWchar -> CInt
+ src/Termbox/Internal/Color.hs view
@@ -0,0 +1,85 @@+module Termbox.Internal.Color+  ( Color (..),+    defaultColor,+    red,+    green,+    yellow,+    blue,+    magenta,+    cyan,+    white,+    bright,+    color,+    gray,+  )+where++import Data.Coerce (coerce)+import Data.Word (Word16)+import qualified Termbox.Bindings.Hs++-- | A color.+--+-- There are three classes of colors:+--+-- * Basic named colors and their bright variants, such as 'red' and 'bright' 'blue'.+-- * Miscellaneous colors, such as @'color' 33@.+-- * Monochrome colors that range from black (@'gray' 0@) to white (@'gray' 23@).+newtype Color+  = Color Termbox.Bindings.Hs.Tb_color++defaultColor :: Color+defaultColor =+  Color 0++red :: Color+red =+  Color 1++green :: Color+green =+  Color 2++yellow :: Color+yellow =+  Color 3++blue :: Color+blue =+  Color 4++magenta :: Color+magenta =+  Color 5++cyan :: Color+cyan =+  Color 6++white :: Color+white =+  Color 7++-- | Make a basic color brighter.+bright :: Color -> Color+bright =+  coerce bright_++bright_ :: Termbox.Bindings.Hs.Tb_color -> Termbox.Bindings.Hs.Tb_color+bright_ c+  | c <= 7 = c + 8+  | otherwise = c++-- | A miscellaneous color.+--+-- Valid values are in the range @[0, 215]@; values outside of this range are clamped.+color :: Int -> Color+color =+  coerce (fromIntegral @Int @Word16 . (+ 16) . max 0 . min 215)++-- | A monochrome color; black is 0 and white is 23.+--+-- Valid values are in the range @[0, 23]@; values outside of this range are clamped.+gray :: Int -> Color+gray =+  coerce (fromIntegral @Int @Word16 . (+ 232) . max 0 . min 23)
+ src/Termbox/Internal/Event.hs view
@@ -0,0 +1,61 @@+module Termbox.Internal.Event+  ( Event (..),+    poll,+  )+where++import Data.Int (Int32)+import GHC.Generics (Generic)+import qualified Termbox.Bindings.Hs+import Termbox.Internal.Key (Key (KeyChar), parseKey)+import Termbox.Internal.Mouse (Mouse (Mouse))+import Termbox.Internal.Pos (Pos (..))+import Termbox.Internal.Size (Size (..))+import Prelude hiding (mod)++-- | An input event.+data Event e+  = -- | Key event+    EventKey !Key+  | -- | Resize event+    EventResize !Size+  | -- | Mouse event+    EventMouse !Mouse !Pos+  | -- | User event+    EventUser !e+  deriving stock (Eq, Generic, Show)++-- Block until an Event arrives.+poll :: IO (Either () (Event e))+poll = do+  result <- Termbox.Bindings.Hs.tb_poll_event+  pure (parseEvent <$> result)++-- Parse an Event from a TbEvent.+parseEvent :: Termbox.Bindings.Hs.Tb_event -> Event e+parseEvent+  Termbox.Bindings.Hs.Tb_event+    { Termbox.Bindings.Hs.type_,+      Termbox.Bindings.Hs.mod = _,+      Termbox.Bindings.Hs.key,+      Termbox.Bindings.Hs.ch,+      Termbox.Bindings.Hs.w,+      Termbox.Bindings.Hs.h,+      Termbox.Bindings.Hs.x,+      Termbox.Bindings.Hs.y+    } =+    case type_ of+      Termbox.Bindings.Hs.TB_EVENT_KEY -> EventKey (if ch == '\0' then parseKey key else KeyChar ch)+      Termbox.Bindings.Hs.TB_EVENT_RESIZE ->+        EventResize+          Size+            { width = fromIntegral @Int32 @Int w,+              height = fromIntegral @Int32 @Int h+            }+      Termbox.Bindings.Hs.TB_EVENT_MOUSE ->+        EventMouse+          (Mouse key)+          Pos+            { row = fromIntegral @Int32 @Int y,+              col = fromIntegral @Int32 @Int x+            }
+ src/Termbox/Internal/Key.hs view
@@ -0,0 +1,182 @@+module Termbox.Internal.Key+  ( Key (..),+    parseKey,+    pattern KeyCtrlH,+    pattern KeyCtrlLsqBracket,+    pattern KeyCtrl2,+    pattern KeyCtrl3,+    pattern KeyCtrl4,+    pattern KeyCtrl5,+    pattern KeyCtrl7,+    pattern KeyCtrlM,+    pattern KeyCtrlI,+    pattern KeyCtrlUnderscore,+  )+where++import qualified Termbox.Bindings.Hs++-- | A key event.+--+-- Some distinct key sequences map to the same key event. For example, to a @termbox@ program, @Enter@ is+-- indistinguishable from @Ctrl+M@. Pattern synonyms below are provided for an alternate syntax in these cases, if+-- desired.+data Key+  = KeyChar Char+  | KeyArrowDown+  | KeyArrowLeft+  | KeyArrowRight+  | KeyArrowUp+  | KeyBackspace+  | -- | Also 'KeyCtrlH'+    KeyCtrlBackspace+  | KeyCtrl6+  | KeyCtrl8+  | KeyCtrlA+  | KeyCtrlB+  | -- | Also 'KeyCtrl4'+    KeyCtrlBackslash+  | KeyCtrlC+  | KeyCtrlD+  | KeyCtrlE+  | KeyCtrlF+  | KeyCtrlG+  | KeyCtrlJ+  | KeyCtrlK+  | KeyCtrlL+  | KeyCtrlN+  | KeyCtrlO+  | KeyCtrlP+  | KeyCtrlQ+  | KeyCtrlR+  | -- | Also 'KeyCtrl5'+    KeyCtrlRsqBracket+  | KeyCtrlS+  | -- | Also 'KeyCtrl7', 'KeyCtrlUnderscore'+    KeyCtrlSlash+  | -- | Also 'KeyCtrl2'+    KeyCtrlTilde+  | KeyCtrlT+  | KeyCtrlU+  | KeyCtrlV+  | KeyCtrlW+  | KeyCtrlX+  | KeyCtrlY+  | KeyCtrlZ+  | KeyDelete+  | KeyEnd+  | -- | Also 'KeyCtrlM'+    KeyEnter+  | -- | Also 'KeyCtrlLsqBracket', 'KeyCtrl3'+    KeyEsc+  | KeyF1+  | KeyF10+  | KeyF11+  | KeyF12+  | KeyF2+  | KeyF3+  | KeyF4+  | KeyF5+  | KeyF6+  | KeyF7+  | KeyF8+  | KeyF9+  | KeyHome+  | KeyInsert+  | KeyPageDn+  | KeyPageUp+  | KeySpace+  | -- | Also 'KeyCtrlI'+    KeyTab+  deriving stock (Eq, Ord, Show)++pattern KeyCtrlH :: Key+pattern KeyCtrlH = KeyCtrlBackspace++pattern KeyCtrlLsqBracket :: Key+pattern KeyCtrlLsqBracket = KeyEsc++pattern KeyCtrl2 :: Key+pattern KeyCtrl2 = KeyCtrlTilde++pattern KeyCtrl3 :: Key+pattern KeyCtrl3 = KeyEsc++pattern KeyCtrl4 :: Key+pattern KeyCtrl4 = KeyCtrlBackslash++pattern KeyCtrl5 :: Key+pattern KeyCtrl5 = KeyCtrlRsqBracket++pattern KeyCtrl7 :: Key+pattern KeyCtrl7 = KeyCtrlSlash++pattern KeyCtrlM :: Key+pattern KeyCtrlM = KeyEnter++pattern KeyCtrlI :: Key+pattern KeyCtrlI = KeyTab++pattern KeyCtrlUnderscore :: Key+pattern KeyCtrlUnderscore = KeyCtrlSlash++parseKey :: Termbox.Bindings.Hs.Tb_key -> Key+parseKey = \case+  Termbox.Bindings.Hs.TB_KEY_ARROW_DOWN -> KeyArrowDown+  Termbox.Bindings.Hs.TB_KEY_ARROW_LEFT -> KeyArrowLeft+  Termbox.Bindings.Hs.TB_KEY_ARROW_RIGHT -> KeyArrowRight+  Termbox.Bindings.Hs.TB_KEY_ARROW_UP -> KeyArrowUp+  Termbox.Bindings.Hs.TB_KEY_BACKSPACE -> KeyBackspace+  Termbox.Bindings.Hs.TB_KEY_CTRL_TILDE -> KeyCtrlTilde+  Termbox.Bindings.Hs.TB_KEY_CTRL_6 -> KeyCtrl6+  Termbox.Bindings.Hs.TB_KEY_CTRL_8 -> KeyCtrl8+  Termbox.Bindings.Hs.TB_KEY_CTRL_A -> KeyCtrlA+  Termbox.Bindings.Hs.TB_KEY_CTRL_B -> KeyCtrlB+  Termbox.Bindings.Hs.TB_KEY_CTRL_BACKSLASH -> KeyCtrlBackslash+  Termbox.Bindings.Hs.TB_KEY_CTRL_C -> KeyCtrlC+  Termbox.Bindings.Hs.TB_KEY_CTRL_D -> KeyCtrlD+  Termbox.Bindings.Hs.TB_KEY_CTRL_E -> KeyCtrlE+  Termbox.Bindings.Hs.TB_KEY_CTRL_F -> KeyCtrlF+  Termbox.Bindings.Hs.TB_KEY_CTRL_G -> KeyCtrlG+  Termbox.Bindings.Hs.TB_KEY_CTRL_H -> KeyCtrlBackspace+  Termbox.Bindings.Hs.TB_KEY_CTRL_J -> KeyCtrlJ+  Termbox.Bindings.Hs.TB_KEY_CTRL_K -> KeyCtrlK+  Termbox.Bindings.Hs.TB_KEY_CTRL_L -> KeyCtrlL+  Termbox.Bindings.Hs.TB_KEY_CTRL_N -> KeyCtrlN+  Termbox.Bindings.Hs.TB_KEY_CTRL_O -> KeyCtrlO+  Termbox.Bindings.Hs.TB_KEY_CTRL_P -> KeyCtrlP+  Termbox.Bindings.Hs.TB_KEY_CTRL_Q -> KeyCtrlQ+  Termbox.Bindings.Hs.TB_KEY_CTRL_R -> KeyCtrlR+  Termbox.Bindings.Hs.TB_KEY_CTRL_RSQ_BRACKET -> KeyCtrlRsqBracket+  Termbox.Bindings.Hs.TB_KEY_CTRL_S -> KeyCtrlS+  Termbox.Bindings.Hs.TB_KEY_CTRL_SLASH -> KeyCtrlSlash+  Termbox.Bindings.Hs.TB_KEY_CTRL_T -> KeyCtrlT+  Termbox.Bindings.Hs.TB_KEY_CTRL_U -> KeyCtrlU+  Termbox.Bindings.Hs.TB_KEY_CTRL_V -> KeyCtrlV+  Termbox.Bindings.Hs.TB_KEY_CTRL_W -> KeyCtrlW+  Termbox.Bindings.Hs.TB_KEY_CTRL_X -> KeyCtrlX+  Termbox.Bindings.Hs.TB_KEY_CTRL_Y -> KeyCtrlY+  Termbox.Bindings.Hs.TB_KEY_CTRL_Z -> KeyCtrlZ+  Termbox.Bindings.Hs.TB_KEY_DELETE -> KeyDelete+  Termbox.Bindings.Hs.TB_KEY_END -> KeyEnd+  Termbox.Bindings.Hs.TB_KEY_ENTER -> KeyEnter+  Termbox.Bindings.Hs.TB_KEY_ESC -> KeyEsc+  Termbox.Bindings.Hs.TB_KEY_F1 -> KeyF1+  Termbox.Bindings.Hs.TB_KEY_F10 -> KeyF10+  Termbox.Bindings.Hs.TB_KEY_F11 -> KeyF11+  Termbox.Bindings.Hs.TB_KEY_F12 -> KeyF12+  Termbox.Bindings.Hs.TB_KEY_F2 -> KeyF2+  Termbox.Bindings.Hs.TB_KEY_F3 -> KeyF3+  Termbox.Bindings.Hs.TB_KEY_F4 -> KeyF4+  Termbox.Bindings.Hs.TB_KEY_F5 -> KeyF5+  Termbox.Bindings.Hs.TB_KEY_F6 -> KeyF6+  Termbox.Bindings.Hs.TB_KEY_F7 -> KeyF7+  Termbox.Bindings.Hs.TB_KEY_F8 -> KeyF8+  Termbox.Bindings.Hs.TB_KEY_F9 -> KeyF9+  Termbox.Bindings.Hs.TB_KEY_HOME -> KeyHome+  Termbox.Bindings.Hs.TB_KEY_INSERT -> KeyInsert+  Termbox.Bindings.Hs.TB_KEY_PGDN -> KeyPageDn+  Termbox.Bindings.Hs.TB_KEY_PGUP -> KeyPageUp+  Termbox.Bindings.Hs.TB_KEY_SPACE -> KeySpace+  Termbox.Bindings.Hs.TB_KEY_TAB -> KeyTab+  key -> error ("unknown key: " ++ show key)
+ src/Termbox/Internal/Mouse.hs view
@@ -0,0 +1,48 @@+module Termbox.Internal.Mouse+  ( Mouse+      ( Mouse,+        MouseLeft,+        MouseMiddle,+        MouseRelease,+        MouseRight,+        MouseWheelUp,+        MouseWheelDown+      ),+  )+where++import qualified Termbox.Bindings.Hs++-- | A mouse event.+newtype Mouse+  = Mouse Termbox.Bindings.Hs.Tb_key+  deriving stock (Eq, Ord)++instance Show Mouse where+  show = \case+    MouseLeft -> "MouseLeft"+    MouseMiddle -> "MouseMiddle"+    MouseRelease -> "MouseRelease"+    MouseRight -> "MouseRight"+    MouseWheelDown -> "MouseWheelDown"+    MouseWheelUp -> "MouseWheelUp"++pattern MouseLeft :: Mouse+pattern MouseLeft = Mouse Termbox.Bindings.Hs.TB_KEY_MOUSE_LEFT++pattern MouseMiddle :: Mouse+pattern MouseMiddle = Mouse Termbox.Bindings.Hs.TB_KEY_MOUSE_MIDDLE++pattern MouseRelease :: Mouse+pattern MouseRelease = Mouse Termbox.Bindings.Hs.TB_KEY_MOUSE_RELEASE++pattern MouseRight :: Mouse+pattern MouseRight = Mouse Termbox.Bindings.Hs.TB_KEY_MOUSE_RIGHT++pattern MouseWheelDown :: Mouse+pattern MouseWheelDown = Mouse Termbox.Bindings.Hs.TB_KEY_MOUSE_WHEEL_DOWN++pattern MouseWheelUp :: Mouse+pattern MouseWheelUp = Mouse Termbox.Bindings.Hs.TB_KEY_MOUSE_WHEEL_UP++{-# COMPLETE MouseLeft, MouseMiddle, MouseRelease, MouseRight, MouseWheelDown, MouseWheelUp #-}
+ src/Termbox/Internal/Pos.hs view
@@ -0,0 +1,37 @@+module Termbox.Internal.Pos+  ( Pos (..),+    posUp,+    posDown,+    posLeft,+    posRight,+  )+where++import GHC.Generics (Generic)++-- | A terminal position.+data Pos = Pos+  { row :: {-# UNPACK #-} !Int,+    col :: {-# UNPACK #-} !Int+  }+  deriving stock (Eq, Generic, Ord, Show)++-- | Move a position up.+posUp :: Int -> Pos -> Pos+posUp n (Pos row col) =+  Pos (row - n) col++-- | Move a position down.+posDown :: Int -> Pos -> Pos+posDown n (Pos row col) =+  Pos (row + n) col++-- | Move a position left.+posLeft :: Int -> Pos -> Pos+posLeft n (Pos row col) =+  Pos row (col - n)++-- | Move a position right.+posRight :: Int -> Pos -> Pos+posRight n (Pos row col) =+  Pos row (col + n)
+ src/Termbox/Internal/Scene.hs view
@@ -0,0 +1,71 @@+module Termbox.Internal.Scene+  ( Scene,+    drawScene,+    cell,+    fill,+    cursor,+  )+where++import qualified Termbox.Bindings.Hs+import Termbox.Internal.Cell (Cell, drawCell)+import Termbox.Internal.Color (Color (Color))+import Termbox.Internal.Pos (Pos (..))++-- | A scene.+--+-- * Set individual cells with 'cell'.+-- * Set the background fill color with 'fill'.+-- * Set the cursor position with 'cursor'.+-- * Combine scenes together with @<>@.+data Scene = Scene+  { sceneFill :: Maybe Termbox.Bindings.Hs.Tb_color,+    sceneDraw :: Termbox.Bindings.Hs.Tb_color -> IO ()+  }++instance Monoid Scene where+  mempty =+    Scene+      { sceneFill = Nothing,+        sceneDraw = \_ -> pure ()+      }++instance Semigroup Scene where+  Scene fill0 draw0 <> Scene fill1 draw1 =+    Scene+      { sceneFill =+          case fill1 of+            Nothing -> fill0+            Just _ -> fill1,+        sceneDraw =+          \color -> do+            draw0 color+            draw1 color+      }++-- Draw a scene.+drawScene :: Scene -> IO ()+drawScene Scene {sceneFill, sceneDraw} = do+  let background =+        case sceneFill of+          Nothing -> Termbox.Bindings.Hs.TB_DEFAULT+          Just color -> color+  Termbox.Bindings.Hs.tb_set_clear_attributes Termbox.Bindings.Hs.TB_DEFAULT background+  Termbox.Bindings.Hs.tb_clear+  sceneDraw background+  Termbox.Bindings.Hs.tb_present++-- | Set the background fill color.+fill :: Color -> Scene+fill (Color color) =+  mempty {sceneFill = Just color}++-- | Set a single cell.+cell :: Pos -> Cell -> Scene+cell Pos {col, row} img =+  mempty {sceneDraw = \bg -> drawCell bg col row img}++-- | Set the cursor position.+cursor :: Pos -> Scene+cursor Pos {col, row} =+  mempty {sceneDraw = \_ -> Termbox.Bindings.Hs.tb_set_cursor (Just (col, row))}
+ src/Termbox/Internal/Size.hs view
@@ -0,0 +1,13 @@+module Termbox.Internal.Size+  ( Size (..),+  )+where++import GHC.Generics (Generic)++-- | A terminal size.+data Size = Size+  { width :: {-# UNPACK #-} !Int,+    height :: {-# UNPACK #-} !Int+  }+  deriving stock (Eq, Generic, Ord, Show)
− src/Termbox/Key.hs
@@ -1,181 +0,0 @@-{-# LANGUAGE PatternSynonyms #-}--module Termbox.Key-  ( Key (..),-    parseKey,-    pattern KeyCtrlH,-    pattern KeyCtrlLsqBracket,-    pattern KeyCtrl2,-    pattern KeyCtrl3,-    pattern KeyCtrl4,-    pattern KeyCtrl5,-    pattern KeyCtrl7,-    pattern KeyCtrlM,-    pattern KeyCtrlI,-    pattern KeyCtrlUnderscore,-  )-where--import Data.Word (Word16)-import Termbox.Internal---- | A key event.-data Key-  = KeyChar Char-  | KeyArrowDown-  | KeyArrowLeft-  | KeyArrowRight-  | KeyArrowUp-  | KeyBackspace-  | -- | Also: @Ctrl+H@-    KeyCtrlBackspace-  | KeyCtrl6-  | KeyCtrl8-  | KeyCtrlA-  | KeyCtrlB-  | -- | Also: @Ctrl-4@-    KeyCtrlBackslash-  | KeyCtrlC-  | KeyCtrlD-  | KeyCtrlE-  | KeyCtrlF-  | KeyCtrlG-  | KeyCtrlJ-  | KeyCtrlK-  | KeyCtrlL-  | KeyCtrlN-  | KeyCtrlO-  | KeyCtrlP-  | KeyCtrlQ-  | KeyCtrlR-  | -- | Also: @Ctrl-5@-    KeyCtrlRsqBracket-  | KeyCtrlS-  | -- | Also: @Ctrl-/@, @Ctrl-_@-    KeyCtrlSlash-  | -- | Also: @Ctrl+2@-    KeyCtrlTilde-  | KeyCtrlT-  | KeyCtrlU-  | KeyCtrlV-  | KeyCtrlW-  | KeyCtrlX-  | KeyCtrlY-  | KeyCtrlZ-  | KeyDelete-  | KeyEnd-  | -- | Also: @Ctrl-M@-    KeyEnter-  | -- | Also: @Ctrl-[@, @Ctrl-3@-    KeyEsc-  | KeyF1-  | KeyF10-  | KeyF11-  | KeyF12-  | KeyF2-  | KeyF3-  | KeyF4-  | KeyF5-  | KeyF6-  | KeyF7-  | KeyF8-  | KeyF9-  | KeyHome-  | KeyInsert-  | KeyPageDn-  | KeyPageUp-  | KeySpace-  | -- | Also: @Ctrl+I@-    KeyTab-  deriving (Eq, Ord, Show)--pattern KeyCtrlH :: Key-pattern KeyCtrlH = KeyCtrlBackspace--pattern KeyCtrlLsqBracket :: Key-pattern KeyCtrlLsqBracket = KeyEsc--pattern KeyCtrl2 :: Key-pattern KeyCtrl2 = KeyCtrlTilde--pattern KeyCtrl3 :: Key-pattern KeyCtrl3 = KeyEsc--pattern KeyCtrl4 :: Key-pattern KeyCtrl4 = KeyCtrlBackslash--pattern KeyCtrl5 :: Key-pattern KeyCtrl5 = KeyCtrlRsqBracket--pattern KeyCtrl7 :: Key-pattern KeyCtrl7 = KeyCtrlSlash--pattern KeyCtrlM :: Key-pattern KeyCtrlM = KeyEnter--pattern KeyCtrlI :: Key-pattern KeyCtrlI = KeyTab--pattern KeyCtrlUnderscore :: Key-pattern KeyCtrlUnderscore = KeyCtrlSlash--parseKey :: Word16 -> Key-parseKey key-  | key == tB_KEY_ARROW_DOWN = KeyArrowDown-  | key == tB_KEY_ARROW_LEFT = KeyArrowLeft-  | key == tB_KEY_ARROW_RIGHT = KeyArrowRight-  | key == tB_KEY_ARROW_UP = KeyArrowUp-  | key == tB_KEY_BACKSPACE = KeyBackspace-  | key == tB_KEY_CTRL_TILDE = KeyCtrlTilde-  | key == tB_KEY_CTRL_6 = KeyCtrl6-  | key == tB_KEY_CTRL_8 = KeyCtrl8-  | key == tB_KEY_CTRL_A = KeyCtrlA-  | key == tB_KEY_CTRL_B = KeyCtrlB-  | key == tB_KEY_CTRL_BACKSLASH = KeyCtrlBackslash-  | key == tB_KEY_CTRL_C = KeyCtrlC-  | key == tB_KEY_CTRL_D = KeyCtrlD-  | key == tB_KEY_CTRL_E = KeyCtrlE-  | key == tB_KEY_CTRL_F = KeyCtrlF-  | key == tB_KEY_CTRL_G = KeyCtrlG-  | key == tB_KEY_CTRL_H = KeyCtrlBackspace-  | key == tB_KEY_CTRL_J = KeyCtrlJ-  | key == tB_KEY_CTRL_K = KeyCtrlK-  | key == tB_KEY_CTRL_L = KeyCtrlL-  | key == tB_KEY_CTRL_N = KeyCtrlN-  | key == tB_KEY_CTRL_O = KeyCtrlO-  | key == tB_KEY_CTRL_P = KeyCtrlP-  | key == tB_KEY_CTRL_Q = KeyCtrlQ-  | key == tB_KEY_CTRL_R = KeyCtrlR-  | key == tB_KEY_CTRL_RSQ_BRACKET = KeyCtrlRsqBracket-  | key == tB_KEY_CTRL_S = KeyCtrlS-  | key == tB_KEY_CTRL_SLASH = KeyCtrlSlash-  | key == tB_KEY_CTRL_T = KeyCtrlT-  | key == tB_KEY_CTRL_U = KeyCtrlU-  | key == tB_KEY_CTRL_V = KeyCtrlV-  | key == tB_KEY_CTRL_W = KeyCtrlW-  | key == tB_KEY_CTRL_X = KeyCtrlX-  | key == tB_KEY_CTRL_Y = KeyCtrlY-  | key == tB_KEY_CTRL_Z = KeyCtrlZ-  | key == tB_KEY_DELETE = KeyDelete-  | key == tB_KEY_END = KeyEnd-  | key == tB_KEY_ENTER = KeyEnter-  | key == tB_KEY_ESC = KeyEsc-  | key == tB_KEY_F1 = KeyF1-  | key == tB_KEY_F10 = KeyF10-  | key == tB_KEY_F11 = KeyF11-  | key == tB_KEY_F12 = KeyF12-  | key == tB_KEY_F2 = KeyF2-  | key == tB_KEY_F3 = KeyF3-  | key == tB_KEY_F4 = KeyF4-  | key == tB_KEY_F5 = KeyF5-  | key == tB_KEY_F6 = KeyF6-  | key == tB_KEY_F7 = KeyF7-  | key == tB_KEY_F8 = KeyF8-  | key == tB_KEY_F9 = KeyF9-  | key == tB_KEY_HOME = KeyHome-  | key == tB_KEY_INSERT = KeyInsert-  | key == tB_KEY_PGDN = KeyPageDn-  | key == tB_KEY_PGUP = KeyPageUp-  | key == tB_KEY_SPACE = KeySpace-  | key == tB_KEY_TAB = KeyTab-  | otherwise = error ("termbox: unknown key " ++ show key)
− src/Termbox/Mouse.hs
@@ -1,28 +0,0 @@-module Termbox.Mouse-  ( Mouse (..),-    parseMouse,-  )-where--import Data.Word (Word16)-import Termbox.Internal---- | A mouse event.-data Mouse-  = MouseLeft-  | MouseMiddle-  | MouseRelease-  | MouseRight-  | MouseWheelDown-  | MouseWheelUp-  deriving (Eq, Ord, Show)--parseMouse :: Word16 -> Mouse-parseMouse key-  | key == tB_KEY_MOUSE_LEFT = MouseLeft-  | key == tB_KEY_MOUSE_MIDDLE = MouseMiddle-  | key == tB_KEY_MOUSE_RELEASE = MouseRelease-  | key == tB_KEY_MOUSE_RIGHT = MouseRight-  | key == tB_KEY_MOUSE_WHEEL_DOWN = MouseWheelDown-  | key == tB_KEY_MOUSE_WHEEL_UP = MouseWheelUp-  | otherwise = error ("termbox: unknown mouse " ++ show key)
termbox.cabal view
@@ -1,33 +1,33 @@ cabal-version: 2.4 -name: termbox-version: 0.3.0 category: User Interfaces+author: Mitchell Rosen+bug-reports: https://github.com/termbox/termbox-haskell/issues+build-type: Simple+copyright: (c) 2018-2022, Mitchell Rosen description:-  This package provides a thin wrapper around @termbox v1.1.2@, a simple C+  This package provides a high-level wrapper around @termbox@, a simple C   library for writing text-based user interfaces: <https://github.com/termbox/termbox>   .-  The full source of @termbox v1.1.2@ (1000 lines of C) is bundled; you do-  not need to install any system packages to use this library.-synopsis: termbox bindings-author: Mitchell Rosen-maintainer: Mitchell Rosen <mitchellwrosen@gmail.com>-homepage: https://github.com/mitchellwrosen/termbox-bug-reports: https://github.com/mitchellwrosen/termbox/issues-copyright: (c) 2018-2020, Mitchell Rosen-license: BSD-3-Clause+  See also:+  .+  * @<https://hackage.haskell.org/package/termbox-bindings-hs termbox-bindings-hs>@ for lower-level bindings.+  * @<https://hackage.haskell.org/package/termbox-bindings-c termbox-bindings-c>@ for even lower-level bindings.+homepage: https://github.com/termbox/termbox-haskell license-file: LICENSE-build-type: Simple+license: BSD-3-Clause+maintainer: Mitchell Rosen <mitchellwrosen@gmail.com>+name: termbox+synopsis: termbox+tested-with: GHC == 9.0.2, GHC == 9.2.4, GHC == 9.4.2+version: 1.0.0  extra-source-files:-  cbits/LICENSE   CHANGELOG.md-  include/termbox.h-  README.md  source-repository head   type: git-  location: git://github.com/mitchellwrosen/termbox.git+  location: git://github.com/termbox/termbox-haskell.git  flag build-examples   default: False@@ -35,12 +35,28 @@  common component   default-extensions:+    BlockArguments+    CApiFFI+    DeriveAnyClass+    DeriveFunctor+    DeriveGeneric+    DerivingStrategies+    DerivingVia+    DuplicateRecordFields+    ExistentialQuantification+    FlexibleInstances     GeneralizedNewtypeDeriving     InstanceSigs     LambdaCase+    NamedFieldPuns+    NumericUnderscores+    OverloadedStrings+    PatternSynonyms+    TypeApplications   default-language: Haskell2010   ghc-options:     -Weverything+    -Wno-all-missed-specialisations     -Wno-implicit-prelude     -Wno-missing-import-lists     -Wno-missing-local-signatures@@ -51,35 +67,38 @@     ghc-options:       -Wno-missing-safe-haskell-mode       -Wno-prepositive-qualified-module+  if impl(ghc >= 9.2)+    ghc-options:+      -Wno-missing-kind-signatures  library   import: component-  build-depends: base ^>= 4.10 || ^>= 4.11 || ^>= 4.12 || ^>= 4.13 || ^>= 4.14-  c-sources:-    cbits/termbox.c-    cbits/utf8.c-  exposed-modules:-    Termbox-    Termbox.Internal+  build-depends:+    base ^>= 4.13 || ^>= 4.14 || ^>= 4.15 || ^>= 4.16 || ^>= 4.17,+    termbox-bindings-hs ^>= 0.1,+    ki ^>= 1.0,+  exposed-modules: Termbox   hs-source-dirs: src-  include-dirs: include   other-modules:-    Termbox.Attr-    Termbox.Cell-    Termbox.Cells-    Termbox.Event-    Termbox.Key-    Termbox.Mouse+    Termbox.Internal.Cell+    Termbox.Internal.Color+    Termbox.Internal.Event+    Termbox.Internal.Key+    Termbox.Internal.Mouse+    Termbox.Internal.Pos+    Termbox.Internal.Size+    Termbox.Internal.Scene -executable termbox-example-colors+executable termbox-example-demo   import: component   if !flag(build-examples)     buildable: False   build-depends:     base,+    ki ^>= 1.0,     termbox,   ghc-options:     -rtsopts     -threaded   hs-source-dirs: examples-  main-is: Colors.hs+  main-is: Demo.hs