packages feed

vty 5.27 → 5.28

raw patch · 12 files changed

+1616/−578 lines, 12 filesdep +ansi-terminaldep +binarydep ~basedep ~filepathnew-component:exe:vty-build-width-table

Dependencies added: ansi-terminal, binary

Dependency ranges changed: base, filepath

Files

CHANGELOG.md view
@@ -1,555 +1,709 @@ +5.28+----++This release improves Vty's support for multi-column Unicode characters+and provides greater compatibility with a wider array of terminal+emulators. The following sections summarize the relevant changes, but an+overview of the new functionality is motivated and detailed in the new+"Multi-Column Character Support" README section. For+historical context, please also consider reading over+[#175](https://github.com/jtdaugherty/vty/issues/175).++API changes:+ * New modules were added:+   * `Graphics.Vty.UnicodeWidthTable.Types`+   * `Graphics.Vty.UnicodeWidthTable.IO`+   * `Graphics.Vty.UnicodeWidthTable.Query`+   * `Graphics.Vty.UnicodeWidthTable.Install`+ * The `Config` type got a new field, `allowCustomUnicodeWidthTables`,+   that controls whether `mkVty` will attempt to load a Unicode width+   table if specified in the configuration.++Configuration file changes:+ * A new syntax was added to support specifying Unicode width tables on+   a per-`TERM` basis. The syntax is `widthMap <TERM> <PATH>`. See the+   documentation for `Graphics.Vty.Config` for details. Since prior+   versions of this library will silently ignore any configuration file+   lines they cannot parse, this change to user configuration files is+   at least non-breaking for older versions of Vty.++Other changes:+ * The `mkVty` function now automatically attempts to load a custom+   Unicode width table if one is specified in the configuration,+   provided `allowCustomUnicodeWidthTables` is not set to `Just False`.+   See the documentation for `Graphics.Vty.mkVty` for details.+ * Vty now includes a command line tool, `vty-build-width-table`, that+   queries the terminal emulator to construct a custom Unicode width+   table and optionally update the Vty configuration file to use it.+   Programs that want to use that tool's functionality may also do so+   via the API exposed in the various modules listed above.+ 5.27- - Added Graphics.Vty.Config.getTtyEraseChar to support querying the-   kernel for the current terminal's settings to obtain the character-   assigned by the "stty erase" command. That can then be added to the-   Vty configuration's input map to map to KBS (backspace) if desired.+---- +* Added `Graphics.Vty.Config.getTtyEraseChar` to support querying the+  kernel for the current terminal's settings to obtain the character+  assigned by the `stty erase` command. That can then be added to the+  Vty configuration's input map to map to `KBS` (backspace) if desired.+ 5.26- - Resolved various import warnings (thanks @glguy)- - Removed the MonadIO constraint from the Output type's fields and-   removed MonadFail uses (PR #177, thanks @glguy)- - Clarified documentation for ANSI colors (thanks Colby Jenn)- - Graphics.Vty.Attributes no longer re-exports-   Graphics.Vty.Attributes.Color- - The Graphics.Vty.Attributes.Color module is now exposed (thanks Colby-   Jenn)- - Raised upper bound for microlens to 0.4.12 (thanks Artyom Kazak)- - Changed from using System.Posix.Env.getEnv to-   System.Environment.lookupEnv (thanks Jonathan Osser)- - Added Graphics.Vty.Image functions for dealing with character width-   computations on Text values instead of Strings:-   - safeWctwidth-   - safeWctlwidth-   - wctwidth-   - wctlwidth+---- +* Resolved various import warnings (thanks @glguy)+* Removed the `MonadIO` constraint from the Output type's fields and+  removed `MonadFail` uses (PR #177, thanks @glguy)+* Clarified documentation for ANSI colors (thanks Colby Jenn)+* `Graphics.Vty.Attributes` no longer re-exports+  `Graphics.Vty.Attributes.Color`+* The `Graphics.Vty.Attributes.Color` module is now exposed (thanks+  Colby Jenn)+* Raised upper bound for `microlens` to 0.4.12 (thanks Artyom Kazak)+* Changed from using `System.Posix.Env.getEnv` to+  `System.Environment.lookupEnv` (thanks Jonathan Osser)+* Added `Graphics.Vty.Image` functions for dealing with character width+  computations on `Text` values instead of `Strings`:+  * `safeWctwidth`+  * `safeWctlwidth`+  * `wctwidth`+  * `wctlwidth`+ 5.25.1- - Avoided a conflict with a Microlens 0.4.10 operator and added an-   upper bound on Microlens of 0.4.11.+------ +* Avoided a conflict with a Microlens 0.4.10 operator and added an+  upper bound on Microlens of 0.4.11.+ 5.25- - The Vty type got a new field, isShutdown, that returns whether the-   Vty handle has had its 'shutdown' function called (thanks Ian-   Jeffries)- - Vty's shutdown function is now thread-safe.+---- +* The Vty type got a new field, isShutdown, that returns whether the+  Vty handle has had its 'shutdown' function called (thanks Ian+  Jeffries)+* Vty's shutdown function is now thread-safe.+ 5.24.1- - The "shutdown" method of Vty handles is now idempotent (#159)+------ +* The "shutdown" method of Vty handles is now idempotent (#159)+ 5.24- - Add Generic and NFData instances for some types- - Image: remove custom Show instance, add derived Show and Read instances- - Updated Travis build settings (thanks Eric Mertens)+---- +* Add Generic and NFData instances for some types+* Image: remove custom Show instance, add derived Show and Read+  instances+* Updated Travis build settings (thanks Eric Mertens)+ 5.23.1-  - Fixed a bug where italics did not combine properly with other-    display modes (#155, thanks Eric Mertens)+------ +* Fixed a bug where italics did not combine properly with other display+  modes (#155, thanks Eric Mertens)+ 5.23-  - Added support for italicized output when terminfo supports it. This-    takes the form of a new Style, "italic". Note that most terminfo-    descriptors do not report capabilities for italics, so support for-    this will be very spotty.-  - Updateed text/string function documentation to indicate that escapes-    are not permitted in their inputs.+---- +* Added support for italicized output when terminfo supports it. This+  takes the form of a new Style, "italic". Note that most terminfo+  descriptors do not report capabilities for italics, so support for+  this will be very spotty.+* Updateed text/string function documentation to indicate that escapes+  are not permitted in their inputs.+ 5.22- - Added Graphics.Vty.Attributes.Color240.color240CodeToRGB function-   (thanks Brent Carmer)- - Added nextEventNonblocking function (field) to Vty type (#87)+---- +* Added Graphics.Vty.Attributes.Color240.color240CodeToRGB function+  (thanks Brent Carmer)+* Added nextEventNonblocking function (field) to Vty type (#87)+ 5.21- - Picture and Background now provide Eq instances (thanks Jaro Reinders)- - #145: vty builds with microlens 0.4.9 (thanks Daniel Wagner)- - #142: note requirement of threaded RTS+---- +* Picture and Background now provide Eq instances (thanks Jaro Reinders)+* #145: vty builds with microlens 0.4.9 (thanks Daniel Wagner)+* #142: note requirement of threaded RTS+ 5.20+----+ API changes:- - Split up Monoid instances into Monoid and Semigroup for newer GHCs-   (thanks Ryan Scott)+* Split up Monoid instances into Monoid and Semigroup for newer GHCs+  (thanks Ryan Scott)  5.19.1+------+ API changes:- * Cursor now provides an Eq instance (thanks Jaro Reinders)+* Cursor now provides an Eq instance (thanks Jaro Reinders)  5.19+----+ API changes:- - URL hyperlinking (via 'withURL') is now optional and disabled by-   default due to poor support on some common terminals. A new 'Mode'-   constructor, 'Hyperlink', has been added to enable this feature. To-   change the hyperlinking mode, use 'setMode' on the 'outputIface' of a-   Vty handle.+* URL hyperlinking (via 'withURL') is now optional and disabled by+  default due to poor support on some common terminals. A new 'Mode'+  constructor, 'Hyperlink', has been added to enable this feature. To+  change the hyperlinking mode, use 'setMode' on the 'outputIface' of a+  Vty handle.  5.18.1+------+ Bug fixes:- - Reset the hyperlink state on line endings to avoid run-on hyperlinks+* Reset the hyperlink state on line endings to avoid run-on hyperlinks  5.18+----+ API changes:- - Added support for hyperlinking attributes (thanks Getty Ritter). This-   change adds a new Attr field for containing the hyperlink to apply,-   as per https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda+* Added support for hyperlinking attributes (thanks Getty Ritter). This+  change adds a new Attr field for containing the hyperlink to apply,+  as per https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda  5.17.1- - withStyle now ignores zero arguments, leaving attribute styles-   untouched if the input style is the null style+------ +* withStyle now ignores zero arguments, leaving attribute styles+  untouched if the input style is the null style+ 5.17+----+ API changes:- - Add support for terminal focus events. This change adds a new mode-   usable with setMode, Focus, that requests that the terminal send-   events on focus lose/gain. This change also adds two new Event-   constructors, EvLostFocus and EvGainedFocus.- - No longer enable UTF8 mouse event encoding. This encoding was not-   working properly with Terminal.app, and using the other modes (SGR,-   etc.) work.- - Graphics.Vty.Attributes: escape backticks in Haddock comment (fixes-   #131)+* Add support for terminal focus events. This change adds a new mode+  usable with setMode, Focus, that requests that the terminal send+  events on focus lose/gain. This change also adds two new Event+  constructors, EvLostFocus and EvGainedFocus.+* No longer enable UTF8 mouse event encoding. This encoding was not+  working properly with Terminal.app, and using the other modes (SGR,+  etc.) work.+* Graphics.Vty.Attributes: escape backticks in Haddock comment (fixes+  #131)  5.16+----+ API changes:-  - Added support for mouse wheel events while in mouse mode. The Button-    type got two new constructors as a result: BScrollUp and BScrollDown.-    Thanks to doublescale@tutanota.com for this contribution!+* Added support for mouse wheel events while in mouse mode. The Button+  type got two new constructors as a result: BScrollUp and BScrollDown.+  Thanks to doublescale@tutanota.com for this contribution!  Bug fixes:-  - charFill now clamps negative arguments to zero (thanks Eric-    Mertens!)+* charFill now clamps negative arguments to zero (thanks Eric Mertens!)  5.15.1+------+ Package changes:-  - Documentation files are now marked accordingly (thanks Michal-    Suchánek)+* Documentation files are now marked accordingly (thanks Michal+  Suchánek)  Bug fixes:-  - translateX/Y: fix negative translations+* translateX/Y: fix negative translations  5.15+----+ Package changes:-  - Discontinued support for GHC versions prior to 7.10.1.-  - Removed instructions and configuration for Stack builds since they-    are no longer supported.-  - Clarified README mention of (lack of) Windows support (contributors-    wanted, though!)-  - Removed dependency on data-default (see below).+* Discontinued support for GHC versions prior to 7.10.1.+* Removed instructions and configuration for Stack builds since they+  are no longer supported.+* Clarified README mention of (lack of) Windows support (contributors+  wanted, though!)+* Removed dependency on data-default (see below).  API changes:-  - Moved color definitions from Attributes to Color module.-  - In lieu of data-default (Default) instances for Attr and Config, use-    'defAttr' and the new 'defaultConfig' (or 'mempty') instead of 'def'.-  - Graphics.Vty.Output no longer re-exports-    Graphics.Vty.Output.Interface.-  - Removed Graphics.Vty.Prelude module and moved DisplayRegion and its-    accessors to Graphics.Vty.Image.-  - Graphics.Vty.Image no longer re-exports Graphics.Vty.Attributes.-  - Graphics.Vty.Picture no longer re-exports Graphics.Vty.Image.+* Moved color definitions from Attributes to Color module.+* In lieu of data-default (Default) instances for Attr and Config, use+  'defAttr' and the new 'defaultConfig' (or 'mempty') instead of 'def'.+* Graphics.Vty.Output no longer re-exports+  Graphics.Vty.Output.Interface.+* Removed Graphics.Vty.Prelude module and moved DisplayRegion and its+  accessors to Graphics.Vty.Image.+* Graphics.Vty.Image no longer re-exports Graphics.Vty.Attributes.+* Graphics.Vty.Picture no longer re-exports Graphics.Vty.Image.  5.14-  - addMaybeClippedJoin: instead of raising an exception when the join-    is totally clipped, just reduce the clip amount and continue-  - addMaybeClipped: skip blit of joins when their primary dimension is-    zero-  - 'string' and related text functions no longer treat an empty string-    as an empty image (thanks Chris Penner). This means that now it is-    possible to use 'str ""' as a non-empty image with height 1.+---- +* addMaybeClippedJoin: instead of raising an exception when the join+  is totally clipped, just reduce the clip amount and continue+* addMaybeClipped: skip blit of joins when their primary dimension is+  zero+* 'string' and related text functions no longer treat an empty string+  as an empty image (thanks Chris Penner). This means that now it is+  possible to use 'str ""' as a non-empty image with height 1.+ 5.13-  - Reverted changes in 5.12 due to disagreements between terminal-    emulators and utf8proc; for more details, please see the ticket-    discussion at-    https://github.com/coreyoconnor/vty/issues/115+---- +* Reverted changes in 5.12 due to disagreements between terminal+  emulators and utf8proc; for more details, please see the ticket+  discussion at+  https://github.com/coreyoconnor/vty/issues/115+ 5.12-  - Replaced 'wcwidth' with a call to the utf8proc library's character-    width function, which is much more up to date (by several Unicode-    versions) and returns the right width for a much larger set of-    characters.-  - Added a bundled version of the utf8proc C library.+---- +* Replaced 'wcwidth' with a call to the utf8proc library's character+  width function, which is much more up to date (by several Unicode+  versions) and returns the right width for a much larger set of+  characters.+* Added a bundled version of the utf8proc C library.+ 5.11.3-  - Fix mouse event offsets in mouse-up events+------ +* Fix mouse event offsets in mouse-up events+ 5.11.2-  - Mouse events were modified so that the upper-left corner of the-    window is (0,0) rather than (1,1).+------ +* Mouse events were modified so that the upper-left corner of the window+  is (0,0) rather than (1,1).+ 5.11.1-  - Add Generic instance for Image-  - nextEvent: stop trying to refresh on a resize event (fixes segfault-    on refresh with normal cursor positioning mode)-  - Remove redundant clause from clipForCharWidth (thanks Eric Mertens)-  - Update maintainer+------ +* Add Generic instance for Image+* nextEvent: stop trying to refresh on a resize event (fixes segfault+  on refresh with normal cursor positioning mode)+* Remove redundant clause from clipForCharWidth (thanks Eric Mertens)+* Update maintainer+ 5.11-  - Vty now raises a VtyConfigurationError exception when the TERM-    evironment variable is missing (thanks Eric Mertens)-  - Graphics.Vty.Config got an explicit export list to avoid accidentally-    exporting internal types (thanks Eric Mertens)+---- +* Vty now raises a VtyConfigurationError exception when the TERM+  evironment variable is missing (thanks Eric Mertens)+* Graphics.Vty.Config got an explicit export list to avoid accidentally+  exporting internal types (thanks Eric Mertens)+ 5.10-  - Add absolute cursor positioning mode AbsoluteCursor to Cursor. This-    mode provides greater control over cursor positioning by bypassing-    the logical positioning provided by default. Rather than positioning-    the cursor by looking at the widths of characters involved, this-    constructor lets you provide a physical row and column instead. This-    is useful in more sophisticated programs. (thanks Eric Mertens)-  - Added a new Generic-derived config parser (thanks Eric Mertens)-  - Fixed the MShift case in the configuration file parser (thanks Eric-    Mertens)-  - Fixed wcwidth import and matched safeWcswidth to its documented-    behavior. Previously vty_mk_wcwidth was being imported with-    the wrong type causing the -1 return value to be mapped to the-    wrong Int value. Additionally safeWcswidth was using the unsafe-    character width function and only ensuring that the final result was-    non-negative. (thanks Eric Mertens)+---- +* Add absolute cursor positioning mode AbsoluteCursor to Cursor. This+  mode provides greater control over cursor positioning by bypassing+  the logical positioning provided by default. Rather than positioning+  the cursor by looking at the widths of characters involved, this+  constructor lets you provide a physical row and column instead. This+  is useful in more sophisticated programs. (thanks Eric Mertens)+* Added a new Generic-derived config parser (thanks Eric Mertens)+* Fixed the MShift case in the configuration file parser (thanks Eric+  Mertens)+* Fixed wcwidth import and matched safeWcswidth to its documented+  behavior. Previously vty_mk_wcwidth was being imported with the+  wrong type causing the -1 return value to be mapped to the wrong Int+  value. Additionally safeWcswidth was using the unsafe character width+  function and only ensuring that the final result was non-negative.+  (thanks Eric Mertens)+ 5.9.1-  - Vty now only emits UTF8 charset sequences in terminals without a-    preexisting UTF8 declaration to avoid emitting garbage sequences-    (fixes #89)+----- +* Vty now only emits UTF8 charset sequences in terminals without a+  preexisting UTF8 declaration to avoid emitting garbage sequences+  (fixes #89)+ 5.9-  - Added new Output methods supportsBell and ringTerminalBell to find-    out whether the output device has an audio bell and to ring it (see-    #102)+--- +* Added new Output methods supportsBell and ringTerminalBell to find out+  whether the output device has an audio bell and to ring it (see #102)+ 5.8.1-  - Fixed "refresh" to work as advertised (see #104)+----- +* Fixed "refresh" to work as advertised (see #104)+ 5.8-  - API change: EvPaste input event now provides paste data as a raw-    ByteString rather than a String to allow the application to decode-    how best to decode it+--- +* API change: EvPaste input event now provides paste data as a raw+  ByteString rather than a String to allow the application to decode how+  best to decode it+ 5.7.1-  - ModeDemo: added an explicit Control.Applicative import for older-    GHCs+----- +* ModeDemo: added an explicit Control.Applicative import for older GHCs+ 5.7-  - Mouse and paste modes are now off by default.-  - The Config type got new fields: mouseMode and bracketedPasteMode.-    These determine whether these modes are enabled initially (for-    terminals that support them).-  - Added a Mode type for modal terminal features (mouse events,-    bracketed paste mode) that is used with new Output interface-    functions:-    * supportsMode :: Mode -> Bool tells whether the device supports a-      mode-    * setMode :: Mode -> Bool -> IO () turns a mode on or off-    * getModeStatus :: Mode -> IO Bool tells you whether a mode is on or-      off-  - Added a new demo program, ModeDemo.hs, to demonstrate usage of modes+--- +* Mouse and paste modes are now off by default.+* The Config type got new fields: mouseMode and bracketedPasteMode.+  These determine whether these modes are enabled initially (for+  terminals that support them).+* Added a Mode type for modal terminal features (mouse events,+  bracketed paste mode) that is used with new Output interface+  functions:+  * supportsMode :: Mode -> Bool tells whether the device supports a+    mode+  * setMode :: Mode -> Bool -> IO () turns a mode on or off+  * getModeStatus :: Mode -> IO Bool tells you whether a mode is on or+    off+* Added a new demo program, ModeDemo.hs, to demonstrate usage of modes+ 5.6-  - Added support for normal and extended mouse modes in Xterm-like-    terminals via the MouseDown and MouseUp Event constructors-  - Added support for bracketed paste mode in Xterm-like terminals via-    the EvPaste event constructor-  - Added derived Show instances for Event and Button (thanks Felix-    Hirn)-  - Now TERM values containing "screen" will automatically use the-    XtermColor driver rather than just TerminfoBased+--- +* Added support for normal and extended mouse modes in Xterm-like+  terminals via the MouseDown and MouseUp Event constructors+* Added support for bracketed paste mode in Xterm-like terminals via+  the EvPaste event constructor+* Added derived Show instances for Event and Button (thanks Felix+  Hirn)+* Now TERM values containing "screen" will automatically use the+  XtermColor driver rather than just TerminfoBased+ 5.5.0-  - Replaced lens dependency with microlens, microlens-mtl, microlens-th-    dependencies. Issue #90-    - Thanks Jonathan Daugherty-  - Cabal corrections.-    - Thanks Lennart Spitzner+----- +* Replaced lens dependency with microlens, microlens-mtl, microlens-th+  dependencies. Issue #90+  * Thanks Jonathan Daugherty+* Cabal corrections.+  * Thanks Lennart Spitzner+ 5.4.0-  - Changed eventChannel of Graphics.Vty.Input from Chan to TChan.-    This enables clients to query if there are no pending events. The-    Graphics.Vty interface nextEvent is unchanged. Clients that use-    eventChannel directly will require updating.-    https://github.com/coreyoconnor/vty/issues/60+-----++* Changed eventChannel of Graphics.Vty.Input from Chan to+  TChan. This enables clients to query if there are no pending+  events. The Graphics.Vty interface nextEvent is unchanged.+  Clients that use eventChannel directly will require updating.+  https://github.com/coreyoconnor/vty/issues/60+ 5.3.1-  - Reverted cabal file to depend on Cabal >= 1.18 instead of 1.20 due-    to possibly breaking this on reasonable GHC versions+----- +* Reverted cabal file to depend on Cabal >= 1.18 instead of 1.20 due to+  possibly breaking this on reasonable GHC versions+ 5.3-  - Upgraded QuickCheck dependency to 2.7-  - The standard IO Config (standardIOConfig) was overriding any-    provided application config. In addition, the inputFd and outputFd-    could not be changed if mkVty was used. Fixed.-  - Correct handling of display attributes at end of line. The output-    attributes are set to default at the end of content for the line and-    at the start of a new line. Previously the current attribute would-    extend to the next start of content. This was odd to reason about-    and was the cause of https://github.com/coreyoconnor/vty/issues/76-    IIRC Yi requires the old behavior to display the selection region-    correctly.-  - shutdown of the input thread is now performed using killThread and-    synchronization on an MVar. For correct handling of the terminal-    read vmin and vtime the read must be a blocking read on an OS-    thread. This places a threadWaitRead, which will be interrupted by-    the killThread, prior to the uninterruptable read. An alternative-    would be to re-import the read foreign call as interruptable.+--- +* Upgraded QuickCheck dependency to 2.7+* The standard IO Config (standardIOConfig) was overriding any provided+  application config. In addition, the inputFd and outputFd could not be+  changed if mkVty was used. Fixed.+* Correct handling of display attributes at end of line. The output+  attributes are set to default at the end of content for the line and+  at the start of a new line. Previously the current attribute would+  extend to the next start of content. This was odd to reason about and+  was the cause of https://github.com/coreyoconnor/vty/issues/76 IIRC Yi+  requires the old behavior to display the selection region correctly.+* shutdown of the input thread is now performed using killThread and+  synchronization on an MVar. For correct handling of the terminal read+  vmin and vtime the read must be a blocking read on an OS thread.+  This places a threadWaitRead, which will be interrupted by the+  killThread, prior to the uninterruptable read. An alternative would be+  to re-import the read foreign call as interruptable.+ 5.2.11-  - deepseq bounds increased for tests.-  - Clean up warnings when compiling on 7.10-    - Thanks Eric Mertens-  - Avoid discarding input bytes after multi-byte encoded codepoint-    - Thanks Eric Mertens+------ +* deepseq bounds increased for tests.+* Clean up warnings when compiling on 7.10+  * Thanks Eric Mertens+* Avoid discarding input bytes after multi-byte encoded codepoint+  * Thanks Eric Mertens+ 5.2.10-  - "str" now returns EmptyImage for empty strings to match behavior of-    other string-like Image constructors (fixes #74)-    - Thanks Jonathan Daugherty+------ +* "str" now returns EmptyImage for empty strings to match behavior of+  other string-like Image constructors (fixes #74)+  * Thanks Jonathan Daugherty+ 5.2.9-  - dependency version bumps-    - https://github.com/coreyoconnor/vty/pull/71-    - https://github.com/coreyoconnor/vty/pull/70-  - Correct/Simplify the example code-    - Thanks glguy-    - https://github.com/coreyoconnor/vty/pull/69+----- +* dependency version bumps+  * https://github.com/coreyoconnor/vty/pull/71+  * https://github.com/coreyoconnor/vty/pull/70+* Correct/Simplify the example code+  * Thanks glguy+  * https://github.com/coreyoconnor/vty/pull/69+ 5.2.8-  - blaze-builder, lens, utf8-string version constraint bump-      - Thanks glguy-      - https://github.com/coreyoconnor/vty/pull/67-  - Do not differentiate based on TERM_PROGRAM-      - https://github.com/coreyoconnor/vty/issues/68+----- +* blaze-builder, lens, utf8-string version constraint bump+    * Thanks glguy+    * https://github.com/coreyoconnor/vty/pull/67+* Do not differentiate based on TERM_PROGRAM+    * https://github.com/coreyoconnor/vty/issues/68+ 5.2.7-  - lens and deepseq constraint bump + misc-      - Thanks ethercrow-      - https://github.com/coreyoconnor/vty/pull/66+----- +* lens and deepseq constraint bump + misc+    * Thanks ethercrow+    * https://github.com/coreyoconnor/vty/pull/66+ 5.2.6-  - lens constraint bump-      - Thanks alexander-b!-      - https://github.com/coreyoconnor/vty/pull/64+----- +* lens constraint bump+    * Thanks alexander-b!+    * https://github.com/coreyoconnor/vty/pull/64+ 5.2.5-  - lens and random version constraint bump.-      - Thanks RyanGlScott!-      - https://github.com/coreyoconnor/vty/pull/62+----- +* lens and random version constraint bump.+    * Thanks RyanGlScott!+    * https://github.com/coreyoconnor/vty/pull/62+ 5.2.4-  - removed -fpic from cc-options. No longer required.-      - https://github.com/coreyoconnor/vty/issues/61-      - https://ghc.haskell.org/trac/ghc/ticket/9657-      - Thanks Fuuzetsu!+----- +* removed -fpic from cc-options. No longer required.+    * https://github.com/coreyoconnor/vty/issues/61+    * https://ghc.haskell.org/trac/ghc/ticket/9657+    * Thanks Fuuzetsu!+ 5.2.3-  - evaluate/compile the input parsing table once instead of each-    keystroke.-      - https://github.com/coreyoconnor/vty/pull/59-      - Thanks ethercrow!+----- +* evaluate/compile the input parsing table once instead of each+  keystroke.+    * https://github.com/coreyoconnor/vty/pull/59+    * Thanks ethercrow!+ 5.2.2-  - When looking at input for an event, don't look too deep.-      - https://github.com/coreyoconnor/vty/pull/57-      - Thanks ethercrow!+----- +* When looking at input for an event, don't look too deep.+    * https://github.com/coreyoconnor/vty/pull/57+    * Thanks ethercrow!+ 5.2.1-  - Bump upper version bound for lens to 4.5. Thanks markus1189!+----- +* Bump upper version bound for lens to 4.5. Thanks markus1189!+ 5.2.0-  - Config structure now specifies file descriptor to use. The default-    is stdInput and stdOutput file descriptors. Previously Vty used-    stdInput for input and the follow code for output:-      - hDuplicate stdout >>= handleToFd >>= (`hSetBuffering`-        NoBuffering)-      - the difference was required by Vty.Inline. Now, Vty.Inline uses-        the Config structure options to acheive the same effect.-  - removed: derivedVtime, derivedVmin, inputForCurrentTerminal,-    inputForNameAndIO, outputForCurrentTerminal, outputForNameAndIO-  - added: inputForConfig, outputForConfig-  - updates to vty-rogue from jtdaugherty. Thanks!-  - the oldest version of GHC tested to support vty is 7.6.2.-  - the oldest version of GHC that vty compiles under is 7.4.2+----- +* Config structure now specifies file descriptor to use. The default+  is stdInput and stdOutput file descriptors. Previously Vty used+  stdInput for input and the follow code for output:+    * hDuplicate stdout >>= handleToFd >>= (`hSetBuffering`+      NoBuffering)+    * the difference was required by Vty.Inline. Now, Vty.Inline uses+      the Config structure options to acheive the same effect.+* removed: derivedVtime, derivedVmin, inputForCurrentTerminal,+  inputForNameAndIO, outputForCurrentTerminal, outputForNameAndIO+* added: inputForConfig, outputForConfig+* updates to vty-rogue from jtdaugherty. Thanks!+* the oldest version of GHC tested to support vty is 7.6.2.+* the oldest version of GHC that vty compiles under is 7.4.2+ 5.1.4-  - merged https://github.com/coreyoconnor/vty/pull/51 thanks trofi!+----- +* merged https://github.com/coreyoconnor/vty/pull/51 thanks trofi!+ 5.1.1-  - merged https://github.com/coreyoconnor/vty/pull/48 thanks sjmielke!-  - jtdaugherty resolved a number of compiler warnings. Thanks!+----- +* merged https://github.com/coreyoconnor/vty/pull/48 thanks sjmielke!+* jtdaugherty resolved a number of compiler warnings. Thanks!+ 5.1.0-  - vmin and vtime can be specified however the application requires.-    See Graphics.Vty.Config.-  - fixed the processing of input when vmin is set > 1.+----- +* vmin and vtime can be specified however the application requires.+  See Graphics.Vty.Config.+* fixed the processing of input when vmin is set > 1.+ 5.0.0-  - The naming convention now matches:-    - http://www.haskell.org/haskellwiki/Programming_guidelines#Naming_Conventions-  - all projects using vty for input must be compiled with -threaded.-    Please notify vty author if this is not acceptable.-  - mkVtyEscDelay has been removed. Use "mkVty def". Which initialized-    vty with the default configuration.-  - input handling changes-    - KASCII is now KChar-    - KPN5 is now KCenter-    - tests exist.-    - Applications can add to the input tables by setting inputMap of-      the Config. See Graphics.Vty.Config-    - Users can define input table extensions that will apply to all vty-      applications. See Graphics.Vty.Config-    - terminal timing is now handled by selecting an appropriate VTIME.-      Previously this was implemented within Vty itself. This reduced-      complexity in vty but provides a different meta key behavior and-      implies a requirement on -threaded.-    - The time vty will wait to verify an ESC byte means a single ESC-      key is the singleEscPeriod of the Input Config structure.-  - removed the typeclass based terminal and display context interface-    in favor of a data structure of properties interface.-  - renamed the Terminal interface to Output-  - The default picture for an image now uses the "clear" background.-    This background fills background spans with spaces or just ends the-    line.-    - Previously the background defaulted to the space character. This-      causes issues copying text from a text editor. The text would end-      up with extra spaces at the end of the line.-  - Layer support-    - Each layer is an image.-    - The layers for a picture are a list of images.-    - The first image is the top-most layer. The images are ordered from-      top to bottom.-    - The transparent areas for a layer are the backgroundFill areas.-      backgroundFill is added to pad images when images of different-      sizes are joined.-    - If the background is clear there is no background layer.-    - If there is a background character then the bottom layer is the-      background layer.-    - emptyPicture is a Picture with no layers and no cursor-    - addToTop and addToBottom add a layer to the top and bottom of the-      given Picture.-  - compatibility improvements:-    - terminfo based terminals with no cursor support are silently-      accepted. The cursor visibility changes in the Picture will have-      no effect.-    - alternate (setf/setb) color maps supported. Though colors beyond-      the first 8 are just a guess.-    - added "rgbColor" for easy support of RGB specified colors.-    - Both applications and users can add to the mapping used to-      translate from input bytes to events.-  - Additional information about input and output process can be-    appended to a debug log-    - Set environment variable VTY_DEBUG_LOG to path of debug log-    - Or use "debugLog <path>" config directive-    - Or set 'debugLog' property of the Config provided to mkVty.-  - examples moved to vty-examples package. See test directory for cabal-    file.-    - vty-interactive-terminal-test-      - interactive test. Useful for building a bug report for vty's-        author.-      - test/interactive_terminal_test.hs-    - vty-event-echo-      - view a input event log for vty. Example of interacting with-        user.-      - test/EventEcho.hs-    - vty-rogue-      - The start of a rogue-like game. Example of layers and image-        build operations.-      - test/Rogue.hs-    - vty-benchmark-      - benchmarks vty. A series of tests that push random pictures to-        the terminal. The random pictures are generated using-        QuickCheck. The same generators used in the automated tests.-      - test/benchmark.hs+----- +* The naming convention now matches:+  * http://www.haskell.org/haskellwiki/Programming_guidelines#Naming_Conventions+* all projects using vty for input must be compiled with -threaded.+  Please notify vty author if this is not acceptable.+* mkVtyEscDelay has been removed. Use "mkVty def". Which initialized+  vty with the default configuration.+* input handling changes+  * KASCII is now KChar+  * KPN5 is now KCenter+  * tests exist.+  * Applications can add to the input tables by setting inputMap of+    the Config. See Graphics.Vty.Config+  * Users can define input table extensions that will apply to all vty+    applications. See Graphics.Vty.Config+  * terminal timing is now handled by selecting an appropriate VTIME.+    Previously this was implemented within Vty itself. This reduced+    complexity in vty but provides a different meta key behavior and+    implies a requirement on -threaded.+  * The time vty will wait to verify an ESC byte means a single ESC+    key is the singleEscPeriod of the Input Config structure.+* removed the typeclass based terminal and display context interface+  in favor of a data structure of properties interface.+* renamed the Terminal interface to Output+* The default picture for an image now uses the "clear" background.+  This background fills background spans with spaces or just ends the+  line.+  * Previously the background defaulted to the space character. This+    causes issues copying text from a text editor. The text would end+    up with extra spaces at the end of the line.+* Layer support+  * Each layer is an image.+  * The layers for a picture are a list of images.+  * The first image is the top-most layer. The images are ordered from+    top to bottom.+  * The transparent areas for a layer are the backgroundFill areas.+    backgroundFill is added to pad images when images of different+    sizes are joined.+  * If the background is clear there is no background layer.+  * If there is a background character then the bottom layer is the+    background layer.+  * emptyPicture is a Picture with no layers and no cursor+  * addToTop and addToBottom add a layer to the top and bottom of the+    given Picture.+* compatibility improvements:+  * terminfo based terminals with no cursor support are silently+    accepted. The cursor visibility changes in the Picture will have+    no effect.+  * alternate (setf/setb) color maps supported. Though colors beyond+    the first 8 are just a guess.+  * added "rgbColor" for easy support of RGB specified colors.+  * Both applications and users can add to the mapping used to+    translate from input bytes to events.+* Additional information about input and output process can be+  appended to a debug log+  * Set environment variable VTY_DEBUG_LOG to path of debug log+  * Or use "debugLog <path>" config directive+  * Or set 'debugLog' property of the Config provided to mkVty.+* examples moved to vty-examples package. See test directory for cabal+  file.+  * vty-interactive-terminal-test+    * interactive test. Useful for building a bug report for vty's+      author.+    * test/interactive_terminal_test.hs+  * vty-event-echo+    * view a input event log for vty. Example of interacting with+      user.+    * test/EventEcho.hs+  * vty-rogue+    * The start of a rogue-like game. Example of layers and image+      build operations.+    * test/Rogue.hs+  * vty-benchmark+    * benchmarks vty. A series of tests that push random pictures to+      the terminal. The random pictures are generated using+      QuickCheck. The same generators used in the automated tests.+    * test/benchmark.hs+ 4.7.0.0-  - API changes:-    - Added Graphics.Vty.Image.crop: Ensure an image is no larger-      than the specified size.-    - Added Graphics.Vty.Image.pad: Ensure an image is no smaller-      than the specified size.-    - Added Graphics.Vty.Image.translate: Offset an image.-  - Thanks Ben Boeckel <MathStuf@gmail.com> for these features.+------- -4.3.0.0+API changes:+* Added Graphics.Vty.Image.crop: Ensure an image is no larger+  than the specified size.+* Added Graphics.Vty.Image.pad: Ensure an image is no smaller+  than the specified size.+* Added Graphics.Vty.Image.translate: Offset an image.+* Thanks Ben Boeckel <MathStuf@gmail.com> for these features.  4.2.1.0-  - API changes:-    - Attr record accessor fore_color changed to attr_fore_color-    - Attr record accessor back_color changed to attr_back_color-    - Attr record accessor style changed to attr_style-    - Added an "inline" display attribute changing DSL:-      - put_attr_change applies a display attribute change-        immediately to a terminal-      - For instance, can be used to change the display attrbiutes-        of text output via putStrLn and putStr. EX:-        "put_attr_change $ back_color red" will set the background-        color to red.-      - Changes do not apply to a Picture output via output_picture.-      - See Graphics.Vty.Inline-    - Moved all IO actions into any monad an instance of MonadIO+------- +API changes:+* Attr record accessor fore_color changed to attr_fore_color+* Attr record accessor back_color changed to attr_back_color+* Attr record accessor style changed to attr_style+* Added an "inline" display attribute changing DSL:+  * put_attr_change applies a display attribute change+    immediately to a terminal+  * For instance, can be used to change the display attrbiutes+    of text output via putStrLn and putStr. EX:+    "put_attr_change $ back_color red" will set the background+    color to red.+  * Changes do not apply to a Picture output via output_picture.+  * See Graphics.Vty.Inline+* Moved all IO actions into any monad an instance of MonadIO+ 4.0.0.1-  - binding for mk_wcswidth was incorrect. Most platforms just-    magically worked due to coincidence.+------- +* binding for mk_wcswidth was incorrect. Most platforms just+  magically worked due to coincidence.+ 4.0.0-  - API changes:-    - "getSize" has been removed. Use "terminal vty >>= display_bounds"-      where "vty" is an instance of the Vty data structure.-    - added a "terminal" field to the Vty data structure. Accesses the-      TerminalHandle associated with the Vty instance.-    - Graphics.Vty.Types has undergone a number of changes. Summary:-      - Partitioned into Graphics.Vty.Attributes for display attributes.-        Graphics.Vty.Image for image combinators. Graphics.Vty.Picture-        for final picture construction.-    - Graphics.Vty.Attributes:-      - "setFG" and "setBG" are now "with_fore_color" and-        "with_back_color"-      - All other "set.." equations similarly replaced.-      - "attr" is now "def_attr", short for "default display attributes"-        Also added a "current_attr" for "currently applied display-        attributes"-    - Graphics.Vty.Image:-      - "horzcat" is now "horiz_cat"-      - "vertcat" is now "vert_cat"-      - "renderBS" is now "utf8_bytestring"-      - "renderChar" is now "char"-      - "renderFill" is now "char_fill"-      - added a "utf8_string" and "string" (AKA "iso_10464_string") for-        UTF-8 encoded Strings and ISO-10464 encoded Strings. String-        literals in GHC have an ISO-10464 runtime representation.-    - Graphics.Vty.Picture:-      - exports Graphics.Vty.Image-      - "pic" is now "pic_for_image"-      - added API for setting background fill pattern.-  - Completely rewritten output backend.-    - Efficient, scanline style output span generator. Has not been-      fully optimized, but good enough.-    - The details required to display the desired picture on a-      terminal are well encapsulated.-    - Terminfo based display terminal implementation. With specialized-      derivitives for xterm, Terminal.app, and iTerm.app.-        - Attempts to robustly handle even terminals that don't-          support all display attributes.-        - I've tested the following terminals with success: iTerm.app,-          Terminal.app, xterm, rxvt, mlterm, Eterm, gnome-terminal,-          konsole, screen, linux vty. Hopefully you will be as-          successfull.-    - Improved unicode support. Double wide characters will display as-      expected.-  - 256 color support. See Graphics.Vty.Attributes.Color240. The actual-    output color is adjusted according to the number of colors the-    terminal supports.-  - The Graphics.Vty.Image combinators no longer require matching-    dimensions to arguments. Unspecified areas are filled in with a-    user-customizable background pattern. See Graphics.Vty.Picture.-  - output images are always cropped to display size.-  - Significant code coverage by QuickCheck tests. An interactive test-    for those final properties that couldn't be automatically verified.+----- +API changes:+* "getSize" has been removed. Use "terminal vty >>= display_bounds"+  where "vty" is an instance of the Vty data structure.+* added a "terminal" field to the Vty data structure. Accesses the+  TerminalHandle associated with the Vty instance.+* Graphics.Vty.Types has undergone a number of changes. Summary:+  * Partitioned into Graphics.Vty.Attributes for display attributes.+    Graphics.Vty.Image for image combinators. Graphics.Vty.Picture+    for final picture construction.+* Graphics.Vty.Attributes:+  * "setFG" and "setBG" are now "with_fore_color" and+    "with_back_color"+  * All other "set.." equations similarly replaced.+  * "attr" is now "def_attr", short for "default display attributes"+    Also added a "current_attr" for "currently applied display+    attributes"+* Graphics.Vty.Image:+  * "horzcat" is now "horiz_cat"+  * "vertcat" is now "vert_cat"+  * "renderBS" is now "utf8_bytestring"+  * "renderChar" is now "char"+  * "renderFill" is now "char_fill"+  * added a "utf8_string" and "string" (AKA "iso_10464_string") for+    UTF-8 encoded Strings and ISO-10464 encoded Strings. String+    literals in GHC have an ISO-10464 runtime representation.+* Graphics.Vty.Picture:+  * exports Graphics.Vty.Image+  * "pic" is now "pic_for_image"+  * added API for setting background fill pattern.+* Completely rewritten output backend.+  * Efficient, scanline style output span generator. Has not been+    fully optimized, but good enough.+  * The details required to display the desired picture on a+    terminal are well encapsulated.+  * Terminfo based display terminal implementation. With specialized+    derivitives for xterm, Terminal.app, and iTerm.app.+      * Attempts to robustly handle even terminals that don't+        support all display attributes.+      * I've tested the following terminals with success: iTerm.app,+        Terminal.app, xterm, rxvt, mlterm, Eterm, gnome-terminal,+        konsole, screen, linux vty. Hopefully you will be as+        successfull.+  * Improved unicode support. Double wide characters will display as+    expected.+* 256 color support. See Graphics.Vty.Attributes.Color240. The actual+  output color is adjusted according to the number of colors the+  terminal supports.+* The Graphics.Vty.Image combinators no longer require matching+  dimensions to arguments. Unspecified areas are filled in with a+  user-customizable background pattern. See Graphics.Vty.Picture.+* output images are always cropped to display size.+* Significant code coverage by QuickCheck tests. An interactive test+  for those final properties that couldn't be automatically verified.+ Issues resolved:-  - "gnome terminal displays non-basic attributes as strikethrough"-    - http://trac.haskell.org/vty/ticket/14-  - "Multi-byte characters are not displayed correctly on update"-    - http://trac.haskell.org/vty/ticket/10-  - "Redraw does not handle rendering a line that extends beyond screen-    width characters"-    - http://trac.haskell.org/vty/ticket/13-  - "The <|> and <-> combinators should be more forgiving of mismatched-    dimensions"-    - http://trac.haskell.org/vty/ticket/9-  - "256-color support"-    - http://trac.haskell.org/vty/ticket/19+* "gnome terminal displays non-basic attributes as strikethrough"+  * http://trac.haskell.org/vty/ticket/14+* "Multi-byte characters are not displayed correctly on update"+  * http://trac.haskell.org/vty/ticket/10+* "Redraw does not handle rendering a line that extends beyond screen+  width characters"+  * http://trac.haskell.org/vty/ticket/13+* "The <|> and <-> combinators should be more forgiving of mismatched+  dimensions"+  * http://trac.haskell.org/vty/ticket/9+* "256-color support"+  * http://trac.haskell.org/vty/ticket/19
README.md view
@@ -18,24 +18,21 @@  # Features -* Supports a large number of terminals. vt100, ansi, hurd, linux,-  screen, etc., or anything with a sufficient terminfo entry.--* Automatic handling of window resizes.+* Supports a large number of terminals, i.e., vt100, ansi, hurd, linux,+  `screen`, etc., or anything with a sufficient terminfo entry. -* Unicode output on terminals with UTF-8 support.+* Automatically handles window resizes. -* Handles multi-column glyphs. (Requires user to properly configure-  terminal.)+* Supports Unicode output on terminals with UTF-8 support. -* Efficient output. Output buffering and terminal state changes are-  minimized.+* Provides an efficient output algorithm. Output buffering and terminal+  state changes are minimized.  * Minimizes repaint area, which virtually eliminates the flicker   problems that plague ncurses programs. -* A pure, compositional interface for efficiently constructing display-  images.+* Provides a pure, compositional interface for efficiently constructing+  display images.  * Automatically decodes keyboard keys into (key,[modifier]) tuples. @@ -44,7 +41,7 @@ * Supports a keypress timeout after for lone ESC. The timeout is   customizable. -* The interface is designed for easy extension.+* Provides extensible input and output interfaces.  * Supports ANSI graphics modes (SGR as defined in `console_codes(4)`)   with a type-safe interface and graceful fallback for terminals@@ -60,6 +57,12 @@ * Supports bracketed paste mode as described at   http://cirw.in/blog/bracketed-paste +* Supports multi-column Unicode characters such as emoji characters. In+  cases where Vty and your terminal emulator disagree on character+  widths, Vty provides a tool `vty-build-width-table` and library+  functionality to build a width table that will work for your terminal+  and load it on application startup.+ # Development Notes  Vty uses threads internally, so programs made with Vty need to be@@ -69,64 +72,111 @@  ## Posix Terminals -Uses terminfo to determine terminal protocol. With some special rules to-handle some omissions from terminfo.+For the most part, Vty uses `terminfo` to determine terminal protocol+with some special rules to handle some omissions from `terminfo`.  ## Windows -Unsupported (but contributions and testing here are very welcome!)+Windows is not supported. -# Contributing+# Multi-Column Character Support -If you decide to contribute, that's great! Here are some guidelines you-should consider to make submitting patches easier for all concerned:+Vty supports rendering of multi-column characters such as two-column+Asian characters and Emoji characters. This section details how to+take advantage of this feature, since its behavior will depend on the+terminal emulator in use. - - If you want to take on big things, talk to me first; let's have a-   design/vision discussion before you start coding. Create a GitHub-   issue and we can use that as the place to hash things out.- - If you make changes, make them consistent with the syntactic-   conventions already used in the codebase.- - Please provide Haddock documentation for any changes you make.+Terminal emulators support Unicode to varying degrees, and each terminal+emulator relies on a table of column widths for each supported Unicode+character. Vty also needs to rely on such a table to compute the width+of Vty images to do image layout. Since those tables can disagree if+Vty and the terminal emulator support different versions of Unicode,+and since different terminal emulators will support different versions+of Unicode, it's likely that for some wide characters, Vty applications+will exhibit rendering problems. Those rendering problems arise from Vty+and the terminal emulator coming to different conclusions about how wide+some characters are. -# Development Notes+To address this, Vty supports loading custom character width tables+that are based on the terminal's behavior in order to eliminate these+disagreements. By default, though, Vty will use its built-in Unicode+character width table. Since the built-in table is likely to eventually+disagree with your terminal, Vty provides an API and a command-line tool+to generate and install custom tables. -## Under NixOS+Custom Unicode width tables based on your terminal emulator can be+built by running Vty's built-in tool, `vty-build-width-table`. The tool+works by querying the current terminal emulator to obtain its width+measurements for the entire supported Unicode range. The+results are then saved to a disk file. These custom tables+can also be generated programmatically by using the API in+`Graphics.Vty.UnicodeWidthTable.Query`. -### Using cabal+Saved width tables can then be loaded in one of two ways: -After installing `ncurses` to the user environment:+* Via the library API in `Graphics.Vty.UnicodeWidthTable.IO`+* By adding a `widthMap` directive to your Vty configuration file and+  then invoking `mkVty` to initialize Vty -~~~-LIBRARY_PATH=$HOME/.nix-profile/lib/ cabal configure --enable-tests --extra-lib-dirs=$HOME/.nix-profile/lib-LIBRARY_PATH=$HOME/.nix-profile/lib/ cabal build-LIBRARY_PATH=$HOME/.nix-profile/lib/ cabal test-~~~+The Vty configuration file supports the `widthMap` directive to allow+users to specify which custom width table should be loaded for a given+terminal type. This is done by specifying, e.g., -## Code Coverage+```+widthMap "xterm" "/path/to/map.dat"+``` -As of last testing, profiling causes issues with coverage when enabled.-To evaluate coverage, configure as follows:+where the first argument is the value that `TERM` must have in order for+the table to be loaded, and the second argument is the path to the table+file itself as generated by the two alternatives listed above. If the+Vty configuration file contains multiple matching `widthMap` directives+for the current value of `TERM`, the last one listed in the file is+used. -~~~-rm -rf dist ; cabal configure --enable-tests --enable-library-coverage \-  --disable-library-profiling \-  --disable-executable-profiling-~~~+The tables declared in the configuration file are only ever+automatically loaded when applications set up Vty by calling+`Graphics.Vty.mkVty`. -## Profiling+Before a custom table has been loaded, calls to the library's character+width functions (e.g. `wcwidth`) will use the default built-in table.+Once a custom table has been loaded, the functions will use the new+custom table. Only one custom table load can be performed in a Vty+program. Once a custom table has been loaded, it cannot be replaced or+removed. -~~~-rm -rf dist ; cabal configure --enable-tests --disable-library-coverage \-  --enable-library-profiling \-  --enable-executable-profiling-~~~+Without using a custom width table, users of Vty-based applications+are likely to eventually experience rendering problems with with wide+characters. We recommend that developers of Vty-based applications either: +* Provide the `vty-build-width-table` tool and documentation for running+  it and updating the Vty configuration, or+* Have the application invoke the Vty library's table-building+  functionality and load the table at startup without using the Vty+  configuration.++The best option will depend on a number of factors: the user audience,+the amount of risk posed by wide character rendering, the terminal+emulators in use, etc.++# Contributing++If you decide to contribute, that's great! Here are some guidelines you+should consider to make submitting patches easier for all concerned:++ - If you want to take on big things, talk to me first; let's have a+   design/vision discussion before you start coding. Create a GitHub+   issue and we can use that as the place to hash things out.+ - If you make changes, make them consistent with the syntactic+   conventions already used in the codebase.+ - Please provide Haddock documentation for any changes you make.+ # Known Issues -* Terminals have numerous quirks and bugs. Vty picks what works best for-  the author in ambiguous or buggy situations.+* Terminals have numerous quirks and bugs, so mileage may vary. Please+  report issues as you encounter them and provide details on your+  terminal emulator, operating system, etc. -* Signal handling of STOP, TERM and INT are nonexistent.+* STOP, TERM and INT signals are not handled.  * The character encoding of the terminal is assumed to be UTF-8 if   unicode is used.@@ -136,10 +186,10 @@   (shifted F10 etc). See `Config` for customizing vty's behavior for a   particular terminal. -* Uses the `TIOCGWINSZ` ioctl to find the current window size, which+* Vty uses the `TIOCGWINSZ` ioctl to find the current window size, which   appears to be limited to Linux and BSD. -# Sources+# Further Reading  Good sources of documentation for terminal programming are: 
cbits/mk_wcwidth.c view
@@ -60,7 +60,29 @@  */  #include <HsFFI.h>+#include <stdlib.h>+#include <string.h> +// The maximum size for a custom character width table is the total+// number of possible characters as dictated by the compiler.+#define MAX_CUSTOM_TABLE_SIZE (HS_CHAR_MAX + 1)++// The pointer to the start of the custom character width table, if+// any. If this is NULL or this is set but the ready flag is false, the+// built-in tree search logic is used.+static uint8_t* custom_table = NULL;++// The size of the custom table, in entries. This should only be set+// if custom_table is not NULL. Its value should be the size of the+// custom_table array.+static uint32_t custom_table_size = 0;++// A flag indicating whether the custom table is ready for+// use. This should only be set once the table has been+// allocated with vty_init_custom_table and initialized with+// vty_set_custom_table_range.+static uint8_t custom_table_ready = 0;+ struct interval {   int first;   int last;@@ -119,7 +141,7 @@  * in ISO 10646.  */ -HsInt vty_mk_wcwidth(HsChar ucs)+static HsInt builtin_wcwidth(HsChar ucs) {   /* sorted list of non-overlapping intervals of non-spacing characters */   /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */@@ -182,7 +204,7 @@    /* binary search in table of non-spacing characters */   if (vty_bisearch(ucs, combining,-	       sizeof(combining) / sizeof(struct interval) - 1))+              sizeof(combining) / sizeof(struct interval) - 1))     return 0;    /* if we arrive here, ucs is not a combining or C0/C1 control character */@@ -201,4 +223,116 @@       (ucs >= 0xffe0 && ucs <= 0xffe6) ||       (ucs >= 0x20000 && ucs <= 0x2fffd) ||       (ucs >= 0x30000 && ucs <= 0x3fffd)));+}++// Return the width, in terminal cells, of the specified character.+//+// If the global custom width table is present, that table will be+// consulted for the character's width. If the character is not in+// the table, zero will be returned. If the custom width table is not+// present, the built-in width table will be used.+HsInt vty_mk_wcwidth(HsChar ch)+{+    if (custom_table_ready) {+        if ((ch >= 0) && (ch < custom_table_size)) {+            return custom_table[ch];+        } else {+            return -1;+        }+    } else {+        return builtin_wcwidth(ch);+    }+}++// Initialize a custom character width table.+//+// This allocates a new character width table of the specified size+// (in characters). If a custom table has already been allocated, this+// returns 1. Otherwise it allocates a new table, initializes all of its+// entries to 1, and returns zero.+//+// Note that this does *not* mark the table as ready for use. Until the+// table is marked ready, it will not be used by vty_mk_wcwidth. To mark+// the table as ready, call vty_activate_custom_table() after the table+// has been set up with calls to vty_set_custom_table_range.+int vty_init_custom_table(int size)+{+    if (custom_table == NULL) {+        if (size > 0 && size <= MAX_CUSTOM_TABLE_SIZE) {+            custom_table_ready = 0;+            custom_table = malloc(size);+            memset(custom_table, 1, size);+            custom_table_size = size;+            return 0;+        } else {+            return 1;+        }+    } else {+        return 1;+    }+}++// Set the specified character range in the custom width table to the+// specified width.+//+// This function sets 'width' as the character width for all entries+// in the custom character table starting at the 'start' entry and+// including all entries up to and including 'start + size - 1'.+//+// If this succeeds, it returns zero. If it fails, it returns 1. It+// fails if the table is not allocated, marked as ready (i.e. it is in+// use and has already been populated), or if the start or size values+// are not in bounds for the table.+int vty_set_custom_table_range(uint32_t start, uint32_t size, uint8_t width)+{+    if ((custom_table == NULL) ||+        (size >= custom_table_size) ||+        (start >= custom_table_size) ||+        ((start + 1) >= (custom_table_size - size)) ||+        custom_table_ready) {+        return 1;+    } else {+        memset(custom_table + start, width, size);+        return 0;+    }+}++// Mark the allocated custom character width table as ready for use.+//+// After this call, further calls to vty_set_custom_table_range will+// fail.+//+// This function returns 0 if it succeeds. If it fails, it returns 1.+// It fails if the custom table is already ready or if it has not been+// allocated.+int vty_activate_custom_table()+{+    if (custom_table_ready || (custom_table == NULL)) {+        return 1;+    } else {+        custom_table_ready = 1;+        return 0;+    }+}++// Returns whether a custom character width table has been marked ready.+int vty_custom_table_ready()+{+    return custom_table_ready;+}++// Deallocate the custom width table.+//+// This does nothing if there is no allocated custom width table, or if+// there is one but it is in use (marked ready). This is only useful if+// an initial allocation succeeds, but range population fails, after+// which point the application may want to deallocate the table to avoid+// leaving it in an intermediate state.+void vty_deallocate_custom_table()+{+    if ((custom_table != NULL) && (!custom_table_ready)) {+        free(custom_table);+        custom_table = NULL;+        custom_table_size = 0;+    } }
src/Graphics/Text/Width.hs view
@@ -1,6 +1,15 @@ -- Copyright 2009 Corey O'Connor {-# OPTIONS_GHC -D_XOPEN_SOURCE #-} {-# LANGUAGE ForeignFunctionInterface #-}+-- | This module provides functions to measure the terminal column width+-- of characters and strings.+--+-- The functions provided in this module all ultimately make calls to+-- the C implementation in @cbits/mk_wcwidth.c@. That code manages some+-- global state that carries a table of Unicode character widths. For+-- more details, see 'Graphics.Vty.UnicodeWidthTable.Install', the C+-- code, and the "Multi-Column Character Support" section of the project+-- @README@. module Graphics.Text.Width   ( wcwidth   , wcswidth@@ -33,14 +42,6 @@ "wcswidth/unpack" forall x. wcswidth (T.unpack x) = wctwidth x "wcswidth/lazy-unpack" forall x. wcswidth (TL.unpack x) = wctlwidth x   #-}---- XXX: Characters with unknown widths occupy 1 column?------ Not sure if this is actually correct. I presume there is a--- replacement character that is output by the terminal instead of the--- character and this replacement character is 1 column wide. If this is--- not true for all terminals then a per-terminal replacement character--- width needs to be implemented.  -- | Returns the display width of a character. Assumes all characters -- with unknown widths are 0 width.
src/Graphics/Vty.hs view
@@ -13,7 +13,7 @@ --   values in the "Graphics.Vty.Attributes" module. -- -- Vty uses threads internally, so programs made with Vty need to be--- compiled with the threaded runtime using the GHC `-threaded` option.+-- compiled with the threaded runtime using the GHC @-threaded@ option. -- -- @ --  import "Graphics.Vty"@@ -51,7 +51,10 @@ import Graphics.Vty.Picture import Graphics.Vty.Image import Graphics.Vty.Attributes+import Graphics.Vty.UnicodeWidthTable.IO+import Graphics.Vty.UnicodeWidthTable.Install +import qualified Control.Exception as E import Control.Monad (when) import Control.Concurrent.STM @@ -65,7 +68,7 @@ -- -- The use of Vty typically follows this process: -----    1. Initialize vty+--    1. Initialize vty with 'mkVty' (this takes control of the terminal). -- --    2. Use 'update' to display a picture. --@@ -73,31 +76,33 @@ -- --    4. Depending on the event, go to 2 or 5. -----    5. Shutdown vty.+--    5. Shutdown vty and restore the terminal state with 'shutdown'. At+--    this point the 'Vty' handle cannot be used again. -- -- Operations on Vty handles are not thread-safe.-data Vty = Vty-    { -- | Outputs the given 'Picture'.-      update :: Picture -> IO ()-      -- | Return the next 'Event' or block until one becomes available.-    , nextEvent :: IO Event-      -- | Non-blocking version of 'nextEvent'.-    , nextEventNonblocking :: IO (Maybe Event)-      -- | The input interface. See 'Input'.-    , inputIface :: Input-      -- | The output interface. See 'Output'.-    , outputIface :: Output-      -- | Refresh the display. If other programs output to the terminal-      -- and mess up the display then the application might want to-      -- force a refresh using this function.-    , refresh :: IO ()-      -- | Clean up after vty. A call to this function is necessary to-      -- cleanly restore the terminal state before application exit. The-      -- above methods will throw an exception if executed after this is-      -- executed. Idempotent.-    , shutdown :: IO ()-    , isShutdown :: IO Bool-    }+data Vty =+    Vty { update :: Picture -> IO ()+        -- ^ Outputs the given 'Picture'.+        , nextEvent :: IO Event+        -- ^ Return the next 'Event' or block until one becomes+        -- available.+        , nextEventNonblocking :: IO (Maybe Event)+        -- ^ Non-blocking version of 'nextEvent'.+        , inputIface :: Input+        -- ^ The input interface. See 'Input'.+        , outputIface :: Output+        -- ^ The output interface. See 'Output'.+        , refresh :: IO ()+        -- ^ Refresh the display. If other programs output to the+        -- terminal and mess up the display then the application might+        -- want to force a refresh using this function.+        , shutdown :: IO ()+        -- ^ Clean up after vty. A call to this function is necessary to+        -- cleanly restore the terminal state before application exit.+        -- The above methods will throw an exception if executed after+        -- this is executed. Idempotent.+        , isShutdown :: IO Bool+        }  -- | Create a Vty handle. At most one handle should be created at a time -- for a given terminal device.@@ -110,12 +115,51 @@ mkVty :: Config -> IO Vty mkVty appConfig = do     config <- (<> appConfig) <$> userConfig-    input  <- inputForConfig config-    out    <- outputForConfig config-    intMkVty input out -intMkVty :: Input -> Output -> IO Vty-intMkVty input out = do+    when (allowCustomUnicodeWidthTables config /= Just False) $+        installCustomWidthTable config++    input <- inputForConfig config+    out <- outputForConfig config+    internalMkVty input out++installCustomWidthTable :: Config -> IO ()+installCustomWidthTable c = do+    let doLog s = case debugLog c of+            Nothing -> return ()+            Just path -> appendFile path $ "installWidthTable: " <> s <> "\n"++    customInstalled <- isCustomTableReady+    when (not customInstalled) $ do+        mTerm <- currentTerminalName+        case mTerm of+            Nothing ->+                doLog "No current terminal name available"+            Just currentTerm ->+                case lookup currentTerm (termWidthMaps c) of+                    Nothing ->+                        doLog "Current terminal not found in custom character width mapping list"+                    Just path -> do+                        tableResult <- E.try $ readUnicodeWidthTable path+                        case tableResult of+                            Left (e::E.SomeException) ->+                                doLog $ "Error reading custom character width table " <>+                                        "at " <> show path <> ": " <> show e+                            Right (Left msg) ->+                                doLog $ "Error reading custom character width table " <>+                                        "at " <> show path <> ": " <> msg+                            Right (Right table) -> do+                                installResult <- E.try $ installUnicodeWidthTable table+                                case installResult of+                                    Left (e::E.SomeException) ->+                                        doLog $ "Error installing unicode table (" <>+                                                show path <> ": " <> show e+                                    Right () ->+                                        doLog $ "Successfully installed Unicode width table " <>+                                                " from " <> show path++internalMkVty :: Input -> Output -> IO Vty+internalMkVty input out = do     reserveDisplay out      shutdownVar <- atomically $ newTVar False
src/Graphics/Vty/Config.hs view
@@ -55,6 +55,32 @@ -- table to the log in the above format. A workflow for using this is -- to set @VTY_DEBUG_LOG@. Run the application. Check the debug log for -- incorrect mappings. Add corrected mappings to @$HOME/.vty/config@.+--+-- = Unicode Character Width Maps+--+-- == @widthMap@+--+-- Format:+--+-- @+--  \"widthMap\" string string+-- @+--+-- E.g.,+--+-- @+--   widthMap \"xterm\" \"\/home\/user\/.vty\/xterm\_map.dat\"+-- @+--+-- This directive specifies the path to a Unicode character width+-- map (the second argument) that should be loaded and used when+-- the value of TERM matches the first argument. Unicode character+-- width maps can be produced either by running the provided binary+-- @vty-build-width-table@ or by calling the library routine+-- 'Graphics.Vty.UnicodeWidthTable.Query.buildUnicodeWidthTable'. The+-- 'Graphics.Vty.mkVty' function will use these configuration settings+-- to attempt to load and install the specified width map. See the+-- documentation for 'Graphics.Vty.mkVty' for details. module Graphics.Vty.Config   ( InputMap   , Config(..)@@ -66,6 +92,16 @@   , parseConfigFile   , defaultConfig   , getTtyEraseChar+  , currentTerminalName++  , vtyConfigPath+  , widthTableFilename+  , vtyDataDirectory+  , terminalWidthTablePath+  , vtyConfigFileEnvName++  , ConfigUpdateResult(..)+  , addConfigWidthMap   ) where @@ -89,8 +125,11 @@  import GHC.Generics -import System.Directory (getAppUserDataDirectory)+import System.Directory ( getAppUserDataDirectory, doesFileExist+                        , createDirectoryIfMissing+                        ) import System.Environment (lookupEnv)+import System.FilePath ((</>), takeDirectory) import System.Posix.IO (stdInput, stdOutput) import System.Posix.Types (Fd(..)) import Foreign.C.Types (CInt(..), CChar(..))@@ -100,12 +139,13 @@ import qualified Text.Parsec.Token as P  -- | Type of errors that can be thrown when configuring VTY-data VtyConfigurationError-  = VtyMissingTermEnvVar -- ^ TERM environment variable not set-  deriving (Show, Eq, Typeable)+data VtyConfigurationError =+    VtyMissingTermEnvVar+    -- ^ TERM environment variable not set+    deriving (Show, Eq, Typeable)  instance Exception VtyConfigurationError where-  displayException VtyMissingTermEnvVar = "TERM environment variable not set"+    displayException VtyMissingTermEnvVar = "TERM environment variable not set"  -- | Mappings from input bytes to event in the order specified. Later -- entries take precedence over earlier in the case multiple entries@@ -113,77 +153,125 @@ type InputMap = [(Maybe String, String, Event)]  -- | A Vty configuration.-data Config = Config-    {-    -- | The default is 1 character.-      vmin  :: Maybe Int-    -- | The default is 100 milliseconds, 0.1 seconds.-    , vtime :: Maybe Int-    -- | The default is False.-    , mouseMode :: Maybe Bool-    -- | The default is False.-    , bracketedPasteMode :: Maybe Bool-    -- | Debug information is appended to this file if not Nothing.-    , debugLog           :: Maybe FilePath-    -- | The (input byte, output event) pairs extend the internal input-    -- table of VTY and the table from terminfo.-    ---    -- See "Graphics.Vty.Config" module documentation for documentation-    -- of the @map@ directive.-    , inputMap           :: InputMap-    -- | The input file descriptor to use. The default is-    -- 'System.Posix.IO.stdInput'-    , inputFd           :: Maybe Fd-    -- | The output file descriptor to use. The default is-    -- 'System.Posix.IO.stdOutput'-    , outputFd          :: Maybe Fd-    -- | The terminal name used to look up terminfo capabilities.-    -- The default is the value of the TERM environment variable.-    , termName           :: Maybe String-    } deriving (Show, Eq)+data Config =+    Config { vmin  :: Maybe Int+           -- ^ The default is 1 character.+           , vtime :: Maybe Int+           -- ^ The default is 100 milliseconds, 0.1 seconds.+           , mouseMode :: Maybe Bool+           -- ^ The default is False.+           , bracketedPasteMode :: Maybe Bool+           -- ^ The default is False.+           , debugLog :: Maybe FilePath+           -- ^ Debug information is appended to this file if not+           -- Nothing.+           , inputMap :: InputMap+           -- ^ The (input byte, output event) pairs extend the internal+           -- input table of VTY and the table from terminfo.+           --+           -- See "Graphics.Vty.Config" module documentation for+           -- documentation of the @map@ directive.+           , inputFd :: Maybe Fd+           -- ^ The input file descriptor to use. The default is+           -- 'System.Posix.IO.stdInput'+           , outputFd :: Maybe Fd+           -- ^ The output file descriptor to use. The default is+           -- 'System.Posix.IO.stdOutput'+           , termName :: Maybe String+           -- ^ The terminal name used to look up terminfo capabilities.+           -- The default is the value of the TERM environment variable.+           , termWidthMaps :: [(String, FilePath)]+           -- ^ Terminal width map files.+           , allowCustomUnicodeWidthTables :: Maybe Bool+           -- ^ Whether to permit custom Unicode width table loading by+           -- 'Graphics.Vty.mkVty'. @'Just' 'False'@ indicates that+           -- table loading should not be performed. Other values permit+           -- table loading.+           --+           -- If a table load is attempted and fails, information+           -- about the failure will be logged to the debug log if the+           -- configuration specifies one. If no custom table is loaded+           -- (or if a load fails), the built-in character width table+           -- will be used.+           }+           deriving (Show, Eq)  defaultConfig :: Config defaultConfig = mempty  instance Semigroup Config where-    c0 <> c1 = Config+    c0 <> c1 =         -- latter config takes priority for everything but inputMap-        { vmin          = vmin c1     <|> vmin c0-        , vtime         = vtime c1    <|> vtime c0-        , mouseMode     = mouseMode c1-        , bracketedPasteMode = bracketedPasteMode c1-        , debugLog      = debugLog c1 <|> debugLog c0-        , inputMap      = inputMap c0 <>  inputMap c1-        , inputFd      = inputFd c1 <|> inputFd c0-        , outputFd     = outputFd c1 <|> outputFd c0-        , termName      = termName c1 <|> termName c0-        }+        Config { vmin = vmin c1 <|> vmin c0+               , vtime = vtime c1 <|> vtime c0+               , mouseMode = mouseMode c1+               , bracketedPasteMode = bracketedPasteMode c1+               , debugLog = debugLog c1 <|> debugLog c0+               , inputMap = inputMap c0 <> inputMap c1+               , inputFd = inputFd c1 <|> inputFd c0+               , outputFd = outputFd c1 <|> outputFd c0+               , termName = termName c1 <|> termName c0+               , termWidthMaps = termWidthMaps c1 <|> termWidthMaps c0+               , allowCustomUnicodeWidthTables =+                   allowCustomUnicodeWidthTables c1 <|> allowCustomUnicodeWidthTables c0+               }  instance Monoid Config where-    mempty = Config-        { vmin         = Nothing-        , vtime        = Nothing-        , mouseMode    = Nothing-        , bracketedPasteMode = Nothing-        , debugLog     = mempty-        , inputMap     = mempty-        , inputFd     = Nothing-        , outputFd    = Nothing-        , termName     = Nothing-        }+    mempty =+        Config { vmin = Nothing+               , vtime = Nothing+               , mouseMode = Nothing+               , bracketedPasteMode = Nothing+               , debugLog = mempty+               , inputMap = mempty+               , inputFd = Nothing+               , outputFd = Nothing+               , termName = Nothing+               , termWidthMaps = []+               , allowCustomUnicodeWidthTables = Nothing+               } #if !(MIN_VERSION_base(4,11,0))     mappend = (<>) #endif --- | Load a configuration from @'getAppUserDataDirectory'/config@ and--- @$VTY_CONFIG_FILE@.+vtyDataDirectory :: IO FilePath+vtyDataDirectory = getAppUserDataDirectory "vty"++vtyConfigPath :: IO FilePath+vtyConfigPath = do+    dir <- vtyDataDirectory+    return $ dir </> "config"++vtyConfigFileEnvName :: String+vtyConfigFileEnvName = "VTY_CONFIG_FILE"++-- | Load a configuration from 'vtyConfigPath' and @$VTY_CONFIG_FILE@. userConfig :: IO Config userConfig = do-    configFile <- (mappend <$> getAppUserDataDirectory "vty" <*> pure "/config") >>= parseConfigFile-    overrideConfig <- maybe (return defaultConfig) parseConfigFile =<< lookupEnv "VTY_CONFIG_FILE"+    configFile <- vtyConfigPath >>= parseConfigFile+    overrideConfig <- maybe (return defaultConfig) parseConfigFile =<<+        lookupEnv vtyConfigFileEnvName     let base = configFile <> overrideConfig     mappend base <$> overrideEnvConfig +widthTableFilename :: String -> String+widthTableFilename term = "width_table_" <> term <> ".dat"++termVariable :: String+termVariable = "TERM"++currentTerminalName :: IO (Maybe String)+currentTerminalName = lookupEnv termVariable++terminalWidthTablePath :: IO (Maybe FilePath)+terminalWidthTablePath = do+    dataDir <- vtyDataDirectory+    result <- lookupEnv termVariable+    case result of+        Nothing -> return Nothing+        Just term -> do+            return $ Just $ dataDir </> widthTableFilename term+ overrideEnvConfig :: IO Config overrideEnvConfig = do     d <- lookupEnv "VTY_DEBUG_LOG"@@ -193,7 +281,7 @@ -- can raise 'VtyConfigurationError'. standardIOConfig :: IO Config standardIOConfig = do-    mb <- lookupEnv "TERM"+    mb <- lookupEnv termVariable     case mb of       Nothing -> throwIO VtyMissingTermEnvVar       Just t ->@@ -256,13 +344,20 @@     path       <- P.stringLiteral configLexer     return defaultConfig { debugLog = Just path } +widthMapDecl :: Parser Config+widthMapDecl = do+    "widthMap" <- P.identifier configLexer+    tName <- P.stringLiteral configLexer+    path <- P.stringLiteral configLexer+    return defaultConfig { termWidthMaps = [(tName, path)] }+ ignoreLine :: Parser () ignoreLine = void $ manyTill anyChar newline  parseConfig :: Parser Config parseConfig = liftM mconcat $ many $ do     P.whiteSpace configLexer-    let directives = [try mapDecl, try debugLogDecl]+    let directives = [try mapDecl, try debugLogDecl, try widthMapDecl]     choice directives <|> (ignoreLine >> return defaultConfig)  class    Parse a        where parseValue :: Parser a@@ -329,3 +424,56 @@     if c /= 0        then return $ Just $ toEnum $ fromEnum c        else return Nothing++data ConfigUpdateResult =+    ConfigurationCreated+    | ConfigurationModified+    | ConfigurationConflict String+    | ConfigurationRedundant+    deriving (Eq, Show)++-- | Add a @widthMap@ directive to the Vty configuration file at the+-- specified path.+--+-- If the configuration path refers to a configuration that already+-- contains the directive for the specified map and terminal type, the+-- configuration file will not be modified. If the file does not contain+-- the directive, it will be appended to the file.+--+-- If the configuration path does not exist, a new configuration file+-- will be created and any directories in the path will also be created.+--+-- This returns @True@ if the configuration was created or modified and+-- @False@ otherwise. This does not handle exceptions raised by file or+-- directory permissions issues.+addConfigWidthMap :: FilePath+                  -- ^ The configuration file path of the configuration+                  -- to modify or create.+                  -> String+                  -- ^ The @TERM@ value for the @widthMap@ directive.+                  -> FilePath+                  -- ^ The width table file path for the directive.+                  -> IO ConfigUpdateResult+addConfigWidthMap configPath term tablePath = do+    configEx <- doesFileExist configPath+    if configEx+       then updateConfig+       else createConfig >> return ConfigurationCreated++    where+        directive = "widthMap " <> show term <> " " <> show tablePath <> "\n"++        createConfig = do+            let dir = takeDirectory configPath+            createDirectoryIfMissing True dir+            writeFile configPath directive++        updateConfig = do+            config <- parseConfigFile configPath+            if (term, tablePath) `elem` termWidthMaps config+               then return ConfigurationRedundant+               else case lookup term (termWidthMaps config) of+                   Just other -> return $ ConfigurationConflict other+                   Nothing -> do+                       appendFile configPath directive+                       return ConfigurationModified
+ src/Graphics/Vty/UnicodeWidthTable/IO.hs view
@@ -0,0 +1,105 @@+{-# LANGUAGE CPP #-}+module Graphics.Vty.UnicodeWidthTable.IO+  ( readUnicodeWidthTable+  , parseUnicodeWidthTable+  , writeUnicodeWidthTable+  )+where++import Control.Monad (when, forM)+import Data.Binary+import Data.Binary.Get+import Data.Binary.Put+import qualified Data.ByteString.Lazy as BSL+#if !(MIN_VERSION_base(4,11,0))+import Data.Semigroup ((<>))+#endif++import Graphics.Vty.UnicodeWidthTable.Types++-- | Load a binary unicode width table from the specified file.+--+-- This either returns a successfully parsed table or a table parsing+-- error message. This does not handle I/O exceptions.+readUnicodeWidthTable :: FilePath -> IO (Either String UnicodeWidthTable)+readUnicodeWidthTable path = parseUnicodeWidthTable <$> BSL.readFile path++-- | Parse a binary unicode width table.+parseUnicodeWidthTable :: BSL.ByteString -> Either String UnicodeWidthTable+parseUnicodeWidthTable bs =+    case runGetOrFail tableParser bs of+        Left (_, _, msg) ->+            Left msg++        -- Even if we parsed a table, leftover bytes indicate something+        -- could be wrong.+        Right (remainingBytes, _, _) | not (BSL.null remainingBytes) ->+            Left $ "Error: " <> show (BSL.length remainingBytes) <>+                   " byte(s) left unconsumed"++        Right (_, _, table) ->+            Right table++-- | Write the unicode width table to the specified path.+--+-- This does not handle I/O exceptions.+writeUnicodeWidthTable :: FilePath -> UnicodeWidthTable -> IO ()+writeUnicodeWidthTable path table = do+    let body = runPut (tableV1Writer table)+    BSL.writeFile path body++-- | Width table magic bytes for use in the binary format.+widthTableMagic :: Word32+widthTableMagic = 0xc1a9f7e0++tableParser :: Get UnicodeWidthTable+tableParser = do+    magic <- getWord32le++    when (magic /= widthTableMagic) $+        fail "Table magic number invalid"++    version <- getWord8++    case version of+        1 -> tableV1Parser+        _ -> fail "Table version invalid"++tableV1Parser :: Get UnicodeWidthTable+tableV1Parser = do+    numRanges <- getWord32le++    let parseRange = do+            start <- getWord32le+            size <- getWord32le+            cols <- getWord8+            return WidthTableRange { rangeStart = start+                                   , rangeSize = size+                                   , rangeColumns = cols+                                   }++    ranges <- forM [1..numRanges] $ const parseRange++    return UnicodeWidthTable { unicodeWidthTableRanges = ranges+                             }++tableV1Writer :: UnicodeWidthTable -> Put+tableV1Writer table = do+    -- Magic bytes+    putWord32le widthTableMagic++    -- Version+    putWord8 1++    -- Number of ranges+    let ranges = unicodeWidthTableRanges table+    let numRanges = length ranges+    putWord32le (fromIntegral numRanges)++    -- Ranges+    let putRange r = do+            putWord32le $ rangeStart r+            putWord32le $ rangeSize r+            putWord8 $ rangeColumns r++    mapM_ putRange ranges
+ src/Graphics/Vty/UnicodeWidthTable/Install.hs view
@@ -0,0 +1,135 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE ForeignFunctionInterface #-}+module Graphics.Vty.UnicodeWidthTable.Install+  ( TableInstallException(..)+  , installUnicodeWidthTable+  , isCustomTableReady+  )+where++import Control.Monad (when, forM_)+import qualified Control.Exception as E+import Control.Concurrent.MVar (MVar, newMVar, takeMVar, putMVar)+import Data.Word (Word8, Word32)+#if !(MIN_VERSION_base(4,11,0))+import Data.Semigroup ((<>))+#endif+import System.IO.Unsafe (unsafePerformIO)++import Graphics.Vty.UnicodeWidthTable.Types++-- | The lock used to make functions in this module thread-safe.+installLock :: MVar ()+{-# NOINLINE installLock #-}+installLock = unsafePerformIO $ newMVar ()++-- | Returns True if and only if a custom table has been allocated and+-- marked as ready for use.+--+-- This function is thread-safe.+isCustomTableReady :: IO Bool+isCustomTableReady = withInstallLock $ (== 1) <$> c_isCustomTableReady++withInstallLock :: IO a -> IO a+withInstallLock act =+    E.bracket takeInstallLock (const releaseInstallLock) $ const act++takeInstallLock :: IO ()+takeInstallLock = takeMVar installLock++releaseInstallLock :: IO ()+releaseInstallLock = putMVar installLock ()++-- This is the size of the allocated custom character width table, in+-- character slots. It's important that this be large enough to hold all+-- possible Unicode character values. At the time of this writing, the+-- valid Unicode range is 0 - 0x10ffff, hence this value.+tableSize :: Int+tableSize = 0x110000++-- | Exception type raised by 'installUnicodeWidthTable'.+data TableInstallException =+    TableInitFailure Int Int+    -- ^ The width table could not be initialized. Args: failure status,+    -- requested table size.+    | TableRangeFailure Int WidthTableRange+    -- ^ A code point range could not be configured. Args: failure+    -- status, offending range.+    | TableActivationFailure Int+    -- ^ The table could not be activated. Args: failure status.+    deriving (Eq, Show)++instance E.Exception TableInstallException++-- | Install a custom unicode character width+-- table. Such tables are obtained with+-- 'Graphics.Vty.UnicodeWidthTable.Query.buildUnicodeWidthTable' and+-- 'Graphics.Vty.UnicodeWidthTable.IO.readUnicodeWidthTable'.+--+-- ALERT! This function is probably not what you want to use because+-- it is automatically called by 'Graphics.Vty.mkVty'. You will only+-- ever need to call this function if you want to use functions+-- in 'Graphics.Text.Width' without controlling the terminal with+-- 'Graphics.Vty.mkVty'.+--+-- This affects the behavior of the 'Graphics.Vty.Image.wcwidth'+-- function and functions that call it. It does so by+-- changing global state available to the C implementation+-- of 'Graphics.Vty.Image.wcwidth'. To ensure that your+-- program gets consistent results from evaluating calls to+-- 'Graphics.Vty.Image.wcwidth', the installation of a custom table+-- should be performed before you call 'Graphics.Vty.Image.wcwidth' in+-- your program.+--+-- This is best done at Vty startup, and if you use+-- 'Graphics.Vty.mkVty', that function calls this automatically based on+-- the Vty configuration's declared width tables. It is exposed as part+-- of the public API so that applications can call this as needed if+-- they don't want to control the terminal with 'mkVty' but do want to+-- make calls to 'Graphics.Vty.Image.wcwidth'.+--+-- It's also important to note that once a custom table has been+-- installed, it is permanent for the life of the process. No new table+-- can be installed, and the new custom table cannot be removed.+--+-- If this function fails for any reason -- if the table cannot be+-- installed or is invalid, or if a custom table already exists -- this+-- will raise a 'TableInstallException' exception.+--+-- This function is thread-safe.+installUnicodeWidthTable :: UnicodeWidthTable -> IO ()+installUnicodeWidthTable table = withInstallLock $ do+    initResult <- initCustomTable tableSize+    when (initResult /= 0) $+        E.throw $ TableInitFailure initResult tableSize++    forM_ (unicodeWidthTableRanges table) $ \r -> do+        result <- setCustomTableRange (rangeStart r)+                                      (rangeSize r)+                                      (rangeColumns r)++        when (result /= 0) $ do+            deallocateCustomTable+            E.throw $ TableRangeFailure result r++    actResult <- activateCustomTable+    when (actResult /= 0) $+        E.throw $ TableActivationFailure actResult++------------------------------------------------------------------------+-- C imports++foreign import ccall unsafe "vty_init_custom_table"+    initCustomTable :: Int -> IO Int++foreign import ccall unsafe "vty_set_custom_table_range"+    setCustomTableRange :: Word32 -> Word32 -> Word8 -> IO Int++foreign import ccall unsafe "vty_activate_custom_table"+    activateCustomTable :: IO Int++foreign import ccall unsafe "vty_custom_table_ready"+    c_isCustomTableReady :: IO Int++foreign import ccall unsafe "vty_deallocate_custom_table"+    deallocateCustomTable :: IO ()
+ src/Graphics/Vty/UnicodeWidthTable/Query.hs view
@@ -0,0 +1,79 @@+{-# LANGUAGE TupleSections #-}+module Graphics.Vty.UnicodeWidthTable.Query+  ( buildUnicodeWidthTable+  , defaultUnicodeTableUpperBound+  )+where++import Control.Monad (forM)+import Data.Char (generalCategory, GeneralCategory(..))+import System.Console.ANSI (getCursorPosition)+import Text.Printf (printf)++import Graphics.Vty.UnicodeWidthTable.Types++shouldConsider :: Char -> Bool+shouldConsider c =+    case generalCategory c of+        Control     -> False+        NotAssigned -> False+        Surrogate   -> False+        _           -> True++charWidth :: Char -> IO Int+charWidth c = do+    printf "\r"+    putChar c+    Just (_, col) <- getCursorPosition+    return col++-- | Convert a sequence of character/width pairs into a list of+-- run-length encoded ranges. This function assumes the pairs come+-- sorted by character ordinal value. It does not require that the+-- character range is fully covered by the sequence.+--+-- The result of this function is a list of ranges in reverse order+-- relative to the input sequence.+mkRanges :: [(Char, Int)] -> [WidthTableRange]+mkRanges pairs =+    let convertedPairs = convert <$> pairs+        convert (c, i) = (fromIntegral $ fromEnum c, fromIntegral i)++        go Nothing finishedRanges [] = finishedRanges+        go (Just r) finishedRanges [] = r:finishedRanges++        go Nothing finishedRanges ((c, width):rest) =+            go (Just $ WidthTableRange c 1 width) finishedRanges rest++        go (Just r@(WidthTableRange prevCh sz prevWidth)) finishedRanges ((c, width):rest) =+            if c == prevCh + sz && prevWidth == width+            then go (Just (WidthTableRange prevCh (sz + 1) prevWidth)) finishedRanges rest+            else go (Just (WidthTableRange c 1 width)) (r:finishedRanges) rest++    in go Nothing [] convertedPairs++-- The uppermost code point to consider when building Unicode width+-- tables.+defaultUnicodeTableUpperBound :: Char+defaultUnicodeTableUpperBound = '\xe0000'++-- | Construct a unicode character width table by querying the terminal+-- connected to stdout. This works by emitting characters to stdout+-- and then querying the terminal to determine the resulting cursor+-- position in order to measure character widths. Consequently this will+-- generate a lot of output and may take a while, depending on your+-- system performance. This should not be run in a terminal while it is+-- controlled by Vty.+--+-- The argument specifies the upper bound code point to test when+-- building the table. This allows callers to decide how much of the+-- Unicode code point space to scan when building the table.+--+-- This does not handle exceptions.+buildUnicodeWidthTable :: Char -> IO UnicodeWidthTable+buildUnicodeWidthTable tableUpperBound = do+    pairs <- forM (filter shouldConsider ['\0'..tableUpperBound]) $ \i ->+        (i,) <$> charWidth i++    return UnicodeWidthTable { unicodeWidthTableRanges = reverse $ mkRanges pairs+                             }
+ src/Graphics/Vty/UnicodeWidthTable/Types.hs view
@@ -0,0 +1,29 @@+module Graphics.Vty.UnicodeWidthTable.Types+  ( UnicodeWidthTable(..)+  , WidthTableRange(..)+  )+where++import Data.Word (Word8, Word32)++-- | A range of code points in a width table.+data WidthTableRange =+    WidthTableRange { rangeStart :: Word32+                    -- ^ The range's starting code point.+                    , rangeSize :: Word32+                    -- ^ The number of code points in the contiguous+                    -- range, beginning with the starting code point+                    -- ('rangeStart').+                    , rangeColumns :: Word8+                    -- ^ The terminal width, in columns, of all of the+                    -- characters corresponding to the code points in+                    -- this range.+                    }+    deriving (Eq, Show)++-- | A run-length-encoded table of Unicode character widths.+data UnicodeWidthTable =+    UnicodeWidthTable { unicodeWidthTableRanges :: [WidthTableRange]+                      -- ^ The ranges in the table.+                      }+    deriving (Show)
+ tools/BuildWidthTable.hs view
@@ -0,0 +1,144 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE CPP #-}+module Main where++import qualified Control.Exception as E+import Control.Monad (when)+import Data.Maybe (fromMaybe)+#if !(MIN_VERSION_base(4,11,0))+import Data.Semigroup ((<>))+#endif+import System.Directory (createDirectoryIfMissing)+import System.Environment (getArgs, getProgName)+import System.FilePath (takeDirectory)+import System.Exit (exitFailure)+import System.Console.GetOpt+import Text.Read (readMaybe)++import Graphics.Vty.Config ( terminalWidthTablePath, currentTerminalName+                           , vtyConfigPath, addConfigWidthMap+                           , ConfigUpdateResult(..)+                           )+import Graphics.Vty.UnicodeWidthTable.IO+import Graphics.Vty.UnicodeWidthTable.Query++data Arg = Help+         | OutputPath String+         | TableUpperBound String+         | UpdateConfig+         | VtyConfigPath String+         deriving (Eq, Show)++options :: Config -> [OptDescr Arg]+options config =+    [ Option "h" ["help"] (NoArg Help)+      "This help output"+    , Option "b" ["bound"] (ReqArg TableUpperBound "MAX_CHAR")+      ("The maximum Unicode code point to test when building the table " <>+       "(default: " <> (show $ fromEnum $ configBound config) <> ")")+    , Option "p" ["path"] (ReqArg OutputPath "PATH")+      ("The output path to write to (default: " <>+       fromMaybe "<none>" (configOutputPath config) <> ")")+    , Option "u" ["update-config"] (NoArg UpdateConfig)+      "Create or update the Vty configuration file to use the new table (default: no)"+    , Option "c" ["config-path"] (ReqArg VtyConfigPath "PATH")+      ("Update the specified Vty configuration file path when -u is set (default: " <>+       configPath config <> ")")+    ]++data Config =+    Config { configOutputPath :: Maybe FilePath+           , configBound :: Char+           , configUpdate :: Bool+           , configPath :: FilePath+           }+           deriving (Show)++mkDefaultConfig :: IO Config+mkDefaultConfig = do+    Config <$> terminalWidthTablePath+           <*> pure defaultUnicodeTableUpperBound+           <*> pure False+           <*> vtyConfigPath++usage :: IO ()+usage = do+    config <- mkDefaultConfig+    pn <- getProgName+    putStrLn $ "Usage: " <> pn <> " [options]"+    putStrLn ""+    putStrLn "This tool queries the terminal on stdout to determine the widths"+    putStrLn "of Unicode characters rendered to the terminal. The resulting data"+    putStrLn "is written to a table at the specified output path for later"+    putStrLn "loading by Vty-based applications."+    putStrLn ""++    putStrLn $ usageInfo pn (options config)++updateConfigFromArg :: Arg -> Config -> Config+updateConfigFromArg Help c =+    c+updateConfigFromArg UpdateConfig c =+    c { configUpdate = True }+updateConfigFromArg (VtyConfigPath p) c =+    c { configPath = p }+updateConfigFromArg (TableUpperBound s) c =+    case readMaybe s of+        Nothing -> error $ "Invalid table upper bound: " <> show s+        Just v  -> c { configBound = toEnum v }+updateConfigFromArg (OutputPath p) c =+    c { configOutputPath = Just p }++main :: IO ()+main = do+    defConfig <- mkDefaultConfig+    strArgs <- getArgs+    let (args, unused, errors) = getOpt Permute (options defConfig) strArgs++    when (not $ null errors) $ do+        mapM_ putStrLn errors+        exitFailure++    when ((not $ null unused) || (Help `elem` args)) $ do+        usage+        exitFailure++    let config = foldr updateConfigFromArg defConfig args++    outputPath <- case configOutputPath config of+        Nothing -> do+            putStrLn "Error: could not obtain terminal width table path"+            exitFailure+        Just path -> return path++    putStrLn "Querying terminal:"+    builtTable <- buildUnicodeWidthTable $ configBound config++    let dir = takeDirectory outputPath+    createDirectoryIfMissing True dir+    writeUnicodeWidthTable outputPath builtTable++    putStrLn $ "\nOutput table written to " <> outputPath++    when (configUpdate config) $ do+        let cPath = configPath config+        Just tName <- currentTerminalName++        result <- E.try $ addConfigWidthMap cPath tName outputPath++        case result of+            Left (e::E.SomeException) -> do+                putStrLn $ "Error updating Vty configuration at " <> cPath <> ": " <>+                           show e+                exitFailure+            Right ConfigurationCreated -> do+                putStrLn $ "Configuration file created: " <> cPath+            Right ConfigurationModified -> do+                putStrLn $ "Configuration file updated: " <> cPath+            Right (ConfigurationConflict other) -> do+                putStrLn $ "Configuration file not updated: uses a different table " <>+                           "for TERM=" <> tName <> ": " <> other+            Right ConfigurationRedundant -> do+                putStrLn $ "Configuration file not updated: configuration " <>+                           cPath <> " already uses table " <> outputPath <>+                           " for TERM=" <> tName
vty.cabal view
@@ -1,5 +1,5 @@ name:                vty-version:             5.27+version:             5.28 license:             BSD3 license-file:        LICENSE author:              AUTHORS@@ -48,7 +48,6 @@                        microlens < 0.4.12,                        microlens-mtl,                        microlens-th,-                       -- required for nice installation with yi                        hashable >= 1.2,                        mtl >= 1.1.1.0 && < 2.3,                        parallel >= 2.2 && < 3.3,@@ -59,7 +58,9 @@                        text >= 0.11.3,                        unix,                        utf8-string >= 0.3 && < 1.1,-                       vector >= 0.7+                       vector >= 0.7,+                       binary,+                       ansi-terminal >= 0.10.3    if !impl(ghc >= 8.0)     build-depends:     semigroups >= 0.16,@@ -78,7 +79,6 @@                        Graphics.Vty.Picture                        Graphics.Vty.Output                        Graphics.Text.Width-                       -- the modules below are only meant to be used by the tests.                        Codec.Binary.UTF8.Debug                        Data.Terminfo.Parse                        Data.Terminfo.Eval@@ -99,6 +99,10 @@                        Graphics.Vty.Output.Interface                        Graphics.Vty.Output.XTermColor                        Graphics.Vty.Output.TerminfoBased+                       Graphics.Vty.UnicodeWidthTable.Types+                       Graphics.Vty.UnicodeWidthTable.IO+                       Graphics.Vty.UnicodeWidthTable.Query+                       Graphics.Vty.UnicodeWidthTable.Install    other-modules:       Graphics.Vty.Attributes.Color240                        Graphics.Vty.Debug.Image@@ -114,11 +118,22 @@   hs-source-dirs:      src    default-extensions:  ScopedTypeVariables-                       ForeignFunctionInterface    ghc-options:         -O2 -funbox-strict-fields -Wall -fspec-constr -fspec-constr-count=10    ghc-prof-options:    -O2 -funbox-strict-fields -caf-all -Wall -fspec-constr -fspec-constr-count=10++executable vty-build-width-table+  main-is:             BuildWidthTable.hs+  hs-source-dirs:      tools++  default-language:    Haskell2010+  ghc-options:         -threaded -Wall++  build-depends:       vty,+                       directory,+                       filepath,+                       base >= 4.8 && < 5  executable vty-mode-demo   main-is:             ModeDemo.hs