on f6c3cbf help sign in
136 files changed+5131−12287
since splitsnapshots
unifiedsplit
.gitignore−15 .travis.yml−109 AUTHORS−22 CHANGELOG+77 CHANGELOG.md−861 docs/DESIGN → DESIGN LICENSE−2 README+48 README.md−201 TODO+15 cbits/get_tty_erase.c−18 cbits/gwinsz.c+6−15 cbits/gwinsz.h+1−2 cbits/mk_wcwidth.c+99−128 cbits/set_term_timing.c+6−5 ci/build-and-test−44 ci/travis_ci_install−12 ci/travis_ci_script−11 demos/Demo.hs−90 demos/ModeDemo.hs−55 scripts/256colres.pl−98 src/Codec/Binary/UTF8/Debug.hs+8−8 src/Codec/Binary/UTF8/Width.hs+40 src/Data/Marshalling.hs+31 src/Data/Sequence/Extra.hs+2−3 src/Data/Terminfo/Eval.hs+224−112 src/Data/Terminfo/Parse.hs+248−276 src/Graphics/Text/Width.hs−64 src/Graphics/Vty.hs+124−246 src/Graphics/Vty/Attributes.hs+162−165 src/Graphics/Vty/Attributes/Color.hs−191 src/Graphics/Vty/Attributes/Color240.hs−298 src/Graphics/Vty/Config.hs−488 src/Graphics/Vty/Debug.hs+64−24 src/Graphics/Vty/Debug/Image.hs−32 src/Graphics/Vty/DisplayAttributes.hs+65−124 src/Graphics/Vty/DisplayRegion.hs+11 src/Graphics/Vty/Error.hs−8 src/Graphics/Vty/Image.hs+301−353 src/Graphics/Vty/Image/Internal.hs−285 src/Graphics/Vty/Inline.hs+40−108 src/Graphics/Vty/Inline/Unsafe.hs−59 src/Graphics/Vty/Input.hs−176 src/Graphics/Vty/Input/Classify.hs−110 src/Graphics/Vty/Input/Classify/Parse.hs−61 src/Graphics/Vty/Input/Classify/Types.hs−25 src/Graphics/Vty/Input/Events.hs−83 src/Graphics/Vty/Input/Focus.hs−49 src/Graphics/Vty/Input/Loop.hs−286 src/Graphics/Vty/Input/Mouse.hs−163 src/Graphics/Vty/Input/Paste.hs−41 src/Graphics/Vty/Input/Terminfo.hs−186 src/Graphics/Vty/Input/Terminfo/ANSIVT.hs−88 src/Graphics/Vty/LLInput.hs+226 src/Graphics/Vty/Output.hs−107 src/Graphics/Vty/Output/Interface.hs−331 src/Graphics/Vty/Output/Mock.hs−90 src/Graphics/Vty/Output/TerminfoBased.hs−606 src/Graphics/Vty/Output/XTermColor.hs−131 src/Graphics/Vty/Picture.hs+57−116 src/Graphics/Vty/PictureToSpans.hs+3−344 src/Graphics/Vty/Span.hs+213−119 src/Graphics/Vty/Terminal.hs+129 src/Graphics/Vty/Terminal/Debug.hs+117 src/Graphics/Vty/Terminal/Generic.hs+405 src/Graphics/Vty/Terminal/MacOSX.hs+95 src/Graphics/Vty/Terminal/TerminfoBased.hs+412 src/Graphics/Vty/Terminal/XTermColor.hs+94 src/Graphics/Vty/UnicodeWidthTable/IO.hs−105 src/Graphics/Vty/UnicodeWidthTable/Install.hs−129 src/Graphics/Vty/UnicodeWidthTable/Query.hs−79 src/Graphics/Vty/UnicodeWidthTable/Types.hs−29 test/Bench.hs+62 test/Bench2.hs+30 test/BenchImageFuzz.hs−29 test/BenchNoDiffOpt.hs−38 test/BenchRenderChar.hs+26−32 test/BenchVerticalScroll.hs−59 test/ControlTable.hs+4−4 test/EventEcho.hs−40 test/HereDoc.hs+5−3 test/Issue76.hs−17 test/LICENSE−31 test/Makefile+8−20 test/MinimalExample.hs−14 test/Rogue.hs−175 test/Setup.lhs−3 test/Test.hs+44 test/Test2.hs−1 test/Verify.hs+55−59 test/Verify/Data/Terminfo/Parse.hs+63−61 test/Verify/Graphics/Vty/Attributes.hs+19−52 test/Verify/Graphics/Vty/DisplayAttributes.hs+5−5 test/Verify/Graphics/Vty/DisplayRegion.hs+27 test/Verify/Graphics/Vty/Image.hs+51−178 test/Verify/Graphics/Vty/Output.hs−59 test/Verify/Graphics/Vty/Picture.hs+28−30 test/Verify/Graphics/Vty/Prelude.hs−24 test/Verify/Graphics/Vty/Span.hs+17−25 test/VerifyColor240.hs−269 test/VerifyConfig.hs−44 test/VerifyCropSpanGeneration.hs−92 test/VerifyEmptyImageProps.hs−14 test/VerifyEvalTerminfoCaps.hs−92 test/VerifyImageOps.hs−174 test/VerifyImageTrans.hs−34 test/VerifyInline.hs−31 test/VerifyLayersSpanGeneration.hs−130 test/VerifyOutput.hs−58 test/VerifyParseTerminfoCaps.hs−101 test/VerifySimpleSpanGeneration.hs−188 test/VerifyUsingMockInput.hs−223 test/VerifyUsingMockTerminal.hs−108 test/VerifyUtf8Width.hs−30 test/benchmark.hs−56 test/current_benchmark_results.txt−40 test/interactive_terminal_test.hs+450−739 test/verify_attribute_ops.hs+8 test/verify_debug_terminal.hs+115 test/VerifyDisplayAttributes.hs → test/verify_display_attributes.hs+3−3 test/verify_empty_image_props.hs+13 test/verify_eval_terminfo_caps.hs+129 test/verify_image_ops.hs+124 test/verify_image_trans.hs+31 test/verify_inline.hs+11 test/verify_parse_terminfo_caps.hs+158 test/verify_picture_ops.hs+9 test/verify_picture_to_span.hs+10 test/verify_span_ops.hs+179 test/verify_utf8_width.hs+17 test/vty-examples.cabal−138 test/vty_inline_example.hs+5−2 test/Issue18.hs → test/vty_issue_18.hs+6−4 test/YiIssue264.hs → test/yi_issue_264.hs−1 tools/BuildWidthTable.hs−144 vty.cabal+96−627
.gitignoredeleted−15
@@ -1,15 +0,0 @@
1−*.o
2−*.swp
3−*.hi
4−*.prof
5−Setup
6−dist
7−dist-newstyle
8−cabal.project.local
9−.ghc.environment.*
10−test/benchmark
11−.hpc
12−.cabal-sandbox
13−cabal.sandbox.config
14−test/cabal.sandbox.config
15−.stack-work
.travis.ymldeleted−109
@@ -1,109 +0,0 @@
1−# This Travis job script has been generated by a script via
2−#
3−# runghc make_travis_yml_2.hs 'vty.cabal'
4−#
5−# For more information, see https://github.com/haskell-CI/haskell-ci
6−#
7−language: c
8−sudo: false
9−
10−git:
11− submodules: false # whether to recursively clone submodules
12−
13−cache:
14− directories:
15− - $HOME/.cabal/packages
16− - $HOME/.cabal/store
17−
18−before_cache:
19− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log
20− # remove files that are regenerated by 'cabal update'
21− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.*
22− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/*.json
23− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.cache
24− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar
25− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar.idx
26−
27− - rm -rfv $HOME/.cabal/packages/head.hackage
28−
29−matrix:
30− include:
31− - compiler: "ghc-8.4.3"
32− # env: TEST=--disable-tests BENCH=--disable-benchmarks
33− addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.4.3], sources: [hvr-ghc]}}
34− - compiler: "ghc-8.2.2"
35− # env: TEST=--disable-tests BENCH=--disable-benchmarks
36− addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.2.2], sources: [hvr-ghc]}}
37− - compiler: "ghc-8.0.2"
38− # env: TEST=--disable-tests BENCH=--disable-benchmarks
39− addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.0.2], sources: [hvr-ghc]}}
40− - compiler: "ghc-7.10.3"
41− # env: TEST=--disable-tests BENCH=--disable-benchmarks
42− addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.10.3], sources: [hvr-ghc]}}
43−
44−before_install:
45− - HC=${CC}
46− - HCPKG=${HC/ghc/ghc-pkg}
47− - unset CC
48− - ROOTDIR=$(pwd)
49− - mkdir -p $HOME/.local/bin
50− - "PATH=/opt/ghc/bin:/opt/ghc-ppa-tools/bin:$HOME/local/bin:$PATH"
51− - HCNUMVER=$(( $(${HC} --numeric-version|sed -E 's/([0-9]+)\.([0-9]+)\.([0-9]+).*/\1 * 10000 + \2 * 100 + \3/') ))
52− - echo $HCNUMVER
53−
54−install:
55− - cabal --version
56− - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"
57− - BENCH=${BENCH---enable-benchmarks}
58− - TEST=${TEST---enable-tests}
59− - HADDOCK=${HADDOCK-true}
60− - UNCONSTRAINED=${UNCONSTRAINED-true}
61− - NOINSTALLEDCONSTRAINTS=${NOINSTALLEDCONSTRAINTS-false}
62− - GHCHEAD=${GHCHEAD-false}
63− - travis_retry cabal update -v
64− - "sed -i.bak 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config"
65− - rm -fv cabal.project cabal.project.local
66− - grep -Ev -- '^\s*--' ${HOME}/.cabal/config | grep -Ev '^\s*$'
67− - "printf 'packages: \".\"\\n' > cabal.project"
68− - touch cabal.project.local
69− - "if ! $NOINSTALLEDCONSTRAINTS; then for pkg in $($HCPKG list --simple-output); do echo $pkg | grep -vw -- vty | sed 's/^/constraints: /' | sed 's/-[^-]*$/ installed/' >> cabal.project.local; done; fi"
70− - cat cabal.project || true
71− - cat cabal.project.local || true
72− - if [ -f "./configure.ac" ]; then
73− (cd "." && autoreconf -i);
74− fi
75− - rm -f cabal.project.freeze
76− - cabal new-build -w ${HC} ${TEST} ${BENCH} --project-file="cabal.project" --dep -j2 all
77− - cabal new-build -w ${HC} --disable-tests --disable-benchmarks --project-file="cabal.project" --dep -j2 all
78− - rm -rf .ghc.environment.* "."/dist
79− - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)
80−
81−# Here starts the actual work to be performed for the package under test;
82−# any command which exits with a non-zero exit code causes the build to fail.
83−script:
84− # test that source-distributions can be generated
85− - (cd "." && cabal sdist)
86− - mv "."/dist/vty-*.tar.gz ${DISTDIR}/
87− - cd ${DISTDIR} || false
88− - find . -maxdepth 1 -name '*.tar.gz' -exec tar -xvf '{}' \;
89− - "printf 'packages: vty-*/*.cabal\\n' > cabal.project"
90− - touch cabal.project.local
91− - "if ! $NOINSTALLEDCONSTRAINTS; then for pkg in $($HCPKG list --simple-output); do echo $pkg | grep -vw -- vty | sed 's/^/constraints: /' | sed 's/-[^-]*$/ installed/' >> cabal.project.local; done; fi"
92− - cat cabal.project || true
93− - cat cabal.project.local || true
94− # this builds all libraries and executables (without tests/benchmarks)
95− - cabal new-build -w ${HC} --disable-tests --disable-benchmarks all
96−
97− # build & run tests, build benchmarks
98− - cabal new-build -w ${HC} ${TEST} ${BENCH} all
99− - if [ "x$TEST" = "x--enable-tests" ]; then cabal new-test -w ${HC} ${TEST} ${BENCH} all; fi
100−
101− # haddock
102− - rm -rf ./dist-newstyle
103− - if $HADDOCK; then cabal new-haddock -w ${HC} ${TEST} ${BENCH} all; else echo "Skipping haddock generation";fi
104−
105− # Build without installed constraints for packages in global-db
106− - if $UNCONSTRAINED; then rm -f cabal.project.local; echo cabal new-build -w ${HC} --disable-tests --disable-benchmarks all; else echo "Not building without installed constraints"; fi
107−
108−# REGENDATA ["vty.cabal"]
109−# EOF
AUTHORSdeleted−22
@@ -1,22 +0,0 @@
1−The following people should be thanked for contributing to the vty
2−library:
3−
4− * Andrea Vezzosi
5− * Corey O'Connor
6− * Emily Backes
7− * Josef Svenningsson
8− * Nicolas Pouillard
9− * Roman Cheplyaka
10− * Stefan O'Rear
11− * Yusaku Hashimoto
12− * allan.clark
13− * gwern0
14− * jeanphilippe.bernardy
15− * m.niloc
16− * mikesteele81
17− * Mikolaj Konarski
18− * Eyal Lotem
19− * Yoshikuni Jujo
20− * Dmitry Ivanov
21−
22−Plus others.. Check the git log and CHANGELOG.md for a full list.
CHANGELOGnew file+77
@@ -0,0 +1,77 @@
1+4.2.1.0
2+ * API changes:
3+ * Attr record accessor fore_color changed to attr_fore_color
4+ * Attr record accessor back_color changed to attr_back_color
5+ * Attr record accessor style changed to attr_style
6+ * Added an "inline" display attribute changing DSL:
7+ * put_attr_change applies a display attribute change immediately to a terminal
8+ * For instance, can be used to change the display attrbiutes of text output via putStrLn
9+ and putStr. EX: "put_attr_change $ back_color red" will set the
10+ background color to red.
11+ * Changes do not apply to a Picture output via output_picture.
12+ * See Graphics.Vty.Inline
13+ * Moved all IO actions into any monad an instance of MonadIO
14+
15+4.0.0.1
16+ * binding for mk_wcswidth was incorrect. Most platforms just magically worked due to
17+ coincidence.
18+
19+4.0.0
20+ * API changes:
21+ * "getSize" has been removed. Use "terminal vty >>= display_bounds" where "vty" is an
22+ instance of the Vty data structure.
23+ * added a "terminal" field to the Vty data structure. Accesses the TerminalHandle associated
24+ with the Vty instance.
25+ * Graphics.Vty.Types has undergone a number of changes. Summary:
26+ * Partitioned into Graphics.Vty.Attributes for display attributes. Graphics.Vty.Image for
27+ image combinators. Graphics.Vty.Picture for final picture construction.
28+ * Graphics.Vty.Attributes:
29+ * "setFG" and "setBG" are now "with_fore_color" and "with_back_color"
30+ * All other "set.." equations similarly replaced.
31+ * "attr" is now "def_attr", short for "default display attributes" Also added a
32+ "current_attr" for "currently applied display attributes"
33+ * Graphics.Vty.Image:
34+ * "horzcat" is now "horiz_cat"
35+ * "vertcat" is now "vert_cat"
36+ * "renderBS" is now "utf8_bytestring"
37+ * "renderChar" is now "char"
38+ * "renderFill" is now "char_fill"
39+ * added a "utf8_string" and "string" (AKA "iso_10464_string") for UTF-8 encoded Strings
40+ and ISO-10464 encoded Strings. String literals in GHC have an ISO-10464 runtime
41+ representation.
42+ * Graphics.Vty.Picture:
43+ * exports Graphics.Vty.Image
44+ * "pic" is now "pic_for_image"
45+ * added API for setting background fill pattern.
46+ * Completely rewritten output backend.
47+ * Efficient, scanline style output span generator. Has not been fully optimized, but good
48+ enough.
49+ * The details required to display the desired picture on a terminal are well encapsulated.
50+ * Terminfo based display terminal implementation. With specialized derivitives for xterm,
51+ Terminal.app, and iTerm.app.
52+ * Attempts to robustly handle even terminals that don't support all display attributes.
53+ * I've tested the following terminals with success: iTerm.app, Terminal.app, xterm,
54+ rxvt, mlterm, Eterm, gnome-terminal, konsole, screen, linux vty. Hopefully you will be
55+ as successfull.
56+ * Improved unicode support. Double wide characters will display as expected.
57+ * 256 color support. See Graphics.Vty.Attributes.Color240. The actual output color is adjusted
58+ according to the number of colors the terminal supports.
59+ * The Graphics.Vty.Image combinators no longer require matching dimensions to arguments.
60+ Unspecified areas are filled in with a user-customizable background pattern. See
61+ Graphics.Vty.Picture.
62+ * output images are always cropped to display size.
63+ * Significant code coverage by QuickCheck tests. An interactive test for those final properties
64+ that couldn't be automatically verified.
65+
66+ issues resolved:
67+ * "gnome terminal displays non-basic attributes as strikethrough"
68+ * http://trac.haskell.org/vty/ticket/14
69+ * "Multi-byte characters are not displayed correctly on update"
70+ * http://trac.haskell.org/vty/ticket/10
71+ * "Redraw does not handle rendering a line that extends beyond screen width characters"
72+ * http://trac.haskell.org/vty/ticket/13
73+ * "The <|> and <-> combinators should be more forgiving of mismatched dimensions"
74+ * http://trac.haskell.org/vty/ticket/9
75+ * "256-color support"
76+ * http://trac.haskell.org/vty/ticket/19
77+
CHANGELOG.mddeleted−861
@@ -1,861 +0,0 @@
1−
2−5.35.1
3−------
4−
5−Bug fixes:
6− * Fixed a build issue with a test program.
7−
8−5.35
9−----
10−
11−New features:
12− * Add support for 24-bit color (thanks @u-quark). This change
13− updates Vty to look at the `COLORTERM` environment variable that is
14− conventionally used to advertise support for truecolor escape
15− sequences. The change also updates the Vty demo to demonstrate
16− 24-bit colors. This change also adds a new data type, `ColorMode`,
17− to represent the color mode in use, as well as an `Output` interface
18− field, `outputColorMode`, to track the active color mode and use it
19− to clamp emitted color escape sequences to the active color range.
20−
21−API changes:
22− * All types in `Graphics.Vty.Input.Events` now have strict constructor
23− fields.
24− * Internal events are now wrapped in a new `InternalEvent` type to
25− improve how signal handling is done. This change modifies the `Input`
26− type's event channel API to produce `InternalEvents`, not `Events`.
27− The new `InternalEvent` either wraps `Event` with the `InputEvent`
28− constructor (the previous behavior) or indicates that Vty resumed
29− after handling a signal using the `ResumeAfterSignal` constructor.
30− This change avoids the previous use of `EvResize` with lazy exception
31− arguments as a sentinel value for `ResumeAfterSignal`.
32−
33−Other enhancements:
34− * Bracketed paste parsing performance has been greatly improved thanks
35− to benchmarking and optimization work by @iphydf. As part of that
36− work, Vty now uses bytestrings rather than Strings internally when
37− parsing input to look for events.
38− * The `\b` value is now interpreted as `KBS` (thanks @vglfr)
39−
40−5.34
41−----
42−
43−API changes:
44− * Added an `NFData` instance for `Event` (thanks Mario Lang)
45− * Removed `Monoid` and `Semigroup` instances for `Attr` and
46− `MaybeDefault`. This change removed the instances because they were
47− misbehaved; merging `Attr` and `MaybeDefault` values with these
48− instances resulted in field value losses. For example, before this
49− change,
50−```
51−(defAttr `withForeColor` blue) <> (defAttr `withBackColor` green)
52−```
53− would result in just
54−```
55− (defAttr `withBackColor` green)
56−```
57− because the instances were designed to favor the right-hand
58− arguments' fields even if they had not been explicitly set
59− (a consequence of the `MaybeDefault` `Semigroup` instance).
60− While that behavior was sensible specifically in the context of
61− `Graphics.Vty.Inline`, it wasn't a useful user-facing API and it made
62− for surprising instance behavior. Since there is actually no good way
63− to handle this in a `Semigroup` instance for `Attr` -- some choices
64− have to be made about how to merge two attributes' foreground colors,
65− and that won't be much better than what we had -- the instance was
66− just removed.
67−
68−
69−5.33
70−----
71−
72−API changes:
73−* The `Cursor` type got a new `PositionOnly` constructor for cursor
74− placement without visibility.
75−
76−Package changes:
77−* Relaxed upper bound for `random`
78−* Updated `microlens` bounds to allow 0.4.12
79−
80−Other improvements:
81−* Various hlint-driven improvements (thanks Willem Van Onsem)
82−* The implementation of `color240` was improved (thanks (Willem Van
83− Onsem)
84−
85−5.32
86−----
87−
88−New features:
89− * Meta-PageUp and Meta-PageDown are now supported (#193)
90− * Added `supportsItalics` and `supportsStrikethrough` functions to
91− check for feature support in terminfo
92−
93−Bug fixes:
94− * Detect utf-8 mode in `LANG` regardless of case (thanks Emeka
95− Nkurumeh)
96−
97−5.31
98−----
99−
100−New features and API changes:
101− * Added support for strikethrough mode. This change adds a new
102− `strikethrough` `Style` value and uses the `smxx` and `rmxx`
103− Terminfo capabilities to activate and deactivate strikethrough mode,
104− respectively. If the terminfo does not report those capabilities,
105− this style is ignored.
106− * `Output`: added the `setDisplayBounds` field to set the output
107− dimensions of the output handle; added an implementation of this for
108− the `TerminfoBased` backend.
109−
110−Other changes:
111− * The C prototype for `vty_c_get_window_size` in `gwinsz.h` was fixed.
112−
113−5.30
114−----
115−
116−New features:
117− * Added `Graphics.Vty.setWindowTitle` to emit an escape
118− sequence to set the window title, provide the terminal emulator
119− accepts Xterm-style title sequences. For details, see:
120− https://tldp.org/HOWTO/Xterm-Title-3.html
121−
122−5.29
123−----
124−
125−API changes:
126− * The Input type got a new field, 'restoreInputState'. This field
127− allows the end user to have direct access to the logic needed to
128− restore the terminal's input state flags. Prior to having this field,
129− this state restoration logic could only be invoked as part of calling
130− 'shutdownInput', but since that function does other things (like
131− killing threads) it is not advisable to call it repeatedly (which is
132− necessary in the use case this change is intended to support). This
133− can be called directly to restore the input state flags as needed,
134− although this is not required if 'shutdown' (or 'shutdownInput') is
135− called.
136−
137−Other changes:
138− * attributeControl: explicitly enable the ICRNL terminal mode flag (see
139− #187 and c572ad).
140−
141−5.28.2
142−------
143−
144−Bug fixes:
145− * Added a package dependency on `semigroups` for the
146− `vty-build-width-table` tool on older GHCs (#185)
147−
148−5.28.1
149−------
150−
151−Bug fixes:
152− * `installUnicodeWidthTable`: use `throwIO`, not `throw`
153−
154−5.28
155−----
156−
157−This release improves Vty's support for multi-column Unicode characters
158−and provides greater compatibility with a wider array of terminal
159−emulators. The following sections summarize the relevant changes, but an
160−overview of the new functionality is motivated and detailed in the new
161−"Multi-Column Character Support" README section. For
162−historical context, please also consider reading over
163−[#175](https://github.com/jtdaugherty/vty/issues/175).
164−
165−API changes:
166− * New modules were added:
167− * `Graphics.Vty.UnicodeWidthTable.Types`
168− * `Graphics.Vty.UnicodeWidthTable.IO`
169− * `Graphics.Vty.UnicodeWidthTable.Query`
170− * `Graphics.Vty.UnicodeWidthTable.Install`
171− * The `Config` type got a new field, `allowCustomUnicodeWidthTables`,
172− that controls whether `mkVty` will attempt to load a Unicode width
173− table if specified in the configuration.
174−
175−Configuration file changes:
176− * A new syntax was added to support specifying Unicode width tables on
177− a per-`TERM` basis. The syntax is `widthMap <TERM> <PATH>`. See the
178− documentation for `Graphics.Vty.Config` for details. Since prior
179− versions of this library will silently ignore any configuration file
180− lines they cannot parse, this change to user configuration files is
181− at least non-breaking for older versions of Vty.
182−
183−Other changes:
184− * The `mkVty` function now automatically attempts to load a custom
185− Unicode width table if one is specified in the configuration,
186− provided `allowCustomUnicodeWidthTables` is not set to `Just False`.
187− See the documentation for `Graphics.Vty.mkVty` for details.
188− * Vty now includes a command line tool, `vty-build-width-table`, that
189− queries the terminal emulator to construct a custom Unicode width
190− table and optionally update the Vty configuration file to use it.
191− Programs that want to use that tool's functionality may also do so
192− via the API exposed in the various modules listed above.
193−
194−5.27
195−----
196−
197−* Added `Graphics.Vty.Config.getTtyEraseChar` to support querying the
198− kernel for the current terminal's settings to obtain the character
199− assigned by the `stty erase` command. That can then be added to the
200− Vty configuration's input map to map to `KBS` (backspace) if desired.
201−
202−5.26
203−----
204−
205−* Resolved various import warnings (thanks @glguy)
206−* Removed the `MonadIO` constraint from the Output type's fields and
207− removed `MonadFail` uses (PR #177, thanks @glguy)
208−* Clarified documentation for ANSI colors (thanks Colby Jenn)
209−* `Graphics.Vty.Attributes` no longer re-exports
210− `Graphics.Vty.Attributes.Color`
211−* The `Graphics.Vty.Attributes.Color` module is now exposed (thanks
212− Colby Jenn)
213−* Raised upper bound for `microlens` to 0.4.12 (thanks Artyom Kazak)
214−* Changed from using `System.Posix.Env.getEnv` to
215− `System.Environment.lookupEnv` (thanks Jonathan Osser)
216−* Added `Graphics.Vty.Image` functions for dealing with character width
217− computations on `Text` values instead of `Strings`:
218− * `safeWctwidth`
219− * `safeWctlwidth`
220− * `wctwidth`
221− * `wctlwidth`
222−
223−5.25.1
224−------
225−
226−* Avoided a conflict with a Microlens 0.4.10 operator and added an
227− upper bound on Microlens of 0.4.11.
228−
229−5.25
230−----
231−
232−* The Vty type got a new field, isShutdown, that returns whether the
233− Vty handle has had its 'shutdown' function called (thanks Ian
234− Jeffries)
235−* Vty's shutdown function is now thread-safe.
236−
237−5.24.1
238−------
239−
240−* The "shutdown" method of Vty handles is now idempotent (#159)
241−
242−5.24
243−----
244−
245−* Add Generic and NFData instances for some types
246−* Image: remove custom Show instance, add derived Show and Read
247− instances
248−* Updated Travis build settings (thanks Eric Mertens)
249−
250−5.23.1
251−------
252−
253−* Fixed a bug where italics did not combine properly with other display
254− modes (#155, thanks Eric Mertens)
255−
256−5.23
257−----
258−
259−* Added support for italicized output when terminfo supports it. This
260− takes the form of a new Style, "italic". Note that most terminfo
261− descriptors do not report capabilities for italics, so support for
262− this will be very spotty.
263−* Updateed text/string function documentation to indicate that escapes
264− are not permitted in their inputs.
265−
266−5.22
267−----
268−
269−* Added Graphics.Vty.Attributes.Color240.color240CodeToRGB function
270− (thanks Brent Carmer)
271−* Added nextEventNonblocking function (field) to Vty type (#87)
272−
273−5.21
274−----
275−
276−* Picture and Background now provide Eq instances (thanks Jaro Reinders)
277−* #145: vty builds with microlens 0.4.9 (thanks Daniel Wagner)
278−* #142: note requirement of threaded RTS
279−
280−5.20
281−----
282−
283−API changes:
284−* Split up Monoid instances into Monoid and Semigroup for newer GHCs
285− (thanks Ryan Scott)
286−
287−5.19.1
288−------
289−
290−API changes:
291−* Cursor now provides an Eq instance (thanks Jaro Reinders)
292−
293−5.19
294−----
295−
296−API changes:
297−* URL hyperlinking (via 'withURL') is now optional and disabled by
298− default due to poor support on some common terminals. A new 'Mode'
299− constructor, 'Hyperlink', has been added to enable this feature. To
300− change the hyperlinking mode, use 'setMode' on the 'outputIface' of a
301− Vty handle.
302−
303−5.18.1
304−------
305−
306−Bug fixes:
307−* Reset the hyperlink state on line endings to avoid run-on hyperlinks
308−
309−5.18
310−----
311−
312−API changes:
313−* Added support for hyperlinking attributes (thanks Getty Ritter). This
314− change adds a new Attr field for containing the hyperlink to apply,
315− as per https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
316−
317−5.17.1
318−------
319−
320−* withStyle now ignores zero arguments, leaving attribute styles
321− untouched if the input style is the null style
322−
323−5.17
324−----
325−
326−API changes:
327−* Add support for terminal focus events. This change adds a new mode
328− usable with setMode, Focus, that requests that the terminal send
329− events on focus lose/gain. This change also adds two new Event
330− constructors, EvLostFocus and EvGainedFocus.
331−* No longer enable UTF8 mouse event encoding. This encoding was not
332− working properly with Terminal.app, and using the other modes (SGR,
333− etc.) work.
334−* Graphics.Vty.Attributes: escape backticks in Haddock comment (fixes
335− #131)
336−
337−5.16
338−----
339−
340−API changes:
341−* Added support for mouse wheel events while in mouse mode. The Button
342− type got two new constructors as a result: BScrollUp and BScrollDown.
343− Thanks to doublescale@tutanota.com for this contribution!
344−
345−Bug fixes:
346−* charFill now clamps negative arguments to zero (thanks Eric Mertens!)
347−
348−5.15.1
349−------
350−
351−Package changes:
352−* Documentation files are now marked accordingly (thanks Michal
353− Suchánek)
354−
355−Bug fixes:
356−* translateX/Y: fix negative translations
357−
358−5.15
359−----
360−
361−Package changes:
362−* Discontinued support for GHC versions prior to 7.10.1.
363−* Removed instructions and configuration for Stack builds since they
364− are no longer supported.
365−* Clarified README mention of (lack of) Windows support (contributors
366− wanted, though!)
367−* Removed dependency on data-default (see below).
368−
369−API changes:
370−* Moved color definitions from Attributes to Color module.
371−* In lieu of data-default (Default) instances for Attr and Config, use
372− 'defAttr' and the new 'defaultConfig' (or 'mempty') instead of 'def'.
373−* Graphics.Vty.Output no longer re-exports
374− Graphics.Vty.Output.Interface.
375−* Removed Graphics.Vty.Prelude module and moved DisplayRegion and its
376− accessors to Graphics.Vty.Image.
377−* Graphics.Vty.Image no longer re-exports Graphics.Vty.Attributes.
378−* Graphics.Vty.Picture no longer re-exports Graphics.Vty.Image.
379−
380−5.14
381−----
382−
383−* addMaybeClippedJoin: instead of raising an exception when the join
384− is totally clipped, just reduce the clip amount and continue
385−* addMaybeClipped: skip blit of joins when their primary dimension is
386− zero
387−* 'string' and related text functions no longer treat an empty string
388− as an empty image (thanks Chris Penner). This means that now it is
389− possible to use 'str ""' as a non-empty image with height 1.
390−
391−5.13
392−----
393−
394−* Reverted changes in 5.12 due to disagreements between terminal
395− emulators and utf8proc; for more details, please see the ticket
396− discussion at
397− https://github.com/coreyoconnor/vty/issues/115
398−
399−5.12
400−----
401−
402−* Replaced 'wcwidth' with a call to the utf8proc library's character
403− width function, which is much more up to date (by several Unicode
404− versions) and returns the right width for a much larger set of
405− characters.
406−* Added a bundled version of the utf8proc C library.
407−
408−5.11.3
409−------
410−
411−* Fix mouse event offsets in mouse-up events
412−
413−5.11.2
414−------
415−
416−* Mouse events were modified so that the upper-left corner of the window
417− is (0,0) rather than (1,1).
418−
419−5.11.1
420−------
421−
422−* Add Generic instance for Image
423−* nextEvent: stop trying to refresh on a resize event (fixes segfault
424− on refresh with normal cursor positioning mode)
425−* Remove redundant clause from clipForCharWidth (thanks Eric Mertens)
426−* Update maintainer
427−
428−5.11
429−----
430−
431−* Vty now raises a VtyConfigurationError exception when the TERM
432− evironment variable is missing (thanks Eric Mertens)
433−* Graphics.Vty.Config got an explicit export list to avoid accidentally
434− exporting internal types (thanks Eric Mertens)
435−
436−5.10
437−----
438−
439−* Add absolute cursor positioning mode AbsoluteCursor to Cursor. This
440− mode provides greater control over cursor positioning by bypassing
441− the logical positioning provided by default. Rather than positioning
442− the cursor by looking at the widths of characters involved, this
443− constructor lets you provide a physical row and column instead. This
444− is useful in more sophisticated programs. (thanks Eric Mertens)
445−* Added a new Generic-derived config parser (thanks Eric Mertens)
446−* Fixed the MShift case in the configuration file parser (thanks Eric
447− Mertens)
448−* Fixed wcwidth import and matched safeWcswidth to its documented
449− behavior. Previously vty_mk_wcwidth was being imported with the
450− wrong type causing the -1 return value to be mapped to the wrong Int
451− value. Additionally safeWcswidth was using the unsafe character width
452− function and only ensuring that the final result was non-negative.
453− (thanks Eric Mertens)
454−
455−5.9.1
456−-----
457−
458−* Vty now only emits UTF8 charset sequences in terminals without a
459− preexisting UTF8 declaration to avoid emitting garbage sequences
460− (fixes #89)
461−
462−5.9
463−---
464−
465−* Added new Output methods supportsBell and ringTerminalBell to find out
466− whether the output device has an audio bell and to ring it (see #102)
467−
468−5.8.1
469−-----
470−
471−* Fixed "refresh" to work as advertised (see #104)
472−
473−5.8
474−---
475−
476−* API change: EvPaste input event now provides paste data as a raw
477− ByteString rather than a String to allow the application to decode how
478− best to decode it
479−
480−5.7.1
481−-----
482−
483−* ModeDemo: added an explicit Control.Applicative import for older GHCs
484−
485−5.7
486−---
487−
488−* Mouse and paste modes are now off by default.
489−* The Config type got new fields: mouseMode and bracketedPasteMode.
490− These determine whether these modes are enabled initially (for
491− terminals that support them).
492−* Added a Mode type for modal terminal features (mouse events,
493− bracketed paste mode) that is used with new Output interface
494− functions:
495− * supportsMode :: Mode -> Bool tells whether the device supports a
496− mode
497− * setMode :: Mode -> Bool -> IO () turns a mode on or off
498− * getModeStatus :: Mode -> IO Bool tells you whether a mode is on or
499− off
500−* Added a new demo program, ModeDemo.hs, to demonstrate usage of modes
501−
502−5.6
503−---
504−
505−* Added support for normal and extended mouse modes in Xterm-like
506− terminals via the MouseDown and MouseUp Event constructors
507−* Added support for bracketed paste mode in Xterm-like terminals via
508− the EvPaste event constructor
509−* Added derived Show instances for Event and Button (thanks Felix
510− Hirn)
511−* Now TERM values containing "screen" will automatically use the
512− XtermColor driver rather than just TerminfoBased
513−
514−5.5.0
515−-----
516−
517−* Replaced lens dependency with microlens, microlens-mtl, microlens-th
518− dependencies. Issue #90
519− * Thanks Jonathan Daugherty
520−* Cabal corrections.
521− * Thanks Lennart Spitzner
522−
523−5.4.0
524−-----
525−
526−* Changed eventChannel of Graphics.Vty.Input from Chan to
527− TChan. This enables clients to query if there are no pending
528− events. The Graphics.Vty interface nextEvent is unchanged.
529− Clients that use eventChannel directly will require updating.
530− https://github.com/coreyoconnor/vty/issues/60
531−
532−5.3.1
533−-----
534−
535−* Reverted cabal file to depend on Cabal >= 1.18 instead of 1.20 due to
536− possibly breaking this on reasonable GHC versions
537−
538−5.3
539−---
540−
541−* Upgraded QuickCheck dependency to 2.7
542−* The standard IO Config (standardIOConfig) was overriding any provided
543− application config. In addition, the inputFd and outputFd could not be
544− changed if mkVty was used. Fixed.
545−* Correct handling of display attributes at end of line. The output
546− attributes are set to default at the end of content for the line and
547− at the start of a new line. Previously the current attribute would
548− extend to the next start of content. This was odd to reason about and
549− was the cause of https://github.com/coreyoconnor/vty/issues/76 IIRC Yi
550− requires the old behavior to display the selection region correctly.
551−* shutdown of the input thread is now performed using killThread and
552− synchronization on an MVar. For correct handling of the terminal read
553− vmin and vtime the read must be a blocking read on an OS thread.
554− This places a threadWaitRead, which will be interrupted by the
555− killThread, prior to the uninterruptable read. An alternative would be
556− to re-import the read foreign call as interruptable.
557−
558−5.2.11
559−------
560−
561−* deepseq bounds increased for tests.
562−* Clean up warnings when compiling on 7.10
563− * Thanks Eric Mertens
564−* Avoid discarding input bytes after multi-byte encoded codepoint
565− * Thanks Eric Mertens
566−
567−5.2.10
568−------
569−
570−* "str" now returns EmptyImage for empty strings to match behavior of
571− other string-like Image constructors (fixes #74)
572− * Thanks Jonathan Daugherty
573−
574−5.2.9
575−-----
576−
577−* dependency version bumps
578− * https://github.com/coreyoconnor/vty/pull/71
579− * https://github.com/coreyoconnor/vty/pull/70
580−* Correct/Simplify the example code
581− * Thanks glguy
582− * https://github.com/coreyoconnor/vty/pull/69
583−
584−5.2.8
585−-----
586−
587−* blaze-builder, lens, utf8-string version constraint bump
588− * Thanks glguy
589− * https://github.com/coreyoconnor/vty/pull/67
590−* Do not differentiate based on TERM_PROGRAM
591− * https://github.com/coreyoconnor/vty/issues/68
592−
593−5.2.7
594−-----
595−
596−* lens and deepseq constraint bump + misc
597− * Thanks ethercrow
598− * https://github.com/coreyoconnor/vty/pull/66
599−
600−5.2.6
601−-----
602−
603−* lens constraint bump
604− * Thanks alexander-b!
605− * https://github.com/coreyoconnor/vty/pull/64
606−
607−5.2.5
608−-----
609−
610−* lens and random version constraint bump.
611− * Thanks RyanGlScott!
612− * https://github.com/coreyoconnor/vty/pull/62
613−
614−5.2.4
615−-----
616−
617−* removed -fpic from cc-options. No longer required.
618− * https://github.com/coreyoconnor/vty/issues/61
619− * https://ghc.haskell.org/trac/ghc/ticket/9657
620− * Thanks Fuuzetsu!
621−
622−5.2.3
623−-----
624−
625−* evaluate/compile the input parsing table once instead of each
626− keystroke.
627− * https://github.com/coreyoconnor/vty/pull/59
628− * Thanks ethercrow!
629−
630−5.2.2
631−-----
632−
633−* When looking at input for an event, don't look too deep.
634− * https://github.com/coreyoconnor/vty/pull/57
635− * Thanks ethercrow!
636−
637−5.2.1
638−-----
639−
640−* Bump upper version bound for lens to 4.5. Thanks markus1189!
641−
642−5.2.0
643−-----
644−
645−* Config structure now specifies file descriptor to use. The default
646− is stdInput and stdOutput file descriptors. Previously Vty used
647− stdInput for input and the follow code for output:
648− * hDuplicate stdout >>= handleToFd >>= (`hSetBuffering`
649− NoBuffering)
650− * the difference was required by Vty.Inline. Now, Vty.Inline uses
651− the Config structure options to acheive the same effect.
652−* removed: derivedVtime, derivedVmin, inputForCurrentTerminal,
653− inputForNameAndIO, outputForCurrentTerminal, outputForNameAndIO
654−* added: inputForConfig, outputForConfig
655−* updates to vty-rogue from jtdaugherty. Thanks!
656−* the oldest version of GHC tested to support vty is 7.6.2.
657−* the oldest version of GHC that vty compiles under is 7.4.2
658−
659−5.1.4
660−-----
661−
662−* merged https://github.com/coreyoconnor/vty/pull/51 thanks trofi!
663−
664−5.1.1
665−-----
666−
667−* merged https://github.com/coreyoconnor/vty/pull/48 thanks sjmielke!
668−* jtdaugherty resolved a number of compiler warnings. Thanks!
669−
670−5.1.0
671−-----
672−
673−* vmin and vtime can be specified however the application requires.
674− See Graphics.Vty.Config.
675−* fixed the processing of input when vmin is set > 1.
676−
677−5.0.0
678−-----
679−
680−* The naming convention now matches:
681− * http://www.haskell.org/haskellwiki/Programming_guidelines#Naming_Conventions
682−* all projects using vty for input must be compiled with -threaded.
683− Please notify vty author if this is not acceptable.
684−* mkVtyEscDelay has been removed. Use "mkVty def". Which initialized
685− vty with the default configuration.
686−* input handling changes
687− * KASCII is now KChar
688− * KPN5 is now KCenter
689− * tests exist.
690− * Applications can add to the input tables by setting inputMap of
691− the Config. See Graphics.Vty.Config
692− * Users can define input table extensions that will apply to all vty
693− applications. See Graphics.Vty.Config
694− * terminal timing is now handled by selecting an appropriate VTIME.
695− Previously this was implemented within Vty itself. This reduced
696− complexity in vty but provides a different meta key behavior and
697− implies a requirement on -threaded.
698− * The time vty will wait to verify an ESC byte means a single ESC
699− key is the singleEscPeriod of the Input Config structure.
700−* removed the typeclass based terminal and display context interface
701− in favor of a data structure of properties interface.
702−* renamed the Terminal interface to Output
703−* The default picture for an image now uses the "clear" background.
704− This background fills background spans with spaces or just ends the
705− line.
706− * Previously the background defaulted to the space character. This
707− causes issues copying text from a text editor. The text would end
708− up with extra spaces at the end of the line.
709−* Layer support
710− * Each layer is an image.
711− * The layers for a picture are a list of images.
712− * The first image is the top-most layer. The images are ordered from
713− top to bottom.
714− * The transparent areas for a layer are the backgroundFill areas.
715− backgroundFill is added to pad images when images of different
716− sizes are joined.
717− * If the background is clear there is no background layer.
718− * If there is a background character then the bottom layer is the
719− background layer.
720− * emptyPicture is a Picture with no layers and no cursor
721− * addToTop and addToBottom add a layer to the top and bottom of the
722− given Picture.
723−* compatibility improvements:
724− * terminfo based terminals with no cursor support are silently
725− accepted. The cursor visibility changes in the Picture will have
726− no effect.
727− * alternate (setf/setb) color maps supported. Though colors beyond
728− the first 8 are just a guess.
729− * added "rgbColor" for easy support of RGB specified colors.
730− * Both applications and users can add to the mapping used to
731− translate from input bytes to events.
732−* Additional information about input and output process can be
733− appended to a debug log
734− * Set environment variable VTY_DEBUG_LOG to path of debug log
735− * Or use "debugLog <path>" config directive
736− * Or set 'debugLog' property of the Config provided to mkVty.
737−* examples moved to vty-examples package. See test directory for cabal
738− file.
739− * vty-interactive-terminal-test
740− * interactive test. Useful for building a bug report for vty's
741− author.
742− * test/interactive_terminal_test.hs
743− * vty-event-echo
744− * view a input event log for vty. Example of interacting with
745− user.
746− * test/EventEcho.hs
747− * vty-rogue
748− * The start of a rogue-like game. Example of layers and image
749− build operations.
750− * test/Rogue.hs
751− * vty-benchmark
752− * benchmarks vty. A series of tests that push random pictures to
753− the terminal. The random pictures are generated using
754− QuickCheck. The same generators used in the automated tests.
755− * test/benchmark.hs
756−
757−4.7.0.0
758−-------
759−
760−API changes:
761−* Added Graphics.Vty.Image.crop: Ensure an image is no larger
762− than the specified size.
763−* Added Graphics.Vty.Image.pad: Ensure an image is no smaller
764− than the specified size.
765−* Added Graphics.Vty.Image.translate: Offset an image.
766−* Thanks Ben Boeckel <MathStuf@gmail.com> for these features.
767−
768−4.2.1.0
769−-------
770−
771−API changes:
772−* Attr record accessor fore_color changed to attr_fore_color
773−* Attr record accessor back_color changed to attr_back_color
774−* Attr record accessor style changed to attr_style
775−* Added an "inline" display attribute changing DSL:
776− * put_attr_change applies a display attribute change
777− immediately to a terminal
778− * For instance, can be used to change the display attrbiutes
779− of text output via putStrLn and putStr. EX:
780− "put_attr_change $ back_color red" will set the background
781− color to red.
782− * Changes do not apply to a Picture output via output_picture.
783− * See Graphics.Vty.Inline
784−* Moved all IO actions into any monad an instance of MonadIO
785−
786−4.0.0.1
787−-------
788−
789−* binding for mk_wcswidth was incorrect. Most platforms just
790− magically worked due to coincidence.
791−
792−4.0.0
793−-----
794−
795−API changes:
796−* "getSize" has been removed. Use "terminal vty >>= display_bounds"
797− where "vty" is an instance of the Vty data structure.
798−* added a "terminal" field to the Vty data structure. Accesses the
799− TerminalHandle associated with the Vty instance.
800−* Graphics.Vty.Types has undergone a number of changes. Summary:
801− * Partitioned into Graphics.Vty.Attributes for display attributes.
802− Graphics.Vty.Image for image combinators. Graphics.Vty.Picture
803− for final picture construction.
804−* Graphics.Vty.Attributes:
805− * "setFG" and "setBG" are now "with_fore_color" and
806− "with_back_color"
807− * All other "set.." equations similarly replaced.
808− * "attr" is now "def_attr", short for "default display attributes"
809− Also added a "current_attr" for "currently applied display
810− attributes"
811−* Graphics.Vty.Image:
812− * "horzcat" is now "horiz_cat"
813− * "vertcat" is now "vert_cat"
814− * "renderBS" is now "utf8_bytestring"
815− * "renderChar" is now "char"
816− * "renderFill" is now "char_fill"
817− * added a "utf8_string" and "string" (AKA "iso_10464_string") for
818− UTF-8 encoded Strings and ISO-10464 encoded Strings. String
819− literals in GHC have an ISO-10464 runtime representation.
820−* Graphics.Vty.Picture:
821− * exports Graphics.Vty.Image
822− * "pic" is now "pic_for_image"
823− * added API for setting background fill pattern.
824−* Completely rewritten output backend.
825− * Efficient, scanline style output span generator. Has not been
826− fully optimized, but good enough.
827− * The details required to display the desired picture on a
828− terminal are well encapsulated.
829− * Terminfo based display terminal implementation. With specialized
830− derivitives for xterm, Terminal.app, and iTerm.app.
831− * Attempts to robustly handle even terminals that don't
832− support all display attributes.
833− * I've tested the following terminals with success: iTerm.app,
834− Terminal.app, xterm, rxvt, mlterm, Eterm, gnome-terminal,
835− konsole, screen, linux vty. Hopefully you will be as
836− successfull.
837− * Improved unicode support. Double wide characters will display as
838− expected.
839−* 256 color support. See Graphics.Vty.Attributes.Color240. The actual
840− output color is adjusted according to the number of colors the
841− terminal supports.
842−* The Graphics.Vty.Image combinators no longer require matching
843− dimensions to arguments. Unspecified areas are filled in with a
844− user-customizable background pattern. See Graphics.Vty.Picture.
845−* output images are always cropped to display size.
846−* Significant code coverage by QuickCheck tests. An interactive test
847− for those final properties that couldn't be automatically verified.
848−
849−Issues resolved:
850−* "gnome terminal displays non-basic attributes as strikethrough"
851− * http://trac.haskell.org/vty/ticket/14
852−* "Multi-byte characters are not displayed correctly on update"
853− * http://trac.haskell.org/vty/ticket/10
854−* "Redraw does not handle rendering a line that extends beyond screen
855− width characters"
856− * http://trac.haskell.org/vty/ticket/13
857−* "The <|> and <-> combinators should be more forgiving of mismatched
858− dimensions"
859− * http://trac.haskell.org/vty/ticket/9
860−* "256-color support"
861− * http://trac.haskell.org/vty/ticket/19
docs/DESIGN → DESIGNrenamed

The content is the same.

LICENSE−2
@@ -1,5 +1,3 @@
1−BSD 3-Clause License
2−
31Copyright Stefan O'Rear 2006, Corey O'Connor 2008, Corey O'Connor 2009
42
53All rights reserved.
READMEnew file+48
@@ -0,0 +1,48 @@
1+vty is a terminal interface library.
2+
3+Vty currently provides:
4+
5+* Automatic handling of window resizes.
6+
7+* Supports Unicode characters on output, automatically setting and
8+ resetting UTF-8 mode for xterm. Other terminals are assumed to support
9+
10+* Efficient output.
11+
12+* Minimizes repaint area, thus virtually eliminating the flicker
13+ problem that plagues ncurses programs.
14+
15+* A pure, compositional interface for efficiently constructing display
16+ images.
17+
18+* Automatically decodes keyboard keys into (key,[modifier]) tuples.
19+
20+* Automatically supports refresh on Ctrl-L.
21+
22+* Automatically supports timeout after 50ms for lone ESC (a barely
23+ noticable delay)
24+
25+* Interface is designed for relatively easy compatible extension.
26+
27+* Supports all ANSI SGR-modes (defined in console_codes(4)) with
28+ a type-safe interface.
29+
30+* Properly handles cleanup.
31+
32+Current disadvantages:
33+
34+* The character encoding of the output terminal is assumed to be UTF-8.
35+
36+* Minimal support for special keys on terminals other than the
37+ linux-console. (F1-5 and arrow keys should work, but anything
38+ shifted isn't likely to.)
39+
40+* Uses the TIOCGWINSZ ioctl to find the current window size, which
41+ appears to be limited to Linux and *BSD.
42+
43+darcs get --tag=vty-4.0.0.1 http://code.haskell.org/vty/
44+
45+To compile the demonstration program: ghc --make test/Test.hs gwinsz.c
46+
47+The main documentation consists of the haddock-comments and the demonstration
48+program
README.mddeleted−201
@@ -1,201 +0,0 @@
1−[![Build Status](https://travis-ci.org/jtdaugherty/vty.png)](https://travis-ci.org/jtdaugherty/vty)
2−
3−`vty` is a terminal interface library. It provides a high-level
4−interface for doing terminal I/O. Vty is supported on GHC versions
5−7.10.1 and up.
6−
7−Install via `git` with:
8−
9−```
10−git clone git://github.com/jtdaugherty/vty.git
11−```
12−
13−Install via `cabal` with:
14−
15−```
16−cabal install vty
17−```
18−
19−# Features
20−
21−* Supports a large number of terminals, i.e., vt100, ansi, hurd, linux,
22− `screen`, etc., or anything with a sufficient terminfo entry.
23−
24−* Automatically handles window resizes.
25−
26−* Supports Unicode output on terminals with UTF-8 support.
27−
28−* Provides an efficient output algorithm. Output buffering and terminal
29− state changes are minimized.
30−
31−* Minimizes repaint area, which virtually eliminates the flicker
32− problems that plague ncurses programs.
33−
34−* Provides a pure, compositional interface for efficiently constructing
35− display images.
36−
37−* Automatically decodes keyboard keys into (key,[modifier]) tuples.
38−
39−* Automatically supports refresh on Ctrl-L.
40−
41−* Supports a keypress timeout after for lone ESC. The timeout is
42− customizable.
43−
44−* Provides extensible input and output interfaces.
45−
46−* Supports ANSI graphics modes (SGR as defined in `console_codes(4)`)
47− with a type-safe interface and graceful fallback for terminals
48− with limited or nonexistent support for such modes.
49−
50−* Properly handles cleanup (but not due to signals).
51−
52−* Provides a comprehensive test suite.
53−
54−* Supports "normal" and "extended" (SGR) mouse modes as described at
55− http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
56−
57−* Supports bracketed paste mode as described at
58− http://cirw.in/blog/bracketed-paste
59−
60−* Supports multi-column Unicode characters such as emoji characters. In
61− cases where Vty and your terminal emulator disagree on character
62− widths, Vty provides a tool `vty-build-width-table` and library
63− functionality to build a width table that will work for your terminal
64− and load it on application startup.
65−
66−# Development Notes
67−
68−Vty uses threads internally, so programs made with Vty need to be
69−compiled with the threaded runtime using the GHC `-threaded` option.
70−
71−# Platform Support
72−
73−## Posix Terminals
74−
75−For the most part, Vty uses `terminfo` to determine terminal protocol
76−with some special rules to handle some omissions from `terminfo`.
77−
78−## Windows
79−
80−Windows is not supported.
81−
82−# Multi-Column Character Support
83−
84−Vty supports rendering of multi-column characters such as two-column
85−Asian characters and Emoji characters. This section details how to
86−take advantage of this feature, since its behavior will depend on the
87−terminal emulator in use.
88−
89−Terminal emulators support Unicode to varying degrees, and each terminal
90−emulator relies on a table of column widths for each supported Unicode
91−character. Vty also needs to rely on such a table to compute the width
92−of Vty images to do image layout. Since those tables can disagree if
93−Vty and the terminal emulator support different versions of Unicode,
94−and since different terminal emulators will support different versions
95−of Unicode, it's likely that for some wide characters, Vty applications
96−will exhibit rendering problems. Those rendering problems arise from Vty
97−and the terminal emulator coming to different conclusions about how wide
98−some characters are.
99−
100−To address this, Vty supports loading custom character width tables
101−that are based on the terminal's behavior in order to eliminate these
102−disagreements. By default, though, Vty will use its built-in Unicode
103−character width table. Since the built-in table is likely to eventually
104−disagree with your terminal, Vty provides an API and a command-line tool
105−to generate and install custom tables.
106−
107−Custom Unicode width tables based on your terminal emulator can be
108−built by running Vty's built-in tool, `vty-build-width-table`. The tool
109−works by querying the current terminal emulator to obtain its width
110−measurements for the entire supported Unicode range. The
111−results are then saved to a disk file. These custom tables
112−can also be generated programmatically by using the API in
113−`Graphics.Vty.UnicodeWidthTable.Query`.
114−
115−Saved width tables can then be loaded in one of two ways:
116−
117−* Via the library API in `Graphics.Vty.UnicodeWidthTable.IO`
118−* By adding a `widthMap` directive to your Vty configuration file and
119− then invoking `mkVty` to initialize Vty
120−
121−The Vty configuration file supports the `widthMap` directive to allow
122−users to specify which custom width table should be loaded for a given
123−terminal type. This is done by specifying, e.g.,
124−
125−```
126−widthMap "xterm" "/path/to/map.dat"
127−```
128−
129−where the first argument is the value that `TERM` must have in order for
130−the table to be loaded, and the second argument is the path to the table
131−file itself as generated by the two alternatives listed above. If the
132−Vty configuration file contains multiple matching `widthMap` directives
133−for the current value of `TERM`, the last one listed in the file is
134−used.
135−
136−The tables declared in the configuration file are only ever
137−automatically loaded when applications set up Vty by calling
138−`Graphics.Vty.mkVty`.
139−
140−Before a custom table has been loaded, calls to the library's character
141−width functions (e.g. `wcwidth`) will use the default built-in table.
142−Once a custom table has been loaded, the functions will use the new
143−custom table. Only one custom table load can be performed in a Vty
144−program. Once a custom table has been loaded, it cannot be replaced or
145−removed.
146−
147−Without using a custom width table, users of Vty-based applications
148−are likely to eventually experience rendering problems with with wide
149−characters. We recommend that developers of Vty-based applications either:
150−
151−* Provide the `vty-build-width-table` tool and documentation for running
152− it and updating the Vty configuration, or
153−* Have the application invoke the Vty library's table-building
154− functionality and load the table at startup without using the Vty
155− configuration.
156−
157−The best option will depend on a number of factors: the user audience,
158−the amount of risk posed by wide character rendering, the terminal
159−emulators in use, etc.
160−
161−# Contributing
162−
163−If you decide to contribute, that's great! Here are some guidelines you
164−should consider to make submitting patches easier for all concerned:
165−
166− - If you want to take on big things, talk to me first; let's have a
167− design/vision discussion before you start coding. Create a GitHub
168− issue and we can use that as the place to hash things out.
169− - If you make changes, make them consistent with the syntactic
170− conventions already used in the codebase.
171− - Please provide Haddock documentation for any changes you make.
172−
173−# Known Issues
174−
175−* Terminals have numerous quirks and bugs, so mileage may vary. Please
176− report issues as you encounter them and provide details on your
177− terminal emulator, operating system, etc.
178−
179−* STOP, TERM and INT signals are not handled.
180−
181−* The character encoding of the terminal is assumed to be UTF-8 if
182− unicode is used.
183−
184−* Terminfo is assumed to be correct unless there is an override
185− configured. Some terminals will not have correct special key support
186− (shifted F10 etc). See `Config` for customizing vty's behavior for a
187− particular terminal.
188−
189−* Vty uses the `TIOCGWINSZ` ioctl to find the current window size, which
190− appears to be limited to Linux and BSD.
191−
192−# Further Reading
193−
194−Good sources of documentation for terminal programming are:
195−
196−* https://github.com/b4winckler/vim/blob/master/src/term.c
197−* http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
198−* http://ulisse.elettra.trieste.it/services/doc/serial/config.html
199−* http://www.leonerd.org.uk/hacks/hints/xterm-8bit.html
200−* http://www.unixwiz.net/techtips/termios-vmin-vtime.html
201−* http://vt100.net/docs/vt100-ug/chapter3.html
TODOnew file+15
@@ -0,0 +1,15 @@
1+- Improve input handling
2+ - base off of haskeline input system. The haskeline input system appears to be excellent and
3+ satisfy all of Vty's input requirements. The current haskeline distribution does not appear to
4+ export the required modules. Either:
5+ 0. Add the required exports to the haskeline distribution.
6+ - fine for development but complicates the UI for production clients. Though, exposing
7+ the modules would only complicate the appearance of haskeline's interface.
8+ 1. Partition the backend of haskeline into a separate package usable by both vty and
9+ haskeline.
10+- use compact-string for character encoding handling
11+- Custom cursor appearance handling?
12+ - specific color?
13+ - reverse video?
14+ - auto?
15+
cbits/get_tty_erase.cdeleted−18
@@ -1,18 +0,0 @@
1−#include <termios.h>
2−#include <stdio.h>
3−#include <unistd.h>
4−#include <stdlib.h>
5−
6−// Given a file descriptor for a terminal, get the ERASE character for
7−// the terminal. If the terminal info cannot be obtained, this returns
8−// zero.
9−char vty_get_tty_erase(int fd)
10−{
11− struct termios trm;
12−
13− if (0 == tcgetattr(fd, &trm)) {
14− return (char) trm.c_cc[VERASE];
15− } else {
16− return (char) 0;
17− }
18−}
cbits/gwinsz.c+6−15
@@ -1,18 +1,9 @@
11#include <sys/ioctl.h>
22
3−unsigned long vty_c_get_window_size(int fd) {
4− struct winsize w;
5− if (ioctl (fd, TIOCGWINSZ, &w) >= 0)
6− return (w.ws_row << 16) + w.ws_col;
7− else
8− return 0x190050;
9−}
10−
11−void vty_c_set_window_size(int fd, unsigned long val) {
12− struct winsize w;
13− if (ioctl(fd, TIOCGWINSZ, &w) >= 0) {
14− w.ws_row = val >> 16;
15− w.ws_col = val & 0xFFFF;
16− ioctl(fd, TIOCSWINSZ, &w);
17− }
3+unsigned long c_get_window_size(void) {
4+ struct winsize w;
5+ if (ioctl (0, TIOCGWINSZ, &w) >= 0)
6+ return (w.ws_row << 16) + w.ws_col;
7+ else
8+ return 0x190050;
189}
cbits/gwinsz.h+1−2
@@ -1,2 +1 @@
1−unsigned long vty_c_get_window_size(int fd);
2−unsigned long vty_c_set_window_size(int fd, unsigned long val);
1+unsigned long c_get_window_size(void);
cbits/mk_wcwidth.c+99−128
@@ -59,29 +59,7 @@
5959 * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
6060 */
6161
62−#include <HsFFI.h>
63−#include <stdlib.h>
64−#include <string.h>
65−
66−// The maximum size for a custom character width table is the total
67−// number of possible characters as dictated by the compiler.
68−#define MAX_CUSTOM_TABLE_SIZE (HS_CHAR_MAX + 1)
69−
70−// The pointer to the start of the custom character width table, if
71−// any. If this is NULL or this is set but the ready flag is false, the
72−// built-in tree search logic is used.
73−static uint8_t* custom_table = NULL;
74−
75−// The size of the custom table, in entries. This should only be set
76−// if custom_table is not NULL. Its value should be the size of the
77−// custom_table array.
78−static uint32_t custom_table_size = 0;
79−
80−// A flag indicating whether the custom table is ready for
81−// use. This should only be set once the table has been
82−// allocated with vty_init_custom_table and initialized with
83−// vty_set_custom_table_range.
84−static uint8_t custom_table_ready = 0;
62+#include <wchar.h>
8563
8664struct interval {
8765 int first;
@@ -89,7 +67,7 @@ struct interval {
8967};
9068
9169/* auxiliary function for binary search in interval table */
92−static int vty_bisearch(HsChar ucs, const struct interval *table, int max) {
70+static int bisearch(wchar_t ucs, const struct interval *table, int max) {
9371 int min = 0;
9472 int mid;
9573
@@ -141,7 +119,7 @@ static int vty_bisearch(HsChar ucs, const struct interval *table, int max) {
141119 * in ISO 10646.
142120 */
143121
144−static HsInt builtin_wcwidth(HsChar ucs)
122+int mk_wcwidth(wchar_t ucs)
145123{
146124 /* sorted list of non-overlapping intervals of non-spacing characters */
147125 /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */
@@ -203,13 +181,13 @@ static HsInt builtin_wcwidth(HsChar ucs)
203181 return -1;
204182
205183 /* binary search in table of non-spacing characters */
206− if (vty_bisearch(ucs, combining,
207− sizeof(combining) / sizeof(struct interval) - 1))
184+ if (bisearch(ucs, combining,
185+ sizeof(combining) / sizeof(struct interval) - 1))
208186 return 0;
209187
210188 /* if we arrive here, ucs is not a combining or C0/C1 control character */
211189
212− return 1 +
190+ return 1 +
213191 (ucs >= 0x1100 &&
214192 (ucs <= 0x115f || /* Hangul Jamo init. consonants */
215193 ucs == 0x2329 || ucs == 0x232a ||
@@ -225,114 +203,107 @@ static HsInt builtin_wcwidth(HsChar ucs)
225203 (ucs >= 0x30000 && ucs <= 0x3fffd)));
226204}
227205
228−// Return the width, in terminal cells, of the specified character.
229−//
230−// If the global custom width table is present, that table will be
231−// consulted for the character's width. If the character is not in
232−// the table, zero will be returned. If the custom width table is not
233−// present, the built-in width table will be used.
234−HsInt vty_mk_wcwidth(HsChar ch)
235−{
236− if (custom_table_ready) {
237− if ((ch >= 0) && (ch < custom_table_size)) {
238− return custom_table[ch];
239− } else {
240− return -1;
241− }
242− } else {
243− return builtin_wcwidth(ch);
244− }
245−}
246206
247−// Initialize a custom character width table.
248−//
249−// This allocates a new character width table of the specified size
250−// (in characters). If a custom table has already been allocated, this
251−// returns 1. Otherwise it allocates a new table, initializes all of its
252−// entries to 1, and returns zero.
253−//
254−// Note that this does *not* mark the table as ready for use. Until the
255−// table is marked ready, it will not be used by vty_mk_wcwidth. To mark
256−// the table as ready, call vty_activate_custom_table() after the table
257−// has been set up with calls to vty_set_custom_table_range.
258−int vty_init_custom_table(int size)
207+int mk_wcswidth(const wchar_t *pwcs, size_t n)
259208{
260− if (custom_table == NULL) {
261− if (size > 0 && size <= MAX_CUSTOM_TABLE_SIZE) {
262− custom_table_ready = 0;
263− custom_table = malloc(size);
264− memset(custom_table, 1, size);
265− custom_table_size = size;
266− return 0;
267− } else {
268− return 1;
269− }
270− } else {
271− return 1;
272− }
273−}
209+ int w, width = 0;
274210
275−// Set the specified character range in the custom width table to the
276−// specified width.
277−//
278−// This function sets 'width' as the character width for all entries
279−// in the custom character table starting at the 'start' entry and
280−// including all entries up to and including 'start + size - 1'.
281−//
282−// If this succeeds, it returns zero. If it fails, it returns 1. It
283−// fails if the table is not allocated, marked as ready (i.e. it is in
284−// use and has already been populated), or if the start or size values
285−// are not in bounds for the table.
286−int vty_set_custom_table_range(uint32_t start, uint32_t size, uint8_t width)
287−{
288− if ((custom_table == NULL) ||
289− (size >= custom_table_size) ||
290− (start >= custom_table_size) ||
291− ((start + 1) >= (custom_table_size - size)) ||
292− custom_table_ready) {
293− return 1;
294− } else {
295− memset(custom_table + start, width, size);
296− return 0;
297− }
298−}
211+ for (;*pwcs && n-- > 0; pwcs++)
212+ if ((w = mk_wcwidth(*pwcs)) < 0)
213+ return -1;
214+ else
215+ width += w;
299216
300−// Mark the allocated custom character width table as ready for use.
301−//
302−// After this call, further calls to vty_set_custom_table_range will
303−// fail.
304−//
305−// This function returns 0 if it succeeds. If it fails, it returns 1.
306−// It fails if the custom table is already ready or if it has not been
307−// allocated.
308−int vty_activate_custom_table()
309−{
310− if (custom_table_ready || (custom_table == NULL)) {
311− return 1;
312− } else {
313− custom_table_ready = 1;
314− return 0;
315− }
217+ return width;
316218}
317219
318−// Returns whether a custom character width table has been marked ready.
319−int vty_custom_table_ready()
220+
221+/*
222+ * The following functions are the same as mk_wcwidth() and
223+ * mk_wcswidth(), except that spacing characters in the East Asian
224+ * Ambiguous (A) category as defined in Unicode Technical Report #11
225+ * have a column width of 2. This variant might be useful for users of
226+ * CJK legacy encodings who want to migrate to UCS without changing
227+ * the traditional terminal character-width behaviour. It is not
228+ * otherwise recommended for general use.
229+ */
230+int mk_wcwidth_cjk(wchar_t ucs)
320231{
321− return custom_table_ready;
232+ /* sorted list of non-overlapping intervals of East Asian Ambiguous
233+ * characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */
234+ static const struct interval ambiguous[] = {
235+ { 0x00A1, 0x00A1 }, { 0x00A4, 0x00A4 }, { 0x00A7, 0x00A8 },
236+ { 0x00AA, 0x00AA }, { 0x00AE, 0x00AE }, { 0x00B0, 0x00B4 },
237+ { 0x00B6, 0x00BA }, { 0x00BC, 0x00BF }, { 0x00C6, 0x00C6 },
238+ { 0x00D0, 0x00D0 }, { 0x00D7, 0x00D8 }, { 0x00DE, 0x00E1 },
239+ { 0x00E6, 0x00E6 }, { 0x00E8, 0x00EA }, { 0x00EC, 0x00ED },
240+ { 0x00F0, 0x00F0 }, { 0x00F2, 0x00F3 }, { 0x00F7, 0x00FA },
241+ { 0x00FC, 0x00FC }, { 0x00FE, 0x00FE }, { 0x0101, 0x0101 },
242+ { 0x0111, 0x0111 }, { 0x0113, 0x0113 }, { 0x011B, 0x011B },
243+ { 0x0126, 0x0127 }, { 0x012B, 0x012B }, { 0x0131, 0x0133 },
244+ { 0x0138, 0x0138 }, { 0x013F, 0x0142 }, { 0x0144, 0x0144 },
245+ { 0x0148, 0x014B }, { 0x014D, 0x014D }, { 0x0152, 0x0153 },
246+ { 0x0166, 0x0167 }, { 0x016B, 0x016B }, { 0x01CE, 0x01CE },
247+ { 0x01D0, 0x01D0 }, { 0x01D2, 0x01D2 }, { 0x01D4, 0x01D4 },
248+ { 0x01D6, 0x01D6 }, { 0x01D8, 0x01D8 }, { 0x01DA, 0x01DA },
249+ { 0x01DC, 0x01DC }, { 0x0251, 0x0251 }, { 0x0261, 0x0261 },
250+ { 0x02C4, 0x02C4 }, { 0x02C7, 0x02C7 }, { 0x02C9, 0x02CB },
251+ { 0x02CD, 0x02CD }, { 0x02D0, 0x02D0 }, { 0x02D8, 0x02DB },
252+ { 0x02DD, 0x02DD }, { 0x02DF, 0x02DF }, { 0x0391, 0x03A1 },
253+ { 0x03A3, 0x03A9 }, { 0x03B1, 0x03C1 }, { 0x03C3, 0x03C9 },
254+ { 0x0401, 0x0401 }, { 0x0410, 0x044F }, { 0x0451, 0x0451 },
255+ { 0x2010, 0x2010 }, { 0x2013, 0x2016 }, { 0x2018, 0x2019 },
256+ { 0x201C, 0x201D }, { 0x2020, 0x2022 }, { 0x2024, 0x2027 },
257+ { 0x2030, 0x2030 }, { 0x2032, 0x2033 }, { 0x2035, 0x2035 },
258+ { 0x203B, 0x203B }, { 0x203E, 0x203E }, { 0x2074, 0x2074 },
259+ { 0x207F, 0x207F }, { 0x2081, 0x2084 }, { 0x20AC, 0x20AC },
260+ { 0x2103, 0x2103 }, { 0x2105, 0x2105 }, { 0x2109, 0x2109 },
261+ { 0x2113, 0x2113 }, { 0x2116, 0x2116 }, { 0x2121, 0x2122 },
262+ { 0x2126, 0x2126 }, { 0x212B, 0x212B }, { 0x2153, 0x2154 },
263+ { 0x215B, 0x215E }, { 0x2160, 0x216B }, { 0x2170, 0x2179 },
264+ { 0x2190, 0x2199 }, { 0x21B8, 0x21B9 }, { 0x21D2, 0x21D2 },
265+ { 0x21D4, 0x21D4 }, { 0x21E7, 0x21E7 }, { 0x2200, 0x2200 },
266+ { 0x2202, 0x2203 }, { 0x2207, 0x2208 }, { 0x220B, 0x220B },
267+ { 0x220F, 0x220F }, { 0x2211, 0x2211 }, { 0x2215, 0x2215 },
268+ { 0x221A, 0x221A }, { 0x221D, 0x2220 }, { 0x2223, 0x2223 },
269+ { 0x2225, 0x2225 }, { 0x2227, 0x222C }, { 0x222E, 0x222E },
270+ { 0x2234, 0x2237 }, { 0x223C, 0x223D }, { 0x2248, 0x2248 },
271+ { 0x224C, 0x224C }, { 0x2252, 0x2252 }, { 0x2260, 0x2261 },
272+ { 0x2264, 0x2267 }, { 0x226A, 0x226B }, { 0x226E, 0x226F },
273+ { 0x2282, 0x2283 }, { 0x2286, 0x2287 }, { 0x2295, 0x2295 },
274+ { 0x2299, 0x2299 }, { 0x22A5, 0x22A5 }, { 0x22BF, 0x22BF },
275+ { 0x2312, 0x2312 }, { 0x2460, 0x24E9 }, { 0x24EB, 0x254B },
276+ { 0x2550, 0x2573 }, { 0x2580, 0x258F }, { 0x2592, 0x2595 },
277+ { 0x25A0, 0x25A1 }, { 0x25A3, 0x25A9 }, { 0x25B2, 0x25B3 },
278+ { 0x25B6, 0x25B7 }, { 0x25BC, 0x25BD }, { 0x25C0, 0x25C1 },
279+ { 0x25C6, 0x25C8 }, { 0x25CB, 0x25CB }, { 0x25CE, 0x25D1 },
280+ { 0x25E2, 0x25E5 }, { 0x25EF, 0x25EF }, { 0x2605, 0x2606 },
281+ { 0x2609, 0x2609 }, { 0x260E, 0x260F }, { 0x2614, 0x2615 },
282+ { 0x261C, 0x261C }, { 0x261E, 0x261E }, { 0x2640, 0x2640 },
283+ { 0x2642, 0x2642 }, { 0x2660, 0x2661 }, { 0x2663, 0x2665 },
284+ { 0x2667, 0x266A }, { 0x266C, 0x266D }, { 0x266F, 0x266F },
285+ { 0x273D, 0x273D }, { 0x2776, 0x277F }, { 0xE000, 0xF8FF },
286+ { 0xFFFD, 0xFFFD }, { 0xF0000, 0xFFFFD }, { 0x100000, 0x10FFFD }
287+ };
288+
289+ /* binary search in table of non-spacing characters */
290+ if (bisearch(ucs, ambiguous,
291+ sizeof(ambiguous) / sizeof(struct interval) - 1))
292+ return 2;
293+
294+ return mk_wcwidth(ucs);
322295}
323296
324−// Deallocate the custom width table.
325−//
326−// This does nothing if there is no allocated custom width table, or if
327−// there is one but it is in use (marked ready). This is only useful if
328−// an initial allocation succeeds, but range population fails, after
329−// which point the application may want to deallocate the table to avoid
330−// leaving it in an intermediate state.
331−void vty_deallocate_custom_table()
297+
298+int mk_wcswidth_cjk(const wchar_t *pwcs, size_t n)
332299{
333− if ((custom_table != NULL) && (!custom_table_ready)) {
334− free(custom_table);
335− custom_table = NULL;
336− custom_table_size = 0;
337− }
300+ int w, width = 0;
301+
302+ for (;*pwcs && n-- > 0; pwcs++)
303+ if ((w = mk_wcwidth_cjk(*pwcs)) < 0)
304+ return -1;
305+ else
306+ width += w;
307+
308+ return width;
338309}
cbits/set_term_timing.c+6−5
@@ -3,11 +3,12 @@
33#include <unistd.h>
44#include <stdlib.h>
55
6−void vty_set_term_timing(int fd, int vmin, int vtime)
6+void set_term_timing(void)
77{
88 struct termios trm;
9− tcgetattr(fd, &trm);
10− trm.c_cc[VMIN] = vmin;
11− trm.c_cc[VTIME] = vtime;
12− tcsetattr(fd, TCSANOW, &trm);
9+ tcgetattr(STDIN_FILENO, &trm);
10+ trm.c_cc[VMIN] = 0;
11+ trm.c_cc[VTIME] = 0;
12+ tcsetattr(STDIN_FILENO, TCSANOW, &trm);
1313}
14+
ci/build-and-testdeleted−44
@@ -1,44 +0,0 @@
1−#!/usr/bin/env bash
2−set -ex
3−
4−source /etc/profile
5−
6−GHCVER=`ghc --version | cut -d' ' -f8`
7−
8−function init-sandbox() {
9− cabal sandbox init
10−}
11−
12−function build() {
13− if [[ "${GHCVER}" > "7.4.2" ]] ; then
14− cabal install --enable-tests --only-dependencies --force-reinstalls
15− else
16− echo "Skipping tests on ${GHCVER}"
17− cabal install --only-dependencies --force-reinstalls
18− fi
19−}
20−
21−function rebuild() {
22− rm -rf dist
23− build
24−}
25−
26−function rebuild-including-deps() {
27− rm -rf .cabal-sandbox
28− rm -rf cabal.sandbox.config
29− init-sandbox
30− build
31−}
32−
33−set -ex
34−
35−cd vty
36−if [[ ! -f cabal.sandbox.config ]] ; then
37− init-sandbox
38−fi
39−
40−build || rebuild || rebuild-including-deps
41−
42−if [[ "${GHCVER}" > "7.4.2" ]] ; then
43− cabal test
44−fi
ci/travis_ci_installdeleted−12
@@ -1,12 +0,0 @@
1−#!/bin/bash
2−set -ex
3−cabal --version
4−
5−GHC_IS_OLD=$(python -c "from distutils.version import StrictVersion as SV; print '$GHCVER' != 'head' and SV('$GHCVER') < SV('7.2.3')")
6−
7−if [[ "False" == "$GHC_IS_OLD" ]] ; then
8− /opt/cabal/${CABALVER}/bin/cabal install --only-dependencies --enable-tests
9−else
10− /opt/cabal/${CABALVER}/bin/cabal install --only-dependencies
11− echo "Skipping test dependency install on ${GHCVER}"
12−fi
ci/travis_ci_scriptdeleted−11
@@ -1,11 +0,0 @@
1−#!/bin/bash
2−set -ex
3−
4−GHC_IS_OLD=$(python -c "from distutils.version import StrictVersion as SV; print '$GHCVER' != 'head' and SV('$GHCVER') < SV('7.2.3')")
5−
6−if [[ "False" == "$GHC_IS_OLD" ]] ; then
7− /opt/cabal/${CABALVER}/bin/cabal configure --enable-tests && /opt/cabal/${CABALVER}/bin/cabal build && /opt/cabal/${CABALVER}/bin/cabal test
8−else
9− echo "Skipping tests on ${GHCVER}"
10− /opt/cabal/${CABALVER}/bin/cabal configure && /opt/cabal/${CABALVER}/bin/cabal build
11−fi
demos/Demo.hsdeleted−90
@@ -1,90 +0,0 @@
1−{-# LANGUAGE FlexibleContexts #-}
2−module Main where
3−
4−import Graphics.Vty
5−
6−import Control.Applicative hiding ((<|>))
7−import Control.Arrow
8−import Control.Monad.RWS
9−
10−import Data.Sequence (Seq, (<|) )
11−import qualified Data.Sequence as Seq
12−import Data.Foldable
13−
14−eventBufferSize = 1000
15−
16−type App = RWST Vty () (Seq String) IO
17−
18−main = do
19− vty <- if True -- change to false for emacs-like input processing
20− then mkVty defaultConfig
21− else mkVty (defaultConfig { vmin = Just 2, vtime = Just 300 } )
22− _ <- execRWST (vtyInteract False) vty Seq.empty
23− shutdown vty
24−
25−vtyInteract :: Bool -> App ()
26−vtyInteract shouldExit = do
27− updateDisplay
28− unless shouldExit $ handleNextEvent >>= vtyInteract
29−
30−introText = vertCat $ map (string defAttr)
31− [ "this line is hidden by the top layer"
32− , "The vty demo program will echo the events generated by the pressed keys."
33− , "Below there is a 240 color box."
34− , "Followed by a description of the 16 color palette."
35− , "Followed by tones of red using a 24-bit palette."
36− , "If the 240 color box is not visible then the terminal"
37− , "claims 240 colors are not supported."
38− , "Try setting TERM to xterm-256color"
39− , "This text is on a lower layer than the event list."
40− , "Which means it'll be hidden soon."
41− , "Bye!"
42− , "Great Faith in the ¯\\_(ツ)_/¯"
43− , "¯\\_(ツ)_/¯ ¯\\_(ツ)_/¯ ¯\\_(ツ)_/¯ ¯\\_(ツ)_/¯"
44− ]
45−
46−splitColorImages :: [Image] -> [[Image]]
47−splitColorImages [] = []
48−splitColorImages is = (take 20 is ++ [string defAttr " "]) : (splitColorImages (drop 20 is))
49−
50−fullcolorbox :: Image
51−fullcolorbox = vertCat $ map horizCat $ splitColorImages colorImages
52− where
53− colorImages = map (\i -> string (currentAttr `withBackColor` linearColor i 0 0) " ") [0..255]
54−
55−colorbox_240 :: Image
56−colorbox_240 = vertCat $ map horizCat $ splitColorImages colorImages
57− where
58− colorImages = map (\i -> string (currentAttr `withBackColor` Color240 i) " ") [0..239]
59−
60−colorbox_16 :: Image
61−colorbox_16 = border <|> column0 <|> border <|> column1 <|> border <|> column2 <|> border
62− where
63− column0 = vertCat $ map lineWithColor normal
64− column1 = vertCat $ map lineWithColor bright
65− border = vertCat $ replicate (length normal) $ string defAttr " | "
66− column2 = vertCat $ map (string defAttr . snd) normal
67− lineWithColor (c, cName) = string (defAttr `withForeColor` c) cName
68− normal = zip [ black, red, green, yellow, blue, magenta, cyan, white ]
69− [ "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white" ]
70− bright = zip [ brightBlack, brightRed, brightGreen, brightYellow, brightBlue
71− , brightMagenta, brightCyan, brightWhite ]
72− [ "bright black", "bright red", "bright green", "bright yellow"
73− , "bright blue", "bright magenta", "bright cyan", "bright white" ]
74−
75−updateDisplay :: App ()
76−updateDisplay = do
77− let info = string (defAttr `withForeColor` black `withBackColor` green)
78− "Press ESC to exit. Events for keys below."
79− eventLog <- foldMap (string defAttr) <$> get
80− let pic = picForImage (info <-> eventLog)
81− `addToBottom` (introText <-> colorbox_240 <|> colorbox_16 <|> fullcolorbox)
82− vty <- ask
83− liftIO $ update vty pic
84−
85−handleNextEvent = ask >>= liftIO . nextEvent >>= handleEvent
86− where
87− handleEvent e = do
88− modify $ (<|) (show e) >>> Seq.take eventBufferSize
89− return $ e == EvKey KEsc []
90−
demos/ModeDemo.hsdeleted−55
@@ -1,55 +0,0 @@
1−module Main where
2−
3−import Control.Applicative
4−import Data.Monoid
5−import Foreign.C.Types (CInt(..), CChar(..))
6−import System.Posix.Types (Fd(..))
7−
8−import Graphics.Vty
9−
10−mkUI :: (Bool, Bool, Bool, Bool) -> Maybe Event -> Image
11−mkUI (m, ms, p, ps) e =
12− vertCat [ string defAttr $ "Mouse mode supported: " <> show m
13− , string defAttr $ "Mouse mode status: " <> show ms
14− , string defAttr " "
15− , string defAttr $ "Paste mode supported: " <> show p
16− , string defAttr $ "Paste mode status: " <> show ps
17− , string defAttr " "
18− , string defAttr $ "Last event: " <> show e
19− , string defAttr " "
20− , string defAttr "Press 'm' to toggle mouse mode, 'p' to toggle paste mode, and 'q' to quit."
21− ]
22−
23−main :: IO ()
24−main = do
25− cfg <- standardIOConfig
26− vty <- mkVty cfg
27−
28− let renderUI lastE = do
29− let output = outputIface vty
30− info <- (,,,) <$> (pure $ supportsMode output Mouse)
31− <*> getModeStatus output Mouse
32− <*> (pure $ supportsMode output BracketedPaste)
33− <*> getModeStatus output BracketedPaste
34− return $ picForImage $ mkUI info lastE
35−
36− let go lastE = do
37− pic <- renderUI lastE
38− update vty pic
39− e <- nextEvent vty
40− case e of
41− EvKey (KChar 'q') [] -> return ()
42− EvKey (KChar 'm') [] -> do
43− let output = outputIface vty
44− enabled <- getModeStatus output Mouse
45− setMode output Mouse (not enabled)
46− go (Just e)
47− EvKey (KChar 'p') [] -> do
48− let output = outputIface vty
49− enabled <- getModeStatus output BracketedPaste
50− setMode output BracketedPaste (not enabled)
51− go (Just e)
52− _ -> go (Just e)
53−
54− go Nothing
55− shutdown vty
scripts/256colres.pldeleted−98
@@ -1,98 +0,0 @@
1−#! /usr/bin/perl
2−# $XTermId: 256colres.pl,v 1.16 2007/06/08 23:58:37 tom Exp $
3−# -----------------------------------------------------------------------------
4−# this file is part of xterm
5−#
6−# Copyright 1999-2002,2007 by Thomas E. Dickey
7−#
8−# All Rights Reserved
9−#
10−# Permission is hereby granted, free of charge, to any person obtaining a
11−# copy of this software and associated documentation files (the
12−# "Software"), to deal in the Software without restriction, including
13−# without limitation the rights to use, copy, modify, merge, publish,
14−# distribute, sublicense, and/or sell copies of the Software, and to
15−# permit persons to whom the Software is furnished to do so, subject to
16−# the following conditions:
17−#
18−# The above copyright notice and this permission notice shall be included
19−# in all copies or substantial portions of the Software.
20−#
21−# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22−# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23−# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
24−# IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
25−# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
26−# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
27−# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28−#
29−# Except as contained in this notice, the name(s) of the above copyright
30−# holders shall not be used in advertising or otherwise to promote the
31−# sale, use or other dealings in this Software without prior written
32−# authorization.
33−# -----------------------------------------------------------------------------
34−
35−# Construct a header file defining default resources for the 256-color model
36−# of xterm. This is modeled after the 256colors2.pl script.
37−
38−# use the resources for colors 0-15 - usually more-or-less a
39−# reproduction of the standard ANSI colors, but possibly more
40−# pleasing shades
41−
42−use strict;
43−
44−our ( $line1, $line2, $line3 );
45−our ( $red, $green, $blue, $gray );
46−our ( $level, $code, @steps );
47−
48−print <<EOF;
49−{-
50− - This header file was generated by $0
51− -}
52−module Graphics.Vty.Attributes.Color240 where
53−
54−import Graphics.Vty.Attributes.Color
55−
56−import Text.Printf
57−
58−-- | RGB color to 240 color palette.
59−--
60−-- generated from 256colres.pl which is forked from xterm 256colres.pl
61−-- todo: all values get clamped high.
62−rgbColor :: Integral i => i -> i -> i -> Color
63−rgbColor r g b
64− | r < 0 && g < 0 && b < 0 = error "rgbColor with negative color component intensity"
65−EOF
66−
67−my $grey_line=" | r == %d && g == %d && b == %d = Color240 %d\n";
68−my $rgb_line=" | r <= %d && g <= %d && b <= %d = Color240 %d\n";
69−
70−# colors 216-239 are a grayscale ramp, intentionally leaving out
71−# black and white
72−for ($gray = 0; $gray < 24; $gray++) {
73− $level = ($gray * 10) + 8;
74− $code = 216 + $gray;
75− printf($grey_line, $level, $level, $level, $code);
76−}
77−
78−# colors 16-231 are a 6x6x6 color cube
79−for ($red = 0; $red < 6; $red++) {
80− for ($green = 0; $green < 6; $green++) {
81− for ($blue = 0; $blue < 6; $blue++) {
82− $code = ($red * 36) + ($green * 6) + $blue;
83− printf($rgb_line,
84− ($red ? ($red * 40 + 55) : 0),
85− ($green ? ($green * 40 + 55) : 0),
86− ($blue ? ($blue * 40 + 55) : 0),
87− $code);
88− }
89− }
90−}
91−
92−print <<EOF;
93− | otherwise = error (printf "RGB color %d %d %d does not map to 240 palette."
94− (fromIntegral r :: Int)
95− (fromIntegral g :: Int)
96− (fromIntegral b :: Int))
97−
98−EOF
src/Codec/Binary/UTF8/Debug.hs+8−8
@@ -1,6 +1,6 @@
1−{-# OPTIONS_HADDOCK hide #-}
21-- Copyright 2009 Corey O'Connor
3−module Codec.Binary.UTF8.Debug where
2+module Codec.Binary.UTF8.Debug
3+ where
44
55import Codec.Binary.UTF8.String ( encode )
66
@@ -8,10 +8,10 @@ import Data.Word
88
99import Numeric
1010
11−-- | Converts an array of ISO-10646 characters (Char type) to an array
12−-- of Word8 bytes that is the corresponding UTF8 byte sequence
13−utf8FromIso :: [Int] -> [Word8]
14−utf8FromIso = encode . map toEnum
11+-- Converts an array of ISO-10646 characters (Char type) to an array of Word8 bytes that is the
12+-- corresponding UTF8 byte sequence
13+utf8_from_iso :: Integral i => [i] -> [Word8]
14+utf8_from_iso = encode . map toEnum
15+
16+pp_utf8 = print . map (\f -> f "") . map showHex . utf8_from_iso
1517
16−ppUtf8 :: [Int] -> IO ()
17−ppUtf8 = print . map (`showHex` "") . utf8FromIso
src/Codec/Binary/UTF8/Width.hsnew file+40
@@ -0,0 +1,40 @@
1+-- Copyright 2009 Corey O'Connor
2+{-# OPTIONS_GHC -D_XOPEN_SOURCE -fno-cse #-}
3+{-# LANGUAGE ForeignFunctionInterface, BangPatterns #-}
4+{-# INCLUDE <wchar.h> #-}
5+module Codec.Binary.UTF8.Width ( wcwidth
6+ , wcswidth
7+ )
8+ where
9+
10+import Foreign.C.Types
11+import Foreign.C.String
12+import Foreign.Storable
13+import Foreign.Ptr
14+
15+-- import Numeric ( showHex )
16+
17+import System.IO.Unsafe
18+
19+wcwidth :: Char -> Int
20+wcwidth c = unsafePerformIO (withCWString [c] $! \ws -> do
21+ wc <- peek ws
22+ -- putStr $ "wcwidth(0x" ++ showHex (fromEnum wc) "" ++ ")"
23+ let !w = fromIntegral $! wcwidth' wc
24+ -- putStrLn $ " -> " ++ show w
25+ return w
26+ )
27+{-# NOINLINE wcwidth #-}
28+
29+foreign import ccall unsafe "mk_wcwidth" wcwidth' :: CWchar -> CInt
30+
31+wcswidth :: String -> Int
32+wcswidth str = unsafePerformIO (withCWStringLen str $! \(ws, ws_len) -> do
33+ -- putStr $ "wcswidth(...)"
34+ let !w = fromIntegral $! wcswidth' ws (fromIntegral ws_len)
35+ -- putStrLn $ " -> " ++ show w
36+ return w
37+ )
38+{-# NOINLINE wcswidth #-}
39+
40+foreign import ccall unsafe "mk_wcswidth" wcswidth' :: Ptr CWchar -> CSize -> CInt
src/Data/Marshalling.hsnew file+31
@@ -0,0 +1,31 @@
1+{-# LANGUAGE BangPatterns #-}
2+-- Copyright 2009 Corey O'Connor
3+module Data.Marshalling ( module Data.Marshalling
4+ , module Data.Word
5+ , module Foreign.Ptr
6+ , module Foreign.ForeignPtr
7+ , module Foreign.Marshal
8+ , module Foreign.Storable
9+ )
10+ where
11+
12+import Control.Monad.Trans
13+
14+import Data.Word
15+
16+import Foreign.Ptr
17+import Foreign.ForeignPtr
18+import Foreign.Marshal
19+import Foreign.Marshal.Array
20+import Foreign.Storable
21+
22+type OutputBuffer = Ptr Word8
23+
24+string_to_bytes :: String -> [Word8]
25+string_to_bytes str = map (toEnum . fromEnum) str
26+
27+serialize_bytes :: MonadIO m => [Word8] -> OutputBuffer -> m OutputBuffer
28+serialize_bytes bytes !out_ptr = do
29+ liftIO $! pokeArray out_ptr bytes
30+ return $! out_ptr `plusPtr` ( length bytes )
31+
src/Data/Sequence/Extra.hs+2−3
@@ -1,4 +1,3 @@
1−{-# OPTIONS_HADDOCK hide #-}
21-- Copyright 2009 Corey O'Connor
32module Data.Sequence.Extra where
43
@@ -7,7 +6,7 @@ import Data.Sequence
76import Control.Parallel.Strategies
87
98instance NFData a => NFData (Seq a) where
10− rnf = rnf' . viewl
11− where
9+ rnf = \v -> rnf' (viewl v)
10+ where
1211 rnf' EmptyL = ()
1312 rnf' (a :< r) = rnf a >| rnf' (viewl r)
src/Data/Terminfo/Eval.hs+224−112
@@ -1,132 +1,244 @@
1+{-# LANGUAGE MagicHash #-}
12{-# LANGUAGE BangPatterns #-}
3+{-# LANGUAGE ScopedTypeVariables #-}
4+{-# LANGUAGE RecordWildCards #-}
5+{-# LANGUAGE FlexibleContexts #-}
26{-# LANGUAGE NoMonomorphismRestriction #-}
3−{-# OPTIONS_GHC -funbox-strict-fields #-}
4−{-# OPTIONS_HADDOCK hide #-}
5−
6−-- | Evaluates the paramaterized terminfo string capability with the
7−-- given parameters.
8−module Data.Terminfo.Eval
9− ( writeCapExpr
10− )
11−where
12−
13−import Blaze.ByteString.Builder.Word
14−import Blaze.ByteString.Builder
7+{-# LANGUAGE NamedFieldPuns #-}
8+{-# OPTIONS_GHC -funbox-strict-fields -O #-}
9+{- Evaluates the paramaterized terminfo string capability with the given parameters.
10+ -
11+ - todo: This can be greatly simplified.
12+ -}
13+module Data.Terminfo.Eval ( cap_expression_required_bytes
14+ , serialize_cap_expression
15+ , bytes_for_range
16+ )
17+ where
18+
19+import Data.Marshalling
1520import Data.Terminfo.Parse
1621
1722import Control.Monad.Identity
23+import Control.Monad.Reader
1824import Control.Monad.State.Strict
19−import Control.Monad.Writer
20−
21−import Data.Bits ((.|.), (.&.), xor)
22−import Data.List
25+import Control.Monad.Trans
2326
24−import qualified Data.Vector.Unboxed as Vector
27+import Data.Array.Unboxed
28+import Data.Bits ( (.|.), (.&.), xor )
29+import Data.List
2530
26−-- | capability evaluator state
27−data EvalState = EvalState
28− { evalStack :: ![CapParam]
29− , evalExpression :: !CapExpression
30− , evalParams :: ![CapParam]
31− }
31+import GHC.Prim
32+import GHC.Word
3233
33−type Eval a = StateT EvalState (Writer Write) a
34+type EvalT m a = ReaderT (CapExpression,[CapParam]) (StateT [CapParam] m) a
35+type Eval a = EvalT Identity a
3436
35−pop :: Eval CapParam
37+pop :: MonadState [CapParam] m => m CapParam
3638pop = do
37− s <- get
38− let v : stack' = evalStack s
39− s' = s { evalStack = stack' }
40− put s'
39+ v : stack <- get
40+ put stack
4141 return v
4242
43−readParam :: Word -> Eval CapParam
44−readParam pn = do
45− !params <- evalParams <$> get
43+read_param :: MonadReader (CapExpression, [CapParam]) m => Word -> m CapParam
44+read_param pn = do
45+ (_,params) <- ask
4646 return $! genericIndex params pn
4747
48−push :: CapParam -> Eval ()
48+push :: MonadState [CapParam] m => CapParam -> m ()
4949push !v = do
50− s <- get
51− let s' = s { evalStack = v : evalStack s }
52− put s'
53−
54−applyParamOps :: CapExpression -> [CapParam] -> [CapParam]
55−applyParamOps cap params = foldl applyParamOp params (paramOps cap)
56−
57−applyParamOp :: [CapParam] -> ParamOp -> [CapParam]
58−applyParamOp params IncFirstTwo = map (+ 1) params
59−
60−writeCapExpr :: CapExpression -> [CapParam] -> Write
61−writeCapExpr cap params =
62− let params' = applyParamOps cap params
63− s0 = EvalState [] cap params'
64− in snd $ runWriter (runStateT (writeCapOps (capOps cap)) s0)
65−
66−writeCapOps :: CapOps -> Eval ()
67−writeCapOps = mapM_ writeCapOp
68−
69−writeCapOp :: CapOp -> Eval ()
70−writeCapOp (Bytes !offset !count) = do
71− !cap <- evalExpression <$> get
72− let bytes = Vector.take count $ Vector.drop offset (capBytes cap)
73− Vector.forM_ bytes $ tell.writeWord8
74−writeCapOp DecOut = do
50+ stack <- get
51+ put (v : stack)
52+
53+apply_param_ops :: CapExpression -> [CapParam] -> [CapParam]
54+apply_param_ops cap params = foldl apply_param_op params (param_ops cap)
55+
56+apply_param_op :: [CapParam] -> ParamOp -> [CapParam]
57+apply_param_op params IncFirstTwo = map (+ 1) params
58+
59+-- | range is 0-based offset into cap_bytes and count
60+--
61+-- todo: The returned list is not assured to have a length st. length == count
62+bytes_for_range :: CapExpression -> Word8 -> Word8 -> [Word8]
63+bytes_for_range cap !offset !count
64+ = take (fromEnum count)
65+ $ drop (fromEnum offset)
66+ $ elems
67+ $ cap_bytes cap
68+
69+cap_expression_required_bytes :: CapExpression -> [CapParam] -> Word
70+cap_expression_required_bytes cap params =
71+ let params' = apply_param_ops cap params
72+ in fst $! runIdentity $ runStateT (runReaderT (cap_ops_required_bytes $ cap_ops cap) (cap, params')) []
73+
74+cap_ops_required_bytes :: CapOps -> Eval Word
75+cap_ops_required_bytes ops = do
76+ counts <- mapM cap_op_required_bytes ops
77+ return $ sum counts
78+
79+cap_op_required_bytes :: CapOp -> Eval Word
80+cap_op_required_bytes (Bytes _ c) = return $ toEnum $ fromEnum c
81+cap_op_required_bytes DecOut = do
7582 p <- pop
76− forM_ (show p) $ tell.writeWord8.toEnum.fromEnum
77−writeCapOp CharOut = do
78− pop >>= tell.writeWord8.toEnum.fromEnum
79−writeCapOp (PushParam pn) = do
80− readParam pn >>= push
81−writeCapOp (PushValue v) = do
83+ return $ toEnum $ length $ show p
84+cap_op_required_bytes CharOut = do
85+ pop
86+ return 1
87+cap_op_required_bytes (PushParam pn) = do
88+ read_param pn >>= push
89+ return 0
90+cap_op_required_bytes (PushValue v) = do
8291 push v
83−writeCapOp (Conditional expr parts) = do
84− writeCapOps expr
85− writeContitionalParts parts
86− where
87− writeContitionalParts [] = return ()
88− writeContitionalParts ((trueOps, falseOps) : falseParts) = do
92+ return 0
93+cap_op_required_bytes (Conditional expr parts) = do
94+ c_expr <- cap_ops_required_bytes expr
95+ c_parts <- cond_parts_required_bytes parts
96+ return $ c_expr + c_parts
97+ where
98+ cond_parts_required_bytes [] = return 0
99+ cond_parts_required_bytes ( (true_ops, false_ops) : false_parts ) = do
89100 -- (man 5 terminfo)
90− -- Usually the %? expr part pushes a value onto the stack,
91− -- and %t pops it from the stack, testing if it is nonzero
92− -- (true). If it is zero (false), control passes to the %e
93− -- (else) part.
101+ -- Usually the %? expr part pushes a value onto the stack, and %t pops it from the
102+ -- stack, testing if it is nonzero (true). If it is zero (false), control
103+ -- passes to the %e (else) part.
94104 v <- pop
95− if v /= 0
96− then writeCapOps trueOps
97− else do
98− writeCapOps falseOps
99− writeContitionalParts falseParts
100−
101−writeCapOp BitwiseOr = do
102− v0 <- pop
103− v1 <- pop
104− push $ v0 .|. v1
105−writeCapOp BitwiseAnd = do
106− v0 <- pop
107− v1 <- pop
108− push $ v0 .&. v1
109−writeCapOp BitwiseXOr = do
110− v1 <- pop
111− v0 <- pop
112− push $ v0 `xor` v1
113−writeCapOp ArithPlus = do
114− v1 <- pop
115− v0 <- pop
116− push $ v0 + v1
117−writeCapOp ArithMinus = do
118− v1 <- pop
119− v0 <- pop
120− push $ v0 - v1
121−writeCapOp CompareEq = do
122− v1 <- pop
123− v0 <- pop
124− push $ if v0 == v1 then 1 else 0
125−writeCapOp CompareLt = do
126− v1 <- pop
127− v0 <- pop
128− push $ if v0 < v1 then 1 else 0
129−writeCapOp CompareGt = do
130− v1 <- pop
131− v0 <- pop
132− push $ if v0 > v1 then 1 else 0
105+ c_total <- if v /= 0
106+ then cap_ops_required_bytes true_ops
107+ else do
108+ c_false <- cap_ops_required_bytes false_ops
109+ c_remain <- cond_parts_required_bytes false_parts
110+ return $ c_false + c_remain
111+ return c_total
112+cap_op_required_bytes BitwiseOr = do
113+ v_1 <- pop
114+ v_0 <- pop
115+ push $ v_0 .|. v_1
116+ return 0
117+cap_op_required_bytes BitwiseAnd = do
118+ v_1 <- pop
119+ v_0 <- pop
120+ push $ v_0 .&. v_1
121+ return 0
122+cap_op_required_bytes BitwiseXOr = do
123+ v_1 <- pop
124+ v_0 <- pop
125+ push $ v_0 `xor` v_1
126+ return 0
127+cap_op_required_bytes ArithPlus = do
128+ v_1 <- pop
129+ v_0 <- pop
130+ push $ v_0 + v_1
131+ return 0
132+cap_op_required_bytes ArithMinus = do
133+ v_1 <- pop
134+ v_0 <- pop
135+ push $ v_0 - v_1
136+ return 0
137+cap_op_required_bytes CompareEq = do
138+ v_1 <- pop
139+ v_0 <- pop
140+ push $ if v_0 == v_1 then 1 else 0
141+ return 0
142+cap_op_required_bytes CompareLt = do
143+ v_1 <- pop
144+ v_0 <- pop
145+ push $ if v_0 < v_1 then 1 else 0
146+ return 0
147+cap_op_required_bytes CompareGt = do
148+ v_1 <- pop
149+ v_0 <- pop
150+ push $ if v_0 > v_1 then 1 else 0
151+ return 0
152+
153+serialize_cap_expression :: MonadIO m => CapExpression -> [CapParam] -> OutputBuffer -> m OutputBuffer
154+serialize_cap_expression cap params out_ptr = do
155+ let params' = apply_param_ops cap params
156+ (!out_ptr', _) <- runStateT (runReaderT (serialize_cap_ops out_ptr (cap_ops cap)) (cap, params')) []
157+ return out_ptr'
158+
159+serialize_cap_ops :: MonadIO m => OutputBuffer -> CapOps -> EvalT m OutputBuffer
160+serialize_cap_ops out_ptr ops = foldM serialize_cap_op out_ptr ops
161+
162+serialize_cap_op :: MonadIO m => OutputBuffer -> CapOp -> EvalT m OutputBuffer
163+serialize_cap_op out_ptr (Bytes offset c) = do
164+ (cap, _) <- ask
165+ let out_bytes = bytes_for_range cap offset c
166+ serialize_bytes out_bytes out_ptr
167+serialize_cap_op out_ptr DecOut = do
168+ p <- pop
169+ let out_str = show p
170+ out_bytes = string_to_bytes out_str
171+ serialize_bytes out_bytes out_ptr
172+serialize_cap_op out_ptr CharOut = do
173+ W# p <- pop
174+ -- todo? Truncate the character value to a single byte?
175+ let !out_byte = W8# (and# p 0xFF##)
176+ !out_ptr' = out_ptr `plusPtr` 1
177+ liftIO $ poke out_ptr out_byte
178+ return out_ptr'
179+serialize_cap_op out_ptr (PushParam pn) = do
180+ read_param pn >>= push
181+ return out_ptr
182+serialize_cap_op out_ptr (PushValue v) = do
183+ push v
184+ return out_ptr
185+serialize_cap_op out_ptr (Conditional expr parts) = do
186+ out_ptr' <- serialize_cap_ops out_ptr expr
187+ out_ptr'' <- serialize_cond_parts out_ptr' parts
188+ return out_ptr''
189+ where
190+ serialize_cond_parts ptr [] = return ptr
191+ serialize_cond_parts ptr ( (true_ops, false_ops) : false_parts ) = do
192+ -- (man 5 terminfo)
193+ -- Usually the %? expr part pushes a value onto the stack, and %t pops it from the
194+ -- stack, testing if it is nonzero (true). If it is zero (false), control
195+ -- passes to the %e (else) part.
196+ v <- pop
197+ ptr'' <- if v /= 0
198+ then serialize_cap_ops ptr true_ops
199+ else do
200+ ptr' <- serialize_cap_ops ptr false_ops
201+ serialize_cond_parts ptr' false_parts
202+ return ptr''
203+
204+serialize_cap_op out_ptr BitwiseOr = do
205+ v_0 <- pop
206+ v_1 <- pop
207+ push $ v_0 .|. v_1
208+ return out_ptr
209+serialize_cap_op out_ptr BitwiseAnd = do
210+ v_0 <- pop
211+ v_1 <- pop
212+ push $ v_0 .&. v_1
213+ return out_ptr
214+serialize_cap_op out_ptr BitwiseXOr = do
215+ v_1 <- pop
216+ v_0 <- pop
217+ push $ v_0 `xor` v_1
218+ return out_ptr
219+serialize_cap_op out_ptr ArithPlus = do
220+ v_1 <- pop
221+ v_0 <- pop
222+ push $ v_0 + v_1
223+ return out_ptr
224+serialize_cap_op out_ptr ArithMinus = do
225+ v_1 <- pop
226+ v_0 <- pop
227+ push $ v_0 - v_1
228+ return out_ptr
229+serialize_cap_op out_ptr CompareEq = do
230+ v_1 <- pop
231+ v_0 <- pop
232+ push $ if v_0 == v_1 then 1 else 0
233+ return out_ptr
234+serialize_cap_op out_ptr CompareLt = do
235+ v_1 <- pop
236+ v_0 <- pop
237+ push $ if v_0 < v_1 then 1 else 0
238+ return out_ptr
239+serialize_cap_op out_ptr CompareGt = do
240+ v_1 <- pop
241+ v_0 <- pop
242+ push $ if v_0 > v_1 then 1 else 0
243+ return out_ptr
244+
src/Data/Terminfo/Parse.hs+248−276
@@ -1,348 +1,320 @@
1+{-# LANGUAGE ScopedTypeVariables #-}
12{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE CPP #-}
3+{-# LANGUAGE FlexibleContexts #-}
34{-# LANGUAGE NoMonomorphismRestriction #-}
4−{-# LANGUAGE ScopedTypeVariables #-}
5+{-# LANGUAGE NamedFieldPuns #-}
56{-# OPTIONS_GHC -funbox-strict-fields -O #-}
6−{-# OPTIONS_HADDOCK hide #-}
7−
8−module Data.Terminfo.Parse
9− ( module Data.Terminfo.Parse
10− , Text.Parsec.ParseError
11− )
12−where
7+module Data.Terminfo.Parse ( module Data.Terminfo.Parse
8+ , Text.ParserCombinators.Parsec.ParseError
9+ )
10+ where
1311
1412import Control.Monad ( liftM )
15−import Control.DeepSeq
13+import Control.Parallel.Strategies
1614
17−#if !(MIN_VERSION_base(4,11,0))
18−import Data.Semigroup (Semigroup(..))
19−#endif
15+import Data.Array.Unboxed
16+import Data.Monoid
2017import Data.Word
21−import qualified Data.Vector.Unboxed as Vector
2218
23−import Numeric (showHex)
19+import Text.ParserCombinators.Parsec
2420
25−import Text.Parsec
21+type BytesLength = Word8
22+type BytesOffset = Word8
23+type CapBytes = UArray Word8 Word8
2624
2725data CapExpression = CapExpression
28− { capOps :: !CapOps
29− , capBytes :: !(Vector.Vector Word8)
30− , sourceString :: !String
31− , paramCount :: !Int
32− , paramOps :: !ParamOps
33− } deriving (Eq)
34−
35−instance Show CapExpression where
36− show c
37− = "CapExpression { " ++ show (capOps c) ++ " }"
38− ++ " <- [" ++ hexDump ( map ( toEnum . fromEnum ) $! sourceString c ) ++ "]"
39− ++ " <= " ++ show (sourceString c)
40− where
41− hexDump :: [Word8] -> String
42− hexDump = foldr showHex ""
26+ { cap_ops :: !CapOps
27+ , cap_bytes :: !CapBytes
28+ , source_string :: !String
29+ , param_count :: !Word
30+ , param_ops :: !ParamOps
31+ }
4332
4433instance NFData CapExpression where
45− rnf (CapExpression ops !_bytes !str !c !pOps)
46− = rnf ops `seq` rnf str `seq` rnf c `seq` rnf pOps
34+ rnf (CapExpression ops !_bytes !_str !_c !_p_ops)
35+ = rnf ops
4736
4837type CapParam = Word
4938
5039type CapOps = [CapOp]
51−data CapOp =
52− Bytes !Int !Int -- offset count
40+data CapOp =
41+ Bytes !BytesOffset !BytesLength
5342 | DecOut | CharOut
54− -- This stores a 0-based index to the parameter. However the
55− -- operation that implies this op is 1-based
43+ -- This stores a 0-based index to the parameter. However the operation that implies this op is
44+ -- 1-based
5645 | PushParam !Word | PushValue !Word
57− -- The conditional parts are the sequence of (%t expression, %e
58− -- The expression) pairs. %e expression may be NOP
59− | Conditional
60− { conditionalExpr :: !CapOps
61− , conditionalParts :: ![(CapOps, CapOps)]
46+ -- The conditional parts are the sequence of (%t expression, %e expression) pairs.
47+ -- The %e expression may be NOP
48+ | Conditional
49+ { conditional_expr :: !CapOps
50+ , conditional_parts :: ![(CapOps, CapOps)]
6251 }
6352 | BitwiseOr | BitwiseXOr | BitwiseAnd
6453 | ArithPlus | ArithMinus
6554 | CompareEq | CompareLt | CompareGt
66− deriving (Show, Eq)
55+ deriving ( Show )
6756
6857instance NFData CapOp where
69− rnf (Bytes offset byteCount ) = rnf offset `seq` rnf byteCount
70− rnf (PushParam pn) = rnf pn
71− rnf (PushValue v) = rnf v
72− rnf (Conditional cExpr cParts) = rnf cExpr `seq` rnf cParts
73− rnf BitwiseOr = ()
74− rnf BitwiseXOr = ()
75− rnf BitwiseAnd = ()
76− rnf ArithPlus = ()
77− rnf ArithMinus = ()
78− rnf CompareEq = ()
79− rnf CompareLt = ()
80− rnf CompareGt = ()
81− rnf DecOut = ()
82− rnf CharOut = ()
58+ rnf (Bytes offset c) = rnf offset >| rnf c
59+ rnf (PushParam !_pn) = ()
60+ rnf (PushValue !_v) = ()
61+ rnf (Conditional c_expr c_parts) = rnf c_expr >| rnf c_parts
62+ rnf _ = ()
8363
8464type ParamOps = [ParamOp]
8565data ParamOp =
8666 IncFirstTwo
87− deriving (Show, Eq)
88−
89−instance NFData ParamOp where
90− rnf IncFirstTwo = ()
91−
92−parseCapExpression :: String -> Either ParseError CapExpression
93−parseCapExpression capString =
94− let v = runParser capExpressionParser
95− initialBuildState
96− "terminfo cap"
97− capString
67+ deriving ( Show )
68+
69+parse_cap_expression :: String -> Either ParseError CapExpression
70+parse_cap_expression cap_string =
71+ let v = runParser cap_expression_parser
72+ initial_build_state
73+ "terminfo cap"
74+ cap_string
9875 in case v of
9976 Left e -> Left e
100− Right buildResults -> Right $ constructCapExpression capString buildResults
101−
102−constructCapExpression :: String -> BuildResults -> CapExpression
103−constructCapExpression capString buildResults =
104− let expr = CapExpression
105− { capOps = outCapOps buildResults
106− -- The cap bytes are the lower 8 bits of the input
107− -- string's characters.
108− , capBytes = Vector.fromList $ map (toEnum.fromEnum) capString
109− , sourceString = capString
110− , paramCount = outParamCount buildResults
111− , paramOps = outParamOps buildResults
112− }
113− in rnf expr `seq` expr
114−
115−type CapParser a = Parsec String BuildState a
116−
117−capExpressionParser :: CapParser BuildResults
118−capExpressionParser = do
119− rs <- many $ paramEscapeParser <|> bytesOpParser
77+ Right build_results ->
78+ Right $! ( CapExpression
79+ { cap_ops = out_cap_ops build_results
80+ -- The cap bytes are the lower 8 bits of the input string's characters.
81+ -- \todo Verify the input string actually contains an 8bit byte per character.
82+ , cap_bytes = listArray (0, toEnum $ length cap_string - 1)
83+ $ map (toEnum . fromEnum) cap_string
84+ , source_string = cap_string
85+ , param_count = out_param_count build_results
86+ , param_ops = out_param_ops build_results
87+ }
88+ `using` rnf
89+ )
90+
91+type CapParser a = GenParser Char BuildState a
92+
93+cap_expression_parser :: CapParser BuildResults
94+cap_expression_parser = do
95+ rs <- many $ param_escape_parser <|> bytes_op_parser
12096 return $ mconcat rs
12197
122−paramEscapeParser :: CapParser BuildResults
123−paramEscapeParser = do
124− _ <- char '%'
125− incOffset 1
126− literalPercentParser <|> paramOpParser
127−
128−literalPercentParser :: CapParser BuildResults
129−literalPercentParser = do
130− _ <- char '%'
131− startOffset <- nextOffset <$> getState
132− incOffset 1
133− return $ BuildResults 0 [Bytes startOffset 1] []
134−
135−paramOpParser :: CapParser BuildResults
136−paramOpParser
137− = incrementOpParser
138− <|> pushOpParser
139− <|> decOutParser
140− <|> charOutParser
141− <|> conditionalOpParser
142− <|> bitwiseOpParser
143− <|> arithOpParser
144− <|> literalIntOpParser
145− <|> compareOpParser
146− <|> charConstParser
147−
148−incrementOpParser :: CapParser BuildResults
149−incrementOpParser = do
150− _ <- char 'i'
151− incOffset 1
98+param_escape_parser :: CapParser BuildResults
99+param_escape_parser = do
100+ char '%'
101+ inc_offset 1
102+ literal_percent_parser <|> param_op_parser
103+
104+literal_percent_parser :: CapParser BuildResults
105+literal_percent_parser = do
106+ char '%'
107+ start_offset <- getState >>= return . next_offset
108+ inc_offset 1
109+ return $ BuildResults 0 [Bytes start_offset 1] []
110+
111+param_op_parser :: CapParser BuildResults
112+param_op_parser
113+ = increment_op_parser
114+ <|> push_op_parser
115+ <|> dec_out_parser
116+ <|> char_out_parser
117+ <|> conditional_op_parser
118+ <|> bitwise_op_parser
119+ <|> arith_op_parser
120+ <|> literal_int_op_parser
121+ <|> compare_op_parser
122+ <|> char_const_parser
123+
124+increment_op_parser :: CapParser BuildResults
125+increment_op_parser = do
126+ char 'i'
127+ inc_offset 1
152128 return $ BuildResults 0 [] [ IncFirstTwo ]
153129
154−pushOpParser :: CapParser BuildResults
155−pushOpParser = do
156− _ <- char 'p'
157− paramN <- read . pure <$> digit
158− incOffset 2
159− return $ BuildResults (fromEnum paramN) [PushParam $ paramN - 1] []
160−
161−decOutParser :: CapParser BuildResults
162−decOutParser = do
163− _ <- char 'd'
164− incOffset 1
130+push_op_parser :: CapParser BuildResults
131+push_op_parser = do
132+ char 'p'
133+ param_n <- digit >>= return . (\d -> read [d])
134+ inc_offset 2
135+ return $ BuildResults param_n [ PushParam $ param_n - 1 ] []
136+
137+dec_out_parser :: CapParser BuildResults
138+dec_out_parser = do
139+ char 'd'
140+ inc_offset 1
165141 return $ BuildResults 0 [ DecOut ] []
166142
167−charOutParser :: CapParser BuildResults
168−charOutParser = do
169− _ <- char 'c'
170− incOffset 1
143+char_out_parser :: CapParser BuildResults
144+char_out_parser = do
145+ char 'c'
146+ inc_offset 1
171147 return $ BuildResults 0 [ CharOut ] []
172148
173−conditionalOpParser :: CapParser BuildResults
174−conditionalOpParser = do
175− _ <- char '?'
176− incOffset 1
177− condPart <- manyExpr conditionalTrueParser
178− parts <- manyP
179− ( do
180− truePart <- manyExpr $ choice [ try $ lookAhead conditionalEndParser
181− , conditionalFalseParser
182− ]
183− falsePart <- manyExpr $ choice [ try $ lookAhead conditionalEndParser
184− , conditionalTrueParser
185− ]
186− return ( truePart, falsePart )
187− )
188− conditionalEndParser
189−
190− let trueParts = map fst parts
191− falseParts = map snd parts
192− BuildResults n cond condParamOps = condPart
193−
194− let n' = maximum $ n : map outParamCount trueParts
195− n'' = maximum $ n' : map outParamCount falseParts
196−
197− let trueOps = map outCapOps trueParts
198− falseOps = map outCapOps falseParts
199− condParts = zip trueOps falseOps
200−
201− let trueParamOps = mconcat $ map outParamOps trueParts
202− falseParamOps = mconcat $ map outParamOps falseParts
203− pOps = mconcat [condParamOps, trueParamOps, falseParamOps]
204−
205− return $ BuildResults n'' [ Conditional cond condParts ] pOps
206−
207− where
208− manyP !p !end = choice
149+conditional_op_parser :: CapParser BuildResults
150+conditional_op_parser = do
151+ char '?'
152+ inc_offset 1
153+ cond_part <- many_expr conditional_true_parser
154+ parts <- many_p
155+ ( do
156+ true_part <- many_expr $ choice [ try $ lookAhead conditional_end_parser
157+ , conditional_false_parser
158+ ]
159+ false_part <- many_expr $ choice [ try $ lookAhead conditional_end_parser
160+ , conditional_true_parser
161+ ]
162+ return ( true_part, false_part )
163+ )
164+ conditional_end_parser
165+
166+ let true_parts = map fst parts
167+ false_parts = map snd parts
168+ BuildResults n cond cond_param_ops = cond_part
169+
170+ let n' = maximum $ n : map out_param_count true_parts
171+ n'' = maximum $ n' : map out_param_count false_parts
172+
173+ let true_ops = map out_cap_ops true_parts
174+ false_ops = map out_cap_ops false_parts
175+ cond_parts = zip true_ops false_ops
176+
177+ let true_param_ops = mconcat $ map out_param_ops true_parts
178+ false_param_ops = mconcat $ map out_param_ops false_parts
179+ p_ops = mconcat [cond_param_ops, true_param_ops, false_param_ops]
180+
181+ return $ BuildResults n'' [ Conditional cond cond_parts ] p_ops
182+
183+ where
184+ many_p !p !end = choice
209185 [ try end >> return []
210− , do !v <- p
211− !vs <- manyP p end
186+ , do !v <- p
187+ !vs <- many_p p end
212188 return $! v : vs
213189 ]
214− manyExpr end = liftM mconcat $ manyP ( paramEscapeParser <|> bytesOpParser ) end
215−
216−conditionalTrueParser :: CapParser ()
217−conditionalTrueParser = do
218− _ <- string "%t"
219− incOffset 2
220−
221−conditionalFalseParser :: CapParser ()
222−conditionalFalseParser = do
223− _ <- string "%e"
224− incOffset 2
225−
226−conditionalEndParser :: CapParser ()
227−conditionalEndParser = do
228− _ <- string "%;"
229− incOffset 2
230−
231−bitwiseOpParser :: CapParser BuildResults
232−bitwiseOpParser
233− = bitwiseOrParser
234− <|> bitwiseAndParser
235− <|> bitwiseXorParser
236−
237−bitwiseOrParser :: CapParser BuildResults
238−bitwiseOrParser = do
239− _ <- char '|'
240− incOffset 1
190+ many_expr end = liftM mconcat $ many_p ( param_escape_parser <|> bytes_op_parser ) end
191+
192+conditional_true_parser :: CapParser ()
193+conditional_true_parser = do
194+ string "%t"
195+ inc_offset 2
196+
197+conditional_false_parser :: CapParser ()
198+conditional_false_parser = do
199+ string "%e"
200+ inc_offset 2
201+
202+conditional_end_parser :: CapParser ()
203+conditional_end_parser = do
204+ string "%;"
205+ inc_offset 2
206+
207+bitwise_op_parser :: CapParser BuildResults
208+bitwise_op_parser
209+ = bitwise_or_parser
210+ <|> bitwise_and_parser
211+ <|> bitwise_xor_parser
212+
213+bitwise_or_parser :: CapParser BuildResults
214+bitwise_or_parser = do
215+ char '|'
216+ inc_offset 1
241217 return $ BuildResults 0 [ BitwiseOr ] [ ]
242218
243−bitwiseAndParser :: CapParser BuildResults
244−bitwiseAndParser = do
245− _ <- char '&'
246− incOffset 1
219+bitwise_and_parser :: CapParser BuildResults
220+bitwise_and_parser = do
221+ char '&'
222+ inc_offset 1
247223 return $ BuildResults 0 [ BitwiseAnd ] [ ]
248224
249−bitwiseXorParser :: CapParser BuildResults
250−bitwiseXorParser = do
251− _ <- char '^'
252− incOffset 1
225+bitwise_xor_parser :: CapParser BuildResults
226+bitwise_xor_parser = do
227+ char '^'
228+ inc_offset 1
253229 return $ BuildResults 0 [ BitwiseXOr ] [ ]
254230
255−arithOpParser :: CapParser BuildResults
256−arithOpParser
257− = plusOp
258− <|> minusOp
231+arith_op_parser :: CapParser BuildResults
232+arith_op_parser
233+ = plus_op
234+ <|> minus_op
259235 where
260− plusOp = do
261− _ <- char '+'
262− incOffset 1
236+ plus_op = do
237+ char '+'
238+ inc_offset 1
263239 return $ BuildResults 0 [ ArithPlus ] [ ]
264− minusOp = do
265− _ <- char '-'
266− incOffset 1
240+ minus_op = do
241+ char '-'
242+ inc_offset 1
267243 return $ BuildResults 0 [ ArithMinus ] [ ]
268244
269−literalIntOpParser :: CapParser BuildResults
270−literalIntOpParser = do
271− _ <- char '{'
272− incOffset 1
273− nStr <- many1 digit
274− incOffset $ toEnum $ length nStr
275− let n :: Word = read nStr
276− _ <- char '}'
277− incOffset 1
245+literal_int_op_parser :: CapParser BuildResults
246+literal_int_op_parser = do
247+ char '{'
248+ inc_offset 1
249+ n_str <- many1 digit
250+ inc_offset $ toEnum $ length n_str
251+ let n :: Word = read n_str
252+ char '}'
253+ inc_offset 1
278254 return $ BuildResults 0 [ PushValue n ] [ ]
279255
280−compareOpParser :: CapParser BuildResults
281−compareOpParser
282− = compareEqOp
283− <|> compareLtOp
284− <|> compareGtOp
256+compare_op_parser :: CapParser BuildResults
257+compare_op_parser
258+ = compare_eq_op
259+ <|> compare_lt_op
260+ <|> compare_gt_op
285261 where
286− compareEqOp = do
287− _ <- char '='
288− incOffset 1
262+ compare_eq_op = do
263+ char '='
264+ inc_offset 1
289265 return $ BuildResults 0 [ CompareEq ] [ ]
290− compareLtOp = do
291− _ <- char '<'
292− incOffset 1
266+ compare_lt_op = do
267+ char '<'
268+ inc_offset 1
293269 return $ BuildResults 0 [ CompareLt ] [ ]
294− compareGtOp = do
295− _ <- char '>'
296− incOffset 1
270+ compare_gt_op = do
271+ char '>'
272+ inc_offset 1
297273 return $ BuildResults 0 [ CompareGt ] [ ]
298274
299−bytesOpParser :: CapParser BuildResults
300−bytesOpParser = do
275+bytes_op_parser :: CapParser BuildResults
276+bytes_op_parser = do
301277 bytes <- many1 $ satisfy (/= '%')
302− startOffset <- nextOffset <$> getState
303− let !c = length bytes
278+ start_offset <- getState >>= return . next_offset
279+ let !c = toEnum $ length bytes
304280 !s <- getState
305− let s' = s { nextOffset = startOffset + c }
281+ let s' = s { next_offset = start_offset + c }
306282 setState s'
307− return $ BuildResults 0 [Bytes startOffset c] []
308−
309−charConstParser :: CapParser BuildResults
310−charConstParser = do
311− _ <- char '\''
312− charValue <- liftM (toEnum . fromEnum) anyChar
313− _ <- char '\''
314− incOffset 3
315− return $ BuildResults 0 [ PushValue charValue ] [ ]
316−
317−data BuildState = BuildState
318− { nextOffset :: Int
319− }
320−
321−incOffset :: Int -> CapParser ()
322−incOffset n = do
283+ return $ BuildResults 0 [Bytes start_offset c] []
284+
285+char_const_parser :: CapParser BuildResults
286+char_const_parser = do
287+ char '\''
288+ char_value <- liftM (toEnum . fromEnum) anyChar
289+ char '\''
290+ inc_offset 3
291+ return $ BuildResults 0 [ PushValue char_value ] [ ]
292+
293+data BuildState = BuildState
294+ { next_offset :: Word8
295+ }
296+
297+inc_offset :: Word8 -> CapParser ()
298+inc_offset n = do
323299 s <- getState
324− let s' = s { nextOffset = nextOffset s + n }
300+ let s' = s { next_offset = next_offset s + n }
325301 setState s'
326302
327−initialBuildState :: BuildState
328−initialBuildState = BuildState 0
303+initial_build_state :: BuildState
304+initial_build_state = BuildState 0
329305
330306data BuildResults = BuildResults
331− { outParamCount :: !Int
332− , outCapOps :: !CapOps
333− , outParamOps :: !ParamOps
307+ { out_param_count :: !Word
308+ , out_cap_ops :: !CapOps
309+ , out_param_ops :: !ParamOps
334310 }
335311
336−instance Semigroup BuildResults where
337− v0 <> v1
312+instance Monoid BuildResults where
313+ mempty = BuildResults 0 [] []
314+ v0 `mappend` v1
338315 = BuildResults
339− { outParamCount = outParamCount v0 `max` outParamCount v1
340− , outCapOps = outCapOps v0 <> outCapOps v1
341− , outParamOps = outParamOps v0 <> outParamOps v1
316+ { out_param_count = (out_param_count v0) `max` (out_param_count v1)
317+ , out_cap_ops = (out_cap_ops v0) `mappend` (out_cap_ops v1)
318+ , out_param_ops = (out_param_ops v0) `mappend` (out_param_ops v1)
342319 }
343320
344−instance Monoid BuildResults where
345− mempty = BuildResults 0 [] []
346−#if !(MIN_VERSION_base(4,11,0))
347− mappend = (<>)
348−#endif
src/Graphics/Text/Width.hsdeleted−64
@@ -1,64 +0,0 @@
1−-- Copyright 2009 Corey O'Connor
2−{-# OPTIONS_GHC -D_XOPEN_SOURCE #-}
3−{-# LANGUAGE ForeignFunctionInterface #-}
4−-- | This module provides functions to measure the terminal column width
5−-- of characters and strings.
6−--
7−-- The functions provided in this module all ultimately make calls to
8−-- the C implementation in @cbits/mk_wcwidth.c@. That code manages some
9−-- global state that carries a table of Unicode character widths. For
10−-- more details, see 'Graphics.Vty.UnicodeWidthTable.Install', the C
11−-- code, and the "Multi-Column Character Support" section of the project
12−-- @README@.
13−module Graphics.Text.Width
14− ( wcwidth
15− , wcswidth
16− , wctwidth
17− , wctlwidth
18− , safeWcwidth
19− , safeWcswidth
20− , safeWctwidth
21− , safeWctlwidth
22− )
23−where
24−
25−import Data.List (foldl')
26−import qualified Data.Text as T
27−import qualified Data.Text.Lazy as TL
28−
29−foreign import ccall unsafe "vty_mk_wcwidth" wcwidth :: Char -> Int
30−
31−wcswidth :: String -> Int
32−wcswidth = foldl' (\l c -> wcwidth c + l) 0
33−{-# INLINE [1] wcswidth #-}
34−
35−wctwidth :: T.Text -> Int
36−wctwidth = T.foldl' (\l c -> wcwidth c + l) 0
37−
38−wctlwidth :: TL.Text -> Int
39−wctlwidth = TL.foldl' (\l c -> wcwidth c + l) 0
40−
41−{-# RULES
42−"wcswidth/unpack" forall x. wcswidth (T.unpack x) = wctwidth x
43−"wcswidth/lazy-unpack" forall x. wcswidth (TL.unpack x) = wctlwidth x
44− #-}
45−
46−-- | Returns the display width of a character. Assumes all characters
47−-- with unknown widths are 0 width.
48−safeWcwidth :: Char -> Int
49−safeWcwidth = max 0 . wcwidth
50−
51−-- | Returns the display width of a string. Assumes all characters with
52−-- unknown widths are 0 width.
53−safeWcswidth :: String -> Int
54−safeWcswidth = foldl' (\l c -> safeWcwidth c + l) 0
55−
56−-- | Returns the display width of a text. Assumes all characters with
57−-- unknown widths are 0 width.
58−safeWctwidth :: T.Text -> Int
59−safeWctwidth = T.foldl' (\l c -> safeWcwidth c + l) 0
60−
61−-- | Returns the display width of a lazy text. Assumes all characters
62−-- with unknown widths are 0 width.
63−safeWctlwidth :: TL.Text -> Int
64−safeWctlwidth = TL.foldl' (\l c -> safeWcwidth c + l) 0
src/Graphics/Vty.hs+124−246
@@ -1,255 +1,133 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE ScopedTypeVariables #-}
3−
4−-- | Vty provides interfaces for both terminal input and terminal
5−-- output.
6−--
7−-- - Input to the terminal is provided to the Vty application as a
8−-- sequence of 'Event's.
9−--
10−-- - Output is provided to Vty by the application in the form of a
11−-- 'Picture'. A 'Picture' is one or more layers of 'Image's.
12−-- 'Image' values can be built by the various constructors in
13−-- "Graphics.Vty.Image". Output can be syled using 'Attr' (attribute)
14−-- values in the "Graphics.Vty.Attributes" module.
15−--
16−-- Vty uses threads internally, so programs made with Vty need to be
17−-- compiled with the threaded runtime using the GHC @-threaded@ option.
18−--
19−-- @
20−-- import "Graphics.Vty"
21−--
22−-- main = do
23−-- cfg <- 'standardIOConfig'
24−-- vty <- 'mkVty' cfg
25−-- let line0 = 'string' ('defAttr' ` 'withForeColor' ` 'green') \"first line\"
26−-- line1 = 'string' ('defAttr' ` 'withBackColor' ` 'blue') \"second line\"
27−-- img = line0 '<->' line1
28−-- pic = 'picForImage' img
29−-- 'update' vty pic
30−-- e <- 'nextEvent' vty
31−-- 'shutdown' vty
32−-- 'print' (\"Last event was: \" '++' 'show' e)
33−-- @
34−module Graphics.Vty
35− ( Vty(..)
36− , mkVty
37− , setWindowTitle
38− , Mode(..)
39− , module Graphics.Vty.Config
40− , module Graphics.Vty.Input
41− , module Graphics.Vty.Output
42− , module Graphics.Vty.Output.Interface
43− , module Graphics.Vty.Picture
44− , module Graphics.Vty.Image
45− , module Graphics.Vty.Attributes
46− )
47−where
48−
49−import Graphics.Vty.Config
50−import Graphics.Vty.Input
51−import Graphics.Vty.Input.Events
52−import Graphics.Vty.Output
53−import Graphics.Vty.Output.Interface
1+-- Copyright 2009 Corey O'Connor
2+{-# LANGUAGE ForeignFunctionInterface, BangPatterns, UnboxedTuples #-}
3+{-# CFILES gwinsz.c #-}
4+
5+-- Good sources of documentation for terminal programming are:
6+-- vt100 control sequences: http://vt100.net/docs/vt100-ug/chapter3.html#S3.3.3
7+-- Xterm control sequences: http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
8+
9+module Graphics.Vty ( Vty(..)
10+ , mkVty
11+ , mkVtyEscDelay
12+ , module Graphics.Vty.Terminal
13+ , module Graphics.Vty.Picture
14+ , module Graphics.Vty.DisplayRegion
15+ , Key(..)
16+ , Modifier(..)
17+ , Button(..)
18+ , Event(..)
19+ )
20+ where
21+
22+
23+import Graphics.Vty.Terminal
5424import Graphics.Vty.Picture
55−import Graphics.Vty.Image
56−import Graphics.Vty.Attributes
57−import Graphics.Vty.UnicodeWidthTable.IO
58−import Graphics.Vty.UnicodeWidthTable.Install
59−
60−import Data.Char (isPrint, showLitChar)
61−import qualified Data.ByteString.Char8 as BS8
62−
63−import qualified Control.Exception as E
64−import Control.Monad (when)
65−import Control.Concurrent.STM
25+import Graphics.Vty.DisplayRegion
26+import Graphics.Vty.LLInput
6627
6728import Data.IORef
68−#if !(MIN_VERSION_base(4,11,0))
69−import Data.Semigroup ((<>))
70−#endif
7129
72−-- | A Vty value represents a handle to the Vty library that the
73−-- application must create in order to use Vty.
74−--
75−-- The use of Vty typically follows this process:
76−--
77−-- 1. Initialize vty with 'mkVty' (this takes control of the terminal).
78−--
79−-- 2. Use 'update' to display a picture.
80−--
81−-- 3. Use 'nextEvent' to get the next input event.
82−--
83−-- 4. Depending on the event, go to 2 or 5.
84−--
85−-- 5. Shutdown vty and restore the terminal state with 'shutdown'. At
86−-- this point the 'Vty' handle cannot be used again.
87−--
88−-- Operations on Vty handles are not thread-safe.
89−data Vty =
90− Vty { update :: Picture -> IO ()
91− -- ^ Outputs the given 'Picture'.
92− , nextEvent :: IO Event
93− -- ^ Return the next 'Event' or block until one becomes
94− -- available.
95− , nextEventNonblocking :: IO (Maybe Event)
96− -- ^ Non-blocking version of 'nextEvent'.
97− , inputIface :: Input
98− -- ^ The input interface. See 'Input'.
99− , outputIface :: Output
100− -- ^ The output interface. See 'Output'.
101− , refresh :: IO ()
102− -- ^ Refresh the display. If other programs output to the
103− -- terminal and mess up the display then the application might
104− -- want to force a refresh using this function.
105− , shutdown :: IO ()
106− -- ^ Clean up after vty. A call to this function is necessary to
107− -- cleanly restore the terminal state before application exit.
108− -- The above methods will throw an exception if executed after
109− -- this is executed. Idempotent.
110− , isShutdown :: IO Bool
111− }
112−
113−-- | Create a Vty handle. At most one handle should be created at a time
114−-- for a given terminal device.
115−--
116−-- The specified configuration is added to the the configuration
117−-- loaded by 'userConfig' with the 'userConfig' configuration taking
118−-- precedence. See "Graphics.Vty.Config".
119−--
120−-- For most applications @mkVty defaultConfig@ is sufficient.
121−mkVty :: Config -> IO Vty
122−mkVty appConfig = do
123− config <- (<> appConfig) <$> userConfig
124−
125− when (allowCustomUnicodeWidthTables config /= Just False) $
126− installCustomWidthTable config
127−
128− input <- inputForConfig config
129− out <- outputForConfig config
130− internalMkVty input out
131−
132−installCustomWidthTable :: Config -> IO ()
133−installCustomWidthTable c = do
134− let doLog s = case debugLog c of
135− Nothing -> return ()
136− Just path -> appendFile path $ "installWidthTable: " <> s <> "\n"
137−
138− customInstalled <- isCustomTableReady
139− when (not customInstalled) $ do
140− mTerm <- currentTerminalName
141− case mTerm of
142− Nothing ->
143− doLog "No current terminal name available"
144− Just currentTerm ->
145− case lookup currentTerm (termWidthMaps c) of
146− Nothing ->
147− doLog "Current terminal not found in custom character width mapping list"
148− Just path -> do
149− tableResult <- E.try $ readUnicodeWidthTable path
150− case tableResult of
151− Left (e::E.SomeException) ->
152− doLog $ "Error reading custom character width table " <>
153− "at " <> show path <> ": " <> show e
154− Right (Left msg) ->
155− doLog $ "Error reading custom character width table " <>
156− "at " <> show path <> ": " <> msg
157− Right (Right table) -> do
158− installResult <- E.try $ installUnicodeWidthTable table
159− case installResult of
160− Left (e::E.SomeException) ->
161− doLog $ "Error installing unicode table (" <>
162− show path <> ": " <> show e
163− Right () ->
164− doLog $ "Successfully installed Unicode width table " <>
165− " from " <> show path
166−
167−internalMkVty :: Input -> Output -> IO Vty
168−internalMkVty input out = do
169− reserveDisplay out
170−
171− shutdownVar <- newTVarIO False
172− let shutdownIo = do
173− alreadyShutdown <- atomically $ swapTVar shutdownVar True
174− when (not alreadyShutdown) $ do
175− shutdownInput input
176− releaseDisplay out
177− releaseTerminal out
178−
179− let shutdownStatus = readTVarIO shutdownVar
180−
181− lastPicRef <- newIORef Nothing
182− lastUpdateRef <- newIORef Nothing
183−
184− let innerUpdate inPic = do
185− b <- displayBounds out
186− mlastUpdate <- readIORef lastUpdateRef
187− updateData <- case mlastUpdate of
30+import Data.Maybe ( maybe )
31+
32+import qualified System.Console.Terminfo as Terminfo
33+import System.IO
34+
35+-- | The main object. At most one should be created.
36+-- An alternative is to use unsafePerformIO to automatically create a singleton Vty instance when
37+-- required.
38+--
39+-- This does not assure any thread safety. In theory, as long as an update action is not executed
40+-- when another update action is already then it's safe to call this on multiple threads.
41+--
42+-- todo: Once the Terminal interface encompasses input this interface will be deprecated.
43+-- Currently, just using the Terminal interface there is no support for input events.
44+data Vty = Vty
45+ { -- | Outputs the given Picture. Equivalent to output_picture applied to a display context
46+ -- implicitly managed by Vty.
47+ update :: Picture -> IO ()
48+ -- | Get one Event object, blocking if necessary.
49+ , next_event :: IO Event
50+ -- | Handle to the terminal interface. See `Terminal`
51+ --
52+ -- The use of Vty typically follows this process:
53+ --
54+ -- 0. initialize vty
55+ --
56+ -- 1. use the update equation of Vty to display a picture
57+ --
58+ -- 2. repeat
59+ --
60+ -- 3. shutdown vty.
61+ --
62+ -- todo: provide a similar abstraction to Graphics.Vty.Terminal for input. Use haskeline's
63+ -- input backend for implementation.
64+ --
65+ -- todo: remove explicit `shutdown` requirement.
66+ , terminal :: TerminalHandle
67+ -- | Refresh the display. Normally the library takes care of refreshing. Nonetheless, some
68+ -- other program might output to the terminal and mess the display. In that case the user
69+ -- might want to force a refresh.
70+ , refresh :: IO ()
71+ -- | Clean up after vty.
72+ , shutdown :: IO ()
73+ }
74+
75+-- | Set up the state object for using vty. At most one state object should be
76+-- created at a time.
77+mkVty :: IO Vty
78+mkVty = mkVtyEscDelay 0
79+
80+mkVtyEscDelay :: Int -> IO Vty
81+mkVtyEscDelay escDelay = do
82+ term_info <- Terminfo.setupTermFromEnv
83+ t <- terminal_handle
84+ reserve_display t
85+ (kvar, endi) <- initTermInput escDelay term_info
86+ intMkVty kvar ( endi >> release_display t >> release_terminal t ) t
87+
88+intMkVty :: IO Event -> IO () -> TerminalHandle -> IO Vty
89+intMkVty kvar fend t = do
90+ last_pic_ref <- newIORef undefined
91+ last_update_ref <- newIORef Nothing
92+
93+ let inner_update in_pic = do
94+ b <- display_bounds t
95+ mlast_update <- readIORef last_update_ref
96+ update_data <- case mlast_update of
18897 Nothing -> do
189− dc <- displayContext out b
190− outputPicture dc inPic
191− return (b, dc)
192− Just (lastBounds, lastContext) -> do
193− if b /= lastBounds
98+ d <- display_context t b
99+ output_picture d in_pic
100+ return (b, d)
101+ Just (last_bounds, last_context) -> do
102+ if b /= last_bounds
194103 then do
195− dc <- displayContext out b
196− outputPicture dc inPic
197− return (b, dc)
104+ d <- display_context t b
105+ output_picture d in_pic
106+ return (b, d)
198107 else do
199− outputPicture lastContext inPic
200− return (b, lastContext)
201− writeIORef lastUpdateRef $ Just updateData
202− writeIORef lastPicRef $ Just inPic
203−
204− let innerRefresh = do
205− writeIORef lastUpdateRef Nothing
206− bounds <- displayBounds out
207− dc <- displayContext out bounds
208− writeIORef (assumedStateRef $ contextDevice dc) initialAssumedState
209− mPic <- readIORef lastPicRef
210− maybe (return ()) innerUpdate mPic
211−
212− let mkResize = uncurry EvResize <$> displayBounds out
213−
214− translateInternalEvent ResumeAfterSignal = mkResize
215− translateInternalEvent (InputEvent e) = return e
216−
217− gkey = do
218− e <- atomically $ readTChan $ _eventChannel input
219− translateInternalEvent e
220− gkey' = do
221− mEv <- atomically $ tryReadTChan $ _eventChannel input
222− case mEv of
223− Just e -> Just <$> translateInternalEvent e
224− Nothing -> return Nothing
225−
226− return $ Vty { update = innerUpdate
227− , nextEvent = gkey
228− , nextEventNonblocking = gkey'
229− , inputIface = input
230− , outputIface = out
231− , refresh = innerRefresh
232− , shutdown = shutdownIo
233− , isShutdown = shutdownStatus
108+ output_picture last_context in_pic
109+ return (b, last_context)
110+ writeIORef last_update_ref $ Just update_data
111+ writeIORef last_pic_ref $ Just in_pic
112+
113+ let inner_refresh
114+ = writeIORef last_update_ref Nothing
115+ >> readIORef last_pic_ref
116+ >>= maybe ( return () ) ( \pic -> inner_update pic )
117+
118+ let gkey = do k <- kvar
119+ case k of
120+ (EvResize _ _) -> inner_refresh
121+ >> display_bounds t
122+ >>= return . ( \(DisplayRegion w h)
123+ -> EvResize (fromEnum w) (fromEnum h)
124+ )
125+ _ -> return k
126+
127+ return $ Vty { update = inner_update
128+ , next_event = gkey
129+ , terminal = t
130+ , refresh = inner_refresh
131+ , shutdown = fend
234132 }
235133
236−-- | Set the terminal window title string.
237−--
238−-- This function emits an Xterm-compatible escape sequence that we
239−-- anticipate will work for essentially all modern terminal emulators.
240−-- Ideally we'd use a terminal capability for this, but there does not
241−-- seem to exist a termcap for setting window titles. If you find that
242−-- this function does not work for a given terminal emulator, please
243−-- report the issue.
244−--
245−-- For details, see:
246−--
247−-- https://tldp.org/HOWTO/Xterm-Title-3.html
248−setWindowTitle :: Vty -> String -> IO ()
249−setWindowTitle vty title = do
250− let sanitize :: String -> String
251− sanitize = concatMap sanitizeChar
252− sanitizeChar c | not (isPrint c) = showLitChar c ""
253− | otherwise = [c]
254− let buf = BS8.pack $ "\ESC]2;" <> sanitize title <> "\007"
255− outputByteBuffer (outputIface vty) buf
src/Graphics/Vty/Attributes.hs+162−165
@@ -1,89 +1,9 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE DeriveGeneric #-}
3−{-# LANGUAGE DeriveAnyClass #-}
4−
5−-- | Display attributes
6−--
7−-- Attributes have three components: a foreground color, a background
8−-- color, and a style mask. The simplest attribute is the default
9−-- attribute, or 'defAttr'. Attributes can be modified with
10−-- 'withForeColor', 'withBackColor', and 'withStyle', e.g.,
11−--
12−-- @
13−-- defAttr \`withForeColor\` red
14−-- @
15−--
16−-- 'Image' constructors often require an 'Attr' to indicate the
17−-- attributes used in the image, e.g.,
1+-- Copyright 2009 Corey O'Connor
2+-- Display attributes
183--
19−-- @
20−-- string (defAttr \`withForeColor\` red) "this text will be red"
21−-- @
22−--
23−-- The appearance of 'Image's using 'defAttr' is determined by the The
24−-- terminal, so this is not something VTY can control. The user is free
25−-- to The define the color scheme of the terminal as they see fit.
26−--
27−-- The value 'currentAttr' will keep the attributes of whatever was
28−-- output previously.
29−module Graphics.Vty.Attributes
30− ( module Graphics.Vty.Attributes.Color
31−
32− , Attr(..)
33− , FixedAttr(..)
34− , MaybeDefault(..)
35− , defAttr
36− , currentAttr
37−
38− -- * Styles
39− , Style
40− , withStyle
41− , standout
42− , italic
43− , strikethrough
44− , underline
45− , reverseVideo
46− , blink
47− , dim
48− , bold
49− , defaultStyleMask
50− , styleMask
51− , hasStyle
52−
53− -- * Setting attribute colors
54− , withForeColor
55− , withBackColor
56−
57− -- * Setting hyperlinks
58− , withURL
59− )
60−where
61−
62−import Control.DeepSeq
63−import Data.Bits
64−import Data.Text (Text)
65−import Data.Word
66−import GHC.Generics
67−
68−import Graphics.Vty.Attributes.Color
69−
70−-- | A display attribute defines the Color and Style of all the
71−-- characters rendered after the attribute is applied.
72−--
73−-- At most 256 colors, picked from a 240 and 16 color palette, are
74−-- possible for the background and foreground. The 240 colors and
75−-- 16 colors are points in different palettes. See Color for more
76−-- information.
77−data Attr = Attr
78− { attrStyle :: !(MaybeDefault Style)
79− , attrForeColor :: !(MaybeDefault Color)
80− , attrBackColor :: !(MaybeDefault Color)
81− , attrURL :: !(MaybeDefault Text)
82− } deriving ( Eq, Show, Read, Generic, NFData )
83−
84−-- This could be encoded into a single 32 bit word. The 32 bit word is
85−-- first divided into 4 groups of 8 bits where: The first group codes
86−-- what action should be taken with regards to the other groups.
4+-- For efficiency this can be, in the future, encoded into a single 32 bit word. The 32 bit word is
5+-- first divided into 4 groups of 8 bits where:
6+-- The first group codes what action should be taken with regards to the other groups.
877-- XXYYZZ__
888-- XX - style action
899-- 00 => reset to default
@@ -100,53 +20,151 @@ data Attr = Attr
10020-- __ - unused
10121--
10222-- Next is the style flags
103−-- SURBDOI_
23+-- SURBDO__
10424-- S - standout
10525-- U - underline
10626-- R - reverse video
10727-- B - blink
10828-- D - dim
10929-- O - bold
110−-- I - italic
111−-- _ - unused
30+-- __ - unused
11231--
11332-- Then the foreground color encoded into 8 bits.
11433-- Then the background color encoded into 8 bits.
34+--
35+module Graphics.Vty.Attributes
36+ where
11537
116−-- | Specifies the display attributes such that the final style and
117−-- color values do not depend on the previously applied display
118−-- attribute. The display attributes can still depend on the terminal's
119−-- default colors (unfortunately).
120−data FixedAttr = FixedAttr
121− { fixedStyle :: !Style
122− , fixedForeColor :: !(Maybe Color)
123− , fixedBackColor :: !(Maybe Color)
124− , fixedURL :: !(Maybe Text)
38+import Data.Bits
39+import Data.Monoid
40+import Data.Word
41+
42+-- | A display attribute defines the Color and Style of all the characters rendered after the
43+-- attribute is applied.
44+--
45+-- At most 256 colors, picked from a 240 and 16 color palette, are possible for the background and
46+-- foreground. The 240 colors and 16 colors are points in different palettes. See Color for more
47+-- information.
48+data Attr = Attr
49+ { attr_style :: !(MaybeDefault Style)
50+ , attr_fore_color :: !(MaybeDefault Color)
51+ , attr_back_color :: !(MaybeDefault Color)
12552 } deriving ( Eq, Show )
12653
127−-- | The style and color attributes can either be the terminal defaults.
128−-- Or be equivalent to the previously applied style. Or be a specific
129−-- value.
130−data MaybeDefault v = Default | KeepCurrent | SetTo !v
131− deriving (Eq, Read, Show)
54+instance Monoid Attr where
55+ mempty = Attr mempty mempty mempty
56+ mappend attr_0 attr_1 =
57+ Attr ( attr_style attr_0 `mappend` attr_style attr_1 )
58+ ( attr_fore_color attr_0 `mappend` attr_fore_color attr_1 )
59+ ( attr_back_color attr_0 `mappend` attr_back_color attr_1 )
13260
133−instance (NFData v) => NFData (MaybeDefault v) where
134− rnf Default = ()
135− rnf KeepCurrent = ()
136− rnf (SetTo v) = rnf v
61+-- | Specifies the display attributes such that the final style and color values do not depend on
62+-- the previously applied display attribute. The display attributes can still depend on the
63+-- terminal's default colors (unfortunately).
64+data FixedAttr = FixedAttr
65+ { fixed_style :: !Style
66+ , fixed_fore_color :: !(Maybe Color)
67+ , fixed_back_color :: !(Maybe Color)
68+ } deriving ( Eq, Show )
13769
138−-- | Styles are represented as an 8 bit word. Each bit in the word is 1
139−-- if the style attribute assigned to that bit should be applied and 0
140−-- if the style attribute should not be applied.
70+-- | The style and color attributes can either be the terminal defaults. Or be equivalent to the
71+-- previously applied style. Or be a specific value.
72+data Eq v => MaybeDefault v = Default | KeepCurrent | SetTo !v
73+ deriving ( Eq, Show )
74+
75+instance Eq v => Monoid ( MaybeDefault v ) where
76+ mempty = KeepCurrent
77+ mappend Default Default = Default
78+ mappend Default KeepCurrent = Default
79+ mappend Default ( SetTo v ) = SetTo v
80+ mappend KeepCurrent Default = Default
81+ mappend KeepCurrent KeepCurrent = KeepCurrent
82+ mappend KeepCurrent ( SetTo v ) = SetTo v
83+ mappend ( SetTo _v ) Default = Default
84+ mappend ( SetTo v ) KeepCurrent = SetTo v
85+ mappend ( SetTo _ ) ( SetTo v ) = SetTo v
86+
87+-- | Abstract data type representing a color.
88+--
89+-- Currently the foreground and background color are specified as points in either a:
90+--
91+-- * 16 color palette. Where the first 8 colors are equal to the 8 colors of the ISO 6429 (ANSI) 8
92+-- color palette and the second 8 colors are bright/vivid versions of the first 8 colors.
93+--
94+-- * 240 color palette. This palette is a regular sampling of the full RGB colorspace.
95+--
96+-- The 8 ISO 6429 (ANSI) colors are as follows:
97+--
98+-- 0. black
99+--
100+-- 1. red
101+--
102+-- 2. green
103+--
104+-- 3. yellow
105+--
106+-- 4. blue
107+--
108+-- 5. magenta
109+--
110+-- 6. cyan
111+--
112+-- 7. white
113+--
114+-- The mapping from points in the 240 color palette to colors actually displayable by the terminal
115+-- depends on the number of colors the terminal claims to support. Which is usually determined by
116+-- the terminfo "colors" property. If this property is not being accurately reported then the color
117+-- reproduction will be incorrect.
118+--
119+-- If the terminal reports <= 16 colors then the 240 color palette points are only mapped to the 8
120+-- color pallete. I'm not sure of the RGB points for the "bright" colors which is why they are not
121+-- addressable via the 240 color palette.
122+--
123+-- If the terminal reports > 16 colors then the 240 color palette points are mapped to the nearest
124+-- points in a ("color count" - 16) subsampling of the 240 color palette.
125+--
126+-- All of this assumes the terminals are behaving similarly to xterm and rxvt when handling colors.
127+-- And that the individual colors have not been remapped by the user. There may be a way to verify
128+-- this through terminfo but I don't know it.
129+--
130+-- Seriously, terminal color support is INSANE.
131+data Color = ISOColor !Word8 | Color240 !Word8
132+ deriving ( Eq, Show )
133+
134+-- | Standard 8-color ANSI terminal color codes.
135+black, red, green, yellow, blue, magenta, cyan, white :: Color
136+black = ISOColor 0
137+red = ISOColor 1
138+green = ISOColor 2
139+yellow = ISOColor 3
140+blue = ISOColor 4
141+magenta= ISOColor 5
142+cyan = ISOColor 6
143+white = ISOColor 7
144+
145+-- | Bright/Vivid variants of the standard 8-color ANSI
146+bright_black, bright_red, bright_green, bright_yellow :: Color
147+bright_blue, bright_magenta, bright_cyan, bright_white :: Color
148+bright_black = ISOColor 8
149+bright_red = ISOColor 9
150+bright_green = ISOColor 10
151+bright_yellow = ISOColor 11
152+bright_blue = ISOColor 12
153+bright_magenta= ISOColor 13
154+bright_cyan = ISOColor 14
155+bright_white = ISOColor 15
156+
157+-- | Styles are represented as an 8 bit word. Each bit in the word is 1 if the style attribute
158+-- assigned to that bit should be applied and 0 if the style attribute should not be applied.
141159type Style = Word8
142160
143−-- | Valid style attributes include:
161+-- | The 6 possible style attributes:
144162--
145163-- * standout
146164--
147165-- * underline
148166--
149−-- * reverseVideo
167+-- * reverse_video
150168--
151169-- * blink
152170--
@@ -154,75 +172,54 @@ type Style = Word8
154172--
155173-- * bold/bright
156174--
157−-- * italic
158−--
159−-- * strikethrough (via the smxx/rmxx terminfo capabilities)
160−--
161−-- (The invisible, protect, and altcharset display attributes some
162−-- terminals support are not supported via VTY.)
163−standout, underline, reverseVideo, blink, dim, bold, italic, strikethrough :: Style
175+-- ( The invisible, protect, and altcharset display attributes some terminals support are not
176+-- supported via VTY.)
177+standout, underline, reverse_video, blink, dim, bold :: Style
164178standout = 0x01
165179underline = 0x02
166−reverseVideo = 0x04
180+reverse_video = 0x04
167181blink = 0x08
168182dim = 0x10
169183bold = 0x20
170−italic = 0x40
171−strikethrough = 0x80
172184
173−defaultStyleMask :: Style
174−defaultStyleMask = 0x00
185+default_style_mask :: Style
186+default_style_mask = 0x00
175187
176−styleMask :: Attr -> Word8
177−styleMask attr
178− = case attrStyle attr of
188+style_mask :: Attr -> Word8
189+style_mask attr
190+ = case attr_style attr of
179191 Default -> 0
180192 KeepCurrent -> 0
181193 SetTo v -> v
182194
183195-- | true if the given Style value has the specified Style set.
184−hasStyle :: Style -> Style -> Bool
185−hasStyle s bitMask = ( s .&. bitMask ) /= 0
196+has_style :: Style -> Style -> Bool
197+has_style s bit_mask = ( s .&. bit_mask ) /= 0
186198
187199-- | Set the foreground color of an `Attr'.
188−withForeColor :: Attr -> Color -> Attr
189−withForeColor attr c = attr { attrForeColor = SetTo c }
200+with_fore_color :: Attr -> Color -> Attr
201+with_fore_color attr c = attr { attr_fore_color = SetTo c }
190202
191203-- | Set the background color of an `Attr'.
192−withBackColor :: Attr -> Color -> Attr
193−withBackColor attr c = attr { attrBackColor = SetTo c }
204+with_back_color :: Attr -> Color -> Attr
205+with_back_color attr c = attr { attr_back_color = SetTo c }
194206
195207-- | Add the given style attribute
196−withStyle :: Attr -> Style -> Attr
197−withStyle attr 0 = attr
198−withStyle attr styleFlag = attr { attrStyle = SetTo $ styleMask attr .|. styleFlag }
199−
200−-- | Add a hyperlinked URL using the proposed [escape sequences for
201−-- hyperlinked
202−-- URLs](https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda).
203−-- These escape sequences are comparatively new and aren't widely
204−-- supported in terminal emulators yet, but most terminal emulators
205−-- that don't know about these sequences will ignore these sequences,
206−-- and therefore this should fall back sensibly. In some cases they
207−-- won't and this will result in garbage, so this is why hyperlinking is
208−-- disabled by default, in which case this combinator has no observable
209−-- effect. To enable it, enable 'Hyperlink' mode on your Vty output
210−-- interface.
211−withURL :: Attr -> Text -> Attr
212−withURL attr url = attr { attrURL = SetTo url }
213−
214−-- | Sets the style, background color and foreground color to the
215−-- default values for the terminal. There is no easy way to determine
216−-- what the default background and foreground colors are.
217−defAttr :: Attr
218−defAttr = Attr Default Default Default Default
219−
220−-- | Keeps the style, background color and foreground color that was
221−-- previously set. Used to override some part of the previous style.
222−--
223−-- EG: current_style `withForeColor` brightMagenta
224−--
225−-- Would be the currently applied style (be it underline, bold, etc) but
226−-- with the foreground color set to brightMagenta.
227−currentAttr :: Attr
228−currentAttr = Attr KeepCurrent KeepCurrent KeepCurrent KeepCurrent
208+with_style :: Attr -> Style -> Attr
209+with_style attr style_flag = attr { attr_style = SetTo $ style_mask attr .|. style_flag }
210+
211+-- | Sets the style, background color and foreground color to the default values for the terminal.
212+-- There is no easy way to determine what the default background and foreground colors are.
213+def_attr :: Attr
214+def_attr = Attr Default Default Default
215+
216+-- | Keeps the style, background color and foreground color that was previously set. Used to
217+-- override some part of the previous style.
218+--
219+-- EG: current_style `with_fore_color` bright_magenta
220+--
221+-- Would be the currently applied style (be it underline, bold, etc) but with the foreground color
222+-- set to bright_magenta.
223+current_attr :: Attr
224+current_attr = Attr KeepCurrent KeepCurrent KeepCurrent
225+
src/Graphics/Vty/Attributes/Color.hsdeleted−191
@@ -1,191 +0,0 @@
1−{-# LANGUAGE DeriveGeneric #-}
2−{-# LANGUAGE DeriveAnyClass #-}
3−{-# LANGUAGE MultiWayIf #-}
4−{-# LANGUAGE ScopedTypeVariables #-}
5−
6−module Graphics.Vty.Attributes.Color
7− ( Color(..)
8− , ColorMode(..)
9−
10− -- * Detecting Terminal Color Support
11− , detectColorMode
12−
13− -- ** Fixed Colors
14− -- | Standard 8-color ANSI terminal color codes.
15− --
16− -- Note that these map to colors in the terminal's custom palette. For
17− -- instance, `white` maps to whatever the terminal color theme uses for
18− -- white.
19− --
20− -- Use these functions if you want to make apps that fit the terminal theme.
21− -- If you want access to more/stronger colors use `rgbColor`
22− , black
23− , red
24− , green
25− , yellow
26− , blue
27− , magenta
28− , cyan
29− , white
30−
31− -- | Bright/Vivid variants of the standard 8-color ANSI
32− , brightBlack
33− , brightRed
34− , brightGreen
35− , brightYellow
36− , brightBlue
37− , brightMagenta
38− , brightCyan
39− , brightWhite
40− -- ** Creating Colors From RGB
41− , linearColor
42− , srgbColor
43− , rgbColor
44− , color240
45− , module Graphics.Vty.Attributes.Color240
46− )
47−where
48−
49−import Data.Word
50−import GHC.Generics
51−import Control.DeepSeq
52−import System.Environment (lookupEnv)
53−
54−import qualified System.Console.Terminfo as Terminfo
55−import Control.Exception (catch)
56−import Data.Maybe
57−
58−import Graphics.Vty.Attributes.Color240
59−
60−-- | Abstract data type representing a color.
61−--
62−-- Currently the foreground and background color are specified as points
63−-- in either a:
64−--
65−-- * 16 color palette. Where the first 8 colors are equal to the 8
66−-- colors of the ISO 6429 (ANSI) 8 color palette and the second 8
67−-- colors are bright/vivid versions of the first 8 colors.
68−--
69−-- * 240 color palette. This palette is a regular sampling of the full
70−-- RGB colorspace for the first 224 colors. The remaining 16 colors is
71−-- a greyscale palette.
72−--
73−-- The 8 ISO 6429 (ANSI) colors are as follows:
74−--
75−-- 0. black
76−--
77−-- 1. red
78−--
79−-- 2. green
80−--
81−-- 3. yellow
82−--
83−-- 4. blue
84−--
85−-- 5. magenta
86−--
87−-- 6. cyan
88−--
89−-- 7. white
90−--
91−-- The mapping from points in the 240 color palette to colors actually
92−-- displayable by the terminal depends on the number of colors the
93−-- terminal claims to support. Which is usually determined by the
94−-- terminfo "colors" property. If this property is not being accurately
95−-- reported then the color reproduction will be incorrect.
96−--
97−-- If the terminal reports <= 16 colors then the 240 color palette
98−-- points are only mapped to the 8 color palette. I'm not sure of
99−-- the RGB points for the "bright" colors which is why they are not
100−-- addressable via the 240 color palette.
101−--
102−-- If the terminal reports > 16 colors then the 240 color palette
103−-- points are mapped to the nearest points in a ("color count" - 16)
104−-- subsampling of the 240 color palette.
105−--
106−-- All of this assumes the terminals are behaving similarly to xterm and
107−-- rxvt when handling colors. And that the individual colors have not
108−-- been remapped by the user. There may be a way to verify this through
109−-- terminfo but I don't know it.
110−--
111−-- Seriously, terminal color support is INSANE.
112−data Color = ISOColor !Word8 | Color240 !Word8 | RGBColor !Word8 !Word8 !Word8
113− deriving ( Eq, Show, Read, Generic, NFData )
114−
115−data ColorMode
116− = NoColor
117− | ColorMode8
118− | ColorMode16
119− | ColorMode240 !Word8
120− | FullColor
121− deriving ( Eq, Show )
122−
123−detectColorMode :: String -> IO ColorMode
124−detectColorMode termName' = do
125− term <- catch (Just <$> Terminfo.setupTerm termName')
126− (\(_ :: Terminfo.SetupTermError) -> return Nothing)
127− let getCap cap = term >>= \t -> Terminfo.getCapability t cap
128− termColors = fromMaybe 0 $ getCap (Terminfo.tiGetNum "colors")
129− colorterm <- lookupEnv "COLORTERM"
130− return $ if
131− | termColors < 8 -> NoColor
132− | termColors < 16 -> ColorMode8
133− | termColors == 16 -> ColorMode16
134− | termColors < 256 -> ColorMode240 (fromIntegral termColors - 16)
135− | colorterm == Just "truecolor" -> FullColor
136− | colorterm == Just "24bit" -> FullColor
137− | otherwise -> ColorMode240 240
138−
139−black, red, green, yellow, blue, magenta, cyan, white :: Color
140−black = ISOColor 0
141−red = ISOColor 1
142−green = ISOColor 2
143−yellow = ISOColor 3
144−blue = ISOColor 4
145−magenta= ISOColor 5
146−cyan = ISOColor 6
147−white = ISOColor 7
148−
149−brightBlack, brightRed, brightGreen, brightYellow :: Color
150−brightBlue, brightMagenta, brightCyan, brightWhite :: Color
151−brightBlack = ISOColor 8
152−brightRed = ISOColor 9
153−brightGreen = ISOColor 10
154−brightYellow = ISOColor 11
155−brightBlue = ISOColor 12
156−brightMagenta= ISOColor 13
157−brightCyan = ISOColor 14
158−brightWhite = ISOColor 15
159−
160−linearColor :: Integral i => i -> i -> i -> Color
161−linearColor r g b = RGBColor r' g' b'
162− where
163− r' = fromIntegral (clamp r) :: Word8
164− g' = fromIntegral (clamp g) :: Word8
165− b' = fromIntegral (clamp b) :: Word8
166− clamp = min 255 . max 0
167−
168−srgbColor :: Integral i => i -> i -> i -> Color
169−srgbColor r g b =
170− -- srgb to rgb transformation as described at
171− -- https://en.wikipedia.org/wiki/SRGB#The_reverse_transformation
172− --
173− -- TODO: it may be worth translating this to a lookup table, as with color240
174− let shrink n = fromIntegral n / 255 :: Double
175− -- called gamma^-1 in wiki
176− gamma u
177− | u <= 0.04045 = u/12.92
178− | otherwise = ((u + 0.055) / 1.055) ** 2.4
179− -- TODO: this is a slightly inaccurate conversion. is it worth doing proterly?
180− expand n = round (255 * n)
181− convert = expand . gamma . shrink
182− in RGBColor (convert r) (convert g) (convert b)
183−
184−color240 :: Integral i => i -> i -> i -> Color
185−color240 r g b = Color240 (rgbColorToColor240 r g b)
186−
187−-- | Create a Vty 'Color' (in the 240 color set) from an RGB triple.
188−-- This function is lossy in the sense that we only internally support 240 colors but the
189−-- #RRGGBB format supports 16^3 colors.
190−rgbColor :: Integral i => i -> i -> i -> Color
191−rgbColor = color240
src/Graphics/Vty/Attributes/Color240.hsdeleted−298
@@ -1,298 +0,0 @@
1−-- This header file was generated by ./256colres.pl
2−module Graphics.Vty.Attributes.Color240
3− ( rgbColorToColor240
4− , color240CodeToRGB
5− )
6−where
7−
8−import Data.Word (Word8)
9−import Text.Printf
10−
11−-- Note: rgbColor's mapping from RGB to 240 colors was generated from
12−-- 256colres.pl which is forked from xterm 256colres.pl.
13−
14−-- | Create a value in the Color240 set from an RGB triple
15−rgbColorToColor240 :: Integral i => i -> i -> i -> Word8
16−rgbColorToColor240 r g b
17− | r < 0 && g < 0 && b < 0 = error "rgbColor with negative color component intensity"
18− | r == 8 && g == 8 && b == 8 = 216
19− | r == 18 && g == 18 && b == 18 = 217
20− | r == 28 && g == 28 && b == 28 = 218
21− | r == 38 && g == 38 && b == 38 = 219
22− | r == 48 && g == 48 && b == 48 = 220
23− | r == 58 && g == 58 && b == 58 = 221
24− | r == 68 && g == 68 && b == 68 = 222
25− | r == 78 && g == 78 && b == 78 = 223
26− | r == 88 && g == 88 && b == 88 = 224
27− | r == 98 && g == 98 && b == 98 = 225
28− | r == 108 && g == 108 && b == 108 = 226
29− | r == 118 && g == 118 && b == 118 = 227
30− | r == 128 && g == 128 && b == 128 = 228
31− | r == 138 && g == 138 && b == 138 = 229
32− | r == 148 && g == 148 && b == 148 = 230
33− | r == 158 && g == 158 && b == 158 = 231
34− | r == 168 && g == 168 && b == 168 = 232
35− | r == 178 && g == 178 && b == 178 = 233
36− | r == 188 && g == 188 && b == 188 = 234
37− | r == 198 && g == 198 && b == 198 = 235
38− | r == 208 && g == 208 && b == 208 = 236
39− | r == 218 && g == 218 && b == 218 = 237
40− | r == 228 && g == 228 && b == 228 = 238
41− | r == 238 && g == 238 && b == 238 = 239
42− | otherwise = 36 * go r + 6 * go g + go b
43− where go = simpleColor_ (error (printf "RGB color %d %d %d does not map to 240 palette."
44− (fromIntegral r :: Int)
45− (fromIntegral g :: Int)
46− (fromIntegral b :: Int)))
47−
48−simpleColor_ :: Integral i => Word8 -> i -> Word8
49−simpleColor_ e c
50− | c <= 0 = 0
51− | c <= 95 = 1
52− | c <= 255 = fromIntegral ((c-16) `div` 40)
53− | otherwise = e
54−
55−-- | Create a RGB triple from a value in the Color240 set.
56−color240CodeToRGB :: Word8 -> Maybe (Int, Int, Int)
57−color240CodeToRGB n = case n of
58− 0 -> Just (0, 0, 0)
59− 1 -> Just (0, 0, 95)
60− 2 -> Just (0, 0, 135)
61− 3 -> Just (0, 0, 175)
62− 4 -> Just (0, 0, 215)
63− 5 -> Just (0, 0, 255)
64− 6 -> Just (0, 95, 0)
65− 7 -> Just (0, 95, 95)
66− 8 -> Just (0, 95, 135)
67− 9 -> Just (0, 95, 175)
68− 10 -> Just (0, 95, 215)
69− 11 -> Just (0, 95, 255)
70− 12 -> Just (0, 135, 0)
71− 13 -> Just (0, 135, 95)
72− 14 -> Just (0, 135, 135)
73− 15 -> Just (0, 135, 175)
74− 16 -> Just (0, 135, 215)
75− 17 -> Just (0, 135, 255)
76− 18 -> Just (0, 175, 0)
77− 19 -> Just (0, 175, 95)
78− 20 -> Just (0, 175, 135)
79− 21 -> Just (0, 175, 175)
80− 22 -> Just (0, 175, 215)
81− 23 -> Just (0, 175, 255)
82− 24 -> Just (0, 215, 0)
83− 25 -> Just (0, 215, 95)
84− 26 -> Just (0, 215, 135)
85− 27 -> Just (0, 215, 175)
86− 28 -> Just (0, 215, 215)
87− 29 -> Just (0, 215, 255)
88− 30 -> Just (0, 255, 0)
89− 31 -> Just (0, 255, 95)
90− 32 -> Just (0, 255, 135)
91− 33 -> Just (0, 255, 175)
92− 34 -> Just (0, 255, 215)
93− 35 -> Just (0, 255, 255)
94− 36 -> Just (95, 0, 0)
95− 37 -> Just (95, 0, 95)
96− 38 -> Just (95, 0, 135)
97− 39 -> Just (95, 0, 175)
98− 40 -> Just (95, 0, 215)
99− 41 -> Just (95, 0, 255)
100− 42 -> Just (95, 95, 0)
101− 43 -> Just (95, 95, 95)
102− 44 -> Just (95, 95, 135)
103− 45 -> Just (95, 95, 175)
104− 46 -> Just (95, 95, 215)
105− 47 -> Just (95, 95, 255)
106− 48 -> Just (95, 135, 0)
107− 49 -> Just (95, 135, 95)
108− 50 -> Just (95, 135, 135)
109− 51 -> Just (95, 135, 175)
110− 52 -> Just (95, 135, 215)
111− 53 -> Just (95, 135, 255)
112− 54 -> Just (95, 175, 0)
113− 55 -> Just (95, 175, 95)
114− 56 -> Just (95, 175, 135)
115− 57 -> Just (95, 175, 175)
116− 58 -> Just (95, 175, 215)
117− 59 -> Just (95, 175, 255)
118− 60 -> Just (95, 215, 0)
119− 61 -> Just (95, 215, 95)
120− 62 -> Just (95, 215, 135)
121− 63 -> Just (95, 215, 175)
122− 64 -> Just (95, 215, 215)
123− 65 -> Just (95, 215, 255)
124− 66 -> Just (95, 255, 0)
125− 67 -> Just (95, 255, 95)
126− 68 -> Just (95, 255, 135)
127− 69 -> Just (95, 255, 175)
128− 70 -> Just (95, 255, 215)
129− 71 -> Just (95, 255, 255)
130− 72 -> Just (135, 0, 0)
131− 73 -> Just (135, 0, 95)
132− 74 -> Just (135, 0, 135)
133− 75 -> Just (135, 0, 175)
134− 76 -> Just (135, 0, 215)
135− 77 -> Just (135, 0, 255)
136− 78 -> Just (135, 95, 0)
137− 79 -> Just (135, 95, 95)
138− 80 -> Just (135, 95, 135)
139− 81 -> Just (135, 95, 175)
140− 82 -> Just (135, 95, 215)
141− 83 -> Just (135, 95, 255)
142− 84 -> Just (135, 135, 0)
143− 85 -> Just (135, 135, 95)
144− 86 -> Just (135, 135, 135)
145− 87 -> Just (135, 135, 175)
146− 88 -> Just (135, 135, 215)
147− 89 -> Just (135, 135, 255)
148− 90 -> Just (135, 175, 0)
149− 91 -> Just (135, 175, 95)
150− 92 -> Just (135, 175, 135)
151− 93 -> Just (135, 175, 175)
152− 94 -> Just (135, 175, 215)
153− 95 -> Just (135, 175, 255)
154− 96 -> Just (135, 215, 0)
155− 97 -> Just (135, 215, 95)
156− 98 -> Just (135, 215, 135)
157− 99 -> Just (135, 215, 175)
158− 100 -> Just (135, 215, 215)
159− 101 -> Just (135, 215, 255)
160− 102 -> Just (135, 255, 0)
161− 103 -> Just (135, 255, 95)
162− 104 -> Just (135, 255, 135)
163− 105 -> Just (135, 255, 175)
164− 106 -> Just (135, 255, 215)
165− 107 -> Just (135, 255, 255)
166− 108 -> Just (175, 0, 0)
167− 109 -> Just (175, 0, 95)
168− 110 -> Just (175, 0, 135)
169− 111 -> Just (175, 0, 175)
170− 112 -> Just (175, 0, 215)
171− 113 -> Just (175, 0, 255)
172− 114 -> Just (175, 95, 0)
173− 115 -> Just (175, 95, 95)
174− 116 -> Just (175, 95, 135)
175− 117 -> Just (175, 95, 175)
176− 118 -> Just (175, 95, 215)
177− 119 -> Just (175, 95, 255)
178− 120 -> Just (175, 135, 0)
179− 121 -> Just (175, 135, 95)
180− 122 -> Just (175, 135, 135)
181− 123 -> Just (175, 135, 175)
182− 124 -> Just (175, 135, 215)
183− 125 -> Just (175, 135, 255)
184− 126 -> Just (175, 175, 0)
185− 127 -> Just (175, 175, 95)
186− 128 -> Just (175, 175, 135)
187− 129 -> Just (175, 175, 175)
188− 130 -> Just (175, 175, 215)
189− 131 -> Just (175, 175, 255)
190− 132 -> Just (175, 215, 0)
191− 133 -> Just (175, 215, 95)
192− 134 -> Just (175, 215, 135)
193− 135 -> Just (175, 215, 175)
194− 136 -> Just (175, 215, 215)
195− 137 -> Just (175, 215, 255)
196− 138 -> Just (175, 255, 0)
197− 139 -> Just (175, 255, 95)
198− 140 -> Just (175, 255, 135)
199− 141 -> Just (175, 255, 175)
200− 142 -> Just (175, 255, 215)
201− 143 -> Just (175, 255, 255)
202− 144 -> Just (215, 0, 0)
203− 145 -> Just (215, 0, 95)
204− 146 -> Just (215, 0, 135)
205− 147 -> Just (215, 0, 175)
206− 148 -> Just (215, 0, 215)
207− 149 -> Just (215, 0, 255)
208− 150 -> Just (215, 95, 0)
209− 151 -> Just (215, 95, 95)
210− 152 -> Just (215, 95, 135)
211− 153 -> Just (215, 95, 175)
212− 154 -> Just (215, 95, 215)
213− 155 -> Just (215, 95, 255)
214− 156 -> Just (215, 135, 0)
215− 157 -> Just (215, 135, 95)
216− 158 -> Just (215, 135, 135)
217− 159 -> Just (215, 135, 175)
218− 160 -> Just (215, 135, 215)
219− 161 -> Just (215, 135, 255)
220− 162 -> Just (215, 175, 0)
221− 163 -> Just (215, 175, 95)
222− 164 -> Just (215, 175, 135)
223− 165 -> Just (215, 175, 175)
224− 166 -> Just (215, 175, 215)
225− 167 -> Just (215, 175, 255)
226− 168 -> Just (215, 215, 0)
227− 169 -> Just (215, 215, 95)
228− 170 -> Just (215, 215, 135)
229− 171 -> Just (215, 215, 175)
230− 172 -> Just (215, 215, 215)
231− 173 -> Just (215, 215, 255)
232− 174 -> Just (215, 255, 0)
233− 175 -> Just (215, 255, 95)
234− 176 -> Just (215, 255, 135)
235− 177 -> Just (215, 255, 175)
236− 178 -> Just (215, 255, 215)
237− 179 -> Just (215, 255, 255)
238− 180 -> Just (255, 0, 0)
239− 181 -> Just (255, 0, 95)
240− 182 -> Just (255, 0, 135)
241− 183 -> Just (255, 0, 175)
242− 184 -> Just (255, 0, 215)
243− 185 -> Just (255, 0, 255)
244− 186 -> Just (255, 95, 0)
245− 187 -> Just (255, 95, 95)
246− 188 -> Just (255, 95, 135)
247− 189 -> Just (255, 95, 175)
248− 190 -> Just (255, 95, 215)
249− 191 -> Just (255, 95, 255)
250− 192 -> Just (255, 135, 0)
251− 193 -> Just (255, 135, 95)
252− 194 -> Just (255, 135, 135)
253− 195 -> Just (255, 135, 175)
254− 196 -> Just (255, 135, 215)
255− 197 -> Just (255, 135, 255)
256− 198 -> Just (255, 175, 0)
257− 199 -> Just (255, 175, 95)
258− 200 -> Just (255, 175, 135)
259− 201 -> Just (255, 175, 175)
260− 202 -> Just (255, 175, 215)
261− 203 -> Just (255, 175, 255)
262− 204 -> Just (255, 215, 0)
263− 205 -> Just (255, 215, 95)
264− 206 -> Just (255, 215, 135)
265− 207 -> Just (255, 215, 175)
266− 208 -> Just (255, 215, 215)
267− 209 -> Just (255, 215, 255)
268− 210 -> Just (255, 255, 0)
269− 211 -> Just (255, 255, 95)
270− 212 -> Just (255, 255, 135)
271− 213 -> Just (255, 255, 175)
272− 214 -> Just (255, 255, 215)
273− 215 -> Just (255, 255, 255)
274− 216 -> Just (8, 8, 8)
275− 217 -> Just (18, 18, 18)
276− 218 -> Just (28, 28, 28)
277− 219 -> Just (38, 38, 38)
278− 220 -> Just (48, 48, 48)
279− 221 -> Just (58, 58, 58)
280− 222 -> Just (68, 68, 68)
281− 223 -> Just (78, 78, 78)
282− 224 -> Just (88, 88, 88)
283− 225 -> Just (98, 98, 98)
284− 226 -> Just (108, 108, 108)
285− 227 -> Just (118, 118, 118)
286− 228 -> Just (128, 128, 128)
287− 229 -> Just (138, 138, 138)
288− 230 -> Just (148, 148, 148)
289− 231 -> Just (158, 158, 158)
290− 232 -> Just (168, 168, 168)
291− 233 -> Just (178, 178, 178)
292− 234 -> Just (188, 188, 188)
293− 235 -> Just (198, 198, 198)
294− 236 -> Just (208, 208, 208)
295− 237 -> Just (218, 218, 218)
296− 238 -> Just (228, 228, 228)
297− 239 -> Just (238, 238, 238)
298− _ -> Nothing
src/Graphics/Vty/Config.hsdeleted−488
@@ -1,488 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE DeriveDataTypeable #-}
3−{-# LANGUAGE FlexibleContexts, FlexibleInstances #-}
4−{-# LANGUAGE TypeOperators #-}
5−{-# LANGUAGE ScopedTypeVariables #-}
6−{-# LANGUAGE LambdaCase, MultiWayIf #-}
7−-- | Vty supports a configuration file format and associated 'Config'
8−-- data type. The 'Config' can be provided to 'mkVty' to customize the
9−-- application's use of Vty.
10−--
11−-- Lines in config files that fail to parse are ignored. Later entries
12−-- take precedence over earlier ones.
13−--
14−-- = Debug
15−--
16−-- == @debugLog@
17−--
18−-- Format:
19−--
20−-- @
21−-- \"debugLog\" string
22−-- @
23−--
24−-- The value of the environment variable @VTY_DEBUG_LOG@ is equivalent
25−-- to a debugLog entry at the end of the last config file.
26−--
27−-- = Input Processing
28−--
29−-- == @map@
30−--
31−-- Format:
32−--
33−-- @
34−-- \"map\" term string key modifier_list
35−-- where
36−-- key := KEsc | KChar Char | KBS ... (same as 'Key')
37−-- modifier_list := \"[\" modifier+ \"]\"
38−-- modifier := MShift | MCtrl | MMeta | MAlt
39−-- term := "_" | string
40−-- @
41−--
42−-- E.g., if the contents are
43−--
44−-- @
45−-- map _ \"\\ESC[B\" KUp []
46−-- map _ \"\\ESC[1;3B\" KDown [MAlt]
47−-- map \"xterm\" \"\\ESC[D\" KLeft []
48−-- @
49−--
50−-- Then the bytes @\"\\ESC[B\"@ will result in the KUp event on all
51−-- terminals. The bytes @\"\\ESC[1;3B\"@ will result in the event KDown
52−-- with the MAlt modifier on all terminals. The bytes @\"\\ESC[D\"@ will
53−-- result in the KLeft event when @TERM@ is @xterm@.
54−--
55−-- If a debug log is requested then vty will output the current input
56−-- table to the log in the above format. A workflow for using this is
57−-- to set @VTY_DEBUG_LOG@. Run the application. Check the debug log for
58−-- incorrect mappings. Add corrected mappings to @$HOME\/.vty\/config@.
59−--
60−-- = Unicode Character Width Maps
61−--
62−-- == @widthMap@
63−--
64−-- Format:
65−--
66−-- @
67−-- \"widthMap\" string string
68−-- @
69−--
70−-- E.g.,
71−--
72−-- @
73−-- widthMap \"xterm\" \"\/home\/user\/.vty\/xterm\_map.dat\"
74−-- @
75−--
76−-- This directive specifies the path to a Unicode character width
77−-- map (the second argument) that should be loaded and used when
78−-- the value of TERM matches the first argument. Unicode character
79−-- width maps can be produced either by running the provided binary
80−-- @vty-build-width-table@ or by calling the library routine
81−-- 'Graphics.Vty.UnicodeWidthTable.Query.buildUnicodeWidthTable'. The
82−-- 'Graphics.Vty.mkVty' function will use these configuration settings
83−-- to attempt to load and install the specified width map. See the
84−-- documentation for 'Graphics.Vty.mkVty' for details.
85−module Graphics.Vty.Config
86− ( InputMap
87− , Config(..)
88− , VtyConfigurationError(..)
89− , userConfig
90− , overrideEnvConfig
91− , standardIOConfig
92− , runParseConfig
93− , parseConfigFile
94− , defaultConfig
95− , getTtyEraseChar
96− , currentTerminalName
97−
98− , vtyConfigPath
99− , widthTableFilename
100− , vtyDataDirectory
101− , terminalWidthTablePath
102− , vtyConfigFileEnvName
103−
104− , ConfigUpdateResult(..)
105− , addConfigWidthMap
106− )
107−where
108−
109−import Prelude
110−
111−import Control.Applicative hiding (many)
112−
113−import Control.Exception (catch, IOException, Exception(..), throwIO)
114−import Control.Monad (liftM, guard, void)
115−
116−import qualified Data.ByteString as BS
117−#if !(MIN_VERSION_base(4,8,0))
118−import Data.Monoid (Monoid(..))
119−#endif
120−#if !(MIN_VERSION_base(4,11,0))
121−import Data.Semigroup (Semigroup(..))
122−#endif
123−import Data.Typeable (Typeable)
124−
125−import Graphics.Vty.Input.Events
126−import Graphics.Vty.Attributes.Color (ColorMode(..), detectColorMode)
127−
128−import GHC.Generics
129−
130−import System.Directory ( getAppUserDataDirectory, doesFileExist
131− , createDirectoryIfMissing
132− )
133−import System.Environment (lookupEnv)
134−import System.FilePath ((</>), takeDirectory)
135−import System.Posix.IO (stdInput, stdOutput)
136−import System.Posix.Types (Fd(..))
137−import Foreign.C.Types (CInt(..), CChar(..))
138−
139−import Text.Parsec hiding ((<|>))
140−import Text.Parsec.Token ( GenLanguageDef(..) )
141−import qualified Text.Parsec.Token as P
142−
143−-- | Type of errors that can be thrown when configuring VTY
144−data VtyConfigurationError =
145− VtyMissingTermEnvVar
146− -- ^ TERM environment variable not set
147− deriving (Show, Eq, Typeable)
148−
149−instance Exception VtyConfigurationError where
150− displayException VtyMissingTermEnvVar = "TERM environment variable not set"
151−
152−-- | Mappings from input bytes to event in the order specified. Later
153−-- entries take precedence over earlier in the case multiple entries
154−-- have the same byte string.
155−type InputMap = [(Maybe String, String, Event)]
156−
157−-- | A Vty configuration.
158−data Config =
159− Config { vmin :: Maybe Int
160− -- ^ The default is 1 character.
161− , vtime :: Maybe Int
162− -- ^ The default is 100 milliseconds, 0.1 seconds.
163− , mouseMode :: Maybe Bool
164− -- ^ The default is False.
165− , bracketedPasteMode :: Maybe Bool
166− -- ^ The default is False.
167− , debugLog :: Maybe FilePath
168− -- ^ Debug information is appended to this file if not
169− -- Nothing.
170− , inputMap :: InputMap
171− -- ^ The (input byte, output event) pairs extend the internal
172− -- input table of VTY and the table from terminfo.
173− --
174− -- See "Graphics.Vty.Config" module documentation for
175− -- documentation of the @map@ directive.
176− , inputFd :: Maybe Fd
177− -- ^ The input file descriptor to use. The default is
178− -- 'System.Posix.IO.stdInput'
179− , outputFd :: Maybe Fd
180− -- ^ The output file descriptor to use. The default is
181− -- 'System.Posix.IO.stdOutput'
182− , termName :: Maybe String
183− -- ^ The terminal name used to look up terminfo capabilities.
184− -- The default is the value of the TERM environment variable.
185− , termWidthMaps :: [(String, FilePath)]
186− -- ^ Terminal width map files.
187− , allowCustomUnicodeWidthTables :: Maybe Bool
188− -- ^ Whether to permit custom Unicode width table loading by
189− -- 'Graphics.Vty.mkVty'. @'Just' 'False'@ indicates that
190− -- table loading should not be performed. Other values permit
191− -- table loading.
192− --
193− -- If a table load is attempted and fails, information
194− -- about the failure will be logged to the debug log if the
195− -- configuration specifies one. If no custom table is loaded
196− -- (or if a load fails), the built-in character width table
197− -- will be used.
198− , colorMode :: Maybe ColorMode
199− -- ^ The color mode used to know how many colors the terminal
200− -- supports.
201− }
202− deriving (Show, Eq)
203−
204−defaultConfig :: Config
205−defaultConfig = mempty
206−
207−instance Semigroup Config where
208− c0 <> c1 =
209− -- latter config takes priority for everything but inputMap
210− Config { vmin = vmin c1 <|> vmin c0
211− , vtime = vtime c1 <|> vtime c0
212− , mouseMode = mouseMode c1
213− , bracketedPasteMode = bracketedPasteMode c1
214− , debugLog = debugLog c1 <|> debugLog c0
215− , inputMap = inputMap c0 <> inputMap c1
216− , inputFd = inputFd c1 <|> inputFd c0
217− , outputFd = outputFd c1 <|> outputFd c0
218− , termName = termName c1 <|> termName c0
219− , termWidthMaps = termWidthMaps c1 <|> termWidthMaps c0
220− , allowCustomUnicodeWidthTables =
221− allowCustomUnicodeWidthTables c1 <|> allowCustomUnicodeWidthTables c0
222− , colorMode = colorMode c1 <|> colorMode c0
223− }
224−
225−instance Monoid Config where
226− mempty =
227− Config { vmin = Nothing
228− , vtime = Nothing
229− , mouseMode = Nothing
230− , bracketedPasteMode = Nothing
231− , debugLog = mempty
232− , inputMap = mempty
233− , inputFd = Nothing
234− , outputFd = Nothing
235− , termName = Nothing
236− , termWidthMaps = []
237− , allowCustomUnicodeWidthTables = Nothing
238− , colorMode = Nothing
239− }
240−#if !(MIN_VERSION_base(4,11,0))
241− mappend = (<>)
242−#endif
243−
244−vtyDataDirectory :: IO FilePath
245−vtyDataDirectory = getAppUserDataDirectory "vty"
246−
247−vtyConfigPath :: IO FilePath
248−vtyConfigPath = do
249− dir <- vtyDataDirectory
250− return $ dir </> "config"
251−
252−vtyConfigFileEnvName :: String
253−vtyConfigFileEnvName = "VTY_CONFIG_FILE"
254−
255−-- | Load a configuration from 'vtyConfigPath' and @$VTY_CONFIG_FILE@.
256−userConfig :: IO Config
257−userConfig = do
258− configFile <- vtyConfigPath >>= parseConfigFile
259− overrideConfig <- maybe (return defaultConfig) parseConfigFile =<<
260− lookupEnv vtyConfigFileEnvName
261− let base = configFile <> overrideConfig
262− mappend base <$> overrideEnvConfig
263−
264−widthTableFilename :: String -> String
265−widthTableFilename term = "width_table_" <> term <> ".dat"
266−
267−termVariable :: String
268−termVariable = "TERM"
269−
270−currentTerminalName :: IO (Maybe String)
271−currentTerminalName = lookupEnv termVariable
272−
273−terminalWidthTablePath :: IO (Maybe FilePath)
274−terminalWidthTablePath = do
275− dataDir <- vtyDataDirectory
276− result <- lookupEnv termVariable
277− case result of
278− Nothing -> return Nothing
279− Just term -> do
280− return $ Just $ dataDir </> widthTableFilename term
281−
282−overrideEnvConfig :: IO Config
283−overrideEnvConfig = do
284− d <- lookupEnv "VTY_DEBUG_LOG"
285− return $ defaultConfig { debugLog = d }
286−
287−-- | Configures VTY using defaults suitable for terminals. This function
288−-- can raise 'VtyConfigurationError'.
289−standardIOConfig :: IO Config
290−standardIOConfig = do
291− mb <- lookupEnv termVariable
292− case mb of
293− Nothing -> throwIO VtyMissingTermEnvVar
294− Just t -> do
295− mcolorMode <- detectColorMode t
296− return defaultConfig
297− { vmin = Just 1
298− , mouseMode = Just False
299− , bracketedPasteMode = Just False
300− , vtime = Just 100
301− , inputFd = Just stdInput
302− , outputFd = Just stdOutput
303− , termName = Just t
304− , colorMode = Just mcolorMode
305− }
306−
307−parseConfigFile :: FilePath -> IO Config
308−parseConfigFile path = do
309− catch (runParseConfig path <$> BS.readFile path)
310− (\(_ :: IOException) -> return defaultConfig)
311−
312−runParseConfig :: String -> BS.ByteString -> Config
313−runParseConfig name cfgTxt =
314− case runParser parseConfig () name cfgTxt of
315− Right cfg -> cfg
316− Left{} -> defaultConfig
317−
318−------------------------------------------------------------------------
319−
320−type Parser = Parsec BS.ByteString ()
321−
322−configLanguage :: Monad m => P.GenLanguageDef BS.ByteString () m
323−configLanguage = LanguageDef
324− { commentStart = "{-"
325− , commentEnd = "-}"
326− , commentLine = "--"
327− , nestedComments = True
328− , identStart = letter <|> char '_'
329− , identLetter = alphaNum <|> oneOf "_'"
330− , opStart = opLetter configLanguage
331− , opLetter = oneOf ":!#$%&*+./<=>?@\\^|-~"
332− , reservedOpNames = []
333− , reservedNames = []
334− , caseSensitive = True
335− }
336−
337−configLexer :: Monad m => P.GenTokenParser BS.ByteString () m
338−configLexer = P.makeTokenParser configLanguage
339−
340−mapDecl :: Parser Config
341−mapDecl = do
342− "map" <- P.identifier configLexer
343− termIdent <- (char '_' >> P.whiteSpace configLexer >> return Nothing)
344− <|> (Just <$> P.stringLiteral configLexer)
345− bytes <- P.stringLiteral configLexer
346− key <- parseValue
347− modifiers <- parseValue
348− return defaultConfig { inputMap = [(termIdent, bytes, EvKey key modifiers)] }
349−
350−debugLogDecl :: Parser Config
351−debugLogDecl = do
352− "debugLog" <- P.identifier configLexer
353− path <- P.stringLiteral configLexer
354− return defaultConfig { debugLog = Just path }
355−
356−widthMapDecl :: Parser Config
357−widthMapDecl = do
358− "widthMap" <- P.identifier configLexer
359− tName <- P.stringLiteral configLexer
360− path <- P.stringLiteral configLexer
361− return defaultConfig { termWidthMaps = [(tName, path)] }
362−
363−ignoreLine :: Parser ()
364−ignoreLine = void $ manyTill anyChar newline
365−
366−parseConfig :: Parser Config
367−parseConfig = liftM mconcat $ many $ do
368− P.whiteSpace configLexer
369− let directives = [try mapDecl, try debugLogDecl, try widthMapDecl]
370− choice directives <|> (ignoreLine >> return defaultConfig)
371−
372−class Parse a where parseValue :: Parser a
373−instance Parse Char where parseValue = P.charLiteral configLexer
374−instance Parse Int where parseValue = fromInteger <$> P.natural configLexer
375−instance Parse Key where parseValue = genericParse
376−instance Parse Modifier where parseValue = genericParse
377−instance Parse a => Parse [a] where
378− parseValue = P.brackets configLexer
379− (parseValue `sepBy` P.symbol configLexer ",")
380−
381−------------------------------------------------------------------------
382−-- Derived parser for ADTs via generics
383−------------------------------------------------------------------------
384−
385−genericParse :: (Generic a, GParse (Rep a)) => Parser a
386−genericParse = to <$> gparse
387−
388−class GParse f where gparse :: Parser (f a)
389−instance GParse f => GParse (M1 S i f) where gparse = M1 <$> gparse
390−instance GParse U1 where gparse = return U1
391−instance Parse a => GParse (K1 i a) where gparse = K1 <$> parseValue
392−
393−instance (GParse f, GParse g) => GParse (f :*: g) where
394− gparse = (:*:) <$> gparse <*> gparse
395−
396−instance GParseAlts f => GParse (M1 D i f) where
397− gparse =
398− do con <- P.identifier configLexer
399− M1 <$> gparseAlts con
400−
401−------------------------------------------------------------------------
402−
403−class GParseAlts f where
404− gparseAlts :: String -> Parser (f a)
405−
406−instance (Constructor i, GParse f) => GParseAlts (M1 C i f) where
407− gparseAlts con =
408− do guard (con == conName (M1 Nothing :: C1 i Maybe a))
409− M1 <$> gparse
410−
411−instance (GParseAlts f, GParseAlts g) => GParseAlts (f :+: g) where
412− gparseAlts con = L1 <$> gparseAlts con <|> R1 <$> gparseAlts con
413−
414−instance GParseAlts V1 where gparseAlts _ = fail "GParse: V1"
415−
416−foreign import ccall "vty_get_tty_erase" cGetTtyErase :: Fd -> IO CChar
417−
418−-- | Get the "erase" character for the terminal attached to the
419−-- specified file descriptor. This is the character configured by 'stty
420−-- erase'. If the call to 'tcgetattr' fails, this will return 'Nothing'.
421−-- Otherwise it will return the character that has been configured to
422−-- indicate the canonical mode ERASE behavior. That character can then
423−-- be added to the table of strings that we interpret to mean Backspace.
424−--
425−-- For more details, see:
426−--
427−-- * https://www.gnu.org/software/libc/manual/html_node/Canonical-or-Not.html
428−-- * https://www.gsp.com/cgi-bin/man.cgi?section=1&topic=stty
429−-- * https://github.com/matterhorn-chat/matterhorn/issues/565
430−getTtyEraseChar :: Fd -> IO (Maybe Char)
431−getTtyEraseChar fd = do
432− c <- cGetTtyErase fd
433− if c /= 0
434− then return $ Just $ toEnum $ fromEnum c
435− else return Nothing
436−
437−data ConfigUpdateResult =
438− ConfigurationCreated
439− | ConfigurationModified
440− | ConfigurationConflict String
441− | ConfigurationRedundant
442− deriving (Eq, Show)
443−
444−-- | Add a @widthMap@ directive to the Vty configuration file at the
445−-- specified path.
446−--
447−-- If the configuration path refers to a configuration that already
448−-- contains the directive for the specified map and terminal type, the
449−-- configuration file will not be modified. If the file does not contain
450−-- the directive, it will be appended to the file.
451−--
452−-- If the configuration path does not exist, a new configuration file
453−-- will be created and any directories in the path will also be created.
454−--
455−-- This returns @True@ if the configuration was created or modified and
456−-- @False@ otherwise. This does not handle exceptions raised by file or
457−-- directory permissions issues.
458−addConfigWidthMap :: FilePath
459− -- ^ The configuration file path of the configuration
460− -- to modify or create.
461− -> String
462− -- ^ The @TERM@ value for the @widthMap@ directive.
463− -> FilePath
464− -- ^ The width table file path for the directive.
465− -> IO ConfigUpdateResult
466−addConfigWidthMap configPath term tablePath = do
467− configEx <- doesFileExist configPath
468− if configEx
469− then updateConfig
470− else createConfig >> return ConfigurationCreated
471−
472− where
473− directive = "widthMap " <> show term <> " " <> show tablePath <> "\n"
474−
475− createConfig = do
476− let dir = takeDirectory configPath
477− createDirectoryIfMissing True dir
478− writeFile configPath directive
479−
480− updateConfig = do
481− config <- parseConfigFile configPath
482− if (term, tablePath) `elem` termWidthMaps config
483− then return ConfigurationRedundant
484− else case lookup term (termWidthMaps config) of
485− Just other -> return $ ConfigurationConflict other
486− Nothing -> do
487− appendFile configPath directive
488− return ConfigurationModified
src/Graphics/Vty/Debug.hs+64−24
@@ -1,39 +1,79 @@
1−-- Copyright 2009-2010 Corey O'Connor
2−module Graphics.Vty.Debug
3− ( module Graphics.Vty.Debug
4− , module Graphics.Vty.Debug.Image
5− )
1+-- Copyright 2009 Corey O'Connor
2+{-# LANGUAGE ScopedTypeVariables #-}
3+module Graphics.Vty.Debug ( module Graphics.Vty.Debug
4+ )
65where
76
87import Graphics.Vty.Attributes
9−import Graphics.Vty.Image (DisplayRegion)
10−import Graphics.Vty.Debug.Image
8+import Graphics.Vty.Image
9+import Graphics.Vty.Picture
1110import Graphics.Vty.Span
11+import Graphics.Vty.DisplayRegion
1212
13−import qualified Data.Vector as Vector
13+import Control.Monad
14+import Data.Array
15+import Data.Word
1416
15−rowOpsAffectedColumns :: DisplayOps -> [Int]
16−rowOpsAffectedColumns ops
17− = Vector.toList $ Vector.map spanOpsAffectedColumns ops
17+instance Show SpanOpSequence where
18+ show (SpanOpSequence _ row_ops)
19+ = concat $ ["{ "] ++ map (\ops -> show ops ++ "; " ) (elems row_ops) ++ [" }"]
1820
19−allSpansHaveWidth :: DisplayOps -> Int -> Bool
20−allSpansHaveWidth ops expected
21− = all (== expected) $ Vector.toList $ Vector.map spanOpsAffectedColumns ops
21+instance Show SpanOp where
22+ show (AttributeChange attr) = show attr
23+ show (TextSpan ow cw _) = "TextSpan " ++ show ow ++ " " ++ show cw
2224
23−spanOpsAffectedRows :: DisplayOps -> Int
24−spanOpsAffectedRows ops
25− = toEnum $ length (filter (not . null . Vector.toList) (Vector.toList ops))
25+row_ops_effected_columns :: SpanOpSequence -> [Word]
26+row_ops_effected_columns spans
27+ = map span_ops_effected_columns (elems $ row_ops spans)
2628
29+all_spans_have_width spans expected
30+ = all (== expected) ( map span_ops_effected_columns (elems $ row_ops spans) )
31+
32+span_ops_effected_rows :: SpanOpSequence -> Word
33+span_ops_effected_rows (SpanOpSequence _ row_ops)
34+ = toEnum $ length (filter (not . null) (elems row_ops))
35+
2736type SpanConstructLog = [SpanConstructEvent]
2837data SpanConstructEvent = SpanSetAttr Attr
2938
30−isSetAttr :: Attr -> SpanConstructEvent -> Bool
31−isSetAttr expectedAttr (SpanSetAttr inAttr)
32− | inAttr == expectedAttr = True
33−isSetAttr _attr _event = False
39+is_set_attr :: Attr -> SpanConstructEvent -> Bool
40+is_set_attr expected_attr (SpanSetAttr in_attr)
41+ | in_attr == expected_attr = True
42+is_set_attr _attr _event = False
3443
35−data MockWindow = MockWindow Int Int
44+data DebugWindow = DebugWindow Word Word
3645 deriving (Show, Eq)
3746
38−regionForWindow :: MockWindow -> DisplayRegion
39−regionForWindow (MockWindow w h) = (w,h)
47+region_for_window :: DebugWindow -> DisplayRegion
48+region_for_window (DebugWindow w h) = DisplayRegion w h
49+
50+type TestWindow = DebugWindow
51+
52+type ImageConstructLog = [ImageConstructEvent]
53+data ImageConstructEvent = ImageConstructEvent
54+ deriving ( Show, Eq )
55+
56+forward_image_ops = map forward_transform debug_image_ops
57+
58+forward_transform, reverse_transform :: ImageOp -> (Image -> Image)
59+
60+forward_transform (ImageOp f _) = f
61+reverse_transform (ImageOp _ r) = r
62+
63+data ImageOp = ImageOp ImageEndo ImageEndo
64+type ImageEndo = Image -> Image
65+
66+debug_image_ops =
67+ [ id_image_op
68+ -- , render_single_column_char_op
69+ -- , render_double_column_char_op
70+ ]
71+
72+id_image_op :: ImageOp
73+id_image_op = ImageOp id id
74+
75+-- render_char_op :: ImageOp
76+-- render_char_op = ImageOp id id
77+
78+instance Show Picture where
79+ show (Picture _ image _ ) = "Picture ?? " ++ show image ++ " ??"
src/Graphics/Vty/Debug/Image.hsdeleted−32
@@ -1,32 +0,0 @@
1−{-# OPTIONS_HADDOCK hide #-}
2−module Graphics.Vty.Debug.Image where
3−
4−import Graphics.Vty.Image
5−
6−type ImageConstructLog = [ImageConstructEvent]
7−data ImageConstructEvent = ImageConstructEvent
8− deriving ( Show, Eq )
9−
10−forwardImageOps :: [Image -> Image]
11−forwardImageOps = map forwardTransform debugImageOps
12−
13−forwardTransform, reverseTransform :: ImageOp -> (Image -> Image)
14−
15−forwardTransform (ImageOp f _) = f
16−reverseTransform (ImageOp _ r) = r
17−
18−data ImageOp = ImageOp ImageEndo ImageEndo
19−type ImageEndo = Image -> Image
20−
21−debugImageOps :: [ImageOp]
22−debugImageOps =
23− [ idImageOp
24− -- , renderSingleColumnCharOp
25− -- , renderDoubleColumnCharOp
26− ]
27−
28−idImageOp :: ImageOp
29−idImageOp = ImageOp id id
30−
31−-- renderCharOp :: ImageOp
32−-- renderCharOp = ImageOp id id
src/Graphics/Vty/DisplayAttributes.hs+65−124
@@ -1,103 +1,60 @@
1−-- Copyright 2009-2010 Corey O'Connor
21{-# LANGUAGE BangPatterns #-}
3−{-# LANGUAGE CPP #-}
4−
5−module Graphics.Vty.DisplayAttributes where
2+module Graphics.Vty.DisplayAttributes
3+ where
64
75import Graphics.Vty.Attributes
86
9−import Data.Bits ((.&.))
10−import Data.ByteString (ByteString)
11−#if !(MIN_VERSION_base(4,11,0))
12−import Data.Semigroup (Semigroup(..))
13−#endif
14−import Data.Text (Text)
15−import Data.Text.Encoding (encodeUtf8)
7+import Data.Bits ( (.&.) )
8+import Data.Monoid ( Monoid(..), mconcat )
169
17−-- | Given the previously applied display attributes as a FixedAttr and
18−-- the current display attributes as an Attr produces a FixedAttr that
19−-- represents the current display attributes. This is done by using the
20−-- previously applied display attributes to remove the "KeepCurrent"
10+-- | Given the previously applied display attributes as a FixedAttr and the current display
11+-- attributes as an Attr produces a FixedAttr that represents the current display attributes. This
12+-- is done by using the previously applied display attributes to remove the "KeepCurrent"
2113-- abstraction.
22−fixDisplayAttr :: FixedAttr -> Attr -> FixedAttr
23−fixDisplayAttr fattr attr
24− = FixedAttr (fixStyle (fixedStyle fattr) (attrStyle attr))
25− (fixColor (fixedForeColor fattr) (attrForeColor attr))
26− (fixColor (fixedBackColor fattr) (attrBackColor attr))
27− (fixURL (fixedURL fattr) (attrURL attr))
14+fix_display_attr :: FixedAttr -> Attr -> FixedAttr
15+fix_display_attr fattr attr
16+ = FixedAttr ( fix_style (fixed_style fattr) (attr_style attr) )
17+ ( fix_color (fixed_fore_color fattr) (attr_fore_color attr) )
18+ ( fix_color (fixed_back_color fattr) (attr_back_color attr) )
2819 where
29− fixStyle _s Default = defaultStyleMask
30− fixStyle s KeepCurrent = s
31− fixStyle _s (SetTo newStyle) = newStyle
32− fixColor _c Default = Nothing
33− fixColor c KeepCurrent = c
34− fixColor _c (SetTo c) = Just c
35− fixURL c KeepCurrent = c
36− fixURL _c (SetTo n) = Just n
37− fixURL _c Default = Nothing
20+ fix_style _s Default = default_style_mask
21+ fix_style s KeepCurrent = s
22+ fix_style _s (SetTo new_style) = new_style
23+ fix_color _c Default = Nothing
24+ fix_color c KeepCurrent = c
25+ fix_color _c (SetTo c) = Just c
3826
39−-- | difference between two display attributes. Used in the calculation
40−-- of the operations required to go from one display attribute to the
41−-- next.
42−--
43−-- Previously, vty would reset display attributes to default then apply
44−-- the new display attributes. This turned out to be very expensive: A
45−-- *lot* more data would be sent to the terminal than required.
4627data DisplayAttrDiff = DisplayAttrDiff
47− { styleDiffs :: [StyleStateChange]
48− , foreColorDiff :: DisplayColorDiff
49− , backColorDiff :: DisplayColorDiff
50− , urlDiff :: URLDiff
28+ { style_diffs :: [ StyleStateChange ]
29+ , fore_color_diff :: DisplayColorDiff
30+ , back_color_diff :: DisplayColorDiff
5131 }
52− deriving (Show)
53−
54−instance Semigroup DisplayAttrDiff where
55− d0 <> d1 =
56− let ds = simplifyStyleDiffs (styleDiffs d0) (styleDiffs d1)
57− fcd = simplifyColorDiffs (foreColorDiff d0) (foreColorDiff d1)
58− bcd = simplifyColorDiffs (backColorDiff d0) (backColorDiff d1)
59− ud = simplifyUrlDiffs (urlDiff d0) (urlDiff d1)
60− in DisplayAttrDiff ds fcd bcd ud
32+ deriving ( Show )
6133
6234instance Monoid DisplayAttrDiff where
63− mempty = DisplayAttrDiff [] NoColorChange NoColorChange NoLinkChange
64−#if !(MIN_VERSION_base(4,11,0))
65− mappend = (<>)
66−#endif
35+ mempty = DisplayAttrDiff [] NoColorChange NoColorChange
36+ mappend d_0 d_1 =
37+ let ds = simplify_style_diffs ( style_diffs d_0 ) ( style_diffs d_1 )
38+ fcd = simplify_color_diffs ( fore_color_diff d_0 ) ( fore_color_diff d_1 )
39+ bcd = simplify_color_diffs ( back_color_diff d_0 ) ( back_color_diff d_1 )
40+ in DisplayAttrDiff ds fcd bcd
6741
68−-- | Used in the computation of a final style attribute change.
69−simplifyStyleDiffs :: [StyleStateChange] -> [StyleStateChange] -> [StyleStateChange]
70−simplifyStyleDiffs cs0 cs1 = cs0 `mappend` cs1
42+simplify_style_diffs :: [ StyleStateChange ] -> [ StyleStateChange ] -> [ StyleStateChange ]
43+simplify_style_diffs cs_0 cs_1 = cs_0 `mappend` cs_1
7144
72−-- | Consider two display color attributes diffs. What display color
73−-- attribute diff are these equivalent to?
74−simplifyColorDiffs :: DisplayColorDiff -> DisplayColorDiff -> DisplayColorDiff
75−simplifyColorDiffs _cd ColorToDefault = ColorToDefault
76−simplifyColorDiffs cd NoColorChange = cd
77−simplifyColorDiffs _cd (SetColor !c) = SetColor c
45+simplify_color_diffs _cd ColorToDefault = ColorToDefault
46+simplify_color_diffs cd NoColorChange = cd
47+simplify_color_diffs _cd ( SetColor !c ) = SetColor c
7848
79−-- | Consider two URL changes, which are mostly going to be the latter
80−-- unless the latter specifies no change.
81−simplifyUrlDiffs :: URLDiff -> URLDiff -> URLDiff
82−simplifyUrlDiffs ud NoLinkChange = ud
83−simplifyUrlDiffs _ ud = ud
84−
85−-- | Difference between two display color attribute changes.
86−data DisplayColorDiff
49+data DisplayColorDiff
8750 = ColorToDefault
8851 | NoColorChange
8952 | SetColor !Color
90− deriving (Show, Eq)
53+ deriving ( Show, Eq )
9154
92−-- | Style attribute changes are transformed into a sequence of
93−-- apply/removes of the individual attributes.
94−data StyleStateChange
55+data StyleStateChange
9556 = ApplyStandout
9657 | RemoveStandout
97− | ApplyItalic
98− | RemoveItalic
99− | ApplyStrikethrough
100− | RemoveStrikethrough
10158 | ApplyUnderline
10259 | RemoveUnderline
10360 | ApplyReverseVideo
@@ -108,58 +65,42 @@ data StyleStateChange
10865 | RemoveDim
10966 | ApplyBold
11067 | RemoveBold
111− deriving (Show, Eq)
112−
113−-- Setting and unsetting hyperlinks
114−data URLDiff
115− = LinkTo !ByteString
116− | NoLinkChange
117− | EndLink
118− deriving (Show, Eq)
68+ deriving ( Show, Eq )
11969
120−-- | Determines the diff between two display&color attributes. This diff
121−-- determines the operations that actually get output to the terminal.
122−displayAttrDiffs :: FixedAttr -> FixedAttr -> DisplayAttrDiff
123−displayAttrDiffs attr attr' = DisplayAttrDiff
124− { styleDiffs = diffStyles (fixedStyle attr) (fixedStyle attr')
125− , foreColorDiff = diffColor (fixedForeColor attr) (fixedForeColor attr')
126− , backColorDiff = diffColor (fixedBackColor attr) (fixedBackColor attr')
127− , urlDiff = diffURL (fixedURL attr) (fixedURL attr')
70+display_attr_diffs :: FixedAttr -> FixedAttr -> DisplayAttrDiff
71+display_attr_diffs attr attr' = DisplayAttrDiff
72+ { style_diffs = diff_styles ( fixed_style attr ) ( fixed_style attr' )
73+ , fore_color_diff = diff_color ( fixed_fore_color attr ) ( fixed_fore_color attr' )
74+ , back_color_diff = diff_color ( fixed_back_color attr ) ( fixed_back_color attr' )
12875 }
12976
130−diffURL :: Maybe Text -> Maybe Text -> URLDiff
131−diffURL Nothing Nothing = NoLinkChange
132−diffURL (Just _) Nothing = EndLink
133−diffURL _ (Just url) = LinkTo (encodeUtf8 url)
134−
135−diffColor :: Maybe Color -> Maybe Color -> DisplayColorDiff
136−diffColor Nothing (Just c') = SetColor c'
137−diffColor (Just c) (Just c')
77+diff_color :: Maybe Color -> Maybe Color -> DisplayColorDiff
78+diff_color Nothing (Just c') = SetColor c'
79+diff_color (Just c) (Just c')
13880 | c == c' = NoColorChange
13981 | otherwise = SetColor c'
140−diffColor Nothing Nothing = NoColorChange
141−diffColor (Just _) Nothing = ColorToDefault
142−
143−diffStyles :: Style -> Style -> [StyleStateChange]
144−diffStyles prev cur
145− = mconcat
146− [ styleDiff standout ApplyStandout RemoveStandout
147− , styleDiff underline ApplyUnderline RemoveUnderline
148− , styleDiff italic ApplyItalic RemoveItalic
149− , styleDiff strikethrough ApplyStrikethrough RemoveStrikethrough
150− , styleDiff reverseVideo ApplyReverseVideo RemoveReverseVideo
151− , styleDiff blink ApplyBlink RemoveBlink
152− , styleDiff dim ApplyDim RemoveDim
153− , styleDiff bold ApplyBold RemoveBold
82+diff_color Nothing Nothing = NoColorChange
83+diff_color (Just _) Nothing = ColorToDefault
84+
85+diff_styles :: Style -> Style -> [StyleStateChange]
86+diff_styles prev cur
87+ = mconcat
88+ [ style_diff standout ApplyStandout RemoveStandout
89+ , style_diff underline ApplyUnderline RemoveUnderline
90+ , style_diff reverse_video ApplyReverseVideo RemoveReverseVideo
91+ , style_diff blink ApplyBlink RemoveBlink
92+ , style_diff dim ApplyDim RemoveDim
93+ , style_diff bold ApplyBold RemoveBold
15494 ]
155− where
156− styleDiff s sm rm
157− = case (0 == prev .&. s, 0 == cur .&. s) of
95+ where
96+ style_diff s sm rm
97+ = case ( 0 == prev .&. s, 0 == cur .&. s ) of
15898 -- not set in either
159− (True, True) -> []
99+ ( True, True ) -> []
160100 -- set in both
161− (False, False) -> []
101+ ( False, False ) -> []
162102 -- now set
163− (True, False) -> [sm]
103+ ( True, False) -> [ sm ]
164104 -- now unset
165− (False, True) -> [rm]
105+ ( False, True) -> [ rm ]
106+
src/Graphics/Vty/DisplayRegion.hsnew file+11
@@ -0,0 +1,11 @@
1+-- Copyright 2009 Corey O'Connor
2+module Graphics.Vty.DisplayRegion
3+ where
4+
5+import Data.Word
6+
7+data DisplayRegion = DisplayRegion
8+ { region_width :: !Word
9+ , region_height :: !Word
10+ } deriving ( Show, Eq )
11+
src/Graphics/Vty/Error.hsdeleted−8
@@ -1,8 +0,0 @@
1−module Graphics.Vty.Error where
2−
3−-- | The type of exceptions specific to vty.
4−--
5−-- These have fully qualified names by default since, IMO, exception
6−-- handling requires this.
7−data VtyException
8− = VtyFailure String -- ^ Uncategorized failure specific to vty.
src/Graphics/Vty/Image.hs+301−353
@@ -1,381 +1,329 @@
1−-- Copyright 2009-2010 Corey O'Connor
1+-- Copyright 2009 Corey O'Connor
2+{-# LANGUAGE BangPatterns #-}
23{-# LANGUAGE NamedFieldPuns #-}
34{-# LANGUAGE DisambiguateRecordFields #-}
4−-- | A Vty program makes 'Picture's from 'Image's. This module provides
5−-- the core constructors for creating, combining, and modifying
6−-- 'Image's.
7−module Graphics.Vty.Image
8− (
9− -- * Images
10− Image
11− , imageWidth
12− , imageHeight
13− -- * Image constructors
14− , emptyImage
15− , char
16− , string
17− , iso10646String
18− , utf8String
19− , text
20− , text'
21− , backgroundFill
22− , utf8Bytestring
23− , utf8Bytestring'
24− , charFill
25− -- * Combinators
26− , horizJoin
27− , (<|>)
28− , vertJoin
29− , (<->)
30− , horizCat
31− , vertCat
32− -- * Image modifications
33− , crop
34− , cropRight
35− , cropLeft
36− , cropBottom
37− , cropTop
38− , pad
39− , resize
40− , resizeWidth
41− , resizeHeight
42− , translate
43− , translateX
44− , translateY
45− -- * Character width functions
46− , safeWcwidth
47− , safeWcswidth
48− , safeWctwidth
49− , safeWctlwidth
50− , wcwidth
51− , wcswidth
52− , wctwidth
53− , wctlwidth
54− -- * Display Regions
55− , DisplayText
56− , DisplayRegion
57− , regionWidth
58− , regionHeight
59− )
60−where
5+{-# OPTIONS_GHC -funbox-strict-fields #-}
6+module Graphics.Vty.Image ( Image(..)
7+ , image_width
8+ , image_height
9+ , (<|>)
10+ , (<->)
11+ , horiz_cat
12+ , vert_cat
13+ , background_fill
14+ , char
15+ , string
16+ , iso_10646_string
17+ , utf8_string
18+ , utf8_bytestring
19+ , char_fill
20+ , empty_image
21+ , translate
22+ -- | The possible display attributes used in constructing an `Image`.
23+ , module Graphics.Vty.Attributes
24+ )
25+ where
6126
6227import Graphics.Vty.Attributes
63−import Graphics.Vty.Image.Internal
64−import Graphics.Text.Width
65−
66−import qualified Data.ByteString as B
67−import qualified Data.ByteString.Lazy as BL
68−import qualified Data.Text as T
69−import qualified Data.Text.Encoding as T
70−import qualified Data.Text.Lazy as TL
71−import qualified Data.Text.Lazy.Encoding as TL
72−import Data.Word
7328
74−-- | A region of the display (first width, then height)
75−type DisplayRegion = (Int,Int)
29+import Codec.Binary.UTF8.Width
7630
77−regionWidth :: DisplayRegion -> Int
78−regionWidth = fst
31+import Codec.Binary.UTF8.String ( decode )
7932
80−regionHeight :: DisplayRegion -> Int
81−regionHeight = snd
33+import Data.AffineSpace
34+import qualified Data.ByteString as BS
35+import Data.Monoid
36+import qualified Data.Sequence as Seq
37+import qualified Data.String.UTF8 as UTF8
38+import Data.Word
8239
8340infixr 5 <|>
8441infixr 4 <->
8542
86−-- | An area of the picture's background (See 'Background').
87−backgroundFill :: Int
88− -- ^ Fill width in columns
89− -> Int
90− -- ^ Fill height in rows
91− -> Image
92−backgroundFill w h
43+type StringSeq = Seq.Seq Char
44+
45+-- | An image in VTY defines:
46+--
47+-- * properties required to display the image. These are properties that effect the output image
48+-- but are independent of position
49+--
50+-- * A set of position-dependent text and attribute regions. The possible regions are:
51+--
52+-- * a point. ( char )
53+--
54+-- * a horizontal line of characters with a single attribute. (string, utf8_string,
55+-- utf8_bytestring )
56+--
57+-- * a fill of a single character. (char_fill)
58+--
59+-- * a fill of the picture's background. (background_fill)
60+--
61+-- todo: increase the number of encoded bytestring formats supported.
62+data Image =
63+ -- A horizontal text span is always >= 1 column and has a row height of 1.
64+ HorizText
65+ { attr :: !Attr
66+ -- All character data is stored as Char sequences with the ISO-10646 encoding.
67+ , text :: StringSeq
68+ -- in columns
69+ , output_width :: !Word -- >= 0
70+ , char_width :: !Word -- >= 1
71+ }
72+ -- A horizontal join can be constructed between any two images. However a HorizJoin instance is
73+ -- required to be between two images of equal height. The horiz_join constructor adds background
74+ -- filles to the provided images that assure this is true for the HorizJoin value produced.
75+ | HorizJoin
76+ { part_left :: Image
77+ , part_right :: Image
78+ , output_width :: !Word -- >= 1
79+ , output_height :: !Word -- >= 1
80+ }
81+ -- A veritical join can be constructed between any two images. However a VertJoin instance is
82+ -- required to be between two images of equal width. The vert_join constructor adds background
83+ -- fills to the provides images that assure this is true for the VertJoin value produced.
84+ | VertJoin
85+ { part_top :: Image
86+ , part_bottom :: Image
87+ , output_width :: !Word -- >= 1
88+ , output_height :: !Word -- >= 1
89+ }
90+ -- A background fill will be filled with the background pattern. The background pattern is
91+ -- defined as a property of the Picture this Image is used to form.
92+ | BGFill
93+ { output_width :: !Word -- >= 1
94+ , output_height :: !Word -- >= 1
95+ }
96+ -- The combining operators identity constant.
97+ -- EmptyImage <|> a = a
98+ -- EmptyImage <-> a = a
99+ --
100+ -- Any image of zero size equals the empty image.
101+ | EmptyImage
102+ | Translation (Int, Int) Image
103+ deriving Eq
104+
105+instance Show Image where
106+ show ( HorizText { output_width = ow, text = txt } )
107+ = "HorizText [" ++ show ow ++ "] (" ++ show txt ++ ")"
108+ show ( BGFill { output_width = c, output_height = r } )
109+ = "BGFill (" ++ show c ++ "," ++ show r ++ ")"
110+ show ( HorizJoin { part_left = l, part_right = r, output_width = c } )
111+ = "HorizJoin " ++ show c ++ " ( " ++ show l ++ " <|> " ++ show r ++ " )"
112+ show ( VertJoin { part_top = t, part_bottom = b, output_width = c, output_height = r } )
113+ = "VertJoin (" ++ show c ++ ", " ++ show r ++ ") ( " ++ show t ++ " ) <-> ( " ++ show b ++ " )"
114+ show ( EmptyImage ) = "EmptyImage"
115+
116+-- | Currently append in the Monoid instance is equivalent to <->. Future versions will just stack
117+-- the images.
118+instance Monoid Image where
119+ mempty = empty_image
120+ mappend = (<->)
121+
122+-- A horizontal text image of 0 characters in width simplifies to the EmptyImage
123+horiz_text :: Attr -> StringSeq -> Word -> Image
124+horiz_text a txt ow
125+ | ow == 0 = EmptyImage
126+ | otherwise = HorizText a txt ow (toEnum $ Seq.length txt)
127+
128+horiz_join :: Image -> Image -> Word -> Word -> Image
129+horiz_join i_0 i_1 w h
130+ -- A horiz join of two 0 width images simplifies to the EmptyImage
131+ | w == 0 = EmptyImage
132+ -- A horizontal join where either part is 0 columns in width simplifies to the other part.
133+ -- This covers the case where one part is the EmptyImage.
134+ | image_width i_0 == 0 = i_1
135+ | image_width i_1 == 0 = i_0
136+ -- If the images are of the same height then no BG padding is required
137+ | image_height i_0 == image_height i_1 = HorizJoin i_0 i_1 w h
138+ -- otherwise one of the imagess needs to be padded to the right size.
139+ | image_height i_0 < image_height i_1 -- Pad i_0
140+ = let pad_amount = image_height i_1 - image_height i_0
141+ in horiz_join ( vert_join i_0
142+ ( BGFill ( image_width i_0 ) pad_amount )
143+ ( image_width i_0 )
144+ ( image_height i_1 )
145+ )
146+ i_1
147+ w h
148+ | image_height i_0 > image_height i_1 -- Pad i_1
149+ = let pad_amount = image_height i_0 - image_height i_1
150+ in horiz_join i_0
151+ ( vert_join i_1
152+ ( BGFill ( image_width i_1 ) pad_amount )
153+ ( image_width i_1 )
154+ ( image_height i_0 )
155+ )
156+ w h
157+horiz_join _ _ _ _ = error "horiz_join applied to undefined values."
158+
159+vert_join :: Image -> Image -> Word -> Word -> Image
160+vert_join i_0 i_1 w h
161+ -- A vertical join of two 0 height images simplifies to the EmptyImage
162+ | h == 0 = EmptyImage
163+ -- A vertical join where either part is 0 rows in height simplifies to the other part.
164+ -- This covers the case where one part is the EmptyImage
165+ | image_height i_0 == 0 = i_1
166+ | image_height i_1 == 0 = i_0
167+ -- If the images are of the same height then no background padding is required
168+ | image_width i_0 == image_width i_1 = VertJoin i_0 i_1 w h
169+ -- Otherwise one of the images needs to be padded to the size of the other image.
170+ | image_width i_0 < image_width i_1
171+ = let pad_amount = image_width i_1 - image_width i_0
172+ in vert_join ( horiz_join i_0
173+ ( BGFill pad_amount ( image_height i_0 ) )
174+ ( image_width i_1 )
175+ ( image_height i_0 )
176+ )
177+ i_1
178+ w h
179+ | image_width i_0 > image_width i_1
180+ = let pad_amount = image_width i_0 - image_width i_1
181+ in vert_join i_0
182+ ( horiz_join i_1
183+ ( BGFill pad_amount ( image_height i_1 ) )
184+ ( image_width i_0 )
185+ ( image_height i_1 )
186+ )
187+ w h
188+vert_join _ _ _ _ = error "vert_join applied to undefined values."
189+
190+-- | An area of the picture's bacground (See Background) of w columns and h rows.
191+background_fill :: Word -> Word -> Image
192+background_fill w h
93193 | w == 0 = EmptyImage
94194 | h == 0 = EmptyImage
95195 | otherwise = BGFill w h
96196
97−-- | Combines two images horizontally. This is an alias for 'horizJoin'.
197+-- | The width of an Image. This is the number display columns the image will occupy.
198+image_width :: Image -> Word
199+image_width HorizText { output_width = w } = w
200+image_width HorizJoin { output_width = w } = w
201+image_width VertJoin { output_width = w } = w
202+image_width BGFill { output_width = w } = w
203+image_width EmptyImage = 0
204+image_width ( Translation _v i ) = image_width i
205+
206+-- | The height of an Image. This is the number of display rows the image will occupy.
207+image_height :: Image -> Word
208+image_height HorizText {} = 1
209+image_height HorizJoin { output_height = r } = r
210+image_height VertJoin { output_height = r } = r
211+image_height BGFill { output_height = r } = r
212+image_height EmptyImage = 0
213+image_height ( Translation _v i ) = image_width i
214+
215+-- | Combines two images side by side.
98216--
99−-- infixr 5
217+-- The result image will have a width equal to the sum of the two images width. And the height will
218+-- equal the largest height of the two images. The area not defined in one image due to a height
219+-- missmatch will be filled with the background pattern.
100220(<|>) :: Image -> Image -> Image
101−(<|>) = horizJoin
102221
103−-- | Combines two images vertically. This is an alias for 'vertJoin'.
104−--
105−-- infixr 4
222+-- Two horizontal text spans with the same attributes can be merged.
223+h0@(HorizText attr_0 text_0 ow_0 _) <|> h1@(HorizText attr_1 text_1 ow_1 _)
224+ | attr_0 == attr_1 = horiz_text attr_0 (text_0 Seq.>< text_1) (ow_0 + ow_1)
225+ | otherwise = horiz_join h0 h1 (ow_0 + ow_1) 1
226+
227+-- Anything placed to the right of a join wil be joined to the right sub image.
228+-- The total columns for the join is the sum of the two arguments columns
229+h0@( HorizJoin {} ) <|> h1
230+ = horiz_join ( part_left h0 )
231+ ( part_right h0 <|> h1 )
232+ ( image_width h0 + image_width h1 )
233+ ( max (image_height h0) (image_height h1) )
234+
235+-- Anything but a join placed to the left of a join wil be joined to the left sub image.
236+-- The total columns for the join is the sum of the two arguments columns
237+h0 <|> h1@( HorizJoin {} )
238+ = horiz_join ( h0 <|> part_left h1 )
239+ ( part_right h1 )
240+ ( image_width h0 + image_width h1 )
241+ ( max (image_height h0) (image_height h1) )
242+
243+h0 <|> h1
244+ = horiz_join h0
245+ h1
246+ ( image_width h0 + image_width h1 )
247+ ( max (image_height h0) (image_height h1) )
248+
249+-- | Combines two images vertically.
250+-- The result image will have a height equal to the sum of the heights of both images.
251+-- The width will equal the largest width of the two images.
252+-- The area not defined in one image due to a width missmatch will be filled with the background
253+-- pattern.
106254(<->) :: Image -> Image -> Image
107−(<->) = vertJoin
108−
109−-- | Compose any number of images together horizontally, with the first
110−-- in the list being leftmost.
111−horizCat :: [Image] -> Image
112−horizCat = foldr horizJoin EmptyImage
113−
114−-- | Compose any number of images vertically, with the first in the list
115−-- being topmost.
116−vertCat :: [Image] -> Image
117−vertCat = foldr vertJoin EmptyImage
118−
119−-- | Make an 'Image' from a lazy text value. The text value should be
120−-- sanitized of escape sequences (ASCII 27) and carriage returns;
121−-- otherwise layout and attribute problems may result.
122−text :: Attr -> TL.Text -> Image
123−text a txt = let displayWidth = safeWctlwidth txt
124− in HorizText a txt displayWidth (fromIntegral $! TL.length txt)
125−
126−-- | Make an 'Image' from a text value. The text value should be
127−-- sanitized of escape sequences (ASCII 27) and carriage returns;
128−-- otherwise layout and attribute problems may result.
129−text' :: Attr -> T.Text -> Image
130−text' a txt = let displayWidth = safeWctwidth txt
131− in HorizText a (TL.fromStrict txt) displayWidth (T.length txt)
132−
133−-- | Make an image from a single character. This is a standard Haskell
134−-- 31-bit character assumed to be in the ISO-10646 encoding.
255+im_t <-> im_b
256+ = vert_join im_t
257+ im_b
258+ ( max (image_width im_t) (image_width im_b) )
259+ ( image_height im_t + image_height im_b )
260+
261+-- | Compose any number of images horizontally.
262+horiz_cat :: [Image] -> Image
263+horiz_cat = foldr (<|>) EmptyImage
264+
265+-- | Compose any number of images vertically.
266+vert_cat :: [Image] -> Image
267+vert_cat = foldr (<->) EmptyImage
268+
269+-- | an image of a single character. This is a standard Haskell 31-bit character assumed to be in
270+-- the ISO-10646 encoding.
135271char :: Attr -> Char -> Image
136−char a c =
137− let displayWidth = safeWcwidth c
138− in HorizText a (TL.singleton c) displayWidth 1
139−
140−-- | Make an image from a string of characters laid out on a single
141−-- row with the same display attribute. The string is assumed to be a
142−-- sequence of ISO-10646 characters. The input string should be
143−-- sanitized of escape sequences (ASCII 27) and carriage returns;
144−-- otherwise layout and attribute problems may result.
145−--
146−-- Note: depending on how the Haskell compiler represents string
147−-- literals, a string literal in a UTF-8 encoded source file, for
148−-- example, may be represented as a ISO-10646 string. That is, I think,
149−-- the case with GHC 6.10. This means, for the most part, you don't need
150−-- to worry about the encoding format when outputting string literals.
151−-- Just provide the string literal directly to iso10646String or string.
152−iso10646String :: Attr -> String -> Image
153−iso10646String a str =
154− let displayWidth = safeWcswidth str
155− in HorizText a (TL.pack str) displayWidth (length str)
156−
157−-- | Make an 'Image' from a 'String'.
272+char !a !c = HorizText a (Seq.singleton c) (safe_wcwidth c) 1
273+
274+-- | A string of characters layed out on a single row with the same display attribute. The string is
275+-- assumed to be a sequence of ISO-10646 characters.
158276--
159−-- This is an alias for iso10646String since the usual case is that a
160−-- literal string like "foo" is represented internally as a list of ISO
161−-- 10646 31 bit characters.
277+-- Note: depending on how the Haskell compiler represents string literals a string literal in a
278+-- UTF-8 encoded source file, for example, may be represented as a ISO-10646 string.
279+-- That is, I think, the case with GHC 6.10. This means, for the most part, you don't need to worry
280+-- about the encoding format when outputting string literals. Just provide the string literal
281+-- directly to iso_10646_string or string.
282+--
283+iso_10646_string :: Attr -> String -> Image
284+iso_10646_string !a !str = horiz_text a (Seq.fromList str) (safe_wcswidth str)
285+
286+-- | Alias for iso_10646_string. Since the usual case is that a literal string like "foo" is
287+-- represented internally as a list of ISO 10646 31 bit characters.
162288--
163−-- Note: Keep in mind that GHC will compile source encoded as UTF-8
164−-- but the literal strings, while UTF-8 encoded in the source, will be
165−-- transcoded to a ISO 10646 31 bit characters runtime representation.
289+-- Note: Keep in mind that GHC will compile source encoded as UTF-8 but the literal strings, while
290+-- UTF-8 encoded in the source, will be transcoded to a ISO 10646 31 bit characters runtime
291+-- representation.
166292string :: Attr -> String -> Image
167−string = iso10646String
293+string = iso_10646_string
168294
169−-- | Make an 'Image' from a string of characters layed out on a single
170−-- row. The input is assumed to be the bytes for UTF-8 encoded text.
171−utf8String :: Attr -> [Word8] -> Image
172−utf8String a bytes = utf8Bytestring a (BL.pack bytes)
295+-- | A string of characters layed out on a single row. The string is assumed to be a sequence of
296+-- UTF-8 characters.
297+utf8_string :: Attr -> [Word8] -> Image
298+utf8_string !a !str = string a ( decode str )
173299
174−-- | Make an 'Image' from a UTF-8 encoded lazy bytestring.
175−utf8Bytestring :: Attr -> BL.ByteString -> Image
176−utf8Bytestring a bs = text a (TL.decodeUtf8 bs)
300+safe_wcwidth :: Char -> Word
301+safe_wcwidth c = case wcwidth c of
302+ i | i < 0 -> 0 -- error "negative wcwidth"
303+ | otherwise -> toEnum i
177304
178−-- | Make an 'Image' from a UTF-8 encoded strict bytestring.
179−utf8Bytestring' :: Attr -> B.ByteString -> Image
180−utf8Bytestring' a bs = text' a (T.decodeUtf8 bs)
305+safe_wcswidth :: String -> Word
306+safe_wcswidth str = case wcswidth str of
307+ i | i < 0 -> 0 -- error "negative wcswidth"
308+ | otherwise -> toEnum i
181309
182−-- | Make an image filling a region with the specified character.
183−--
184−-- If either the width or height are less than or equal to 0, then
185−-- the result is the empty image.
186−charFill :: Integral d
187− => Attr
188− -- ^ The attribute to use.
189− -> Char
190− -- ^ The character to use in filling the region.
191− -> d
192− -- ^ The region width.
193− -> d
194− -- ^ The region height.
195− -> Image
196−charFill a c w h
197− | w <= 0 || h <= 0 = EmptyImage
198− | otherwise = vertCat
199− $ replicate (fromIntegral h)
200− $ HorizText a txt displayWidth charWidth
201− where
202− txt = TL.replicate charWidth (TL.singleton c)
203− displayWidth = safeWcwidth c * charWidth
204−
205− charWidth :: Num a => a
206− charWidth = fromIntegral w
207−
208−-- | The empty image. Useful for fold combinators. These occupy no space
209−-- and do not affect display attributes.
210−emptyImage :: Image
211−emptyImage = EmptyImage
212−
213−-- | Pad the given image. This adds background character fills to the
214−-- left, top, right, bottom.
215−pad :: Int
216− -- ^ How much padding to add to the left side of the image.
217− -> Int
218− -- ^ How much padding to add to the top of the image.
219− -> Int
220− -- ^ How much padding to add to the right side of the image.
221− -> Int
222− -- ^ How much padding to add to the bottom of the image.
223− -> Image
224− -- ^ The image to pad.
225− -> Image
226−pad 0 0 0 0 i = i
227−pad inL inT inR inB inImage
228− | inL < 0 || inT < 0 || inR < 0 || inB < 0 = error "cannot pad by negative amount"
229− | otherwise = go inL inT inR inB inImage
230− where
231− go 0 0 0 0 i = i
232− go 0 0 0 b i = VertJoin i (BGFill w b) w h
233− where w = imageWidth i
234− h = imageHeight i + b
235− go 0 0 r b i = go 0 0 0 b $ HorizJoin i (BGFill r h) w h
236− where w = imageWidth i + r
237− h = imageHeight i
238− go 0 t r b i = go 0 0 r b $ VertJoin (BGFill w t) i w h
239− where w = imageWidth i
240− h = imageHeight i + t
241− go l t r b i = go 0 t r b $ HorizJoin (BGFill l h) i w h
242− where w = imageWidth i + l
243− h = imageHeight i
244−
245−-- | Translates an image by padding or cropping the left and top.
246−--
247−-- If translation offsets are negative then the image is cropped.
248−translate :: Int
249− -- ^ The horizontal translation offset (can be negative)
250− -> Int
251− -- ^ The vertical translation offset (can be negative)
252− -> Image
253− -- ^ The image to translate.
254− -> Image
255−translate x y i = translateX x (translateY y i)
256−
257−-- | Translates an image by padding or cropping its left side.
258−translateX :: Int -> Image -> Image
259−translateX x i
260− | x < 0 && (abs x > imageWidth i) = emptyImage
261− | x < 0 = let s = abs x in CropLeft i s (imageWidth i - s) (imageHeight i)
262− | x == 0 = i
263− | otherwise = let h = imageHeight i in HorizJoin (BGFill x h) i (imageWidth i + x) h
264−
265−-- | Translates an image by padding or cropping its top.
266−translateY :: Int -> Image -> Image
267−translateY y i
268− | y < 0 && (abs y > imageHeight i) = emptyImage
269− | y < 0 = let s = abs y in CropTop i s (imageWidth i) (imageHeight i - s)
270− | y == 0 = i
271− | otherwise = let w = imageWidth i in VertJoin (BGFill w y) i w (imageHeight i + y)
272−
273−-- | Ensure an image is no larger than the provided size. If the image
274−-- is larger then crop the right or bottom.
310+-- | Renders a UTF-8 encoded bytestring.
311+utf8_bytestring :: Attr -> BS.ByteString -> Image
312+utf8_bytestring !a !bs = string a (UTF8.toString $ UTF8.fromRep bs)
313+
314+-- | creates a fill of the specified character. The dimensions are in number of characters wide and
315+-- number of rows high.
275316--
276−-- This is equivalent to a vertical crop from the bottom followed by
277−-- horizontal crop from the right.
278−crop :: Int
279− -- ^ Cropping width
280− -> Int
281− -- ^ Cropping height
282− -> Image
283− -- ^ The image to crop
284− -> Image
285−crop 0 _ _ = EmptyImage
286−crop _ 0 _ = EmptyImage
287−crop w h i = cropBottom h (cropRight w i)
288−
289−-- | Crop an image's height. If the image's height is less than or equal
290−-- to the specified height then this operation has no effect. Otherwise
291−-- the image is cropped from the bottom.
292−cropBottom :: Int -> Image -> Image
293−cropBottom 0 _ = EmptyImage
294−cropBottom h inI
295− | h < 0 = error "cannot crop height to less than zero"
296− | otherwise = go inI
297− where
298− go EmptyImage = EmptyImage
299− go i@(CropBottom {croppedImage, outputWidth, outputHeight})
300− | outputHeight <= h = i
301− | otherwise = CropBottom croppedImage outputWidth h
302− go i
303− | h >= imageHeight i = i
304− | otherwise = CropBottom i (imageWidth i) h
305−
306−-- | Crop an image's width. If the image's width is less than or equal
307−-- to the specified width then this operation has no effect. Otherwise
308−-- the image is cropped from the right.
309−cropRight :: Int -> Image -> Image
310−cropRight 0 _ = EmptyImage
311−cropRight w inI
312− | w < 0 = error "cannot crop width to less than zero"
313− | otherwise = go inI
314− where
315− go EmptyImage = EmptyImage
316− go i@(CropRight {croppedImage, outputWidth, outputHeight})
317− | outputWidth <= w = i
318− | otherwise = CropRight croppedImage w outputHeight
319− go i
320− | w >= imageWidth i = i
321− | otherwise = CropRight i w (imageHeight i)
322−
323−-- | Crop an image's width. If the image's width is less than or equal
324−-- to the specified width then this operation has no effect. Otherwise
325−-- the image is cropped from the left.
326−cropLeft :: Int -> Image -> Image
327−cropLeft 0 _ = EmptyImage
328−cropLeft w inI
329− | w < 0 = error "cannot crop the width to less than zero"
330− | otherwise = go inI
331− where
332− go EmptyImage = EmptyImage
333− go i@(CropLeft {croppedImage, leftSkip, outputWidth, outputHeight})
334− | outputWidth <= w = i
335− | otherwise =
336− let leftSkip' = leftSkip + outputWidth - w
337− in CropLeft croppedImage leftSkip' w outputHeight
338− go i
339− | imageWidth i <= w = i
340− | otherwise = CropLeft i (imageWidth i - w) w (imageHeight i)
341−
342−-- | Crop an image's height. If the image's height is less than or equal
343−-- to the specified height then this operation has no effect. Otherwise
344−-- the image is cropped from the top.
345−cropTop :: Int -> Image -> Image
346−cropTop 0 _ = EmptyImage
347−cropTop h inI
348− | h < 0 = error "cannot crop the height to less than zero"
349− | otherwise = go inI
350− where
351− go EmptyImage = EmptyImage
352− go i@(CropTop {croppedImage, topSkip, outputWidth, outputHeight})
353− | outputHeight <= h = i
354− | otherwise =
355− let topSkip' = topSkip + outputHeight - h
356− in CropTop croppedImage topSkip' outputWidth h
357− go i
358− | imageHeight i <= h = i
359− | otherwise = CropTop i (imageHeight i - h) (imageWidth i) h
360−
361−-- | Generic resize. Pads and crops are added to ensure that the
362−-- resulting image matches the specified dimensions. This is biased to
363−-- pad/crop the right and bottom.
364−resize :: Int -> Int -> Image -> Image
365−resize w h i = resizeHeight h (resizeWidth w i)
366−
367−-- | Resize the width. Pads and crops as required to assure the given
368−-- display width. This is biased to pad/crop on the right.
369−resizeWidth :: Int -> Image -> Image
370−resizeWidth w i = case w `compare` imageWidth i of
371− LT -> cropRight w i
372− EQ -> i
373− GT -> i <|> BGFill (w - imageWidth i) (imageHeight i)
374−
375−-- | Resize the height. Pads and crops as required to assure the given
376−-- display height. This is biased to pad/crop on the bottom.
377−resizeHeight :: Int -> Image -> Image
378−resizeHeight h i = case h `compare` imageHeight i of
379− LT -> cropBottom h i
380− EQ -> i
381− GT -> i <-> BGFill (imageWidth i) (h - imageHeight i)
317+-- Unlike the Background fill character this character can have double column display width.
318+char_fill :: Enum d => Attr -> Char -> d -> d -> Image
319+char_fill !a !c w h =
320+ vert_cat $ replicate (fromEnum h) $ horiz_cat $ replicate (fromEnum w) $ char a c
321+
322+-- | The empty image. Useful for fold combinators. These occupy no space nor define any display
323+-- attributes.
324+empty_image :: Image
325+empty_image = EmptyImage
326+
327+translate :: (Int, Int) -> Image -> Image
328+translate v i = Translation v i
329+
src/Graphics/Vty/Image/Internal.hsdeleted−285
@@ -1,285 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE NamedFieldPuns #-}
3−{-# LANGUAGE DeriveGeneric #-}
4−{-# OPTIONS_HADDOCK hide #-}
5−
6−module Graphics.Vty.Image.Internal where
7−
8−import Graphics.Vty.Attributes
9−import Graphics.Text.Width
10−
11−import GHC.Generics
12−
13−import Control.DeepSeq
14−
15−#if !(MIN_VERSION_base(4,11,0))
16−import Data.Semigroup (Semigroup(..))
17−#endif
18−import qualified Data.Text.Lazy as TL
19−
20−-- | A display text is a Data.Text.Lazy
21−type DisplayText = TL.Text
22−
23−clipText :: DisplayText -> Int -> Int -> DisplayText
24−clipText txt leftSkip rightClip =
25− -- CPS would clarify this I think
26− let (toDrop,padPrefix) = clipForCharWidth leftSkip txt 0
27− txt' = if padPrefix then TL.cons '…' (TL.drop (toDrop+1) txt) else TL.drop toDrop txt
28− (toTake,padSuffix) = clipForCharWidth rightClip txt' 0
29− txt'' = TL.append (TL.take toTake txt') (if padSuffix then TL.singleton '…' else TL.empty)
30− -- Note: some characters and zero-width and combining characters
31− -- combine to the left, so keep taking characters even if the
32− -- width is zero.
33− clipForCharWidth w t n
34− | TL.null t = (n, False)
35− | w < cw = (n, w /= 0)
36− | otherwise = clipForCharWidth (w - cw) (TL.tail t) (n + 1)
37− where cw = safeWcwidth (TL.head t)
38− in txt''
39−
40−-- | This is the internal representation of Images. Use the constructors
41−-- in "Graphics.Vty.Image" to create instances.
42−--
43−-- Images are:
44−--
45−-- * a horizontal span of text
46−--
47−-- * a horizontal or vertical join of two images
48−--
49−-- * a two dimensional fill of the 'Picture's background character
50−--
51−-- * a cropped image
52−--
53−-- * an empty image of no size or content.
54−data Image =
55− -- | A horizontal text span has a row height of 1.
56− HorizText
57− { attr :: Attr
58− -- | The text to display. The display width of the text is always
59− -- outputWidth.
60− , displayText :: DisplayText
61− -- | The number of display columns for the text.
62− , outputWidth :: Int
63− -- | the number of characters in the text.
64− , charWidth :: Int
65− }
66− -- | A horizontal join can be constructed between any two images.
67− -- However a HorizJoin instance is required to be between two images
68− -- of equal height. The horizJoin constructor adds background fills
69− -- to the provided images that assure this is true for the HorizJoin
70− -- value produced.
71− | HorizJoin
72− { partLeft :: Image
73− , partRight :: Image
74− , outputWidth :: Int
75− -- ^ imageWidth partLeft == imageWidth partRight. Always > 0
76− , outputHeight :: Int
77− -- ^ imageHeight partLeft == imageHeight partRight. Always > 0
78− }
79− -- | A veritical join can be constructed between any two images.
80− -- However a VertJoin instance is required to be between two images
81− -- of equal width. The vertJoin constructor adds background fills
82− -- to the provides images that assure this is true for the VertJoin
83− -- value produced.
84− | VertJoin
85− { partTop :: Image
86− , partBottom :: Image
87− , outputWidth :: Int
88− -- ^ imageWidth partTop == imageWidth partBottom. always > 0
89− , outputHeight :: Int
90− -- ^ imageHeight partTop == imageHeight partBottom. always > 1
91− }
92− -- | A background fill will be filled with the background char. The
93− -- background char is defined as a property of the Picture this
94− -- Image is used to form.
95− | BGFill
96− { outputWidth :: Int -- ^ always > 0
97− , outputHeight :: Int -- ^ always > 0
98− }
99− -- | Crop an image horizontally to a size by reducing the size from
100− -- the right.
101− | CropRight
102− { croppedImage :: Image
103− -- | Always < imageWidth croppedImage > 0
104− , outputWidth :: Int
105− , outputHeight :: Int -- ^ imageHeight croppedImage
106− }
107− -- | Crop an image horizontally to a size by reducing the size from
108− -- the left.
109− | CropLeft
110− { croppedImage :: Image
111− -- | Always < imageWidth croppedImage > 0
112− , leftSkip :: Int
113− -- | Always < imageWidth croppedImage > 0
114− , outputWidth :: Int
115− , outputHeight :: Int
116− }
117− -- | Crop an image vertically to a size by reducing the size from
118− -- the bottom
119− | CropBottom
120− { croppedImage :: Image
121− -- | imageWidth croppedImage
122− , outputWidth :: Int
123− -- | height image is cropped to. Always < imageHeight croppedImage > 0
124− , outputHeight :: Int
125− }
126− -- | Crop an image vertically to a size by reducing the size from
127− -- the top
128− | CropTop
129− { croppedImage :: Image
130− -- | Always < imageHeight croppedImage > 0
131− , topSkip :: Int
132− -- | imageWidth croppedImage
133− , outputWidth :: Int
134− -- | Always < imageHeight croppedImage > 0
135− , outputHeight :: Int
136− }
137− -- | The empty image
138− --
139− -- The combining operators identity constant.
140− -- EmptyImage <|> a = a
141− -- EmptyImage <-> a = a
142− --
143− -- Any image of zero size equals the empty image.
144− | EmptyImage
145− deriving (Eq, Generic, Show, Read)
146−
147−-- | pretty print just the structure of an image.
148−ppImageStructure :: Image -> String
149−ppImageStructure = go 0
150− where
151− go indent img = tab indent ++ pp indent img
152− tab indent = concat $ replicate indent " "
153− pp _ (HorizText {outputWidth}) = "HorizText(" ++ show outputWidth ++ ")"
154− pp _ (BGFill {outputWidth, outputHeight})
155− = "BGFill(" ++ show outputWidth ++ "," ++ show outputHeight ++ ")"
156− pp i (HorizJoin {partLeft = l, partRight = r, outputWidth = c})
157− = "HorizJoin(" ++ show c ++ ")\n" ++ go (i+1) l ++ "\n" ++ go (i+1) r
158− pp i (VertJoin {partTop = t, partBottom = b, outputWidth = c, outputHeight = r})
159− = "VertJoin(" ++ show c ++ ", " ++ show r ++ ")\n"
160− ++ go (i+1) t ++ "\n"
161− ++ go (i+1) b
162− pp i (CropRight {croppedImage, outputWidth, outputHeight})
163− = "CropRight(" ++ show outputWidth ++ "," ++ show outputHeight ++ ")\n"
164− ++ go (i+1) croppedImage
165− pp i (CropLeft {croppedImage, leftSkip, outputWidth, outputHeight})
166− = "CropLeft(" ++ show leftSkip ++ "->" ++ show outputWidth ++ "," ++ show outputHeight ++ ")\n"
167− ++ go (i+1) croppedImage
168− pp i (CropBottom {croppedImage, outputWidth, outputHeight})
169− = "CropBottom(" ++ show outputWidth ++ "," ++ show outputHeight ++ ")\n"
170− ++ go (i+1) croppedImage
171− pp i (CropTop {croppedImage, topSkip, outputWidth, outputHeight})
172− = "CropTop("++ show outputWidth ++ "," ++ show topSkip ++ "->" ++ show outputHeight ++ ")\n"
173− ++ go (i+1) croppedImage
174− pp _ EmptyImage = "EmptyImage"
175−
176−instance NFData Image where
177− rnf EmptyImage = ()
178− rnf (CropRight i w h) = i `deepseq` w `seq` h `seq` ()
179− rnf (CropLeft i s w h) = i `deepseq` s `seq` w `seq` h `seq` ()
180− rnf (CropBottom i w h) = i `deepseq` w `seq` h `seq` ()
181− rnf (CropTop i s w h) = i `deepseq` s `seq` w `seq` h `seq` ()
182− rnf (BGFill w h) = w `seq` h `seq` ()
183− rnf (VertJoin t b w h) = t `deepseq` b `deepseq` w `seq` h `seq` ()
184− rnf (HorizJoin l r w h) = l `deepseq` r `deepseq` w `seq` h `seq` ()
185− rnf (HorizText a s w cw) = a `seq` s `deepseq` w `seq` cw `seq` ()
186−
187−-- | The width of an Image. This is the number display columns the image
188−-- will occupy.
189−imageWidth :: Image -> Int
190−imageWidth HorizText { outputWidth = w } = w
191−imageWidth HorizJoin { outputWidth = w } = w
192−imageWidth VertJoin { outputWidth = w } = w
193−imageWidth BGFill { outputWidth = w } = w
194−imageWidth CropRight { outputWidth = w } = w
195−imageWidth CropLeft { outputWidth = w } = w
196−imageWidth CropBottom { outputWidth = w } = w
197−imageWidth CropTop { outputWidth = w } = w
198−imageWidth EmptyImage = 0
199−
200−-- | The height of an Image. This is the number of display rows the
201−-- image will occupy.
202−imageHeight :: Image -> Int
203−imageHeight HorizText {} = 1
204−imageHeight HorizJoin { outputHeight = h } = h
205−imageHeight VertJoin { outputHeight = h } = h
206−imageHeight BGFill { outputHeight = h } = h
207−imageHeight CropRight { outputHeight = h } = h
208−imageHeight CropLeft { outputHeight = h } = h
209−imageHeight CropBottom { outputHeight = h } = h
210−imageHeight CropTop { outputHeight = h } = h
211−imageHeight EmptyImage = 0
212−
213−-- | Append in the 'Semigroup' instance is equivalent to '<->'.
214−instance Semigroup Image where
215− (<>) = vertJoin
216−
217−-- | Append in the 'Monoid' instance is equivalent to '<->'.
218−instance Monoid Image where
219− mempty = EmptyImage
220−#if !(MIN_VERSION_base(4,11,0))
221− mappend = (<>)
222−#endif
223−
224−-- | combines two images side by side
225−--
226−-- Combines text chunks where possible. Assures outputWidth and
227−-- outputHeight properties are not violated.
228−--
229−-- The result image will have a width equal to the sum of the two images
230−-- width. And the height will equal the largest height of the two
231−-- images. The area not defined in one image due to a height mismatch
232−-- will be filled with the background pattern.
233−horizJoin :: Image -> Image -> Image
234−horizJoin EmptyImage i = i
235−horizJoin i EmptyImage = i
236−horizJoin i0@(HorizText a0 t0 w0 cw0) i1@(HorizText a1 t1 w1 cw1)
237− | a0 == a1 = HorizText a0 (TL.append t0 t1) (w0 + w1) (cw0 + cw1)
238− -- assumes horiz text height is always 1
239− | otherwise = HorizJoin i0 i1 (w0 + w1) 1
240−horizJoin i0 i1
241− -- If the images are of the same height then no padding is required
242− | h0 == h1 = HorizJoin i0 i1 w h0
243− -- otherwise one of the images needs to be padded to the right size.
244− | h0 < h1 -- Pad i0
245− = let padAmount = h1 - h0
246− in HorizJoin (VertJoin i0 (BGFill w0 padAmount) w0 h1) i1 w h1
247− | h0 > h1 -- Pad i1
248− = let padAmount = h0 - h1
249− in HorizJoin i0 (VertJoin i1 (BGFill w1 padAmount) w1 h0) w h0
250− where
251− w0 = imageWidth i0
252− w1 = imageWidth i1
253− w = w0 + w1
254− h0 = imageHeight i0
255− h1 = imageHeight i1
256−horizJoin _ _ = error "horizJoin applied to undefined values."
257−
258−-- | combines two images vertically
259−--
260−-- The result image will have a height equal to the sum of the heights
261−-- of both images. The width will equal the largest width of the two
262−-- images. The area not defined in one image due to a width mismatch
263−-- will be filled with the background pattern.
264−vertJoin :: Image -> Image -> Image
265−vertJoin EmptyImage i = i
266−vertJoin i EmptyImage = i
267−vertJoin i0 i1
268− -- If the images are of the same width then no background padding is
269− -- required
270− | w0 == w1 = VertJoin i0 i1 w0 h
271− -- Otherwise one of the images needs to be padded to the size of the
272− -- other image.
273− | w0 < w1
274− = let padAmount = w1 - w0
275− in VertJoin (HorizJoin i0 (BGFill padAmount h0) w1 h0) i1 w1 h
276− | w0 > w1
277− = let padAmount = w0 - w1
278− in VertJoin i0 (HorizJoin i1 (BGFill padAmount h1) w0 h1) w0 h
279− where
280− w0 = imageWidth i0
281− w1 = imageWidth i1
282− h0 = imageHeight i0
283− h1 = imageHeight i1
284− h = h0 + h1
285−vertJoin _ _ = error "vertJoin applied to undefined values."
src/Graphics/Vty/Inline.hs+40−108
@@ -1,126 +1,58 @@
1−-- | The inline module provides a limited interface to changing the
2−-- style of terminal output. The intention is for this interface to be
3−-- used inline with other output systems.
4−--
5−-- The changes specified by the InlineM monad are applied to the
6−-- terminal's display attributes. These display attributes affect the
7−-- display of all following text output to the terminal file descriptor.
8−--
9−-- For example, in an IO monad the following code will print the text
10−-- \"Not styled. \" Followed by the text \" Styled! \" drawn over a red
11−-- background and underlined.
12−--
13−-- @
14−-- putStr \"Not styled. \"
15−-- putAttrChange_ $ do
16−-- backColor red
17−-- applyStyle underline
18−-- putStr \" Styled! \"
19−-- putAttrChange_ $ defaultAll
20−-- putStrLn \"Not styled.\"
21−-- @
22−--
23−-- 'putAttrChange' emits the control codes to the terminal device
24−-- attached to 'Handle'. This is a duplicate of the 'stdout' handle when
25−-- the 'terminalHandle' was (first) acquired. If 'stdout' has since been
26−-- changed then 'putStr', 'putStrLn', 'print' etc. will output to a
27−-- different 'Handle' than 'putAttrChange'.
28−--
29−-- Copyright 2009-2010 Corey O'Connor
30−module Graphics.Vty.Inline
31− ( module Graphics.Vty.Inline
32− , withVty
33− )
34−where
1+{-# LANGUAGE BangPatterns #-}
2+module Graphics.Vty.Inline ( module Graphics.Vty.Inline
3+ )
4+ where
355
36−import Graphics.Vty
6+import Graphics.Vty.Attributes
377import Graphics.Vty.DisplayAttributes
38−import Graphics.Vty.Inline.Unsafe
39−
40−import Blaze.ByteString.Builder (writeToByteString)
8+import Graphics.Vty.Terminal.Generic
419
10+import Control.Applicative
4211import Control.Monad.State.Strict
12+import Control.Monad.Trans
4313
4414import Data.Bits ( (.&.), complement )
4515import Data.IORef
16+import Data.Monoid ( mappend )
4617
47−import System.IO
48−
49−type InlineM v = State InlineState v
50−
51−data InlineState =
52− InlineState { inlineAttr :: Attr
53− , inlineUrlsEnabled :: Bool
54− }
18+type InlineM v = State Attr v
5519
56−-- | Set the background color to the provided 'Color'.
57−backColor :: Color -> InlineM ()
58−backColor c = modify $ \s ->
59− s { inlineAttr = inlineAttr s `withBackColor` c
60− }
20+back_color :: Color -> InlineM ()
21+back_color c = modify $ flip mappend ( current_attr `with_back_color` c )
6122
62−-- | Set the foreground color to the provided 'Color'.
63−foreColor :: Color -> InlineM ()
64−foreColor c = modify $ \s ->
65− s { inlineAttr = inlineAttr s `withForeColor` c
66− }
23+fore_color :: Color -> InlineM ()
24+fore_color c = modify $ flip mappend ( current_attr `with_fore_color` c )
6725
68−-- | Attempt to change the 'Style' of the following text..
69−--
70−-- If the terminal does not support the style change then no error is
71−-- produced. The style can still be removed.
72−applyStyle :: Style -> InlineM ()
73−applyStyle st = modify $ \s ->
74− s { inlineAttr = inlineAttr s `withStyle` st
75− }
26+apply_style :: Style -> InlineM ()
27+apply_style s = modify $ flip mappend ( current_attr `with_style` s )
7628
77−-- | Attempt to remove the specified 'Style' from the display of the
78−-- following text.
79−--
80−-- This will fail if 'applyStyle' for the given style has not been
81−-- previously called.
82−removeStyle :: Style -> InlineM ()
83−removeStyle sMask = modify $ \s ->
84− s { inlineAttr =
85− let style' = case attrStyle (inlineAttr s) of
86− Default -> error "Graphics.Vty.Inline: Cannot removeStyle if applyStyle never used."
87− KeepCurrent -> error "Graphics.Vty.Inline: Cannot removeStyle if applyStyle never used."
88− SetTo st -> st .&. complement sMask
89− in (inlineAttr s) { attrStyle = SetTo style' }
90− }
29+remove_style :: Style -> InlineM ()
30+remove_style s_mask = modify $ \attr ->
31+ let style' = case attr_style attr of
32+ Default -> error $ "Graphics.Vty.Inline: Cannot remove_style if apply_style never used."
33+ KeepCurrent -> error $ "Graphics.Vty.Inline: Cannot remove_style if apply_style never used."
34+ SetTo s -> s .&. complement s_mask
35+ in attr { attr_style = SetTo style' }
9136
92−-- | Reset the display attributes.
93−defaultAll :: InlineM ()
94−defaultAll = modify $ \s -> s { inlineAttr = defAttr }
37+default_all :: InlineM ()
38+default_all = put def_attr
9539
96−-- | Apply the provided display attribute changes to the given terminal
97−-- output device.
98−--
99−-- This does not flush the terminal.
100−putAttrChange :: ( Applicative m, MonadIO m ) => Output -> InlineM () -> m ()
101−putAttrChange out c = liftIO $ do
102− bounds <- displayBounds out
103− dc <- displayContext out bounds
104− mfattr <- prevFattr <$> readIORef (assumedStateRef out)
40+put_attr_change :: ( Applicative m, MonadIO m ) => TerminalHandle -> InlineM () -> m ()
41+put_attr_change t c = do
42+ bounds <- display_bounds t
43+ d <- display_context t bounds
44+ mfattr <- liftIO $ known_fattr <$> readIORef ( state_ref t )
10545 fattr <- case mfattr of
10646 Nothing -> do
107− liftIO $ outputByteBuffer out $ writeToByteString $ writeDefaultAttr dc False
108− return $ FixedAttr defaultStyleMask Nothing Nothing Nothing
47+ marshall_to_terminal t (default_attr_required_bytes d) (serialize_default_attr d)
48+ return $ FixedAttr default_style_mask Nothing Nothing
10949 Just v -> return v
110− let InlineState attr urlsEnabled = execState c (InlineState currentAttr False)
111− attr' = limitAttrForDisplay out attr
112− fattr' = fixDisplayAttr fattr attr'
113− diffs = displayAttrDiffs fattr fattr'
114− outputByteBuffer out $ writeToByteString $ writeSetAttr dc urlsEnabled fattr attr' diffs
115− modifyIORef (assumedStateRef out) $ \s -> s { prevFattr = Just fattr' }
116− inlineHack dc
50+ let attr = execState c current_attr
51+ attr' = limit_attr_for_display d attr
52+ fattr' = fix_display_attr fattr attr'
53+ diffs = display_attr_diffs fattr fattr'
54+ marshall_to_terminal t ( attr_required_bytes d fattr attr' diffs )
55+ ( serialize_set_attr d fattr attr' diffs )
56+ liftIO $ modifyIORef ( state_ref t ) $ \s -> s { known_fattr = Just fattr' }
57+ inline_hack d
11758
118−-- | Apply the provided display attributes changes to the terminal
119−-- output device.
120−--
121−-- This will flush the terminal output.
122−putAttrChange_ :: ( Applicative m, MonadIO m ) => InlineM () -> m ()
123−putAttrChange_ c = liftIO $ withOutput $ \out -> do
124− hFlush stdout
125− putAttrChange out c
126− hFlush stdout
src/Graphics/Vty/Inline/Unsafe.hsdeleted−59
@@ -1,59 +0,0 @@
1−{-# OPTIONS_HADDOCK hide #-}
2−
3−module Graphics.Vty.Inline.Unsafe where
4−
5−import Graphics.Vty
6−
7−import Data.IORef
8−
9−import GHC.IO.Handle (hDuplicate)
10−
11−import System.IO (stdin, stdout, hSetBuffering, BufferMode(NoBuffering))
12−
13−import System.IO.Unsafe
14−
15−import System.Posix.IO (handleToFd)
16−
17−globalVty :: IORef (Maybe Vty)
18−{-# NOINLINE globalVty #-}
19−globalVty = unsafePerformIO $ newIORef Nothing
20−
21−globalOutput :: IORef (Maybe Output)
22−{-# NOINLINE globalOutput #-}
23−globalOutput = unsafePerformIO $ newIORef Nothing
24−
25−mkDupeConfig :: IO Config
26−mkDupeConfig = do
27− hSetBuffering stdout NoBuffering
28− hSetBuffering stdin NoBuffering
29− stdinDupe <- hDuplicate stdin >>= handleToFd
30− stdoutDupe <- hDuplicate stdout >>= handleToFd
31− return $ defaultConfig { inputFd = Just stdinDupe, outputFd = Just stdoutDupe }
32−
33−-- | This will create a Vty instance using 'mkVty' and execute an IO
34−-- action provided that instance. The created Vty instance will be
35−-- stored to the unsafe 'IORef' 'globalVty'.
36−--
37−-- This instance will use duplicates of the stdin and stdout Handles.
38−withVty :: (Vty -> IO b) -> IO b
39−withVty f = do
40− mvty <- readIORef globalVty
41− vty <- case mvty of
42− Nothing -> do
43− vty <- mkDupeConfig >>= mkVty
44− writeIORef globalVty (Just vty)
45− return vty
46− Just vty -> return vty
47− f vty
48−
49−withOutput :: (Output -> IO b) -> IO b
50−withOutput f = do
51− mout <- readIORef globalOutput
52− out <- case mout of
53− Nothing -> do
54− config <- mappend <$> userConfig <*> mkDupeConfig
55− out <- outputForConfig config
56− writeIORef globalOutput (Just out)
57− return out
58− Just out -> return out
59− f out
src/Graphics/Vty/Input.hsdeleted−176
@@ -1,176 +0,0 @@
1−{-# LANGUAGE RecordWildCards, CPP #-}
2−
3−-- | This module provides the input layer for Vty, including methods
4−-- for initializing an 'Input' structure and reading 'Event's from the
5−-- terminal.
6−--
7−-- Note that due to the evolution of terminal emulators, some keys
8−-- and combinations will not reliably map to the expected events by
9−-- any terminal program. There is no 1:1 mapping from key events to
10−-- bytes read from the terminal input device. In very limited cases the
11−-- terminal and vty's input process can be customized to resolve these
12−-- issues; see "Graphics.Vty.Config" for how to configure vty's input
13−-- processing.
14−--
15−-- = VTY's Implementation
16−--
17−-- There are two input modes:
18−--
19−-- 1. 7-bit
20−--
21−-- 2. 8-bit
22−--
23−-- The 7-bit input mode is the default and the expected mode in most use
24−-- cases. This is what Vty uses.
25−--
26−-- == 7-bit input encoding
27−--
28−-- Control key combinations are represented by masking the two high bits
29−-- of the 7-bit input. Historically the control key actually grounded
30−-- the two high bit wires: 6 and 7. This is why control key combos
31−-- map to single character events: the input bytes are identical. The
32−-- input byte is the bit encoding of the character with bits 6 and 7
33−-- masked. Bit 6 is set by shift. Bit 6 and 7 are masked by control. For
34−-- example,
35−--
36−-- * Control-I is 'i', `01101001`, and has bit 6 and 7 masked to become
37−-- `00001001`, which is the ASCII and UTF-8 encoding of the Tab key.
38−--
39−-- * Control+Shift-C is 'C', `01000011`, with bit 6 and 7 set to zero
40−-- which is `0000011` and is the "End of Text" code.
41−--
42−-- * Hypothesis: This is why capital-A, 'A', has value 65 in ASCII: this
43−-- is the value 1 with bit 7 set and 6 unset.
44−--
45−-- * Hypothesis: Bit 6 is unset by upper case letters because,
46−-- initially, there were only upper case letters used and a 5 bit
47−-- encoding.
48−--
49−-- == 8-bit encoding
50−--
51−-- The 8th bit was originally used for parity checking which is useless
52−-- for terminal emulators. Some terminal emulators support an 8-bit
53−-- input encoding. While this provides some advantages, the actual usage
54−-- is low. Most systems use 7-bit mode but recognize 8-bit control
55−-- characters when escaped. This is what Vty does.
56−--
57−-- == Escaped Control Keys
58−--
59−-- Using 7-bit input encoding, the @ESC@ byte can signal the start of
60−-- an encoded control key. To differentiate a single @ESC@ event from a
61−-- control key, the timing of the input is used.
62−--
63−-- 1. @ESC@ individually: @ESC@ byte; no bytes following for a period of
64−-- 'VMIN' milliseconds.
65−--
66−-- 2. Control keys that contain @ESC@ in their encoding: The @ESC byte
67−-- is followed by more bytes read within 'VMIN' milliseconds. All bytes
68−-- up until the next valid input block are passed to the classifier.
69−--
70−-- If the current runtime is the threaded runtime then the terminal's
71−-- @VMIN@ and @VTIME@ behavior reliably implement the above rules. If
72−-- the current runtime does not support 'forkOS' then there is currently
73−-- no implementation.
74−--
75−-- == Unicode Input and Escaped Control Key Sequences
76−--
77−-- The input encoding determines how UTF-8 encoded characters are
78−-- recognized.
79−--
80−-- * 7-bit mode: UTF-8 can be input unambiguously. UTF-8 input is
81−-- a superset of ASCII. UTF-8 does not overlap escaped control key
82−-- sequences. However, the escape key must be differentiated from
83−-- escaped control key sequences by the timing of the input bytes.
84−--
85−-- * 8-bit mode: UTF-8 cannot be input unambiguously. This does not
86−-- require using the timing of input bytes to differentiate the escape
87−-- key. Many terminals do not support 8-bit mode.
88−--
89−-- == Terminfo
90−--
91−-- The terminfo system is used to determine how some keys are encoded.
92−-- Terminfo is incomplete and in some cases terminfo is incorrect. Vty
93−-- assumes terminfo is correct but provides a mechanism to override
94−-- terminfo; see "Graphics.Vty.Config", specifically 'inputOverrides'.
95−--
96−-- == Terminal Input is Broken
97−--
98−-- Clearly terminal input has fundamental issues. There is no easy way
99−-- to reliably resolve these issues.
100−--
101−-- One resolution would be to ditch standard terminal interfaces
102−-- entirely and just go directly to scancodes. This would be a
103−-- reasonable option for Vty if everybody used the linux kernel console
104−-- but for obvious reasons this is not possible.
105−--
106−-- The "Graphics.Vty.Config" module supports customizing the
107−-- input-byte-to-event mapping and xterm supports customizing the
108−-- scancode-to-input-byte mapping. With a lot of work a user's system
109−-- can be set up to encode all the key combos in an almost-sane manner.
110−--
111−-- == See also
112−--
113−-- * http://www.leonerd.org.uk/hacks/fixterms/
114−module Graphics.Vty.Input
115− ( Key(..)
116− , Modifier(..)
117− , Button(..)
118− , Event(..)
119− , Input(..)
120− , inputForConfig
121− )
122−where
123−
124−import Graphics.Vty.Config
125−import Graphics.Vty.Input.Events
126−import Graphics.Vty.Input.Loop
127−import Graphics.Vty.Input.Terminfo
128−
129−import Control.Concurrent.STM
130−import Lens.Micro
131−
132−import qualified System.Console.Terminfo as Terminfo
133−import System.Posix.Signals.Exts
134−
135−#if !MIN_VERSION_base(4,11,0)
136−import Data.Monoid ((<>))
137−#endif
138−
139−-- | Set up the terminal with file descriptor `inputFd` for input.
140−-- Returns an 'Input'.
141−--
142−-- The table used to determine the 'Events' to produce for the input
143−-- bytes comes from 'classifyMapForTerm' which is then overridden by
144−-- the the applicable entries from the configuration's 'inputMap'.
145−--
146−-- The terminal device's mode flags are configured by the
147−-- 'attributeControl' function.
148−inputForConfig :: Config -> IO Input
149−inputForConfig config@Config{ termName = Just termName
150− , inputFd = Just termFd
151− , vmin = Just _
152− , vtime = Just _
153− , .. } = do
154− terminal <- Terminfo.setupTerm termName
155− let inputOverrides = [(s,e) | (t,s,e) <- inputMap, t == Nothing || t == Just termName]
156− activeInputMap = classifyMapForTerm termName terminal `mappend` inputOverrides
157− (setAttrs, unsetAttrs) <- attributeControl termFd
158− setAttrs
159− input <- initInput config activeInputMap
160− let pokeIO = Catch $ do
161− setAttrs
162− atomically $ writeTChan (input^.eventChannel) ResumeAfterSignal
163− _ <- installHandler windowChange pokeIO Nothing
164− _ <- installHandler continueProcess pokeIO Nothing
165−
166− let restore = unsetAttrs
167−
168− return $ input
169− { shutdownInput = do
170− shutdownInput input
171− _ <- installHandler windowChange Ignore Nothing
172− _ <- installHandler continueProcess Ignore Nothing
173− restore
174− , restoreInputState = restoreInputState input >> restore
175− }
176−inputForConfig config = (<> config) <$> standardIOConfig >>= inputForConfig
src/Graphics/Vty/Input/Classify.hsdeleted−110
@@ -1,110 +0,0 @@
1−{-# OPTIONS_HADDOCK hide #-}
2−-- This makes a kind of trie. Has space efficiency issues with large
3−-- input blocks. Likely building a parser and just applying that would
4−-- be better.
5−module Graphics.Vty.Input.Classify
6− ( classify
7− , KClass(..)
8− , ClassifierState(..)
9− )
10−where
11−
12−import Graphics.Vty.Input.Events
13−import Graphics.Vty.Input.Mouse
14−import Graphics.Vty.Input.Focus
15−import Graphics.Vty.Input.Paste
16−import Graphics.Vty.Input.Classify.Types
17−
18−import Codec.Binary.UTF8.Generic (decode)
19−
20−import Control.Arrow (first)
21−import qualified Data.Map as M( fromList, lookup )
22−import Data.Maybe ( mapMaybe )
23−import qualified Data.Set as S( fromList, member )
24−
25−import Data.Word
26−
27−import qualified Data.ByteString as BS
28−import qualified Data.ByteString.Char8 as BS8
29−import Data.ByteString.Char8 (ByteString)
30−
31−-- | Whether the classifier is currently processing a chunked format.
32−-- Currently, only bracketed pastes use this.
33−data ClassifierState
34− = ClassifierStart
35− -- ^ Not processing a chunked format.
36− | ClassifierInChunk ByteString [ByteString]
37− -- ^ Currently processing a chunked format. The initial chunk is in the
38− -- first argument and a reversed remainder of the chunks is collected in
39− -- the second argument. At the end of the processing, the chunks are
40− -- reversed and concatenated with the final chunk.
41−
42−compile :: ClassifyMap -> ByteString -> KClass
43−compile table = cl' where
44− -- take all prefixes and create a set of these
45− prefixSet = S.fromList $ concatMap (init . BS.inits . BS8.pack . fst) table
46− maxValidInputLength = maximum (map (length . fst) table)
47− eventForInput = M.fromList $ map (first BS8.pack) table
48− cl' inputBlock | BS8.null inputBlock = Prefix
49− cl' inputBlock = case M.lookup inputBlock eventForInput of
50− -- if the inputBlock is exactly what is expected for an
51− -- event then consume the whole block and return the event
52− Just e -> Valid e BS8.empty
53− Nothing -> case S.member inputBlock prefixSet of
54− True -> Prefix
55− -- look up progressively smaller tails of the input
56− -- block until an event is found The assumption is that
57− -- the event that consumes the most input bytes should
58− -- be produced.
59− -- The test verifyFullSynInputToEvent2x verifies this.
60− -- H: There will always be one match. The prefixSet
61− -- contains, by definition, all prefixes of an event.
62− False ->
63− let inputPrefixes = reverse . take maxValidInputLength . tail . BS8.inits $ inputBlock
64− in case mapMaybe (\s -> (,) s `fmap` M.lookup s eventForInput) inputPrefixes of
65− (s,e) : _ -> Valid e (BS8.drop (BS8.length s) inputBlock)
66− -- neither a prefix or a full event.
67− [] -> Invalid
68−
69−classify :: ClassifyMap -> ClassifierState -> ByteString -> KClass
70−classify table = process
71− where
72− standardClassifier = compile table
73−
74− process ClassifierStart s =
75− case BS.uncons s of
76− _ | bracketedPasteStarted s ->
77− if bracketedPasteFinished s
78− then parseBracketedPaste s
79− else Chunk
80− _ | isMouseEvent s -> classifyMouseEvent s
81− _ | isFocusEvent s -> classifyFocusEvent s
82− Just (c,cs) | c >= 0xC2 -> classifyUtf8 c cs
83− _ -> standardClassifier s
84−
85− process (ClassifierInChunk p ps) s | bracketedPasteStarted p =
86− if bracketedPasteFinished s
87− then parseBracketedPaste $ BS.concat $ p:reverse (s:ps)
88− else Chunk
89− process ClassifierInChunk{} _ = Invalid
90−
91−classifyUtf8 :: Word8 -> ByteString -> KClass
92−classifyUtf8 c cs =
93− let n = utf8Length c
94− (codepoint,rest) = BS8.splitAt (n - 1) cs
95−
96− codepoint8 :: [Word8]
97− codepoint8 = c:BS.unpack codepoint
98−
99− in case decode codepoint8 of
100− _ | n < BS.length codepoint + 1 -> Prefix
101− Just (unicodeChar, _) -> Valid (EvKey (KChar unicodeChar) []) rest
102− -- something bad happened; just ignore and continue.
103− Nothing -> Invalid
104−
105−utf8Length :: Word8 -> Int
106−utf8Length c
107− | c < 0x80 = 1
108− | c < 0xE0 = 2
109− | c < 0xF0 = 3
110− | otherwise = 4
src/Graphics/Vty/Input/Classify/Parse.hsdeleted−61
@@ -1,61 +0,0 @@
1−-- | This module provides a simple parser for parsing input event
2−-- control sequences.
3−module Graphics.Vty.Input.Classify.Parse
4− ( Parser
5− , runParser
6− , failParse
7− , readInt
8− , readChar
9− , expectChar
10− )
11−where
12−
13−import Graphics.Vty.Input.Events
14−import Graphics.Vty.Input.Classify.Types
15−
16−import Control.Monad.Trans.Maybe
17−import Control.Monad.State
18−
19−import qualified Data.ByteString.Char8 as BS8
20−import Data.ByteString.Char8 (ByteString)
21−
22−type Parser a = MaybeT (State ByteString) a
23−
24−-- | Run a parser on a given input string. If the parser fails, return
25−-- 'Invalid'. Otherwise return the valid event ('Valid') and the
26−-- remaining unparsed characters.
27−runParser :: ByteString -> Parser Event -> KClass
28−runParser s parser =
29− case runState (runMaybeT parser) s of
30− (Nothing, _) -> Invalid
31− (Just e, remaining) -> Valid e remaining
32−
33−-- | Fail a parsing operation.
34−failParse :: Parser a
35−failParse = fail "invalid parse"
36−
37−-- | Read an integer from the input stream. If an integer cannot be
38−-- read, fail parsing. E.g. calling readInt on an input of "123abc" will
39−-- return '123' and consume those characters.
40−readInt :: Parser Int
41−readInt = do
42− s <- BS8.unpack <$> get
43− case (reads :: ReadS Int) s of
44− [(i, rest)] -> put (BS8.pack rest) >> return i
45− _ -> failParse
46−
47−-- | Read a character from the input stream. If one cannot be read (e.g.
48−-- we are out of characters), fail parsing.
49−readChar :: Parser Char
50−readChar = do
51− s <- get
52− case BS8.uncons s of
53− Just (c,rest) -> put rest >> return c
54− Nothing -> failParse
55−
56−-- | Read a character from the input stream and fail parsing if it is
57−-- not the specified character.
58−expectChar :: Char -> Parser ()
59−expectChar c = do
60− c' <- readChar
61− if c' == c then return () else failParse
src/Graphics/Vty/Input/Classify/Types.hsdeleted−25
@@ -1,25 +0,0 @@
1−-- | This module exports the input classification type to avoid import
2−-- cycles between other modules that need this.
3−{-# LANGUAGE StrictData #-}
4−module Graphics.Vty.Input.Classify.Types
5− ( KClass(..)
6− )
7−where
8−
9−import Graphics.Vty.Input.Events
10−
11−import Data.ByteString.Char8 (ByteString)
12−
13−data KClass
14− = Valid Event ByteString
15− -- ^ A valid event was parsed. Any unused characters from the input
16− -- stream are also provided.
17− | Invalid
18− -- ^ The input characters did not represent a valid event.
19− | Prefix
20− -- ^ The input characters form the prefix of a valid event character
21− -- sequence.
22− | Chunk
23− -- ^ The input characters are either start of a bracketed paste chunk
24− -- or in the middle of a bracketed paste chunk.
25− deriving(Show, Eq)
src/Graphics/Vty/Input/Events.hsdeleted−83
@@ -1,83 +0,0 @@
1−{-# Language DeriveGeneric #-}
2−{-# Language StrictData #-}
3−module Graphics.Vty.Input.Events where
4−
5−import Control.DeepSeq
6−import Data.ByteString
7−import GHC.Generics
8−
9−-- | Representations of non-modifier keys.
10−--
11−-- * KFun is indexed from 0 to 63. Range of supported FKeys varies by
12−-- terminal and keyboard.
13−--
14−-- * KUpLeft, KUpRight, KDownLeft, KDownRight, KCenter support varies by
15−-- terminal and keyboard.
16−--
17−-- * Actually, support for most of these but KEsc, KChar, KBS, and
18−-- KEnter vary by terminal and keyboard.
19−data Key = KEsc | KChar {-# UNPACK #-} Char | KBS | KEnter
20− | KLeft | KRight | KUp | KDown
21− | KUpLeft | KUpRight | KDownLeft | KDownRight | KCenter
22− | KFun {-# UNPACK #-} Int | KBackTab | KPrtScr | KPause | KIns
23− | KHome | KPageUp | KDel | KEnd | KPageDown | KBegin | KMenu
24− deriving (Eq,Show,Read,Ord,Generic)
25−
26−instance NFData Key
27−
28−-- | Modifier keys. Key codes are interpreted such that users are more
29−-- likely to have Meta than Alt; for instance on the PC Linux console,
30−-- 'MMeta' will generally correspond to the physical Alt key.
31−data Modifier = MShift | MCtrl | MMeta | MAlt
32− deriving (Eq,Show,Read,Ord,Generic)
33−
34−instance NFData Modifier
35−
36−-- | Mouse buttons.
37−data Button = BLeft | BMiddle | BRight | BScrollUp | BScrollDown
38− deriving (Eq,Show,Read,Ord,Generic)
39−
40−instance NFData Button
41−
42−-- | Events.
43−data Event
44− = EvKey Key [Modifier]
45− -- ^ A keyboard key was pressed with the specified modifiers.
46− | EvMouseDown Int Int Button [Modifier]
47− -- ^ A mouse button was pressed at the specified column and row. Any
48− -- modifiers available in the event are also provided.
49− | EvMouseUp Int Int (Maybe Button)
50− -- ^ A mouse button was released at the specified column and
51− -- row. Some terminals report only that a button was released
52− -- without specifying which one; in that case, Nothing is provided.
53− -- Otherwise Just the button released is included in the event.
54− | EvResize Int Int
55− -- The terminal window was resized and the size is provided in the
56− -- integer fields (width, height).
57− | EvPaste ByteString
58− -- ^ A paste event occurs when a bracketed paste input sequence is
59− -- received. For terminals that support bracketed paste mode, these
60− -- events will be triggered on a paste event. Terminals that do not
61− -- support bracketed pastes will send the paste contents as ordinary
62− -- input (which is probably bad, so beware!) Note that the data is
63− -- provided in raw form and you'll have to decode (e.g. as UTF-8) if
64− -- that's what your application expects.
65− | EvLostFocus
66− -- ^ The terminal running the application lost input focus.
67− | EvGainedFocus
68− -- ^ The terminal running the application gained input focus.
69− deriving (Eq,Show,Read,Ord,Generic)
70−
71−instance NFData Event
72−
73−type ClassifyMap = [(String,Event)]
74−
75−-- | The type of internal events that drive the internal Vty event
76−-- dispatching to the application.
77−data InternalEvent =
78− ResumeAfterSignal
79− -- ^ Vty resumed operation after the process was interrupted with a
80− -- signal. In practice this translates into a screen redraw in the
81− -- input event loop.
82− | InputEvent Event
83− -- ^ An input event was received.
src/Graphics/Vty/Input/Focus.hsdeleted−49
@@ -1,49 +0,0 @@
1−module Graphics.Vty.Input.Focus
2− ( requestFocusEvents
3− , disableFocusEvents
4− , isFocusEvent
5− , classifyFocusEvent
6− )
7−where
8−
9−import Graphics.Vty.Input.Events
10−import Graphics.Vty.Input.Classify.Types
11−import Graphics.Vty.Input.Classify.Parse
12−
13−import Control.Monad.State
14−
15−import qualified Data.ByteString.Char8 as BS8
16−import Data.ByteString.Char8 (ByteString)
17−
18−-- | These sequences set xterm-based terminals to send focus event
19−-- sequences.
20−requestFocusEvents :: ByteString
21−requestFocusEvents = BS8.pack "\ESC[?1004h"
22−
23−-- | These sequences disable focus events.
24−disableFocusEvents :: ByteString
25−disableFocusEvents = BS8.pack "\ESC[?1004l"
26−
27−-- | Does the specified string begin with a focus event?
28−isFocusEvent :: ByteString -> Bool
29−isFocusEvent s = BS8.isPrefixOf focusIn s ||
30− BS8.isPrefixOf focusOut s
31−
32−focusIn :: ByteString
33−focusIn = BS8.pack "\ESC[I"
34−
35−focusOut :: ByteString
36−focusOut = BS8.pack "\ESC[O"
37−
38−-- | Attempt to classify an input string as a focus event.
39−classifyFocusEvent :: ByteString -> KClass
40−classifyFocusEvent s = runParser s $ do
41− when (not $ isFocusEvent s) failParse
42−
43− expectChar '\ESC'
44− expectChar '['
45− ty <- readChar
46− case ty of
47− 'I' -> return EvGainedFocus
48− 'O' -> return EvLostFocus
49− _ -> failParse
src/Graphics/Vty/Input/Loop.hsdeleted−286
@@ -1,286 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE NoMonomorphismRestriction #-}
3−{-# LANGUAGE RecordWildCards #-}
4−{-# LANGUAGE ScopedTypeVariables #-}
5−{-# LANGUAGE TemplateHaskell #-}
6−{-# OPTIONS_HADDOCK hide #-}
7−-- | The input layer used to be a single function that correctly
8−-- accounted for the non-threaded runtime by emulating the terminal
9−-- VMIN adn VTIME handling. This has been removed and replace with a
10−-- more straightforward parser. The non-threaded runtime is no longer
11−-- supported.
12−--
13−-- This is an example of an algorithm where code coverage could be high,
14−-- even 100%, but the behavior is still under tested. I should collect
15−-- more of these examples...
16−--
17−-- reference: http://www.unixwiz.net/techtips/termios-vmin-vtime.html
18−module Graphics.Vty.Input.Loop where
19−
20−import Graphics.Vty.Config
21−import Graphics.Vty.Input.Classify
22−import Graphics.Vty.Input.Events
23−
24−import Control.Applicative
25−import Control.Concurrent
26−import Control.Concurrent.STM
27−import Control.Exception (mask, try, SomeException)
28−import Lens.Micro hiding ((<>~))
29−import Lens.Micro.Mtl
30−import Lens.Micro.TH
31−import Control.Monad (when, mzero, forM_)
32−import Control.Monad.IO.Class (liftIO)
33−import Control.Monad.Trans.State (StateT(..), evalStateT)
34−import Control.Monad.State.Class (MonadState, modify)
35−import Control.Monad.Trans.Reader (ReaderT(..))
36−
37−import qualified Data.ByteString.Char8 as BS8
38−import qualified Data.ByteString as BS
39−import Data.ByteString.Char8 (ByteString)
40−import Data.IORef
41−import Data.Word (Word8)
42−
43−import Foreign (allocaArray)
44−import Foreign.C.Types (CInt(..))
45−import Foreign.Ptr (Ptr, castPtr)
46−
47−import System.IO
48−import System.Posix.IO (fdReadBuf, setFdOption, FdOption(..))
49−import System.Posix.Terminal
50−import System.Posix.Types (Fd(..))
51−
52−import Text.Printf (hPrintf)
53−
54−data Input = Input
55− { -- | Channel of events direct from input processing. Unlike
56− -- 'nextEvent' this will not refresh the display if the next event
57− -- is an 'EvResize'.
58− _eventChannel :: TChan InternalEvent
59− -- | Shuts down the input processing. As part of shutting down the
60− -- input, this should also restore the input state.
61− , shutdownInput :: IO ()
62− -- | Restore the terminal's input state to what it was prior
63− -- to configuring input for Vty. This should be done as part of
64− -- 'shutdownInput' but is exposed in case you need to access it
65− -- directly.
66− , restoreInputState :: IO ()
67− -- | Changes to this value are reflected after the next event.
68− , _configRef :: IORef Config
69− -- | input debug log
70− , _inputDebug :: Maybe Handle
71− }
72−
73−makeLenses ''Input
74−
75−data InputBuffer = InputBuffer
76− { _ptr :: Ptr Word8
77− , _size :: Int
78− }
79−
80−makeLenses ''InputBuffer
81−
82−data InputState = InputState
83− { _unprocessedBytes :: ByteString
84− , _classifierState :: ClassifierState
85− , _appliedConfig :: Config
86− , _inputBuffer :: InputBuffer
87− , _classifier :: ClassifierState -> ByteString -> KClass
88− }
89−
90−makeLenses ''InputState
91−
92−type InputM a = StateT InputState (ReaderT Input IO) a
93−
94−logMsg :: String -> InputM ()
95−logMsg msg = do
96− d <- view inputDebug
97− case d of
98− Nothing -> return ()
99− Just h -> liftIO $ hPutStrLn h msg >> hFlush h
100−
101−-- this must be run on an OS thread dedicated to this input handling.
102−-- otherwise the terminal timing read behavior will block the execution
103−-- of the lightweight threads.
104−loopInputProcessor :: InputM ()
105−loopInputProcessor = do
106− readFromDevice >>= addBytesToProcess
107− validEvents <- many parseEvent
108− forM_ validEvents emit
109− dropInvalid
110− loopInputProcessor
111−
112−addBytesToProcess :: ByteString -> InputM ()
113−addBytesToProcess block = unprocessedBytes <>= block
114−
115−emit :: Event -> InputM ()
116−emit event = do
117− logMsg $ "parsed event: " ++ show event
118− view eventChannel >>= liftIO . atomically . flip writeTChan (InputEvent event)
119−
120−-- The timing requirements are assured by the VMIN and VTIME set for the
121−-- device.
122−--
123−-- Precondition: Under the threaded runtime. Only current use is from a
124−-- forkOS thread. That case satisfies precondition.
125−readFromDevice :: InputM ByteString
126−readFromDevice = do
127− newConfig <- view configRef >>= liftIO . readIORef
128− oldConfig <- use appliedConfig
129− let Just fd = inputFd newConfig
130− when (newConfig /= oldConfig) $ do
131− logMsg $ "new config: " ++ show newConfig
132− liftIO $ applyConfig fd newConfig
133− appliedConfig .= newConfig
134− bufferPtr <- use $ inputBuffer.ptr
135− maxBytes <- use $ inputBuffer.size
136− stringRep <- liftIO $ do
137− -- The killThread used in shutdownInput will not interrupt the
138− -- foreign call fdReadBuf uses this provides a location to be
139− -- interrupted prior to the foreign call. If there is input on
140− -- the FD then the fdReadBuf will return in a finite amount of
141− -- time due to the vtime terminal setting.
142− threadWaitRead fd
143− bytesRead <- fdReadBuf fd bufferPtr (fromIntegral maxBytes)
144− if bytesRead > 0
145− then BS.packCStringLen (castPtr bufferPtr, fromIntegral bytesRead)
146− else return BS.empty
147− when (not $ BS.null stringRep) $
148− logMsg $ "input bytes: " ++ show (BS8.unpack stringRep)
149− return stringRep
150−
151−applyConfig :: Fd -> Config -> IO ()
152−applyConfig fd (Config{ vmin = Just theVmin, vtime = Just theVtime })
153− = setTermTiming fd theVmin (theVtime `div` 100)
154−applyConfig _ _ = fail "(vty) applyConfig was not provided a complete configuration"
155−
156−parseEvent :: InputM Event
157−parseEvent = do
158− c <- use classifier
159− s <- use classifierState
160− b <- use unprocessedBytes
161− case c s b of
162− Valid e remaining -> do
163− logMsg $ "valid parse: " ++ show e
164− logMsg $ "remaining: " ++ show remaining
165− classifierState .= ClassifierStart
166− unprocessedBytes .= remaining
167− return e
168− _ -> mzero
169−
170−dropInvalid :: InputM ()
171−dropInvalid = do
172− c <- use classifier
173− s <- use classifierState
174− b <- use unprocessedBytes
175− case c s b of
176− Chunk -> do
177− classifierState .=
178− case s of
179− ClassifierStart -> ClassifierInChunk b []
180− ClassifierInChunk p bs -> ClassifierInChunk p (b:bs)
181− unprocessedBytes .= BS8.empty
182− Invalid -> do
183− logMsg "dropping input bytes"
184− classifierState .= ClassifierStart
185− unprocessedBytes .= BS8.empty
186− _ -> return ()
187−
188−runInputProcessorLoop :: ClassifyMap -> Input -> IO ()
189−runInputProcessorLoop classifyTable input = do
190− let bufferSize = 1024
191− allocaArray bufferSize $ \(bufferPtr :: Ptr Word8) -> do
192− s0 <- InputState BS8.empty ClassifierStart
193− <$> readIORef (_configRef input)
194− <*> pure (InputBuffer bufferPtr bufferSize)
195− <*> pure (classify classifyTable)
196− runReaderT (evalStateT loopInputProcessor s0) input
197−
198−-- | Construct two IO actions: one to configure the terminal for Vty and
199−-- one to restore the terminal mode flags to the values they had at the
200−-- time this function was called.
201−--
202−-- This function constructs a configuration action to clear the
203−-- following terminal mode flags:
204−--
205−-- * IXON disabled: disables software flow control on outgoing data.
206−-- This stops the process from being suspended if the output terminal
207−-- cannot keep up.
208−--
209−-- * Raw mode is used for input.
210−--
211−-- * ISIG (enables keyboard combinations that result in
212−-- signals)
213−--
214−-- * ECHO (input is not echoed to the output)
215−--
216−-- * ICANON (canonical mode (line mode) input is not used)
217−--
218−-- * IEXTEN (extended functions are disabled)
219−--
220−-- The configuration action also explicitly sets these flags:
221−--
222−-- * ICRNL (input carriage returns are mapped to newlines)
223−attributeControl :: Fd -> IO (IO (), IO ())
224−attributeControl fd = do
225− original <- getTerminalAttributes fd
226− let vtyMode = foldl withMode clearedFlags flagsToSet
227− clearedFlags = foldl withoutMode original flagsToUnset
228− flagsToSet = [ MapCRtoLF -- ICRNL
229− ]
230− flagsToUnset = [ StartStopOutput -- IXON
231− , KeyboardInterrupts -- ISIG
232− , EnableEcho -- ECHO
233− , ProcessInput -- ICANON
234− , ExtendedFunctions -- IEXTEN
235− ]
236− let setAttrs = setTerminalAttributes fd vtyMode Immediately
237− unsetAttrs = setTerminalAttributes fd original Immediately
238− return (setAttrs, unsetAttrs)
239−
240−logInitialInputState :: Input -> ClassifyMap -> IO()
241−logInitialInputState input classifyTable = case _inputDebug input of
242− Nothing -> return ()
243− Just h -> do
244− Config{ vmin = Just theVmin
245− , vtime = Just theVtime
246− , termName = Just theTerm } <- readIORef $ _configRef input
247− _ <- hPrintf h "initial (vmin,vtime): %s\n" (show (theVmin, theVtime))
248− forM_ classifyTable $ \i -> case i of
249− (inBytes, EvKey k mods) -> hPrintf h "map %s %s %s %s\n" (show theTerm)
250− (show inBytes)
251− (show k)
252− (show mods)
253− _ -> return ()
254−
255−initInput :: Config -> ClassifyMap -> IO Input
256−initInput config classifyTable = do
257− let Just fd = inputFd config
258− setFdOption fd NonBlockingRead False
259− applyConfig fd config
260− stopSync <- newEmptyMVar
261− input <- Input <$> atomically newTChan
262− <*> pure (return ())
263− <*> pure (return ())
264− <*> newIORef config
265− <*> maybe (return Nothing)
266− (\f -> Just <$> openFile f AppendMode)
267− (debugLog config)
268− logInitialInputState input classifyTable
269− inputThread <- forkOSFinally (runInputProcessorLoop classifyTable input)
270− (\_ -> putMVar stopSync ())
271− let killAndWait = do
272− killThread inputThread
273− takeMVar stopSync
274− return $ input { shutdownInput = killAndWait }
275−
276−foreign import ccall "vty_set_term_timing" setTermTiming :: Fd -> Int -> Int -> IO ()
277−
278−forkOSFinally :: IO a -> (Either SomeException a -> IO ()) -> IO ThreadId
279−forkOSFinally action and_then =
280− mask $ \restore -> forkOS $ try (restore action) >>= and_then
281−
282−(<>=) :: (MonadState s m, Monoid a) => ASetter' s a -> a -> m ()
283−l <>= a = modify (l <>~ a)
284−
285−(<>~) :: Monoid a => ASetter s t a a -> a -> s -> t
286−l <>~ n = over l (`mappend` n)
src/Graphics/Vty/Input/Mouse.hsdeleted−163
@@ -1,163 +0,0 @@
1−-- | This module provides parsers for mouse events for both "normal" and
2−-- "extended" modes. This implementation was informed by
3−--
4−-- http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
5−module Graphics.Vty.Input.Mouse
6− ( requestMouseEvents
7− , disableMouseEvents
8− , isMouseEvent
9− , classifyMouseEvent
10− )
11−where
12−
13−import Graphics.Vty.Input.Events
14−import Graphics.Vty.Input.Classify.Types
15−import Graphics.Vty.Input.Classify.Parse
16−
17−import Control.Monad.State
18−import Data.Maybe (catMaybes)
19−import Data.Bits ((.&.))
20−
21−import qualified Data.ByteString.Char8 as BS8
22−import Data.ByteString.Char8 (ByteString)
23−
24−-- A mouse event in SGR extended mode is
25−--
26−-- '\ESC' '[' '<' B ';' X ';' Y ';' ('M'|'m')
27−--
28−-- where
29−--
30−-- * B is the number with button and modifier bits set,
31−-- * X is the X coordinate of the event starting at 1
32−-- * Y is the Y coordinate of the event starting at 1
33−-- * the final character is 'M' for a press, 'm' for a release
34−
35−-- | These sequences set xterm-based terminals to send mouse event
36−-- sequences.
37−requestMouseEvents :: ByteString
38−requestMouseEvents = BS8.pack "\ESC[?1000h\ESC[?1002h\ESC[?1006h"
39−
40−-- | These sequences disable mouse events.
41−disableMouseEvents :: ByteString
42−disableMouseEvents = BS8.pack "\ESC[?1000l\ESC[?1002l\ESC[?1006l"
43−
44−-- | Does the specified string begin with a mouse event?
45−isMouseEvent :: ByteString -> Bool
46−isMouseEvent s = isSGREvent s || isNormalEvent s
47−
48−isSGREvent :: ByteString -> Bool
49−isSGREvent = BS8.isPrefixOf sgrPrefix
50−
51−sgrPrefix :: ByteString
52−sgrPrefix = BS8.pack "\ESC[M"
53−
54−isNormalEvent :: ByteString -> Bool
55−isNormalEvent = BS8.isPrefixOf normalPrefix
56−
57−normalPrefix :: ByteString
58−normalPrefix = BS8.pack "\ESC[<"
59−
60−-- Modifier bits:
61−shiftBit :: Int
62−shiftBit = 4
63−
64−metaBit :: Int
65−metaBit = 8
66−
67−ctrlBit :: Int
68−ctrlBit = 16
69−
70−-- These bits indicate the buttons involved:
71−buttonMask :: Int
72−buttonMask = 67
73−
74−leftButton :: Int
75−leftButton = 0
76−
77−middleButton :: Int
78−middleButton = 1
79−
80−rightButton :: Int
81−rightButton = 2
82−
83−scrollUp :: Int
84−scrollUp = 64
85−
86−scrollDown :: Int
87−scrollDown = 65
88−
89−hasBitSet :: Int -> Int -> Bool
90−hasBitSet val bit = val .&. bit > 0
91−
92−-- | Attempt to classify an input string as a mouse event.
93−classifyMouseEvent :: ByteString -> KClass
94−classifyMouseEvent s = runParser s $ do
95− when (not $ isMouseEvent s) failParse
96−
97− expectChar '\ESC'
98− expectChar '['
99− ty <- readChar
100− case ty of
101− '<' -> classifySGRMouseEvent
102− 'M' -> classifyNormalMouseEvent
103− _ -> failParse
104−
105−-- Given a modifier/button value, determine which button was indicated
106−getSGRButton :: Int -> Parser Button
107−getSGRButton mods =
108− let buttonMap = [ (leftButton, BLeft)
109− , (middleButton, BMiddle)
110− , (rightButton, BRight)
111− , (scrollUp, BScrollUp)
112− , (scrollDown, BScrollDown)
113− ]
114− in case lookup (mods .&. buttonMask) buttonMap of
115− Nothing -> failParse
116− Just b -> return b
117−
118−getModifiers :: Int -> [Modifier]
119−getModifiers mods =
120− catMaybes [ if mods `hasBitSet` shiftBit then Just MShift else Nothing
121− , if mods `hasBitSet` metaBit then Just MMeta else Nothing
122− , if mods `hasBitSet` ctrlBit then Just MCtrl else Nothing
123− ]
124−
125−-- Attempt to classify a control sequence as a "normal" mouse event. To
126−-- get here we should have already read "\ESC[M" so that will not be
127−-- included in the string to be parsed.
128−classifyNormalMouseEvent :: Parser Event
129−classifyNormalMouseEvent = do
130− statusChar <- readChar
131− xCoordChar <- readChar
132− yCoordChar <- readChar
133−
134− let xCoord = fromEnum xCoordChar - 32
135− yCoord = fromEnum yCoordChar - 32
136− status = fromEnum statusChar
137− modifiers = getModifiers status
138−
139− let press = status .&. buttonMask /= 3
140− case press of
141− True -> do
142− button <- getSGRButton status
143− return $ EvMouseDown (xCoord-1) (yCoord-1) button modifiers
144− False -> return $ EvMouseUp (xCoord-1) (yCoord-1) Nothing
145−
146−-- Attempt to classify a control sequence as an SGR mouse event. To
147−-- get here we should have already read "\ESC[<" so that will not be
148−-- included in the string to be parsed.
149−classifySGRMouseEvent :: Parser Event
150−classifySGRMouseEvent = do
151− mods <- readInt
152− expectChar ';'
153− xCoord <- readInt
154− expectChar ';'
155− yCoord <- readInt
156− final <- readChar
157−
158− let modifiers = getModifiers mods
159− button <- getSGRButton mods
160− case final of
161− 'M' -> return $ EvMouseDown (xCoord-1) (yCoord-1) button modifiers
162− 'm' -> return $ EvMouseUp (xCoord-1) (yCoord-1) (Just button)
163− _ -> failParse
src/Graphics/Vty/Input/Paste.hsdeleted−41
@@ -1,41 +0,0 @@
1−-- | This module provides bracketed paste support as described at
2−--
3−-- http://cirw.in/blog/bracketed-paste
4−module Graphics.Vty.Input.Paste
5− ( parseBracketedPaste
6− , bracketedPasteStarted
7− , bracketedPasteFinished
8− )
9−where
10−
11−import qualified Data.ByteString.Char8 as BS8
12−import Data.ByteString.Char8 (ByteString)
13−
14−import Graphics.Vty.Input.Events
15−import Graphics.Vty.Input.Classify.Types
16−
17−bracketedPasteStart :: ByteString
18−bracketedPasteStart = BS8.pack "\ESC[200~"
19−
20−bracketedPasteEnd :: ByteString
21−bracketedPasteEnd = BS8.pack "\ESC[201~"
22−
23−-- | Does the input start a bracketed paste?
24−bracketedPasteStarted :: ByteString -> Bool
25−bracketedPasteStarted = BS8.isPrefixOf bracketedPasteStart
26−
27−-- | Does the input contain a complete bracketed paste?
28−bracketedPasteFinished :: ByteString -> Bool
29−bracketedPasteFinished = BS8.isInfixOf bracketedPasteEnd
30−
31−-- | Parse a bracketed paste. This should only be called on a string if
32−-- both 'bracketedPasteStarted' and 'bracketedPasteFinished' return
33−-- 'True'.
34−parseBracketedPaste :: ByteString -> KClass
35−parseBracketedPaste s =
36− Valid (EvPaste p) (BS8.drop endLen rest')
37− where
38− startLen = BS8.length bracketedPasteStart
39− endLen = BS8.length bracketedPasteEnd
40− (_, rest ) = BS8.breakSubstring bracketedPasteStart s
41− (p, rest') = BS8.breakSubstring bracketedPasteEnd . BS8.drop startLen $ rest
src/Graphics/Vty/Input/Terminfo.hsdeleted−186
@@ -1,186 +0,0 @@
1−{-# OPTIONS_GHC -fno-warn-warnings-deprecations #-}
2−module Graphics.Vty.Input.Terminfo where
3−
4−import Graphics.Vty.Input.Events
5−import qualified Graphics.Vty.Input.Terminfo.ANSIVT as ANSIVT
6−
7−import Control.Arrow
8−import System.Console.Terminfo
9−
10−-- | Queries the terminal for all capability-based input sequences and
11−-- then adds on a terminal-dependent input sequence mapping.
12−--
13−-- For reference see:
14−--
15−-- * http://vimdoc.sourceforge.net/htmldoc/term.html
16−--
17−-- * vim74/src/term.c
18−--
19−-- * http://invisible-island.net/vttest/
20−--
21−-- * http://aperiodic.net/phil/archives/Geekery/term-function-keys.html
22−--
23−-- Terminfo is incomplete. The vim source implies that terminfo is also
24−-- incorrect. Vty assumes that the internal terminfo table added to the
25−-- system-provided terminfo table is correct.
26−--
27−-- The procedure used here is:
28−--
29−-- 1. Build terminfo table for all caps. Missing caps are not added.
30−--
31−-- 2. Add tables for visible chars, esc, del, ctrl, and meta.
32−--
33−-- 3. Add internally-defined table for given terminal type.
34−--
35−-- Precedence is currently implicit in the 'compile' algorithm.
36−classifyMapForTerm :: String -> Terminal -> ClassifyMap
37−classifyMapForTerm termName term =
38− concat $ capsClassifyMap term keysFromCapsTable
39− : universalTable
40− : termSpecificTables termName
41−
42−-- | The key table applicable to all terminals.
43−--
44−-- Note that some of these entries are probably only applicable to
45−-- ANSI/VT100 terminals.
46−universalTable :: ClassifyMap
47−universalTable = concat [visibleChars, ctrlChars, ctrlMetaChars, specialSupportKeys]
48−
49−capsClassifyMap :: Terminal -> [(String,Event)] -> ClassifyMap
50−capsClassifyMap terminal table = [(x,y) | (Just x,y) <- map extractCap table]
51− where extractCap = first (getCapability terminal . tiGetStr)
52−
53−-- | Tables specific to a given terminal that are not derivable from
54−-- terminfo.
55−--
56−-- Note that this adds the ANSI/VT100/VT50 tables regardless of term
57−-- identifier.
58−termSpecificTables :: String -> [ClassifyMap]
59−termSpecificTables _termName = ANSIVT.classifyTable
60−
61−-- | Visible characters in the ISO-8859-1 and UTF-8 common set.
62−--
63−-- We limit to < 0xC1. The UTF8 sequence detector will catch all values
64−-- 0xC2 and above before this classify table is reached.
65−visibleChars :: ClassifyMap
66−visibleChars = [ ([x], EvKey (KChar x) [])
67− | x <- [' ' .. toEnum 0xC1]
68− ]
69−
70−-- | Non-printable characters in the ISO-8859-1 and UTF-8 common set
71−-- translated to ctrl + char.
72−--
73−-- This treats CTRL-i the same as tab.
74−ctrlChars :: ClassifyMap
75−ctrlChars =
76− [ ([toEnum x],EvKey (KChar y) [MCtrl])
77− | (x,y) <- zip [0..31] ('@':['a'..'z']++['['..'_'])
78− , y /= 'i' -- Resolve issue #3 where CTRL-i hides TAB.
79− , y /= 'h' -- CTRL-h should not hide BS
80− ]
81−
82−-- | Ctrl+Meta+Char
83−ctrlMetaChars :: ClassifyMap
84−ctrlMetaChars = map (\(s,EvKey c m) -> ('\ESC':s, EvKey c (MMeta:m))) ctrlChars
85−
86−-- | Esc, meta-esc, delete, meta-delete, enter, meta-enter.
87−specialSupportKeys :: ClassifyMap
88−specialSupportKeys =
89− [ ("\ESC\ESC[5~",EvKey KPageUp [MMeta])
90− , ("\ESC\ESC[6~",EvKey KPageDown [MMeta])
91− -- special support for ESC
92− , ("\ESC",EvKey KEsc []), ("\ESC\ESC",EvKey KEsc [MMeta])
93− -- Special support for backspace
94− , ("\DEL",EvKey KBS []), ("\ESC\DEL",EvKey KBS [MMeta]), ("\b",EvKey KBS [])
95− -- Special support for Enter
96− , ("\ESC\^J",EvKey KEnter [MMeta]), ("\^J",EvKey KEnter [])
97− -- explicit support for tab
98− , ("\t", EvKey (KChar '\t') [])
99− ]
100−
101−-- | A classification table directly generated from terminfo cap
102−-- strings. These are:
103−--
104−-- * ka1 - keypad up-left
105−--
106−-- * ka3 - keypad up-right
107−--
108−-- * kb2 - keypad center
109−--
110−-- * kbs - keypad backspace
111−--
112−-- * kbeg - begin
113−--
114−-- * kcbt - back tab
115−--
116−-- * kc1 - keypad left-down
117−--
118−-- * kc3 - keypad right-down
119−--
120−-- * kdch1 - delete
121−--
122−-- * kcud1 - down
123−--
124−-- * kend - end
125−--
126−-- * kent - enter
127−--
128−-- * kf0 - kf63 - function keys
129−--
130−-- * khome - KHome
131−--
132−-- * kich1 - insert
133−--
134−-- * kcub1 - left
135−--
136−-- * knp - next page (page down)
137−--
138−-- * kpp - previous page (page up)
139−--
140−-- * kcuf1 - right
141−--
142−-- * kDC - shift delete
143−--
144−-- * kEND - shift end
145−--
146−-- * kHOM - shift home
147−--
148−-- * kIC - shift insert
149−--
150−-- * kLFT - shift left
151−--
152−-- * kRIT - shift right
153−--
154−-- * kcuu1 - up
155−keysFromCapsTable :: ClassifyMap
156−keysFromCapsTable =
157− [ ("ka1", EvKey KUpLeft [])
158− , ("ka3", EvKey KUpRight [])
159− , ("kb2", EvKey KCenter [])
160− , ("kbs", EvKey KBS [])
161− , ("kbeg", EvKey KBegin [])
162− , ("kcbt", EvKey KBackTab [])
163− , ("kc1", EvKey KDownLeft [])
164− , ("kc3", EvKey KDownRight [])
165− , ("kdch1", EvKey KDel [])
166− , ("kcud1", EvKey KDown [])
167− , ("kend", EvKey KEnd [])
168− , ("kent", EvKey KEnter [])
169− , ("khome", EvKey KHome [])
170− , ("kich1", EvKey KIns [])
171− , ("kcub1", EvKey KLeft [])
172− , ("knp", EvKey KPageDown [])
173− , ("kpp", EvKey KPageUp [])
174− , ("kcuf1", EvKey KRight [])
175− , ("kDC", EvKey KDel [MShift])
176− , ("kEND", EvKey KEnd [MShift])
177− , ("kHOM", EvKey KHome [MShift])
178− , ("kIC", EvKey KIns [MShift])
179− , ("kLFT", EvKey KLeft [MShift])
180− , ("kRIT", EvKey KRight [MShift])
181− , ("kcuu1", EvKey KUp [])
182− ] ++ functionKeyCapsTable
183−
184−-- | Cap names for function keys.
185−functionKeyCapsTable :: ClassifyMap
186−functionKeyCapsTable = flip map [0..63] $ \n -> ("kf" ++ show n, EvKey (KFun n) [])
src/Graphics/Vty/Input/Terminfo/ANSIVT.hsdeleted−88
@@ -1,88 +0,0 @@
1−-- | Input mappings for ANSI/VT100/VT50 terminals that is missing from
2−-- terminfo.
3−--
4−-- Or that are sent regardless of terminfo by terminal emulators. EG:
5−-- Terminal emulators will often use VT50 input bytes regardless of
6−-- declared terminal type. This provides compatibility with programs
7−-- that don't follow terminfo.
8−module Graphics.Vty.Input.Terminfo.ANSIVT where
9−
10−import Graphics.Vty.Input.Events
11−
12−-- | Encoding for navigation keys.
13−navKeys0 :: ClassifyMap
14−navKeys0 =
15− [ k "G" KCenter
16− , k "P" KPause
17− , k "A" KUp
18− , k "B" KDown
19− , k "C" KRight
20− , k "D" KLeft
21− , k "H" KHome
22− , k "F" KEnd
23− , k "E" KBegin
24− ]
25− where k c s = ("\ESC["++c,EvKey s [])
26−
27−-- | encoding for shift, meta and ctrl plus arrows/home/end
28−navKeys1 :: ClassifyMap
29−navKeys1 =
30− [("\ESC[" ++ charCnt ++ show mc++c,EvKey s m)
31− | charCnt <- ["1;", ""], -- we can have a count or not
32− (m,mc) <- [([MShift],2::Int), ([MCtrl],5), ([MMeta],3),
33− -- modifiers and their codes
34− ([MShift, MCtrl],6), ([MShift, MMeta],4)],
35− -- directions and their codes
36− (c,s) <- [("A", KUp), ("B", KDown), ("C", KRight), ("D", KLeft), ("H", KHome), ("F", KEnd)]
37− ]
38−
39−-- | encoding for ins, del, pageup, pagedown, home, end
40−navKeys2 :: ClassifyMap
41−navKeys2 =
42− let k n s = ("\ESC["++show n++"~",EvKey s [])
43− in zipWith k [2::Int,3,5,6,1,4]
44− [KIns,KDel,KPageUp,KPageDown,KHome,KEnd]
45−
46−-- | encoding for ctrl + ins, del, pageup, pagedown, home, end
47−navKeys3 :: ClassifyMap
48−navKeys3 =
49− let k n s = ("\ESC["++show n++";5~",EvKey s [MCtrl])
50− in zipWith k [2::Int,3,5,6,1,4]
51− [KIns,KDel,KPageUp,KPageDown,KHome,KEnd]
52−
53−-- | encoding for shift plus function keys
54−--
55−-- According to
56−--
57−-- * http://aperiodic.net/phil/archives/Geekery/term-function-keys.html
58−--
59−-- This encoding depends on the terminal.
60−functionKeys1 :: ClassifyMap
61−functionKeys1 =
62− let f ff nrs m = [ ("\ESC["++show n++"~",EvKey (KFun $ n-head nrs+ff) m) | n <- nrs ] in
63− concat [f 1 [25,26] [MShift], f 3 [28,29] [MShift], f 5 [31..34] [MShift] ]
64−
65−-- | encoding for meta plus char
66−--
67−-- 1. removed 'ESC' from second list due to duplication with
68−-- "special_support_keys".
69−-- 2. removed '[' from second list due to conflict with 7-bit encoding
70−-- for ESC. Whether meta+[ is the same as ESC should examine km and
71−-- current encoding.
72−-- 3. stopped enumeration at '~' instead of '\DEL'. The latter is mapped
73−-- to KBS by special_support_keys.
74−functionKeys2 :: ClassifyMap
75−functionKeys2 = [ ('\ESC':[x],EvKey (KChar x) [MMeta])
76− | x <- '\t':[' ' .. '~']
77− , x /= '['
78− ]
79−
80−classifyTable :: [ClassifyMap]
81−classifyTable =
82− [ navKeys0
83− , navKeys1
84− , navKeys2
85− , navKeys3
86− , functionKeys1
87− , functionKeys2
88− ]
src/Graphics/Vty/LLInput.hsnew file+226
@@ -0,0 +1,226 @@
1+-- Copyright 2009 Corey O'Connor
2+{-# OPTIONS_GHC -Wall #-}
3+{-# LANGUAGE ForeignFunctionInterface #-}
4+module Graphics.Vty.LLInput ( Key(..)
5+ , Modifier(..)
6+ , Button(..)
7+ , Event(..)
8+ , initTermInput
9+ )
10+ where
11+
12+import Data.Char
13+import Data.Maybe ( mapMaybe
14+ )
15+import Data.List( inits )
16+import Data.Word
17+import qualified Data.Map as M( fromList, lookup )
18+import qualified Data.Set as S( fromList, member )
19+
20+import Codec.Binary.UTF8.Generic (decode)
21+
22+import Control.Monad (when)
23+import Control.Concurrent
24+import Control.Exception
25+
26+import System.Console.Terminfo
27+
28+import System.Posix.Signals.Exts
29+import System.Posix.Signals
30+import System.Posix.Terminal
31+import System.Posix.IO ( stdInput
32+ ,fdRead
33+ ,setFdOption
34+ ,FdOption(..)
35+ )
36+
37+-- |Representations of non-modifier keys.
38+data Key = KEsc | KFun Int | KBackTab | KPrtScr | KPause | KASCII Char | KBS | KIns
39+ | KHome | KPageUp | KDel | KEnd | KPageDown | KNP5 | KUp | KMenu
40+ | KLeft | KDown | KRight | KEnter
41+ deriving (Eq,Show,Ord)
42+
43+-- |Modifier keys. Key codes are interpreted such that users are more likely to
44+-- have Meta than Alt; for instance on the PC Linux console, 'MMeta' will
45+-- generally correspond to the physical Alt key.
46+data Modifier = MShift | MCtrl | MMeta | MAlt
47+ deriving (Eq,Show,Ord)
48+
49+-- |Mouse buttons. Not yet used.
50+data Button = BLeft | BMiddle | BRight
51+ deriving (Eq,Show,Ord)
52+
53+-- |Generic events.
54+data Event = EvKey Key [Modifier] | EvMouse Int Int Button [Modifier]
55+ | EvResize Int Int
56+ deriving (Eq,Show,Ord)
57+
58+data KClass = Valid Key [Modifier] | Invalid | Prefix | MisPfx Key [Modifier] [Char]
59+ deriving(Show)
60+
61+-- | Set up the terminal for input. Returns a function which reads key
62+-- events, and a function for shutting down the terminal access.
63+initTermInput :: Int -> Terminal -> IO (IO Event, IO ())
64+initTermInput escDelay terminal = do
65+ eventChannel <- newChan
66+ inputChannel <- newChan
67+ hadInput <- newEmptyMVar
68+ oattr <- getTerminalAttributes stdInput
69+ let nattr = foldl withoutMode oattr [StartStopOutput, KeyboardInterrupts,
70+ EnableEcho, ProcessInput, ExtendedFunctions]
71+ setTerminalAttributes stdInput nattr Immediately
72+ set_term_timing
73+ let inputToEventThread :: IO ()
74+ inputToEventThread = loop []
75+ where loop kb = case (classify kb) of
76+ Prefix -> do c <- readChan inputChannel
77+ loop (kb ++ [c])
78+ Invalid -> loop ""
79+ MisPfx k m s -> writeChan eventChannel (EvKey k m) >> loop s
80+ Valid k m -> writeChan eventChannel (EvKey k m) >> loop ""
81+
82+ finishAtomicInput = writeChan inputChannel '\xFFFE'
83+
84+ inputThread :: IO ()
85+ inputThread = loop
86+ where
87+ loop = do
88+ setFdOption stdInput NonBlockingRead False
89+ threadWaitRead stdInput
90+ setFdOption stdInput NonBlockingRead True
91+ try readAll :: IO (Either IOException ())
92+ when (escDelay == 0) finishAtomicInput
93+ loop
94+ readAll = do
95+ (bytes, bytes_read) <- fdRead stdInput 1
96+ when (bytes_read > 0) $ do
97+ tryPutMVar hadInput () -- signal input
98+ writeChan inputChannel (head bytes)
99+ readAll
100+ -- | If there is no input for some time, this thread puts '\xFFFE' in the
101+ -- inputChannel.
102+ noInputThread :: IO ()
103+ noInputThread = when (escDelay > 0) loop
104+ where loop = do
105+ takeMVar hadInput -- wait for some input
106+ threadDelay escDelay -- microseconds
107+ hadNoInput <- isEmptyMVar hadInput -- no input yet?
108+ when hadNoInput $ do
109+ finishAtomicInput
110+ loop
111+
112+
113+ compile :: [[([Char],(Key,[Modifier]))]] -> [Char] -> KClass
114+ compile lst = cl' where
115+ lst' = concat lst
116+ pfx = S.fromList $ concatMap (init . inits . fst) $ lst'
117+ mlst = M.fromList lst'
118+ cl' str = case S.member str pfx of
119+ True -> Prefix
120+ False -> case M.lookup str mlst of
121+ Just (k,m) -> Valid k m
122+ Nothing -> case head $ mapMaybe (\s -> (,) s `fmap` M.lookup s mlst) $ init $ inits str of
123+ (s,(k,m)) -> MisPfx k m (drop (length s) str)
124+
125+ -- ANSI specific bits
126+
127+
128+ classify, classifyTab :: [Char] -> KClass
129+
130+ -- As soon as
131+ classify "\xFFFE" = Invalid
132+ classify s@(c:_) | ord c >= 0xC2 =
133+ if utf8Length (ord c) > length s then Prefix else classifyUtf8 s -- beginning of an utf8 sequence
134+ classify other = classifyTab other
135+
136+ classifyUtf8 s = case decode ((map (fromIntegral . ord) s) :: [Word8]) of
137+ Just (unicodeChar, _) -> Valid (KASCII unicodeChar) []
138+ _ -> Invalid -- something bad happened; just ignore and continue.
139+
140+ classifyTab = compile (caps_classify_table : ansi_classify_table)
141+
142+ caps_tabls = [("khome", (KHome, [])),
143+ ("kend", (KEnd, [])),
144+ ("cbt", (KBackTab, [])),
145+ ("kcud1", (KDown, [])),
146+ ("kcuu1", (KUp, [])),
147+ ("kcuf1", (KRight, [])),
148+ ("kcub1", (KLeft, [])),
149+
150+ ("kLFT", (KLeft, [MShift])),
151+ ("kRIT", (KRight, [MShift]))
152+ ]
153+
154+ caps_classify_table = [(x,y) | (Just x,y) <- map (first (getCapability terminal . tiGetStr)) $ caps_tabls]
155+
156+ ansi_classify_table :: [[([Char], (Key, [Modifier]))]]
157+ ansi_classify_table =
158+ [ let k c s = ("\ESC["++c,(s,[])) in [ k "G" KNP5, k "P" KPause, k "A" KUp, k "B" KDown, k "C" KRight, k "D" KLeft ],
159+
160+ -- Support for arrows
161+ [("\ESC[" ++ charCnt ++ show mc++c,(s,m))
162+ | charCnt <- ["1;", ""], -- we can have a count or not
163+ (m,mc) <- [([MShift],2::Int), ([MCtrl],5), ([MMeta],3),
164+ ([MShift, MCtrl],6), ([MShift, MMeta],4)], -- modifiers and their codes
165+ (c,s) <- [("A", KUp), ("B", KDown), ("C", KRight), ("D", KLeft)] -- directions and their codes
166+ ],
167+
168+ let k n s = ("\ESC["++show n++"~",(s,[])) in zipWith k [2::Int,3,5,6] [KIns,KDel,KPageUp,KPageDown],
169+
170+ -- Support for simple characters.
171+ [ (x:[],(KASCII x,[])) | x <- map toEnum [0..255] ],
172+
173+ -- Support for function keys (should use terminfo)
174+ [ ("\ESC[["++[toEnum(64+i)],(KFun i,[])) | i <- [1..5] ],
175+ let f ff nrs m = [ ("\ESC["++show n++"~",(KFun (n-(nrs!!0)+ff), m)) | n <- nrs ] in
176+ concat [ f 6 [17..21] [], f 11 [23,24] [], f 1 [25,26] [MShift], f 3 [28,29] [MShift], f 5 [31..34] [MShift] ],
177+ [ ('\ESC':[x],(KASCII x,[MMeta])) | x <- '\ESC':'\t':[' ' .. '\DEL'] ],
178+
179+ -- Ctrl+Char
180+ [ ([toEnum x],(KASCII y,[MCtrl]))
181+ | (x,y) <- zip ([0..31]) ('@':['a'..'z']++['['..'_']),
182+ y /= 'i' -- Resolve issue #3 where CTRL-i hides TAB.
183+ ],
184+
185+ -- Ctrl+Meta+Char
186+ [ ('\ESC':[toEnum x],(KASCII y,[MMeta,MCtrl])) | (x,y) <- zip [0..31] ('@':['a'..'z']++['['..'_']) ],
187+
188+ -- Special support
189+ [ -- special support for ESC
190+ ("\ESC",(KEsc,[])) , ("\ESC\ESC",(KEsc,[MMeta])),
191+
192+ -- Special support for backspace
193+ ("\DEL",(KBS,[])), ("\ESC\DEL",(KBS,[MMeta])),
194+
195+ -- Special support for Enter
196+ ("\ESC\^J",(KEnter,[MMeta])), ("\^J",(KEnter,[])) ]
197+ ]
198+
199+ eventThreadId <- forkIO $ inputToEventThread
200+ inputThreadId <- forkIO $ inputThread
201+ noInputThreadId <- forkIO $ noInputThread
202+ let pokeIO = (Catch $ do let e = error "(getsize in input layer)"
203+ setTerminalAttributes stdInput nattr Immediately
204+ writeChan eventChannel (EvResize e e))
205+ installHandler windowChange pokeIO Nothing
206+ installHandler continueProcess pokeIO Nothing
207+ let uninit = do killThread eventThreadId
208+ killThread inputThreadId
209+ killThread noInputThreadId
210+ installHandler windowChange Ignore Nothing
211+ installHandler continueProcess Ignore Nothing
212+ setTerminalAttributes stdInput oattr Immediately
213+ return (readChan eventChannel, uninit)
214+
215+first :: (a -> b) -> (a,c) -> (b,c)
216+first f (x,y) = (f x, y)
217+
218+utf8Length :: (Num t, Ord a, Num a) => a -> t
219+utf8Length c
220+ | c < 0x80 = 1
221+ | c < 0xE0 = 2
222+ | c < 0xF0 = 3
223+ | otherwise = 4
224+
225+foreign import ccall "set_term_timing" set_term_timing :: IO ()
226+
src/Graphics/Vty/Output.hsdeleted−107
@@ -1,107 +0,0 @@
1−{-# LANGUAGE RecordWildCards, CPP #-}
2−-- | This module provides functions for accessing the current terminal
3−-- or a specific terminal device.
4−--
5−-- See also:
6−--
7−-- 1. "Graphics.Vty.Output": This instantiates an abtract interface
8−-- to the terminal based on the @TERM@ and @COLORTERM@ environment
9−-- variables.
10−--
11−-- 2. "Graphics.Vty.Output.Interface": Defines the generic interface all
12−-- terminal modules need to implement.
13−--
14−-- 3. "Graphics.Vty.Output.TerminfoBased": Defines a terminal instance
15−-- that uses terminfo for all control strings. No attempt is made to
16−-- change the character set to UTF-8 for these terminals.
17−--
18−-- 4. "Graphics.Vty.Output.XTermColor": This module contains an
19−-- interface suitable for xterm-like terminals. These are the terminals
20−-- where @TERM@ begins with @xterm@. This does use terminfo for as many
21−-- control codes as possible.
22−module Graphics.Vty.Output
23− ( outputForConfig
24− , setCursorPos
25− , hideCursor
26− , showCursor
27− )
28−where
29−
30−import Control.Monad (when)
31−
32−import Graphics.Vty.Config
33−import Graphics.Vty.Image (regionWidth, regionHeight)
34−import Graphics.Vty.Output.Interface
35−import Graphics.Vty.Output.XTermColor as XTermColor
36−import Graphics.Vty.Output.TerminfoBased as TerminfoBased
37−
38−import Blaze.ByteString.Builder (writeToByteString)
39−
40−import Data.List (isPrefixOf)
41−
42−#if !MIN_VERSION_base(4,11,0)
43−import Data.Monoid ((<>))
44−#endif
45−
46−-- | Returns an `Output` for the terminal specified in `Config`.
47−--
48−-- The specific Output implementation used is hidden from the API user.
49−-- All terminal implementations are assumed to perform more, or less,
50−-- the same. Currently, all implementations use terminfo for at least
51−-- some terminal specific information.
52−--
53−-- If a terminal implementation is developed for a terminal without
54−-- terminfo support then Vty should work as expected on that terminal.
55−--
56−-- Selection of a terminal is done as follows:
57−--
58−-- * If TERM contains "xterm" or "screen", use XTermColor.
59−-- * otherwise use the TerminfoBased driver.
60−outputForConfig :: Config -> IO Output
61−outputForConfig Config{ outputFd = Just fd, termName = Just termName
62− , colorMode = Just colorMode, .. } = do
63− t <- if "xterm" `isPrefixOf` termName || "screen" `isPrefixOf` termName
64− then XTermColor.reserveTerminal termName fd colorMode
65− -- Not an xterm-like terminal. try for generic terminfo.
66− else TerminfoBased.reserveTerminal termName fd colorMode
67−
68− case mouseMode of
69− Just s -> setMode t Mouse s
70− Nothing -> return ()
71−
72− case bracketedPasteMode of
73− Just s -> setMode t BracketedPaste s
74− Nothing -> return ()
75−
76− return t
77−outputForConfig config = (<> config) <$> standardIOConfig >>= outputForConfig
78−
79−-- | Sets the cursor position to the given output column and row.
80−--
81−-- This is not necessarily the same as the character position with the
82−-- same coordinates. Characters can be a variable number of columns in
83−-- width.
84−--
85−-- Currently, the only way to set the cursor position to a given
86−-- character coordinate is to specify the coordinate in the Picture
87−-- instance provided to 'outputPicture' or 'refresh'.
88−setCursorPos :: Output -> Int -> Int -> IO ()
89−setCursorPos t x y = do
90− bounds <- displayBounds t
91− when (x >= 0 && x < regionWidth bounds && y >= 0 && y < regionHeight bounds) $ do
92− dc <- displayContext t bounds
93− outputByteBuffer t $ writeToByteString $ writeMoveCursor dc x y
94−
95−-- | Hides the cursor.
96−hideCursor :: Output -> IO ()
97−hideCursor t = do
98− bounds <- displayBounds t
99− dc <- displayContext t bounds
100− outputByteBuffer t $ writeToByteString $ writeHideCursor dc
101−
102−-- | Shows the cursor.
103−showCursor :: Output -> IO ()
104−showCursor t = do
105− bounds <- displayBounds t
106− dc <- displayContext t bounds
107− outputByteBuffer t $ writeToByteString $ writeShowCursor dc
src/Graphics/Vty/Output/Interface.hsdeleted−331
@@ -1,331 +0,0 @@
1−-- Copyright Corey O'Connor
2−{-# LANGUAGE RankNTypes #-}
3−{-# LANGUAGE ExistentialQuantification #-}
4−{-# LANGUAGE GADTs #-}
5−{-# LANGUAGE FlexibleContexts #-}
6−{-# LANGUAGE ScopedTypeVariables #-}
7−-- | This module provides an abstract interface for performing terminal
8−-- output. The only user-facing part of this API is 'Output'.
9−module Graphics.Vty.Output.Interface
10− ( Output(..)
11− , AssumedState(..)
12− , DisplayContext(..)
13− , Mode(..)
14− , displayContext
15− , outputPicture
16− , initialAssumedState
17− , limitAttrForDisplay
18− )
19−where
20−
21−import Graphics.Vty.Attributes
22−import Graphics.Vty.Image (DisplayRegion, regionHeight)
23−import Graphics.Vty.Picture
24−import Graphics.Vty.PictureToSpans
25−import Graphics.Vty.Span
26−
27−import Graphics.Vty.DisplayAttributes
28−
29−import Blaze.ByteString.Builder (Write, writeToByteString)
30−import Blaze.ByteString.Builder.ByteString (writeByteString)
31−
32−import qualified Data.ByteString as BS
33−import Data.IORef
34−import qualified Data.Text.Encoding as T
35−import qualified Data.Text.Lazy as TL
36−import qualified Data.Vector as Vector
37−
38−-- | Modal terminal features that can be enabled and disabled.
39−data Mode = Mouse
40− -- ^ Mouse mode (whether the terminal is configured to provide
41− -- mouse input events)
42− | BracketedPaste
43− -- ^ Paste mode (whether the terminal is configured to provide
44− -- events on OS pastes)
45− | Focus
46− -- ^ Focus-in/focus-out events (whether the terminal is
47− -- configured to provide events on focus change)
48− | Hyperlink
49− -- ^ Hyperlink mode via the 'withURL' attribute modifier (see
50− -- https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda).
51− -- Note that this may not work gracefully in all terminal
52− -- emulators so be sure to test this mode with the terminals
53− -- you intend to support. It is off by default.
54− deriving (Eq, Read, Show)
55−
56−-- | The Vty terminal output interface.
57−data Output = Output
58− { -- | Text identifier for the output device. Used for debugging.
59− terminalID :: String
60− -- | Release the terminal just prior to application exit and reset
61− -- it to its state prior to application startup.
62− , releaseTerminal :: IO ()
63− -- | Clear the display and initialize the terminal to some initial
64− -- display state.
65− --
66− -- The expectation of a program is that the display starts in some
67− -- The initial state. initial state would consist of fixed values:
68− --
69− -- - cursor at top left
70− -- - UTF-8 character encoding
71− -- - drawing characteristics are the default
72− , reserveDisplay :: IO ()
73− -- | Return the display to the state before `reserveDisplay` If no
74− -- previous state then set the display state to the initial state.
75− , releaseDisplay :: IO ()
76− -- | Sets the current display bounds (width, height).
77− , setDisplayBounds :: (Int, Int) -> IO ()
78− -- | Returns the current display bounds.
79− , displayBounds :: IO DisplayRegion
80− -- | Output the bytestring to the terminal device.
81− , outputByteBuffer :: BS.ByteString -> IO ()
82− -- | Specifies whether the cursor can be shown / hidden.
83− , supportsCursorVisibility :: Bool
84− -- | Indicates support for terminal modes for this output device.
85− , supportsMode :: Mode -> Bool
86− -- | Enables or disables a mode (does nothing if the mode is
87− -- unsupported).
88− , setMode :: Mode -> Bool -> IO ()
89− -- | Returns whether a mode is enabled.
90− , getModeStatus :: Mode -> IO Bool
91− , assumedStateRef :: IORef AssumedState
92− -- | Acquire display access to the given region of the display.
93− -- Currently all regions have the upper left corner of (0,0) and
94− -- the lower right corner at (max displayWidth providedWidth, max
95− -- displayHeight providedHeight)
96− , mkDisplayContext :: Output -> DisplayRegion -> IO DisplayContext
97− -- | Ring the terminal bell if supported.
98− , ringTerminalBell :: IO ()
99− -- | Returns whether the terminal has an audio bell feature.
100− , supportsBell :: IO Bool
101− -- | Returns whether the terminal supports italicized text.
102− --
103− -- This is terminal-dependent and should make a best effort to
104− -- determine whether this feature is supported, but even if the
105− -- terminal advertises support (e.g. via terminfo) that might not
106− -- be a reliable indicator of whether the feature will work as
107− -- desired.
108− , supportsItalics :: IO Bool
109− -- | Returns whether the terminal supports strikethrough text.
110− --
111− -- This is terminal-dependent and should make a best effort to
112− -- determine whether this feature is supported, but even if the
113− -- terminal advertises support (e.g. via terminfo) that might not
114− -- be a reliable indicator of whether the feature will work as
115− -- desired.
116− , supportsStrikethrough :: IO Bool
117− -- | Returns how many colors the terminal supports.
118− , outputColorMode :: ColorMode
119− }
120−
121−displayContext :: Output -> DisplayRegion -> IO DisplayContext
122−displayContext t = mkDisplayContext t t
123−
124−data AssumedState = AssumedState
125− { prevFattr :: Maybe FixedAttr
126− , prevOutputOps :: Maybe DisplayOps
127− }
128−
129−initialAssumedState :: AssumedState
130−initialAssumedState = AssumedState Nothing Nothing
131−
132−data DisplayContext = DisplayContext
133− { contextDevice :: Output
134− -- | Provide the bounds of the display context.
135− , contextRegion :: DisplayRegion
136− -- | Sets the output position to the specified row and column
137− -- where the number of bytes required for the control codes can be
138− -- specified seperate from the actual byte sequence.
139− , writeMoveCursor :: Int -> Int -> Write
140− , writeShowCursor :: Write
141− , writeHideCursor :: Write
142− -- Ensure that the specified output attributes will be applied to
143− -- all the following text until the next output attribute change
144− -- where the number of bytes required for the control codes can be
145− -- specified seperately from the actual byte sequence. The required
146− -- number of bytes must be at least the maximum number of bytes
147− -- required by any attribute changes. The serialization equations
148− -- must provide the ptr to the next byte to be specified in the
149− -- output buffer.
150− --
151− -- The currently applied display attributes are provided as well.
152− -- The Attr data type can specify the style or color should not be
153− -- changed from the currently applied display attributes. In order
154− -- to support this the currently applied display attributes are
155− -- required. In addition it may be possible to optimize the state
156− -- changes based off the currently applied display attributes.
157− , writeSetAttr :: Bool -> FixedAttr -> Attr -> DisplayAttrDiff -> Write
158− -- | Reset the display attributes to the default display attributes.
159− , writeDefaultAttr :: Bool -> Write
160− , writeRowEnd :: Write
161− -- | See `Graphics.Vty.Output.XTermColor.inlineHack`
162− , inlineHack :: IO ()
163− }
164−
165−-- | All terminals serialize UTF8 text to the terminal device exactly as
166−-- serialized in memory.
167−writeUtf8Text :: BS.ByteString -> Write
168−writeUtf8Text = writeByteString
169−
170−-- | Displays the given `Picture`.
171−--
172−-- 1. The image is cropped to the display size.
173−--
174−-- 2. Converted into a sequence of attribute changes and text spans.
175−--
176−-- 3. The cursor is hidden.
177−--
178−-- 4. Serialized to the display.
179−--
180−-- 5. The cursor is then shown and positioned or kept hidden.
181−outputPicture :: DisplayContext -> Picture -> IO ()
182−outputPicture dc pic = do
183− urlsEnabled <- getModeStatus (contextDevice dc) Hyperlink
184− as <- readIORef (assumedStateRef $ contextDevice dc)
185− let manipCursor = supportsCursorVisibility (contextDevice dc)
186− r = contextRegion dc
187− ops = displayOpsForPic pic r
188− initialAttr = FixedAttr defaultStyleMask Nothing Nothing Nothing
189− -- Diff the previous output against the requested output.
190− -- Differences are currently on a per-row basis.
191− diffs :: [Bool] = case prevOutputOps as of
192− Nothing -> replicate (fromEnum $ regionHeight $ affectedRegion ops) True
193− Just previousOps -> if affectedRegion previousOps /= affectedRegion ops
194− then replicate (displayOpsRows ops) True
195− else Vector.toList $ Vector.zipWith (/=) previousOps ops
196− -- build the Write corresponding to the output image
197− out = (if manipCursor then writeHideCursor dc else mempty)
198− `mappend` writeOutputOps urlsEnabled dc initialAttr diffs ops
199− `mappend`
200− (let (w,h) = contextRegion dc
201− clampX = max 0 . min (w-1)
202− clampY = max 0 . min (h-1) in
203− case picCursor pic of
204− _ | not manipCursor -> mempty
205− NoCursor -> mempty
206− AbsoluteCursor x y ->
207− writeShowCursor dc `mappend`
208− writeMoveCursor dc (clampX x) (clampY y)
209− PositionOnly isAbs x y ->
210− if isAbs
211− then writeMoveCursor dc (clampX x) (clampY y)
212− else let (ox, oy) = charToOutputPos m (clampX x, clampY y)
213− m = cursorOutputMap ops $ picCursor pic
214− in writeMoveCursor dc (clampX ox) (clampY oy)
215− Cursor x y ->
216− let m = cursorOutputMap ops $ picCursor pic
217− (ox, oy) = charToOutputPos m (clampX x, clampY y)
218− in writeShowCursor dc `mappend`
219− writeMoveCursor dc (clampX ox) (clampY oy)
220− )
221− -- ... then serialize
222− outputByteBuffer (contextDevice dc) (writeToByteString out)
223− -- Cache the output spans.
224− let as' = as { prevOutputOps = Just ops }
225− writeIORef (assumedStateRef $ contextDevice dc) as'
226−
227−writeOutputOps :: Bool -> DisplayContext -> FixedAttr -> [Bool] -> DisplayOps -> Write
228−writeOutputOps urlsEnabled dc initialAttr diffs ops =
229− let (_, out, _) = Vector.foldl' writeOutputOps'
230− (0, mempty, diffs)
231− ops
232− in out
233− where
234− writeOutputOps' (y, out, True : diffs') spanOps
235− = let spanOut = writeSpanOps urlsEnabled dc y initialAttr spanOps
236− out' = out `mappend` spanOut
237− in (y+1, out', diffs')
238− writeOutputOps' (y, out, False : diffs') _spanOps
239− = (y + 1, out, diffs')
240− writeOutputOps' (_y, _out, []) _spanOps
241− = error "vty - output spans without a corresponding diff."
242−
243−writeSpanOps :: Bool -> DisplayContext -> Int -> FixedAttr -> SpanOps -> Write
244−writeSpanOps urlsEnabled dc y initialAttr spanOps =
245− -- The first operation is to set the cursor to the start of the row
246− let start = writeMoveCursor dc 0 y `mappend` writeDefaultAttr dc urlsEnabled
247− -- then the span ops are serialized in the order specified
248− in fst $ Vector.foldl' (\(out, fattr) op -> case writeSpanOp urlsEnabled dc op fattr of
249− (opOut, fattr') -> (out `mappend` opOut, fattr')
250− )
251− (start, initialAttr)
252− spanOps
253−
254−writeSpanOp :: Bool -> DisplayContext -> SpanOp -> FixedAttr -> (Write, FixedAttr)
255−writeSpanOp urlsEnabled dc (TextSpan attr _ _ str) fattr =
256− let attr' = limitAttrForDisplay (contextDevice dc) attr
257− fattr' = fixDisplayAttr fattr attr'
258− diffs = displayAttrDiffs fattr fattr'
259− out = writeSetAttr dc urlsEnabled fattr attr' diffs
260− `mappend` writeUtf8Text (T.encodeUtf8 $ TL.toStrict str)
261− in (out, fattr')
262−writeSpanOp _ _ (Skip _) _fattr = error "writeSpanOp for Skip"
263−writeSpanOp urlsEnabled dc (RowEnd _) fattr = (writeDefaultAttr dc urlsEnabled `mappend` writeRowEnd dc, fattr)
264−
265−-- | The cursor position is given in X,Y character offsets. Due to
266−-- multi-column characters this needs to be translated to column, row
267−-- positions.
268−data CursorOutputMap = CursorOutputMap
269− { charToOutputPos :: (Int, Int) -> (Int, Int)
270− }
271−
272−cursorOutputMap :: DisplayOps -> Cursor -> CursorOutputMap
273−cursorOutputMap spanOps _cursor = CursorOutputMap
274− { charToOutputPos = \(cx, cy) -> (cursorColumnOffset spanOps cx cy, cy)
275− }
276−
277−cursorColumnOffset :: DisplayOps -> Int -> Int -> Int
278−cursorColumnOffset ops cx cy =
279− let cursorRowOps = Vector.unsafeIndex ops (fromEnum cy)
280− (outOffset, _, _)
281− = Vector.foldl' ( \(d, currentCx, done) op ->
282− if done then (d, currentCx, done) else case spanOpHasWidth op of
283− Nothing -> (d, currentCx, False)
284− Just (cw, ow) -> case compare cx (currentCx + cw) of
285− GT -> ( d + ow
286− , currentCx + cw
287− , False
288− )
289− EQ -> ( d + ow
290− , currentCx + cw
291− , True
292− )
293− LT -> ( d + columnsToCharOffset (cx - currentCx) op
294− , currentCx + cw
295− , True
296− )
297− )
298− (0, 0, False)
299− cursorRowOps
300− in outOffset
301−
302−-- | Not all terminals support all display attributes. This filters a
303−-- display attribute to what the given terminal can display.
304−limitAttrForDisplay :: Output -> Attr -> Attr
305−limitAttrForDisplay t attr
306− = attr { attrForeColor = clampColor $ attrForeColor attr
307− , attrBackColor = clampColor $ attrBackColor attr
308− }
309− where
310− clampColor Default = Default
311− clampColor KeepCurrent = KeepCurrent
312− clampColor (SetTo c) = clampColor' (outputColorMode t) c
313−
314− clampColor' NoColor _ = Default
315−
316− clampColor' ColorMode8 (ISOColor v)
317− | v >= 8 = SetTo $ ISOColor (v - 8)
318− | otherwise = SetTo $ ISOColor v
319− clampColor' ColorMode8 _ = Default
320−
321− clampColor' ColorMode16 c@(ISOColor _) = SetTo c
322− clampColor' ColorMode16 _ = Default
323−
324− clampColor' (ColorMode240 _) c@(ISOColor _) = SetTo c
325− clampColor' (ColorMode240 colorCount) c@(Color240 n)
326− | n <= colorCount = SetTo c
327− | otherwise = Default
328− clampColor' colorMode@(ColorMode240 _) (RGBColor r g b) =
329− clampColor' colorMode (color240 r g b)
330−
331− clampColor' FullColor c = SetTo c
src/Graphics/Vty/Output/Mock.hsdeleted−90
@@ -1,90 +0,0 @@
1−-- Copyright Corey O'Connor
2−{-# LANGUAGE FlexibleInstances #-}
3−{-# LANGUAGE TypeFamilies #-}
4−-- | This provides a mock terminal implementation that is nice for
5−-- testing. This transforms the output operations to visible characters
6−-- which is useful for testing.
7−module Graphics.Vty.Output.Mock
8− ( MockData
9− , mockTerminal
10− )
11−where
12−
13−import Graphics.Vty.Image (DisplayRegion)
14−import Graphics.Vty.Attributes.Color (ColorMode(ColorMode16))
15−import Graphics.Vty.Output.Interface
16−
17−import Blaze.ByteString.Builder.Word (writeWord8)
18−
19−import Control.Monad.Trans
20−
21−import qualified Data.ByteString as BS
22−import Data.IORef
23−import qualified Data.String.UTF8 as UTF8
24−
25−type MockData = IORef (UTF8.UTF8 BS.ByteString)
26−
27−-- | The mock display terminal produces a string representation of
28−-- the requested picture. There is *not* an isomorphism between the
29−-- string representation and the picture. The string representation is
30−-- a simplification of the picture that is only useful in debugging VTY
31−-- without considering terminal specific issues.
32−--
33−-- The mock implementation is useful in manually determining if the
34−-- sequence of terminal operations matches the expected sequence. The
35−-- requirement of the produced representation is simplicity in parsing
36−-- the text representation and determining how the picture was mapped to
37−-- terminal operations.
38−--
39−-- The string representation is a sequence of identifiers where each
40−-- identifier is the name of an operation in the algebra.
41−mockTerminal :: (Applicative m, MonadIO m) => DisplayRegion -> m (MockData, Output)
42−mockTerminal r = liftIO $ do
43− outRef <- newIORef undefined
44− newAssumedStateRef <- newIORef initialAssumedState
45− let t = Output
46− { terminalID = "mock terminal"
47− , releaseTerminal = return ()
48− , reserveDisplay = return ()
49− , releaseDisplay = return ()
50− , ringTerminalBell = return ()
51− , supportsBell = return False
52− , supportsItalics = return False
53− , supportsStrikethrough = return False
54− , setDisplayBounds = const $ return ()
55− , displayBounds = return r
56− , outputByteBuffer = \bytes -> do
57− putStrLn $ "mock outputByteBuffer of " ++ show (BS.length bytes) ++ " bytes"
58− writeIORef outRef $ UTF8.fromRep bytes
59− , supportsCursorVisibility = True
60− , supportsMode = const False
61− , setMode = const $ const $ return ()
62− , getModeStatus = const $ return False
63− , assumedStateRef = newAssumedStateRef
64− , outputColorMode = ColorMode16
65− , mkDisplayContext = \tActual rActual -> return $ DisplayContext
66− { contextRegion = rActual
67− , contextDevice = tActual
68− -- A cursor move is always visualized as the single
69− -- character 'M'
70− , writeMoveCursor = \_x _y -> writeWord8 $ toEnum $ fromEnum 'M'
71− -- Show cursor is always visualized as the single
72− -- character 'S'
73− , writeShowCursor = writeWord8 $ toEnum $ fromEnum 'S'
74− -- Hide cursor is always visualized as the single
75− -- character 'H'
76− , writeHideCursor = writeWord8 $ toEnum $ fromEnum 'H'
77− -- An attr change is always visualized as the single
78− -- character 'A'
79− , writeSetAttr = \_ _fattr _diffs _attr -> writeWord8 $ toEnum $ fromEnum 'A'
80− -- default attr is always visualized as the single
81− -- character 'D'
82− , writeDefaultAttr = const $ writeWord8 $ toEnum $ fromEnum 'D'
83− -- row end is always visualized as the single character
84− -- 'E'
85− , writeRowEnd = writeWord8 $ toEnum $ fromEnum 'E'
86− , inlineHack = return ()
87− }
88− }
89− return (outRef, t)
90−
src/Graphics/Vty/Output/TerminfoBased.hsdeleted−606
@@ -1,606 +0,0 @@
1−{-# LANGUAGE FlexibleInstances #-}
2−{-# LANGUAGE OverloadedStrings #-}
3−{-# LANGUAGE CPP #-}
4−{-# OPTIONS_GHC -D_XOPEN_SOURCE=500 -fno-warn-warnings-deprecations #-}
5−{-# CFILES gwinsz.c #-}
6−
7−-- | Terminfo-based terminal output driver.
8−--
9−-- Copyright Corey O'Connor (coreyoconnor@gmail.com)
10−module Graphics.Vty.Output.TerminfoBased
11− ( reserveTerminal
12− , setWindowSize
13− )
14−where
15−
16−import Control.Monad (when)
17−import Data.Bits (shiftL, (.&.))
18−import qualified Data.ByteString as BS
19−import Data.ByteString.Internal (toForeignPtr)
20−import Data.Terminfo.Parse
21−import Data.Terminfo.Eval
22−
23−import Graphics.Vty.Attributes
24−import Graphics.Vty.Image (DisplayRegion)
25−import Graphics.Vty.DisplayAttributes
26−import Graphics.Vty.Output.Interface
27−
28−import Blaze.ByteString.Builder (Write, writeToByteString, writeStorable, writeWord8)
29−
30−import Data.IORef
31−import Data.Maybe (isJust, isNothing, fromJust)
32−import Data.Word
33−
34−#if !MIN_VERSION_base(4,8,0)
35−import Data.Foldable (foldMap)
36−#endif
37−
38−import Foreign.C.Types ( CInt(..), CLong(..) )
39−import Foreign.ForeignPtr (withForeignPtr)
40−import Foreign.Ptr (Ptr, plusPtr)
41−
42−import qualified System.Console.Terminfo as Terminfo
43−import System.Posix.IO (fdWriteBuf)
44−import System.Posix.Types (Fd(..))
45−
46−data TerminfoCaps = TerminfoCaps
47− { smcup :: Maybe CapExpression
48− , rmcup :: Maybe CapExpression
49− , cup :: CapExpression
50− , cnorm :: Maybe CapExpression
51− , civis :: Maybe CapExpression
52− , useAltColorMap :: Bool
53− , setForeColor :: CapExpression
54− , setBackColor :: CapExpression
55− , setDefaultAttr :: CapExpression
56− , clearScreen :: CapExpression
57− , clearEol :: CapExpression
58− , displayAttrCaps :: DisplayAttrCaps
59− , ringBellAudio :: Maybe CapExpression
60− }
61−
62−data DisplayAttrCaps = DisplayAttrCaps
63− { setAttrStates :: Maybe CapExpression
64− , enterStandout :: Maybe CapExpression
65− , exitStandout :: Maybe CapExpression
66− , enterItalic :: Maybe CapExpression
67− , exitItalic :: Maybe CapExpression
68− , enterStrikethrough :: Maybe CapExpression
69− , exitStrikethrough :: Maybe CapExpression
70− , enterUnderline :: Maybe CapExpression
71− , exitUnderline :: Maybe CapExpression
72− , enterReverseVideo :: Maybe CapExpression
73− , enterDimMode :: Maybe CapExpression
74− , enterBoldMode :: Maybe CapExpression
75− }
76−
77−-- kinda like:
78−-- https://code.google.com/p/vim/source/browse/src/fileio.c#10422
79−-- fdWriteBuf will throw on error. Unless the error is EINTR. On EINTR
80−-- the write will be retried.
81−fdWriteAll :: Fd -> Ptr Word8 -> Int -> Int -> IO Int
82−fdWriteAll outFd ptr len count
83− | len < 0 = fail "fdWriteAll: len is less than 0"
84− | len == 0 = return count
85− | otherwise = do
86− writeCount <- fromEnum <$> fdWriteBuf outFd ptr (toEnum len)
87− let len' = len - writeCount
88− ptr' = ptr `plusPtr` writeCount
89− count' = count + writeCount
90− fdWriteAll outFd ptr' len' count'
91−
92−sendCapToTerminal :: Output -> CapExpression -> [CapParam] -> IO ()
93−sendCapToTerminal t cap capParams = do
94− outputByteBuffer t $ writeToByteString $ writeCapExpr cap capParams
95−
96−-- | Constructs an output driver that uses terminfo for all control
97−-- codes. While this should provide the most compatible terminal,
98−-- terminfo does not support some features that would increase
99−-- efficiency and improve compatibility:
100−--
101−-- * determining the character encoding supported by the terminal.
102−-- Should this be taken from the LANG environment variable?
103−--
104−-- * Providing independent string capabilities for all display
105−-- attributes.
106−reserveTerminal :: String -> Fd -> ColorMode -> IO Output
107−reserveTerminal termName outFd colorMode = do
108− ti <- Terminfo.setupTerm termName
109− -- assumes set foreground always implies set background exists.
110− -- if set foreground is not set then all color changing style
111− -- attributes are filtered.
112− msetaf <- probeCap ti "setaf"
113− msetf <- probeCap ti "setf"
114− let (useAlt, setForeCap)
115− = case msetaf of
116− Just setaf -> (False, setaf)
117− Nothing -> case msetf of
118− Just setf -> (True, setf)
119− Nothing -> (True, error $ "no fore color support for terminal " ++ termName)
120− msetab <- probeCap ti "setab"
121− msetb <- probeCap ti "setb"
122− let setBackCap
123− = case msetab of
124− Just setab -> setab
125− Nothing -> case msetb of
126− Just setb -> setb
127− Nothing -> error $ "no back color support for terminal " ++ termName
128−
129− hyperlinkModeStatus <- newIORef False
130− newAssumedStateRef <- newIORef initialAssumedState
131−
132− let terminfoSetMode m newStatus = do
133− curStatus <- terminfoModeStatus m
134− when (newStatus /= curStatus) $
135− case m of
136− Hyperlink -> do
137− writeIORef hyperlinkModeStatus newStatus
138− writeIORef newAssumedStateRef initialAssumedState
139− _ -> return ()
140− terminfoModeStatus m =
141− case m of
142− Hyperlink -> readIORef hyperlinkModeStatus
143− _ -> return False
144− terminfoModeSupported Hyperlink = True
145− terminfoModeSupported _ = False
146−
147− terminfoCaps <- pure TerminfoCaps
148− <*> probeCap ti "smcup"
149− <*> probeCap ti "rmcup"
150− <*> requireCap ti "cup"
151− <*> probeCap ti "cnorm"
152− <*> probeCap ti "civis"
153− <*> pure useAlt
154− <*> pure setForeCap
155− <*> pure setBackCap
156− <*> requireCap ti "sgr0"
157− <*> requireCap ti "clear"
158− <*> requireCap ti "el"
159− <*> currentDisplayAttrCaps ti
160− <*> probeCap ti "bel"
161− let t = Output
162− { terminalID = termName
163− , releaseTerminal = do
164− sendCap setDefaultAttr []
165− maybeSendCap cnorm []
166− , supportsBell = return $ isJust $ ringBellAudio terminfoCaps
167− , supportsItalics = return $ (isJust $ enterItalic (displayAttrCaps terminfoCaps)) &&
168− (isJust $ exitItalic (displayAttrCaps terminfoCaps))
169− , supportsStrikethrough = return $ (isJust $ enterStrikethrough (displayAttrCaps terminfoCaps)) &&
170− (isJust $ exitStrikethrough (displayAttrCaps terminfoCaps))
171− , ringTerminalBell = maybeSendCap ringBellAudio []
172− , reserveDisplay = do
173− -- If there is no support for smcup: Clear the screen
174− -- and then move the mouse to the home position to
175− -- approximate the behavior.
176− maybeSendCap smcup []
177− sendCap clearScreen []
178− , releaseDisplay = do
179− maybeSendCap rmcup []
180− maybeSendCap cnorm []
181− , setDisplayBounds = \(w, h) ->
182− setWindowSize outFd (w, h)
183− , displayBounds = do
184− rawSize <- getWindowSize outFd
185− case rawSize of
186− (w, h) | w < 0 || h < 0 -> fail $ "getwinsize returned < 0 : " ++ show rawSize
187− | otherwise -> return (w,h)
188− , outputByteBuffer = \outBytes -> do
189− let (fptr, offset, len) = toForeignPtr outBytes
190− actualLen <- withForeignPtr fptr
191− $ \ptr -> fdWriteAll outFd (ptr `plusPtr` offset) len 0
192− when (toEnum len /= actualLen) $ fail $ "Graphics.Vty.Output: outputByteBuffer "
193− ++ "length mismatch. " ++ show len ++ " /= " ++ show actualLen
194− ++ " Please report this bug to vty project."
195− , supportsCursorVisibility = isJust $ civis terminfoCaps
196− , supportsMode = terminfoModeSupported
197− , setMode = terminfoSetMode
198− , getModeStatus = terminfoModeStatus
199− , assumedStateRef = newAssumedStateRef
200− , outputColorMode = colorMode
201− -- I think fix would help assure tActual is the only
202− -- reference. I was having issues tho.
203− , mkDisplayContext = (`terminfoDisplayContext` terminfoCaps)
204− }
205− sendCap s = sendCapToTerminal t (s terminfoCaps)
206− maybeSendCap s = when (isJust $ s terminfoCaps) . sendCap (fromJust . s)
207− return t
208−
209−requireCap :: Terminfo.Terminal -> String -> IO CapExpression
210−requireCap ti capName
211− = case Terminfo.getCapability ti (Terminfo.tiGetStr capName) of
212− Nothing -> fail $ "Terminal does not define required capability \"" ++ capName ++ "\""
213− Just capStr -> parseCap capStr
214−
215−probeCap :: Terminfo.Terminal -> String -> IO (Maybe CapExpression)
216−probeCap ti capName
217− = case Terminfo.getCapability ti (Terminfo.tiGetStr capName) of
218− Nothing -> return Nothing
219− Just capStr -> Just <$> parseCap capStr
220−
221−parseCap :: String -> IO CapExpression
222−parseCap capStr = do
223− case parseCapExpression capStr of
224− Left e -> fail $ show e
225− Right cap -> return cap
226−
227−currentDisplayAttrCaps :: Terminfo.Terminal -> IO DisplayAttrCaps
228−currentDisplayAttrCaps ti
229− = pure DisplayAttrCaps
230− <*> probeCap ti "sgr"
231− <*> probeCap ti "smso"
232− <*> probeCap ti "rmso"
233− <*> probeCap ti "sitm"
234− <*> probeCap ti "ritm"
235− <*> probeCap ti "smxx"
236− <*> probeCap ti "rmxx"
237− <*> probeCap ti "smul"
238− <*> probeCap ti "rmul"
239− <*> probeCap ti "rev"
240− <*> probeCap ti "dim"
241− <*> probeCap ti "bold"
242−
243−foreign import ccall "gwinsz.h vty_c_get_window_size" c_getWindowSize :: Fd -> IO CLong
244−
245−getWindowSize :: Fd -> IO (Int,Int)
246−getWindowSize fd = do
247− (a,b) <- (`divMod` 65536) `fmap` c_getWindowSize fd
248− return (fromIntegral b, fromIntegral a)
249−
250−foreign import ccall "gwinsz.h vty_c_set_window_size" c_setWindowSize :: Fd -> CLong -> IO ()
251−
252−setWindowSize :: Fd -> (Int, Int) -> IO ()
253−setWindowSize fd (w, h) = do
254− let val = (h `shiftL` 16) + w
255− c_setWindowSize fd $ fromIntegral val
256−
257−terminfoDisplayContext :: Output -> TerminfoCaps -> DisplayRegion -> IO DisplayContext
258−terminfoDisplayContext tActual terminfoCaps r = return dc
259− where dc = DisplayContext
260− { contextDevice = tActual
261− , contextRegion = r
262− , writeMoveCursor = \x y -> writeCapExpr (cup terminfoCaps) [toEnum y, toEnum x]
263− , writeShowCursor = case cnorm terminfoCaps of
264− Nothing -> error "this terminal does not support show cursor"
265− Just c -> writeCapExpr c []
266− , writeHideCursor = case civis terminfoCaps of
267− Nothing -> error "this terminal does not support hide cursor"
268− Just c -> writeCapExpr c []
269− , writeSetAttr = terminfoWriteSetAttr dc terminfoCaps
270− , writeDefaultAttr = \urlsEnabled ->
271− writeCapExpr (setDefaultAttr terminfoCaps) [] `mappend`
272− (if urlsEnabled then writeURLEscapes EndLink else mempty) `mappend`
273− (case exitStrikethrough $ displayAttrCaps terminfoCaps of
274− Just cap -> writeCapExpr cap []
275− Nothing -> mempty
276− )
277− , writeRowEnd = writeCapExpr (clearEol terminfoCaps) []
278− , inlineHack = return ()
279− }
280−
281−-- | Write the escape sequences that are used in some terminals to
282−-- include embedded hyperlinks. As of yet, this information isn't
283−-- included in termcap or terminfo, so this writes them directly
284−-- instead of looking up the appropriate capabilities.
285−writeURLEscapes :: URLDiff -> Write
286−writeURLEscapes (LinkTo url) =
287− foldMap writeStorable (BS.unpack "\x1b]8;;") `mappend`
288− foldMap writeStorable (BS.unpack url) `mappend`
289− writeStorable (0x07 :: Word8)
290−writeURLEscapes EndLink =
291− foldMap writeStorable (BS.unpack "\x1b]8;;\a")
292−writeURLEscapes NoLinkChange =
293− mempty
294−
295−-- | Portably setting the display attributes is a giant pain in the ass.
296−--
297−-- If the terminal supports the sgr capability (which sets the on/off
298−-- state of each style directly; and, for no good reason, resets the
299−-- colors to the default) this procedure is used:
300−--
301−-- 0. set the style attributes. This resets the fore and back color.
302−--
303−-- 1, If a foreground color is to be set then set the foreground color
304−--
305−-- 2. likewise with the background color
306−--
307−-- If the terminal does not support the sgr cap then: if there is a
308−-- change from an applied color to the default (in either the fore or
309−-- back color) then:
310−--
311−-- 0. reset all display attributes (sgr0)
312−--
313−-- 1. enter required style modes
314−--
315−-- 2. set the fore color if required
316−--
317−-- 3. set the back color if required
318−--
319−-- Entering the required style modes could require a reset of the
320−-- display attributes. If this is the case then the back and fore colors
321−-- always need to be set if not default.
322−--
323−-- This equation implements the above logic.
324−--
325−-- Note that this assumes the removal of color changes in the
326−-- display attributes is done as expected with noColors == True. See
327−-- `limitAttrForDisplay`.
328−--
329−-- Note that this optimizes for fewer state changes followed by fewer
330−-- bytes.
331−terminfoWriteSetAttr :: DisplayContext -> TerminfoCaps -> Bool -> FixedAttr -> Attr -> DisplayAttrDiff -> Write
332−terminfoWriteSetAttr dc terminfoCaps urlsEnabled prevAttr reqAttr diffs =
333− urlAttrs urlsEnabled `mappend` case (foreColorDiff diffs == ColorToDefault) || (backColorDiff diffs == ColorToDefault) of
334− -- The only way to reset either color, portably, to the default
335− -- is to use either the set state capability or the set default
336− -- capability.
337− True -> do
338− case reqDisplayCapSeqFor (displayAttrCaps terminfoCaps)
339− (fixedStyle attr)
340− (styleToApplySeq $ fixedStyle attr) of
341− -- only way to reset a color to the defaults
342− EnterExitSeq caps -> writeDefaultAttr dc urlsEnabled
343− `mappend`
344− foldMap (\cap -> writeCapExpr cap []) caps
345− `mappend`
346− setColors
347− -- implicitly resets the colors to the defaults
348− SetState state -> writeCapExpr (fromJust $ setAttrStates
349− $ displayAttrCaps terminfoCaps
350− )
351− (sgrArgsForState state)
352− `mappend` setItalics
353− `mappend` setStrikethrough
354− `mappend` setColors
355− -- Otherwise the display colors are not changing or changing
356− -- between two non-default points.
357− False -> do
358− -- Still, it could be the case that the change in display
359− -- attributes requires the colors to be reset because the
360− -- required capability was not available.
361− case reqDisplayCapSeqFor (displayAttrCaps terminfoCaps)
362− (fixedStyle attr)
363− (styleDiffs diffs) of
364− -- Really, if terminals were re-implemented with modern
365− -- concepts instead of bowing down to 40 yr old dumb
366− -- terminal requirements this would be the only case
367− -- ever reached! Changes the style and color states
368− -- according to the differences with the currently
369− -- applied states.
370− EnterExitSeq caps -> foldMap (\cap -> writeCapExpr cap []) caps
371− `mappend`
372− writeColorDiff Foreground (foreColorDiff diffs)
373− `mappend`
374− writeColorDiff Background (backColorDiff diffs)
375− -- implicitly resets the colors to the defaults
376− SetState state -> writeCapExpr (fromJust $ setAttrStates
377− $ displayAttrCaps terminfoCaps
378− )
379− (sgrArgsForState state)
380− `mappend` setItalics
381− `mappend` setStrikethrough
382− `mappend` setColors
383− where
384− urlAttrs True = writeURLEscapes (urlDiff diffs)
385− urlAttrs False = mempty
386− colorMap = case useAltColorMap terminfoCaps of
387− False -> ansiColorIndex
388− True -> altColorIndex
389− attr = fixDisplayAttr prevAttr reqAttr
390−
391− -- italics can't be set via SGR, so here we manually
392− -- apply the enter and exit sequences as needed after
393− -- changing the SGR
394− setItalics
395− | hasStyle (fixedStyle attr) italic
396− , Just sitm <- enterItalic (displayAttrCaps terminfoCaps)
397− = writeCapExpr sitm []
398− | otherwise = mempty
399− setStrikethrough
400− | hasStyle (fixedStyle attr) strikethrough
401− , Just smxx <- enterStrikethrough (displayAttrCaps terminfoCaps)
402− = writeCapExpr smxx []
403− | otherwise = mempty
404− setColors =
405− (case fixedForeColor attr of
406− Just c -> writeColor Foreground c
407− Nothing -> mempty)
408− `mappend`
409− (case fixedBackColor attr of
410− Just c -> writeColor Background c
411− Nothing -> mempty)
412− writeColorDiff _side NoColorChange
413− = mempty
414− writeColorDiff _side ColorToDefault
415− = error "ColorToDefault is not a possible case for applyColorDiffs"
416− writeColorDiff side (SetColor c)
417− = writeColor side c
418−
419− writeColor side (RGBColor r g b) =
420− case outputColorMode (contextDevice dc) of
421− FullColor ->
422− hardcodeColor side (r, g, b)
423− _ ->
424− error "clampColor should remove rgb colors in standard mode"
425− writeColor side c =
426− writeCapExpr (setSideColor side terminfoCaps) [toEnum $ colorMap c]
427−
428−-- a color can either be in the foreground or the background
429−data ColorSide = Foreground | Background
430−
431−-- get the capability for drawing a color on a specific side
432−setSideColor :: ColorSide -> TerminfoCaps -> CapExpression
433−setSideColor Foreground = setForeColor
434−setSideColor Background = setBackColor
435−
436−hardcodeColor :: ColorSide -> (Word8, Word8, Word8) -> Write
437−hardcodeColor side (r, g, b) =
438− -- hardcoded color codes are formatted as "\x1b[{side};2;{r};{g};{b}m"
439− mconcat [ writeStr "\x1b[", sideCode, delimiter, writeChar '2', delimiter
440− , writeColor r, delimiter, writeColor g, delimiter, writeColor b
441− , writeChar 'm']
442− where
443− writeChar = writeWord8 . fromIntegral . fromEnum
444− writeStr = mconcat . map writeChar
445− writeColor = writeStr . show
446− delimiter = writeChar ';'
447− -- 38/48 are used to set whether we should write to the
448− -- foreground/background. I really don't want to know why.
449− sideCode = case side of
450− Foreground -> writeStr "38"
451− Background -> writeStr "48"
452−
453−-- | The color table used by a terminal is a 16 color set followed by a
454−-- 240 color set that might not be supported by the terminal.
455−--
456−-- This takes a Color which clearly identifies which palette to use and
457−-- computes the index into the full 256 color palette.
458−ansiColorIndex :: Color -> Int
459−ansiColorIndex (ISOColor v) = fromEnum v
460−ansiColorIndex (Color240 v) = 16 + fromEnum v
461−ansiColorIndex (RGBColor _ _ _) =
462− error $ unlines [ "Attempted to create color index from rgb color."
463− , "This is currently unsupported, and shouldn't ever happen"
464− ]
465−
466−-- | For terminals without setaf/setab
467−--
468−-- See table in `man terminfo`
469−-- Will error if not in table.
470−altColorIndex :: Color -> Int
471−altColorIndex (ISOColor 0) = 0
472−altColorIndex (ISOColor 1) = 4
473−altColorIndex (ISOColor 2) = 2
474−altColorIndex (ISOColor 3) = 6
475−altColorIndex (ISOColor 4) = 1
476−altColorIndex (ISOColor 5) = 5
477−altColorIndex (ISOColor 6) = 3
478−altColorIndex (ISOColor 7) = 7
479−altColorIndex (ISOColor v) = fromEnum v
480−altColorIndex (Color240 v) = 16 + fromEnum v
481−altColorIndex (RGBColor _ _ _) =
482− error $ unlines [ "Attempted to create color index from rgb color."
483− , "This is currently unsupported, and shouldn't ever happen"
484− ]
485−
486−{- | The sequence of terminfo caps to apply a given style are determined
487− - according to these rules.
488− -
489− - 1. The assumption is that it's preferable to use the simpler
490− - enter/exit mode capabilities than the full set display attribute
491− - state capability.
492− -
493− - 2. If a mode is supposed to be removed but there is not an exit
494− - capability defined then the display attributes are reset to defaults
495− - then the display attribute state is set.
496− -
497− - 3. If a mode is supposed to be applied but there is not an enter
498− - capability defined then then display attribute state is set if
499− - possible. Otherwise the mode is not applied.
500− -
501− - 4. If the display attribute state is being set then just update the
502− - arguments to that for any apply/remove.
503− -}
504−data DisplayAttrSeq
505− = EnterExitSeq [CapExpression]
506− | SetState DisplayAttrState
507−
508−data DisplayAttrState = DisplayAttrState
509− { applyStandout :: Bool
510− , applyUnderline :: Bool
511− , applyItalic :: Bool
512− , applyStrikethrough :: Bool
513− , applyReverseVideo :: Bool
514− , applyBlink :: Bool
515− , applyDim :: Bool
516− , applyBold :: Bool
517− }
518−
519−sgrArgsForState :: DisplayAttrState -> [CapParam]
520−sgrArgsForState attrState = map (\b -> if b then 1 else 0)
521− [ applyStandout attrState
522− , applyUnderline attrState
523− , applyReverseVideo attrState
524− , applyBlink attrState
525− , applyDim attrState
526− , applyBold attrState
527− , False -- invis
528− , False -- protect
529− , False -- alt char set
530− ]
531−
532−reqDisplayCapSeqFor :: DisplayAttrCaps -> Style -> [StyleStateChange] -> DisplayAttrSeq
533−reqDisplayCapSeqFor caps s diffs
534− -- if the state transition implied by any diff cannot be supported
535− -- with an enter/exit mode cap then either the state needs to be set
536− -- or the attribute change ignored.
537− = case (any noEnterExitCap diffs, isJust $ setAttrStates caps) of
538− -- If all the diffs have an enter-exit cap then just use those
539− ( False, _ ) -> EnterExitSeq $ map enterExitCap diffs
540− -- If not all the diffs have an enter-exit cap and there is no
541− -- set state cap then filter out all unsupported diffs and just
542− -- apply the rest
543− ( True, False ) -> EnterExitSeq $ map enterExitCap
544− $ filter (not . noEnterExitCap) diffs
545− -- if not all the diffs have an enter-exit can and there is a
546− -- set state cap then just use the set state cap.
547− ( True, True ) -> SetState $ stateForStyle s
548− where
549− noEnterExitCap ApplyStrikethrough = isNothing $ enterStrikethrough caps
550− noEnterExitCap RemoveStrikethrough = isNothing $ exitStrikethrough caps
551− noEnterExitCap ApplyItalic = isNothing $ enterItalic caps
552− noEnterExitCap RemoveItalic = isNothing $ exitItalic caps
553− noEnterExitCap ApplyStandout = isNothing $ enterStandout caps
554− noEnterExitCap RemoveStandout = isNothing $ exitStandout caps
555− noEnterExitCap ApplyUnderline = isNothing $ enterUnderline caps
556− noEnterExitCap RemoveUnderline = isNothing $ exitUnderline caps
557− noEnterExitCap ApplyReverseVideo = isNothing $ enterReverseVideo caps
558− noEnterExitCap RemoveReverseVideo = True
559− noEnterExitCap ApplyBlink = True
560− noEnterExitCap RemoveBlink = True
561− noEnterExitCap ApplyDim = isNothing $ enterDimMode caps
562− noEnterExitCap RemoveDim = True
563− noEnterExitCap ApplyBold = isNothing $ enterBoldMode caps
564− noEnterExitCap RemoveBold = True
565− enterExitCap ApplyStrikethrough = fromJust $ enterStrikethrough caps
566− enterExitCap RemoveStrikethrough = fromJust $ exitStrikethrough caps
567− enterExitCap ApplyItalic = fromJust $ enterItalic caps
568− enterExitCap RemoveItalic = fromJust $ exitItalic caps
569− enterExitCap ApplyStandout = fromJust $ enterStandout caps
570− enterExitCap RemoveStandout = fromJust $ exitStandout caps
571− enterExitCap ApplyUnderline = fromJust $ enterUnderline caps
572− enterExitCap RemoveUnderline = fromJust $ exitUnderline caps
573− enterExitCap ApplyReverseVideo = fromJust $ enterReverseVideo caps
574− enterExitCap ApplyDim = fromJust $ enterDimMode caps
575− enterExitCap ApplyBold = fromJust $ enterBoldMode caps
576− enterExitCap _ = error "enterExitCap applied to diff that was known not to have one."
577−
578−stateForStyle :: Style -> DisplayAttrState
579−stateForStyle s = DisplayAttrState
580− { applyStandout = isStyleSet standout
581− , applyUnderline = isStyleSet underline
582− , applyItalic = isStyleSet italic
583− , applyStrikethrough = isStyleSet strikethrough
584− , applyReverseVideo = isStyleSet reverseVideo
585− , applyBlink = isStyleSet blink
586− , applyDim = isStyleSet dim
587− , applyBold = isStyleSet bold
588− }
589− where isStyleSet = hasStyle s
590−
591−styleToApplySeq :: Style -> [StyleStateChange]
592−styleToApplySeq s = concat
593− [ applyIfRequired ApplyStandout standout
594− , applyIfRequired ApplyUnderline underline
595− , applyIfRequired ApplyItalic italic
596− , applyIfRequired ApplyStrikethrough strikethrough
597− , applyIfRequired ApplyReverseVideo reverseVideo
598− , applyIfRequired ApplyBlink blink
599− , applyIfRequired ApplyDim dim
600− , applyIfRequired ApplyBold bold
601− ]
602− where
603− applyIfRequired op flag
604− = if 0 == (flag .&. s)
605− then []
606− else [op]
src/Graphics/Vty/Output/XTermColor.hsdeleted−131
@@ -1,131 +0,0 @@
1−{-# Language CPP #-}
2−-- Copyright 2009-2010 Corey O'Connor
3−-- | Xterm output driver. This uses the Terminfo driver with some
4−-- extensions for Xterm.
5−module Graphics.Vty.Output.XTermColor
6− ( reserveTerminal
7− )
8−where
9−
10−import Graphics.Vty.Output.Interface
11−import Graphics.Vty.Input.Mouse
12−import Graphics.Vty.Input.Focus
13−import Graphics.Vty.Attributes.Color (ColorMode)
14−import qualified Graphics.Vty.Output.TerminfoBased as TerminfoBased
15−
16−import Blaze.ByteString.Builder (writeToByteString)
17−import Blaze.ByteString.Builder.Word (writeWord8)
18−
19−import qualified Data.ByteString.Char8 as BS8
20−import Data.ByteString.Char8 (ByteString)
21−import Foreign.Ptr (castPtr)
22−
23−import Control.Monad (void, when)
24−import Control.Monad.Trans
25−import Data.Char (toLower)
26−import Data.IORef
27−
28−import System.Posix.IO (fdWriteBuf)
29−import System.Posix.Types (ByteCount, Fd)
30−import System.Posix.Env (getEnv)
31−
32−import Data.List (isInfixOf)
33−import Data.Maybe (catMaybes)
34−
35−#if !MIN_VERSION_base(4,11,0)
36−import Data.Monoid ((<>))
37−#endif
38−
39−-- | Write a 'ByteString' to an 'Fd'.
40−fdWrite :: Fd -> ByteString -> IO ByteCount
41−fdWrite fd s =
42− BS8.useAsCStringLen s $ \(buf,len) -> do
43− fdWriteBuf fd (castPtr buf) (fromIntegral len)
44−
45−-- | Construct an Xterm output driver. Initialize the display to UTF-8.
46−reserveTerminal :: ( Applicative m, MonadIO m ) => String -> Fd -> ColorMode -> m Output
47−reserveTerminal variant outFd colorMode = liftIO $ do
48− let flushedPut = void . fdWrite outFd
49− -- If the terminal variant is xterm-color use xterm instead since,
50− -- more often than not, xterm-color is broken.
51− let variant' = if variant == "xterm-color" then "xterm" else variant
52−
53− utf8a <- utf8Active
54− when (not utf8a) $ flushedPut setUtf8CharSet
55− t <- TerminfoBased.reserveTerminal variant' outFd colorMode
56−
57− mouseModeStatus <- newIORef False
58− focusModeStatus <- newIORef False
59− pasteModeStatus <- newIORef False
60−
61− let xtermSetMode t' m newStatus = do
62− curStatus <- getModeStatus t' m
63− when (newStatus /= curStatus) $
64− case m of
65− Focus -> liftIO $ do
66− case newStatus of
67− True -> flushedPut requestFocusEvents
68− False -> flushedPut disableFocusEvents
69− writeIORef focusModeStatus newStatus
70− Mouse -> liftIO $ do
71− case newStatus of
72− True -> flushedPut requestMouseEvents
73− False -> flushedPut disableMouseEvents
74− writeIORef mouseModeStatus newStatus
75− BracketedPaste -> liftIO $ do
76− case newStatus of
77− True -> flushedPut enableBracketedPastes
78− False -> flushedPut disableBracketedPastes
79− writeIORef pasteModeStatus newStatus
80− Hyperlink -> setMode t Hyperlink newStatus
81−
82− xtermGetMode Mouse = liftIO $ readIORef mouseModeStatus
83− xtermGetMode Focus = liftIO $ readIORef focusModeStatus
84− xtermGetMode BracketedPaste = liftIO $ readIORef pasteModeStatus
85− xtermGetMode Hyperlink = getModeStatus t Hyperlink
86−
87− let t' = t
88− { terminalID = terminalID t ++ " (xterm-color)"
89− , releaseTerminal = do
90− when (not utf8a) $ liftIO $ flushedPut setDefaultCharSet
91− setMode t' BracketedPaste False
92− setMode t' Mouse False
93− setMode t' Focus False
94− releaseTerminal t
95− , mkDisplayContext = \tActual r -> do
96− dc <- mkDisplayContext t tActual r
97− return $ dc { inlineHack = xtermInlineHack t' }
98− , supportsMode = const True
99− , getModeStatus = xtermGetMode
100− , setMode = xtermSetMode t'
101− }
102− return t'
103−
104−utf8Active :: IO Bool
105−utf8Active = do
106− let vars = ["LC_ALL", "LANG", "LC_CTYPE"]
107− results <- map (toLower <$>) . catMaybes <$> mapM getEnv vars
108− let matches = filter ("utf8" `isInfixOf`) results <>
109− filter ("utf-8" `isInfixOf`) results
110− return $ not $ null matches
111−
112−-- | Enable bracketed paste mode:
113−-- http://cirw.in/blog/bracketed-paste
114−enableBracketedPastes :: ByteString
115−enableBracketedPastes = BS8.pack "\ESC[?2004h"
116−
117−-- | Disable bracketed paste mode:
118−disableBracketedPastes :: ByteString
119−disableBracketedPastes = BS8.pack "\ESC[?2004l"
120−
121−-- | These sequences set xterm based terminals to UTF-8 output.
122−--
123−-- There is no known terminfo capability equivalent to this.
124−setUtf8CharSet, setDefaultCharSet :: ByteString
125−setUtf8CharSet = BS8.pack "\ESC%G"
126−setDefaultCharSet = BS8.pack "\ESC%@"
127−
128−xtermInlineHack :: Output -> IO ()
129−xtermInlineHack t = do
130− let writeReset = foldMap (writeWord8.toEnum.fromEnum) "\ESC[K"
131− outputByteBuffer t $ writeToByteString writeReset
src/Graphics/Vty/Picture.hs+57−116
@@ -1,131 +1,72 @@
1−-- A 'Picture' is a background paired with a set of 'Image' layers. The
2−-- 'Picture' data structure is representative of the final terminal
3−-- view.
4−module Graphics.Vty.Picture
5− ( Picture(..)
6− , Cursor(..)
7− , Background(..)
8− , emptyPicture
9− , addToTop
10− , addToBottom
11− , picForImage
12− , picForLayers
13− , picImage
14− )
15−where
1+-- Copyright 2009 Corey O'Connor
2+module Graphics.Vty.Picture ( module Graphics.Vty.Picture
3+ , Image
4+ , image_width
5+ , image_height
6+ , (<|>)
7+ , (<->)
8+ , horiz_cat
9+ , vert_cat
10+ , background_fill
11+ , char
12+ , string
13+ , iso_10646_string
14+ , utf8_string
15+ , utf8_bytestring
16+ , char_fill
17+ , empty_image
18+ , translate
19+ -- | The possible display attributes used in constructing an `Image`.
20+ , module Graphics.Vty.Attributes
21+ )
22+ where
1623
17−import Graphics.Vty.Image
1824import Graphics.Vty.Attributes
25+import Graphics.Vty.Image hiding ( attr )
1926
20−import Control.DeepSeq
27+import Data.Word
2128
22−-- | A Vty picture.
23−--
24−-- These can be constructed directly or using `picForImage`.
29+-- |The type of images to be displayed using 'update'.
30+-- Can be constructed directly or using `pic_for_image`. Which provides an initial instance with
31+-- reasonable defaults for pic_cursor and pic_background.
2532data Picture = Picture
26− { picCursor :: Cursor
27− -- ^ The picture's cursor.
28− , picLayers :: [Image]
29− -- ^ The picture's image layers (top-most first).
30− , picBackground :: Background
31− -- ^ The picture's background to be displayed in locations with no
32− -- Image data.
33− } deriving Eq
34−
35−instance Show Picture where
36− show (Picture _ layers _ ) = "Picture ?? " ++ show layers ++ " ??"
37−
38−instance NFData Picture where
39− rnf (Picture c l b) = c `deepseq` l `deepseq` b `deepseq` ()
40−
41−-- | A picture with no cursor, background or image layers.
42−emptyPicture :: Picture
43−emptyPicture = Picture NoCursor [] ClearBackground
44−
45−-- | Add an 'Image' as the top-most layer of a 'Picture'.
46−addToTop :: Picture -> Image -> Picture
47−addToTop p i = p {picLayers = i : picLayers p}
48−
49−-- | Add an 'Image' as the bottom-most layer of a 'Picture'.
50−addToBottom :: Picture -> Image -> Picture
51−addToBottom p i = p {picLayers = picLayers p ++ [i]}
52−
53−-- | Create a picture from the given image. The picture will not have a
54−-- displayed cursor and no background pattern (ClearBackground) will be
55−-- used.
56−picForImage :: Image -> Picture
57−picForImage i = Picture
58− { picCursor = NoCursor
59− , picLayers = [i]
60− , picBackground = ClearBackground
33+ { pic_cursor :: Cursor
34+ , pic_image :: Image
35+ , pic_background :: Background
6136 }
6237
63−-- | Create a picture with the given layers, top-most first.
64−--
65−-- The picture will not have a displayed cursor and no background
66−-- pattern (ClearBackgroun) will be used.
67−picForLayers :: [Image] -> Picture
68−picForLayers is = Picture
69− { picCursor = NoCursor
70− , picLayers = is
71− , picBackground = ClearBackground
38+-- | Create a picture for display for the given image. The picture will not have a displayed cursor
39+-- and the background display attribute will be `current_attr`.
40+pic_for_image :: Image -> Picture
41+pic_for_image i = Picture
42+ { pic_cursor = NoCursor
43+ , pic_image = i
44+ , pic_background = Background ' ' current_attr
7245 }
7346
74−-- | A picture can be configured to hide the cursor or to show the
75−-- cursor at the specified character position.
47+-- | A picture can be configured either to not show the cursor or show the cursor at the specified
48+-- character position.
7649--
77−-- There is not a 1:1 map from character positions to a row and column
78−-- on the screen due to characters that take more than 1 column.
79−data Cursor =
80− -- | Hide the cursor
81− NoCursor
82− -- | Set the terminal's cursor position without displaying a cursor
83− -- character. This is important for accessibility with screen
84− -- readers where a cursor position needs to be reported but we may
85− -- not want to show a block cursor in that location for cosmetic
86− -- reasons. The boolean argument indicates whether the positioning
87− -- should be absolute as with 'AbsoluteCursor' ('True') or logical
88− -- as with 'Cursor' ('False').
89− | PositionOnly !Bool !Int !Int
90− -- | Show the cursor at the given logical column accounting for
91− -- character width in the presence of multi-column characters.
92− | Cursor !Int !Int
93− -- | Show the cursor at the given absolute terminal column and row
94− | AbsoluteCursor !Int !Int
95− deriving Eq
96−
97−instance NFData Cursor where
98− rnf c = c `seq` ()
50+-- There is not a 1 to 1 map from character positions to a row and column on the screen due to
51+-- characters that take more than 1 column.
52+--
53+-- todo: The Cursor can be given a (character,row) offset outside of the visible bounds of the
54+-- output region. In this case the cursor will not be shown.
55+data Cursor =
56+ NoCursor
57+ | Cursor Word Word
9958
100−-- | A 'Picture' has a background pattern. The background is either:
59+-- | Unspecified regions are filled with the picture's background pattern. The background pattern
60+-- can specify a character and a display attribute. If the display attribute used previously should
61+-- be used for a background fill then use `current_attr` for the background attribute. This is the
62+-- default background display attribute.
10163--
102−-- * ClearBackground, which shows the layer below or is blank if the
103−-- bottom layer
104−-- * A character and a display attribute
64+-- (tofix) The current attribute is always set to the default attributes at the start of updating the
65+-- screen to a picture.
10566--
106−-- If the display attribute used previously should be used for a
107−-- background fill then use `currentAttr` for the background attribute.
108−data Background
109− = Background
110− { backgroundChar :: Char
111− , backgroundAttr :: Attr
67+-- (tofix) The background character *must* occupy a single column and no more.
68+data Background = Background
69+ { background_char :: Char
70+ , background_attr :: Attr
11271 }
113− -- | A ClearBackground is:
114− --
115− -- * the space character if there are remaining non-skip ops
116− --
117− -- * End of line if there are no remaining non-skip ops.
118− | ClearBackground
119− deriving Eq
120−
121−instance NFData Background where
122− rnf (Background c a) = c `seq` a `seq` ()
123− rnf ClearBackground = ()
12472
125−-- | Return the top-most 'Image' layer for a picture. This is unsafe for
126−-- 'Picture's without at least one layer.
127−--
128−-- This is provided for compatibility with applications that do not use
129−-- more than a single layer.
130−picImage :: Picture -> Image
131−picImage = head . picLayers
src/Graphics/Vty/PictureToSpans.hs+3−344
@@ -1,348 +1,7 @@
1−-- Copyright Corey O'Connor<coreyoconnor@gmail.com>
2−{-# LANGUAGE BangPatterns #-}
3−{-# LANGUAGE ExistentialQuantification #-}
4−{-# LANGUAGE NamedFieldPuns #-}
5−{-# LANGUAGE RankNTypes #-}
6−{-# LANGUAGE TemplateHaskell #-}
7−
8−-- | Transforms an image into rows of operations.
9−module Graphics.Vty.PictureToSpans where
1+-- Copyright 2009 Corey O'Connor
2+module Graphics.Vty.PictureToSpans
3+ where
104
11−import Graphics.Vty.Attributes (Attr, currentAttr)
12−import Graphics.Vty.Image
13−import Graphics.Vty.Image.Internal
145import Graphics.Vty.Picture
156import Graphics.Vty.Span
167
17−import Lens.Micro
18−import Lens.Micro.Mtl
19−import Lens.Micro.TH
20−import Control.Monad.Reader
21−import Control.Monad.State.Strict hiding ( state )
22−import Control.Monad.ST.Strict
23−
24−import qualified Data.Vector as Vector hiding ( take, replicate )
25−import Data.Vector.Mutable ( MVector(..))
26−import qualified Data.Vector.Mutable as MVector
27−
28−import qualified Data.Text.Lazy as TL
29−
30−type MRowOps s = MVector s SpanOps
31−
32−type MSpanOps s = MVector s SpanOp
33−
34−-- transform plus clip. More or less.
35−data BlitState = BlitState
36− -- we always snoc to the operation vectors. Thus the columnOffset =
37− -- length of row at rowOffset although, one possibility is to merge
38− -- layers right in snocOp (naming it something else, of course). In
39− -- which case columnnOffset would be applicable. Right now we need
40− -- it to exist.
41− { _columnOffset :: Int
42− , _rowOffset :: Int
43− -- clip coordinate space is in image space. Which means it's >= 0
44− -- and < imageWidth.
45− , _skipColumns :: Int
46− -- >= 0 and < imageHeight
47− , _skipRows :: Int
48− -- includes consideration of skipColumns. In display space. The
49− -- number of columns from the next column to be defined to the end
50− -- of the display for the row.
51− , _remainingColumns :: Int
52− -- includes consideration of skipRows. In display space.
53− , _remainingRows :: Int
54− }
55−
56−makeLenses ''BlitState
57−
58−data BlitEnv s = BlitEnv
59− { _region :: DisplayRegion
60− , _mrowOps :: MRowOps s
61− }
62−
63−makeLenses ''BlitEnv
64−
65−type BlitM s a = ReaderT (BlitEnv s) (StateT BlitState (ST s)) a
66−
67−-- | Produces the span ops that will render the given picture, possibly
68−-- cropped or padded, into the specified region.
69−displayOpsForPic :: Picture -> DisplayRegion -> DisplayOps
70−displayOpsForPic pic r = Vector.create (combinedOpsForLayers pic r)
71−
72−-- | Returns the DisplayOps for an image rendered to a window the size
73−-- of the image.
74−--
75−-- largely used only for debugging.
76−displayOpsForImage :: Image -> DisplayOps
77−displayOpsForImage i = displayOpsForPic (picForImage i) (imageWidth i, imageHeight i)
78−
79−-- | Produces the span ops for each layer then combines them.
80−combinedOpsForLayers :: Picture -> DisplayRegion -> ST s (MRowOps s)
81−combinedOpsForLayers pic r
82− | regionWidth r == 0 || regionHeight r == 0 = MVector.new 0
83− | otherwise = do
84− layerOps <- mapM (`buildSpans` r) (picLayers pic)
85− case layerOps of
86− [] -> fail "empty picture"
87− [ops] -> substituteSkips (picBackground pic) ops
88− -- instead of merging ops after generation the merging can
89− -- be performed as part of snocOp.
90− topOps : lowerOps -> do
91− ops <- foldM mergeUnder topOps lowerOps
92− substituteSkips (picBackground pic) ops
93−
94−substituteSkips :: Background -> MRowOps s -> ST s (MRowOps s)
95−substituteSkips ClearBackground ops = do
96− forM_ [0 .. MVector.length ops - 1] $ \row -> do
97− rowOps <- MVector.read ops row
98− -- the image operations assure that background fills are
99− -- combined. clipping a background fill does not split the
100− -- background fill. merging of image layers can split a skip,
101− -- but only by the insertion of a non skip. all this combines to
102− -- mean we can check the last operation and remove it if it's a
103− -- skip
104− let rowOps' = case Vector.last rowOps of
105− Skip w -> Vector.init rowOps `Vector.snoc` RowEnd w
106− _ -> rowOps
107− -- now all the skips can be replaced by replications of ' ' of
108− -- the required width.
109− let rowOps'' = swapSkipsForSingleColumnCharSpan ' ' currentAttr rowOps'
110− MVector.write ops row rowOps''
111− return ops
112−substituteSkips (Background {backgroundChar, backgroundAttr}) ops = do
113− -- At this point we decide if the background character is single
114− -- column or not. obviously, single column is easier.
115− case safeWcwidth backgroundChar of
116− w | w == 0 -> fail $ "invalid background character " ++ show backgroundChar
117− | w == 1 -> do
118− forM_ [0 .. MVector.length ops - 1] $ \row -> do
119− rowOps <- MVector.read ops row
120− let rowOps' = swapSkipsForSingleColumnCharSpan backgroundChar backgroundAttr rowOps
121− MVector.write ops row rowOps'
122− | otherwise -> do
123− forM_ [0 .. MVector.length ops - 1] $ \row -> do
124− rowOps <- MVector.read ops row
125− let rowOps' = swapSkipsForCharSpan w backgroundChar backgroundAttr rowOps
126− MVector.write ops row rowOps'
127− return ops
128−
129−mergeUnder :: MRowOps s -> MRowOps s -> ST s (MRowOps s)
130−mergeUnder upper lower = do
131− forM_ [0 .. MVector.length upper - 1] $ \row -> do
132− upperRowOps <- MVector.read upper row
133− lowerRowOps <- MVector.read lower row
134− let rowOps = mergeRowUnder upperRowOps lowerRowOps
135− MVector.write upper row rowOps
136− return upper
137−
138−mergeRowUnder :: SpanOps -> SpanOps -> SpanOps
139−mergeRowUnder upperRowOps =
140− onUpperOp Vector.empty (Vector.head upperRowOps) (Vector.tail upperRowOps)
141− where
142− -- H: it will never be the case that we are out of upper ops
143− -- before lower ops.
144− onUpperOp :: SpanOps -> SpanOp -> SpanOps -> SpanOps -> SpanOps
145− onUpperOp outOps op@(TextSpan _ w _ _) upperOps lowerOps =
146− let lowerOps' = dropOps w lowerOps
147− outOps' = Vector.snoc outOps op
148− in if Vector.null lowerOps'
149− then outOps'
150− else onUpperOp outOps' (Vector.head upperOps) (Vector.tail upperOps) lowerOps'
151− onUpperOp outOps (Skip w) upperOps lowerOps =
152− let (ops', lowerOps') = splitOpsAt w lowerOps
153− outOps' = outOps `mappend` ops'
154− in if Vector.null lowerOps'
155− then outOps'
156− else onUpperOp outOps' (Vector.head upperOps) (Vector.tail upperOps) lowerOps'
157− onUpperOp _ (RowEnd _) _ _ = error "cannot merge rows containing RowEnd ops"
158−
159−
160−swapSkipsForSingleColumnCharSpan :: Char -> Attr -> SpanOps -> SpanOps
161−swapSkipsForSingleColumnCharSpan c a = Vector.map f
162− where f (Skip ow) = let txt = TL.pack $ replicate ow c
163− in TextSpan a ow ow txt
164− f v = v
165−
166−swapSkipsForCharSpan :: Int -> Char -> Attr -> SpanOps -> SpanOps
167−swapSkipsForCharSpan w c a = Vector.map f
168− where
169− f (Skip ow) = let txt0Cw = ow `div` w
170− txt0 = TL.pack $ replicate txt0Cw c
171− txt1Cw = ow `mod` w
172− txt1 = TL.pack $ replicate txt1Cw '…'
173− cw = txt0Cw + txt1Cw
174− txt = txt0 `TL.append` txt1
175− in TextSpan a ow cw txt
176− f v = v
177−
178−-- | Builds a vector of row operations that will output the given
179−-- picture to the terminal.
180−--
181−-- Crops to the given display region.
182−buildSpans :: Image -> DisplayRegion -> ST s (MRowOps s)
183−buildSpans image outRegion = do
184− -- First we create a mutable vector for each rows output operations.
185− outOps <- MVector.replicate (regionHeight outRegion) Vector.empty
186− -- It's possible that building the span operations in display order
187− -- would provide better performance.
188− --
189− -- A depth first traversal of the image is performed. ordered
190− -- according to the column range defined by the image from least
191− -- to greatest. The output row ops will at least have the region
192− -- of the image specified. Iterate over all output rows and output
193− -- background fills for all unspecified columns.
194− --
195− -- The images are made into span operations from left to right. It's
196− -- possible that this could easily be made to assure top to bottom
197− -- output as well.
198− when (regionHeight outRegion > 0 && regionWidth outRegion > 0) $ do
199− -- The ops builder recursively descends the image and outputs
200− -- span ops that would display that image. The number of columns
201− -- remaining in this row before exceeding the bounds is also
202− -- provided. This is used to clip the span ops produced to the
203− -- display.
204− let fullBuild = do
205− startImageBuild image
206− -- Fill in any unspecified columns with a skip.
207− forM_ [0 .. (regionHeight outRegion - 1)] (addRowCompletion outRegion)
208− initEnv = BlitEnv outRegion outOps
209− initState = BlitState 0 0 0 0 (regionWidth outRegion) (regionHeight outRegion)
210− _ <- runStateT (runReaderT fullBuild initEnv) initState
211− return ()
212− return outOps
213−
214−-- | Add the operations required to build a given image to the current
215−-- set of row operations.
216−startImageBuild :: Image -> BlitM s ()
217−startImageBuild image = do
218− outOfBounds <- isOutOfBounds image <$> get
219− when (not outOfBounds) $ addMaybeClipped image
220−
221−isOutOfBounds :: Image -> BlitState -> Bool
222−isOutOfBounds i s
223− | s ^. remainingColumns <= 0 = True
224− | s ^. remainingRows <= 0 = True
225− | s ^. skipColumns >= imageWidth i = True
226− | s ^. skipRows >= imageHeight i = True
227− | otherwise = False
228−
229−-- | This adds an image that might be partially clipped to the output
230−-- ops.
231−--
232−-- This is a very touchy algorithm. Too touchy. For instance, the
233−-- CropRight and CropBottom implementations are odd. They pass the
234−-- current tests but something seems terribly wrong about all this.
235−addMaybeClipped :: forall s . Image -> BlitM s ()
236−addMaybeClipped EmptyImage = return ()
237−addMaybeClipped (HorizText a textStr ow _cw) = do
238− -- This assumes that text spans are only 1 row high.
239− s <- use skipRows
240− when (s < 1) $ do
241− leftClip <- use skipColumns
242− rightClip <- use remainingColumns
243− let leftClipped = leftClip > 0
244− rightClipped = (ow - leftClip) > rightClip
245− if leftClipped || rightClipped
246− then let textStr' = clipText textStr leftClip rightClip
247− in addUnclippedText a textStr'
248− else addUnclippedText a textStr
249−addMaybeClipped (VertJoin topImage bottomImage _ow oh) = do
250− when (imageHeight topImage + imageHeight bottomImage > 0) $
251− addMaybeClippedJoin "vert_join" skipRows remainingRows rowOffset
252− (imageHeight topImage)
253− topImage
254− bottomImage
255− oh
256−addMaybeClipped (HorizJoin leftImage rightImage ow _oh) = do
257− when (imageWidth leftImage + imageWidth rightImage > 0) $
258− addMaybeClippedJoin "horiz_join" skipColumns remainingColumns columnOffset
259− (imageWidth leftImage)
260− leftImage
261− rightImage
262− ow
263−addMaybeClipped BGFill {outputWidth, outputHeight} = do
264− s <- get
265− let outputWidth' = min (outputWidth - s^.skipColumns) (s^.remainingColumns)
266− outputHeight' = min (outputHeight - s^.skipRows ) (s^.remainingRows)
267− y <- use rowOffset
268− forM_ [y..y+outputHeight'-1] $ snocOp (Skip outputWidth')
269−addMaybeClipped CropRight {croppedImage, outputWidth} = do
270− s <- use skipColumns
271− r <- use remainingColumns
272− let x = outputWidth - s
273− when (x < r) $ remainingColumns .= x
274− addMaybeClipped croppedImage
275−addMaybeClipped CropLeft {croppedImage, leftSkip} = do
276− skipColumns += leftSkip
277− addMaybeClipped croppedImage
278−addMaybeClipped CropBottom {croppedImage, outputHeight} = do
279− s <- use skipRows
280− r <- use remainingRows
281− let x = outputHeight - s
282− when (x < r) $ remainingRows .= x
283− addMaybeClipped croppedImage
284−addMaybeClipped CropTop {croppedImage, topSkip} = do
285− skipRows += topSkip
286− addMaybeClipped croppedImage
287−
288−addMaybeClippedJoin :: forall s . String
289− -> Lens BlitState BlitState Int Int
290− -> Lens BlitState BlitState Int Int
291− -> Lens BlitState BlitState Int Int
292− -> Int
293− -> Image
294− -> Image
295− -> Int
296− -> BlitM s ()
297−addMaybeClippedJoin name skip remaining offset i0Dim i0 i1 size = do
298− state <- get
299− when (state^.remaining <= 0) $ fail $ name ++ " with remaining <= 0"
300− case state^.skip of
301− s | s > size -> put $ state & skip %~ subtract size
302− | s == 0 -> if state^.remaining > i0Dim
303− then do
304− addMaybeClipped i0
305− put $ state & offset %~ (+ i0Dim) & remaining %~ subtract i0Dim
306− addMaybeClipped i1
307− else addMaybeClipped i0
308− | s < i0Dim ->
309− let i0Dim' = i0Dim - s
310− in if state^.remaining <= i0Dim'
311− then addMaybeClipped i0
312− else do
313− addMaybeClipped i0
314− put $ state & offset %~ (+ i0Dim') & remaining %~ subtract i0Dim' & skip .~ 0
315− addMaybeClipped i1
316− | s >= i0Dim -> do
317− put $ state & skip %~ subtract i0Dim
318− addMaybeClipped i1
319− _ -> fail $ name ++ " has unhandled skip class"
320−
321−addUnclippedText :: Attr -> DisplayText -> BlitM s ()
322−addUnclippedText a txt = do
323− let op = TextSpan a usedDisplayColumns
324− (fromIntegral $ TL.length txt)
325− txt
326− usedDisplayColumns = wctlwidth txt
327− use rowOffset >>= snocOp op
328−
329−addRowCompletion :: DisplayRegion -> Int -> BlitM s ()
330−addRowCompletion displayRegion row = do
331− allRowOps <- view mrowOps
332− rowOps <- lift $ lift $ MVector.read allRowOps row
333− let endX = spanOpsAffectedColumns rowOps
334− when (endX < regionWidth displayRegion) $ do
335− let ow = regionWidth displayRegion - endX
336− snocOp (Skip ow) row
337−
338−-- | snocs the operation to the operations for the given row.
339−snocOp :: SpanOp -> Int -> BlitM s ()
340−snocOp !op !row = do
341− theMrowOps <- view mrowOps
342− theRegion <- view region
343− lift $ lift $ do
344− ops <- MVector.read theMrowOps row
345− let ops' = Vector.snoc ops op
346− when (spanOpsAffectedColumns ops' > regionWidth theRegion)
347− $ fail $ "row " ++ show row ++ " now exceeds region width"
348− MVector.write theMrowOps row ops'
src/Graphics/Vty/Span.hs+213−119
@@ -1,129 +1,223 @@
1−-- Copyright Corey O'Connor
1+-- Copyright 2009 Corey O'Connor
2+{-# LANGUAGE BangPatterns #-}
3+{-# LANGUAGE ExistentialQuantification #-}
4+{-# LANGUAGE NamedFieldPuns #-}
25{-# LANGUAGE GADTs #-}
3−-- | A picture is translated into a sequences of state changes and
4−-- character spans. The attribute is applied to all following spans,
5−-- including spans of the next row. The nth element of the sequence
6−-- represents the nth row (from top to bottom) of the picture to render.
7−--
8−-- A span op sequence will be defined for all rows and columns (and no
9−-- more) of the region provided with the picture to 'spansForPic'.
10−module Graphics.Vty.Span where
11−
12−import Graphics.Vty.Attributes (Attr)
6+{-# LANGUAGE ExistentialQuantification #-}
7+{-# LANGUAGE ScopedTypeVariables #-}
8+-- The ops to define the content for an output region.
9+module Graphics.Vty.Span
10+ where
11+
12+import Codec.Binary.UTF8.Width ( wcwidth )
13+
1314import Graphics.Vty.Image
14−import Graphics.Vty.Image.Internal ( clipText )
15+import Graphics.Vty.Picture
16+import Graphics.Vty.DisplayRegion
17+
18+import Codec.Binary.UTF8.String ( encode )
19+
20+import Control.Monad ( forM_ )
21+import Control.Monad.ST.Strict
22+
23+import Data.Array.ST
24+import qualified Data.ByteString as B
25+import qualified Data.ByteString.Internal as BInt
26+import qualified Data.Foldable as Foldable
27+import Data.Function
28+import Data.List
29+import Data.Word
30+import qualified Data.ByteString.UTF8 as BSUTF8
31+import qualified Data.String.UTF8 as UTF8
32+
33+import Foreign.Storable ( pokeByteOff )
34+
35+import GHC.Arr
36+
37+{- | A picture is translated into a sequences of state changes and character spans.
38+ - State changes are currently limited to new attribute values. The attribute is applied to all
39+ - following spans. Including spans of the next row. The nth element of the sequence represents the
40+ - nth row (from top to bottom) of the picture to render.
41+ -
42+ - A span op sequence will be defined for all rows and columns (and no more) of the region provided
43+ - with the picture to spans_for_pic.
44+ -
45+ -
46+ - todo: Partition attribute changes into multiple categories according to the serialized
47+ - representation of the various attributes.
48+ -}
49+data SpanOpSequence = SpanOpSequence
50+ { effected_region :: DisplayRegion
51+ , row_ops :: RowOps
52+ }
53+
54+type RowOps = Array Word SpanOps
55+type SpanOps = [SpanOp]
1556
16−import qualified Data.Text.Lazy as TL
17−import Data.Vector (Vector)
18−import qualified Data.Vector as Vector
57+span_ops_columns :: SpanOpSequence -> Word
58+span_ops_columns ops = region_width $ effected_region ops
1959
20−-- | This represents an operation on the terminal: either an attribute
21−-- change or the output of a text string.
60+span_ops_rows :: SpanOpSequence -> Word
61+span_ops_rows ops = region_height $ effected_region ops
62+
63+span_ops_effected_columns :: [SpanOp] -> Word
64+span_ops_effected_columns in_ops = span_ops_effected_columns' 0 in_ops
65+ where
66+ span_ops_effected_columns' t [] = t
67+ span_ops_effected_columns' t (TextSpan w _ _ : r) = span_ops_effected_columns' (t + w) r
68+ span_ops_effected_columns' t (_ : r) = span_ops_effected_columns' t r
69+
70+-- |
71+--
72+-- todo: This type may need to be restructured to increase sharing in the bytestring
73+--
74+-- todo: Make foldable
2275data SpanOp =
23− -- | A span of UTF-8 text occupies a specific number of screen space
24− -- columns. A single UTF character does not necessarily represent 1
25− -- colunm. See Codec.Binary.UTF8.Width TextSpan [Attr] [output width
26− -- in columns] [number of characters] [data]
27− TextSpan
28− { textSpanAttr :: !Attr
29− , textSpanOutputWidth :: !Int
30− , textSpanCharWidth :: !Int
31− , textSpanText :: DisplayText
32− }
33− -- | Skips the given number of columns.
34− | Skip !Int
35− -- | Marks the end of a row. Specifies how many columns are
36− -- remaining. These columns will not be explicitly overwritten with
37− -- the span ops. The terminal is require to assure the remaining
38− -- columns are clear.
39− | RowEnd !Int
76+ AttributeChange !Attr
77+ -- | a span of UTF-8 text occupies a specific number of screen space columns. A single UTF
78+ -- character does not necessarially represent 1 colunm. See Codec.Binary.UTF8.Width
79+ -- TextSpan [output width in columns] [number of characters] [data]
80+ | TextSpan !Word !Word (UTF8.UTF8 B.ByteString)
4081 deriving Eq
4182
42−-- | A vector of span operations executed in succession. This represents
43−-- the operations required to render a row of the terminal. The
44−-- operations in one row may affect subsequent rows. For example,
45−-- setting the foreground color in one row will affect all subsequent
46−-- rows until the foreground color is changed.
47−type SpanOps = Vector SpanOp
83+span_op_has_width :: SpanOp -> Maybe (Word, Word)
84+span_op_has_width (TextSpan ow cw _) = Just (cw, ow)
85+span_op_has_width _ = Nothing
4886
49−dropOps :: Int -> SpanOps -> SpanOps
50−dropOps w = snd . splitOpsAt w
87+columns_to_char_offset :: Word -> SpanOp -> Word
88+columns_to_char_offset cx (TextSpan _ _ utf8_str) =
89+ let str = UTF8.toString utf8_str
90+ in toEnum $ sum $ map wcwidth $ take (fromEnum cx) str
91+columns_to_char_offset _cx _ = error "columns_to_char_offset applied to span op without width"
5192
52−splitOpsAt :: Int -> SpanOps -> (SpanOps, SpanOps)
53−splitOpsAt = splitOpsAt'
54− where
55− splitOpsAt' 0 ops = (Vector.empty, ops)
56− splitOpsAt' remainingColumns ops = case Vector.head ops of
57− t@(TextSpan {}) -> if remainingColumns >= textSpanOutputWidth t
58− then let (pre,post) = splitOpsAt' (remainingColumns - textSpanOutputWidth t)
59− (Vector.tail ops)
60− in (Vector.cons t pre, post)
61− else let preTxt = clipText (textSpanText t) 0 remainingColumns
62− preOp = TextSpan { textSpanAttr = textSpanAttr t
63− , textSpanOutputWidth = remainingColumns
64− , textSpanCharWidth = fromIntegral $! TL.length preTxt
65− , textSpanText = preTxt
66− }
67− postWidth = textSpanOutputWidth t - remainingColumns
68− postTxt = clipText (textSpanText t) remainingColumns postWidth
69− postOp = TextSpan { textSpanAttr = textSpanAttr t
70− , textSpanOutputWidth = postWidth
71− , textSpanCharWidth = fromIntegral $! TL.length postTxt
72− , textSpanText = postTxt
73− }
74− in ( Vector.singleton preOp
75− , Vector.cons postOp (Vector.tail ops)
76− )
77− Skip w -> if remainingColumns >= w
78− then let (pre,post) = splitOpsAt' (remainingColumns - w) (Vector.tail ops)
79− in (Vector.cons (Skip w) pre, post)
80− else ( Vector.singleton $ Skip remainingColumns
81− , Vector.cons (Skip (w - remainingColumns)) (Vector.tail ops)
82− )
83− RowEnd _ -> error "cannot split ops containing a row end"
84−
85−-- | A vector of span operation vectors for display, one per row of the
86−-- output region.
87−type DisplayOps = Vector SpanOps
88−
89−instance Show SpanOp where
90− show (TextSpan attr ow cw _) = "TextSpan(" ++ show attr ++ ")(" ++ show ow ++ ", " ++ show cw ++ ")"
91− show (Skip ow) = "Skip(" ++ show ow ++ ")"
92− show (RowEnd ow) = "RowEnd(" ++ show ow ++ ")"
93−
94−-- | The number of columns the DisplayOps are defined for.
95−--
96−-- All spans are verified to define same number of columns.
97−displayOpsColumns :: DisplayOps -> Int
98−displayOpsColumns ops
99− | Vector.length ops == 0 = 0
100− | otherwise = Vector.length $ Vector.head ops
101−
102−-- | The number of rows the DisplayOps are defined for.
103−displayOpsRows :: DisplayOps -> Int
104−displayOpsRows = Vector.length
105−
106−affectedRegion :: DisplayOps -> DisplayRegion
107−affectedRegion ops = (displayOpsColumns ops, displayOpsRows ops)
108−
109−-- | The number of columns a SpanOps affects.
110−spanOpsAffectedColumns :: SpanOps -> Int
111−spanOpsAffectedColumns inOps = Vector.foldl' spanOpsAffectedColumns' 0 inOps
93+-- | Produces the span ops that will render the given picture, possibly cropped or padded, into the
94+-- specified region.
95+spans_for_pic :: Picture -> DisplayRegion -> SpanOpSequence
96+spans_for_pic pic r = SpanOpSequence r $ runSTArray (build_spans pic r)
97+
98+build_spans :: Picture -> DisplayRegion -> ST s (STArray s Word [SpanOp])
99+build_spans pic region = do
100+ -- m for mutable
101+ mrow_ops <- newSTArray (0, region_height region - 1) []
102+ -- I think this can be better optimized if the build produced span operations in display order.
103+ -- However, I got stuck trying to implement an algorithm that did this. This will be considered
104+ -- as a possible future optimization.
105+ --
106+ -- A depth first traversal of the image is performed.
107+ -- ordered according to the column range defined by the image from least to greatest.
108+ -- The output row ops will at least have the region of the image specified. Iterate over all
109+ -- output rows and output background fills for all unspecified columns.
110+ if region_height region > 0
111+ then do
112+ -- The ops builder recursively descends the image and outputs span ops that would
113+ -- display that image. The number of columns remaining in this row before exceeding the
114+ -- bounds is also provided. This is used to clip the span ops produced.
115+ ops_for_row mrow_ops (pic_background pic) region (pic_image pic) 0 (region_width region)
116+ -- Fill in any unspecified columns with the background pattern.
117+ forM_ [0 .. region_height region - 1] $ \row -> do
118+ end_x <- readSTArray mrow_ops row >>= return . span_ops_effected_columns
119+ if end_x < region_width region
120+ then snoc_bg_fill mrow_ops (pic_background pic) (region_width region - end_x) row
121+ else return ()
122+ else return ()
123+ return mrow_ops
124+
125+type MRowOps s = STArray s Word SpanOps
126+
127+ops_for_row :: MRowOps s -> Background -> DisplayRegion -> Image -> Word -> Word -> ST s ()
128+ops_for_row mrow_ops bg region image y remaining_columns
129+ | remaining_columns == 0 = return ()
130+ | y >= region_height region = return ()
131+ | otherwise = case image of
132+ EmptyImage -> return ()
133+ -- The width provided is the number of columns this text span will occupy when displayed.
134+ -- if this is greater than the number of remaining columsn the output has to be produced a
135+ -- character at a time.
136+ HorizText a text_str ow cw -> do
137+ snoc_text_span a text_str ow cw mrow_ops y remaining_columns
138+ VertJoin t b _ _ -> do
139+ ops_for_row mrow_ops bg region t y remaining_columns
140+ ops_for_row mrow_ops bg region b (y + image_height t) remaining_columns
141+ HorizJoin l r _ _ -> do
142+ ops_for_row mrow_ops bg region l y remaining_columns
143+ -- Don't output the right part unless there is at least a single column left after
144+ -- outputting the left part.
145+ if image_width l < remaining_columns
146+ then ops_for_row mrow_ops bg region r y (remaining_columns - image_width l)
147+ else return ()
148+ BGFill width height -> do
149+ let actual_height = if y + height > region_height region
150+ then region_height region - y
151+ else height
152+ actual_width = if width > remaining_columns
153+ then remaining_columns
154+ else width
155+ forM_ [y .. y + actual_height - 1] $ \y' -> snoc_bg_fill mrow_ops bg actual_width y'
156+ Translation v i -> ops_for_row mrow_ops bg region i y remaining_columns
157+
158+snoc_text_span :: (Foldable.Foldable t)
159+ => Attr
160+ -> t Char
161+ -> Word -- width of text span in output display columns
162+ -> Word -- width of text span in characters
163+ -> MRowOps s
164+ -> Word
165+ -> Word
166+ -> ST s ()
167+snoc_text_span a text_str ow cw mrow_ops y remaining_columns = do
168+ if ow > remaining_columns
169+ then do
170+ snoc_op mrow_ops y $ AttributeChange a
171+ let (ow', cw', txt) = Foldable.foldl'
172+ build_cropped_txt
173+ ( 0, 0, B.empty )
174+ text_str
175+ snoc_op mrow_ops y $ TextSpan ow' cw' (UTF8.fromRep txt)
176+ else do
177+ snoc_op mrow_ops y $ AttributeChange a
178+ let utf8_bs = ({-# SCC "BSUTF8.fromString" #-} BSUTF8.fromString) $ Foldable.foldMap (\c -> [c]) text_str
179+ snoc_op mrow_ops y $ TextSpan ow cw (UTF8.fromRep utf8_bs)
112180 where
113− spanOpsAffectedColumns' t (TextSpan _ w _ _ ) = t + w
114− spanOpsAffectedColumns' t (Skip w) = t + w
115− spanOpsAffectedColumns' t (RowEnd w) = t + w
116−
117−-- | The width of a single SpanOp in columns.
118−spanOpHasWidth :: SpanOp -> Maybe (Int, Int)
119−spanOpHasWidth (TextSpan _ ow cw _) = Just (cw, ow)
120−spanOpHasWidth (Skip ow) = Just (ow,ow)
121−spanOpHasWidth (RowEnd ow) = Just (ow,ow)
122−
123−-- | The number of columns to the character at the given position in the
124−-- span op.
125−columnsToCharOffset :: Int -> SpanOp -> Int
126−columnsToCharOffset cx (TextSpan _ _ _ utf8Str) =
127− wctlwidth (TL.take (fromIntegral cx) utf8Str)
128−columnsToCharOffset cx (Skip _) = cx
129−columnsToCharOffset cx (RowEnd _) = cx
181+ build_cropped_txt (ow', char_count', b0) c = {-# SCC "build_cropped_txt" #-}
182+ let w = wcwidth c
183+ -- Characters with unknown widths occupy 1 column.
184+ --
185+ -- todo: Not sure if this is actually correct.
186+ -- I presume there is a replacement character that is output by the terminal instead of
187+ -- the character. If so then this replacement process may need to be implemented
188+ -- manually for consistent behavior across terminals.
189+ w' = toEnum $ if w < 0 then 1 else w
190+ in if (w' + ow') > remaining_columns
191+ then ( ow', char_count', b0 )
192+ else ( ow' + w', char_count' + 1, B.append b0 $ B.pack $ encode [c] )
193+
194+snoc_bg_fill :: MRowOps s -> Background -> Word -> Word -> ST s ()
195+snoc_bg_fill _row_ops _bg 0 _row
196+ = return ()
197+snoc_bg_fill mrow_ops (Background c back_attr) fill_length row
198+ = do
199+ snoc_op mrow_ops row $ AttributeChange back_attr
200+ -- By all likelyhood the background character will be an ASCII character. Which is a single
201+ -- byte in utf8. Optimize for this special case.
202+ utf8_bs <- if c <= (toEnum 255 :: Char)
203+ then
204+ let !(c_byte :: Word8) = BInt.c2w c
205+ in unsafeIOToST $ do
206+ BInt.create ( fromEnum fill_length )
207+ $ \ptr -> mapM_ (\i -> pokeByteOff ptr i c_byte)
208+ [0 .. fromEnum (fill_length - 1)]
209+ else
210+ let !(c_bytes :: [Word8]) = encode [c]
211+ in unsafeIOToST $ do
212+ BInt.create (fromEnum fill_length * length c_bytes)
213+ $ \ptr -> mapM_ (\(i,b) -> pokeByteOff ptr i b)
214+ $ zip [0 .. fromEnum (fill_length - 1)] (cycle c_bytes)
215+ snoc_op mrow_ops row $ TextSpan fill_length fill_length (UTF8.fromRep utf8_bs)
216+
217+snoc_op :: MRowOps s -> Word -> SpanOp -> ST s ()
218+snoc_op !mrow_ops !row !op = do
219+ ops <- readSTArray mrow_ops row
220+ let ops' = ops ++ [op]
221+ writeSTArray mrow_ops row ops'
222+ return ()
223+
src/Graphics/Vty/Terminal.hsnew file+129
@@ -0,0 +1,129 @@
1+-- Copyright 2009 Corey O'Connor
2+-- * Graphics.Vty.Terminal: This instantiates an abtract interface to the terminal interface
3+-- based on the TERM and COLORTERM environment variables.
4+-- * Graphics.Vty.Terminal.Generic: Defines the generic interface all terminals need to implement.
5+-- * Graphics.Vty.Terminal.TerminfoBased: Defines a terminal instance that uses terminfo for all
6+-- control strings.
7+-- - No attempt is made to change the character set to UTF-8 for these terminals. I don't
8+-- know a way to reliably determine if that is required or how to do so.
9+-- * Graphics.Vty.Terminal.XTermColor: This module contains an interface suitable for
10+-- xterm-like terminals. These are the terminals where TERM == xterm. This does use terminfo
11+-- for as many control codes as possible. H: This should derive functionality from the
12+-- TerminfoBased terminal.
13+--
14+--
15+{-# LANGUAGE ScopedTypeVariables #-}
16+module Graphics.Vty.Terminal ( module Graphics.Vty.Terminal
17+ , Terminal(..)
18+ , TerminalHandle(..)
19+ , DisplayHandle(..)
20+ , output_picture
21+ , display_context
22+ )
23+ where
24+
25+
26+import Graphics.Vty.Terminal.Generic
27+import Graphics.Vty.Terminal.MacOSX as MacOSX
28+import Graphics.Vty.Terminal.XTermColor as XTermColor
29+import Graphics.Vty.Terminal.TerminfoBased as TerminfoBased
30+
31+import Control.Applicative
32+import Control.Exception ( SomeException, try )
33+import Control.Monad.Trans
34+
35+import Data.Either
36+import Data.List ( isPrefixOf )
37+import Data.Word
38+
39+import System.Environment
40+
41+-- | Returns a TerminalHandle (an abstract Terminal instance) for the current terminal.
42+--
43+-- The specific Terminal implementation used is hidden from the API user. All terminal
44+-- implementations are assumed to perform more, or less, the same. Currently all implementations use
45+-- terminfo for at least some terminal specific information. This is why platforms without terminfo
46+-- are not supported. However, as mentioned before, any specifics about it being based on terminfo
47+-- are hidden from the API user. If a terminal implementation is developed for a terminal for a
48+-- platform without terminfo support then Vty should work as expected on that terminal.
49+--
50+-- Selection of a terminal is done as follows:
51+--
52+-- * If TERM == xterm
53+-- then the terminal might be one of the Mac OS X .app terminals. Check if that might be
54+-- the case and use MacOSX if so.
55+-- otherwise use XTermColor.
56+--
57+-- * for any other TERM value TerminfoBased is used.
58+--
59+--
60+-- The terminal has to be determined dynamically at runtime. To satisfy this requirement all
61+-- terminals instances are lifted into an abstract terminal handle via existential qualification.
62+-- This implies that the only equations that can used are those in the terminal class.
63+--
64+-- To differentiate between Mac OS X terminals this uses the TERM_PROGRAM environment variable.
65+-- However, an xterm started by Terminal or iTerm *also* has TERM_PROGRAM defined since the
66+-- environment variable is not reset/cleared by xterm. However a Terminal.app or iTerm.app started
67+-- from an xterm under X11 on mac os x will likely be done via open. Since this does not propogate
68+-- environment variables (I think?) this assumes that XTERM_VERSION will never be set for a true
69+-- Terminal.app or iTerm.app session.
70+--
71+-- todo: add an implementation for windows that does not depend on terminfo. Should be installable
72+-- with only what is provided in the haskell platform.
73+--
74+-- todo: The Terminal interface does not provide any input support.
75+terminal_handle :: ( Applicative m, MonadIO m ) => m TerminalHandle
76+terminal_handle = do
77+ term_type <- liftIO $ getEnv "TERM"
78+ t <- if "xterm" `isPrefixOf` term_type
79+ then do
80+ maybe_terminal_app <- get_env "TERM_PROGRAM"
81+ case maybe_terminal_app of
82+ Nothing
83+ -> XTermColor.terminal_instance term_type >>= new_terminal_handle
84+ Just v | v == "Apple_Terminal" || v == "iTerm.app"
85+ -> do
86+ maybe_xterm <- get_env "XTERM_VERSION"
87+ case maybe_xterm of
88+ Nothing -> MacOSX.terminal_instance v >>= new_terminal_handle
89+ Just _ -> XTermColor.terminal_instance term_type >>= new_terminal_handle
90+ -- Assume any other terminal that sets TERM_PROGRAM to not be an OS X terminal.app
91+ -- like terminal?
92+ _ -> XTermColor.terminal_instance term_type >>= new_terminal_handle
93+ -- Not an xterm-like terminal. try for generic terminfo.
94+ else TerminfoBased.terminal_instance term_type >>= new_terminal_handle
95+ return t
96+ where
97+ get_env var = do
98+ mv <- liftIO $ try $ getEnv var
99+ case mv of
100+ Left (_e :: SomeException) -> return $ Nothing
101+ Right v -> return $ Just v
102+
103+-- | Sets the cursor position to the given output column and row.
104+--
105+-- This is not necessarially the same as the character position with the same coordinates.
106+-- Characters can be a variable number of columns in width.
107+--
108+-- Currently, the only way to set the cursor position to a given character coordinate is to specify
109+-- the coordinate in the Picture instance provided to output_picture or refresh.
110+set_cursor_pos :: MonadIO m => TerminalHandle -> Word -> Word -> m ()
111+set_cursor_pos t x y = do
112+ bounds <- display_bounds t
113+ d <- display_context t bounds
114+ marshall_to_terminal t (move_cursor_required_bytes d x y) (serialize_move_cursor d x y)
115+
116+-- | Hides the cursor
117+hide_cursor :: MonadIO m => TerminalHandle -> m ()
118+hide_cursor t = do
119+ bounds <- display_bounds t
120+ d <- display_context t bounds
121+ marshall_to_terminal t (hide_cursor_required_bytes d) (serialize_hide_cursor d)
122+
123+-- | Shows the cursor
124+show_cursor :: MonadIO m => TerminalHandle -> m ()
125+show_cursor t = do
126+ bounds <- display_bounds t
127+ d <- display_context t bounds
128+ marshall_to_terminal t (show_cursor_required_bytes d) (serialize_show_cursor d)
129+
src/Graphics/Vty/Terminal/Debug.hsnew file+117
@@ -0,0 +1,117 @@
1+-- Copyright 2009 Corey O'Connor
2+{-# LANGUAGE FlexibleInstances #-}
3+{-# LANGUAGE TypeFamilies #-}
4+module Graphics.Vty.Terminal.Debug ( DebugTerminal(..)
5+ , DebugDisplay(..)
6+ , terminal_instance
7+ , dehandle
8+ )
9+ where
10+
11+import Graphics.Vty.DisplayRegion
12+import Graphics.Vty.Terminal.Generic
13+
14+import Control.Applicative
15+import Control.Monad.Trans
16+import Control.Monad.State.Strict
17+
18+import qualified Data.ByteString.UTF8 as BS
19+import qualified Data.ByteString as BSCore
20+import Data.IORef
21+import qualified Data.Sequence as Seq
22+import qualified Data.String.UTF8 as UTF8
23+import Data.Word
24+
25+import Foreign.Marshal.Array ( peekArray )
26+import Foreign.Ptr ( plusPtr )
27+import Foreign.Storable ( poke )
28+
29+import Unsafe.Coerce
30+
31+-- | The debug display terminal produces a string representation of the requested picture. There is
32+-- *not* an isomorphism between the string representation and the picture. The string
33+-- representation is a simplification of the picture that is only useful in debugging VTY without
34+-- considering terminal specific issues.
35+--
36+-- The debug implementation is useful in manually determining if the sequence of terminal operations
37+-- matches the expected sequence. So requirement of the produced representation is simplicity in
38+-- parsing the text representation and determining how the picture was mapped to terminal
39+-- operations.
40+--
41+-- All terminals support the operations specified in the Terminal class defined in
42+-- Graphics.Vty.Terminal. As an instance of the Terminal class is also an instance of the Monad
43+-- class there exists a monoid that defines it's algebra. The string representation is a sequence of
44+-- identifiers where each identifier is the name of an operation in the algebra.
45+
46+data DebugTerminal = DebugTerminal
47+ { debug_terminal_last_output :: IORef (UTF8.UTF8 BS.ByteString)
48+ , debug_terminal_bounds :: DisplayRegion
49+ }
50+
51+instance Terminal DebugTerminal where
52+ terminal_ID _t = "debug_terminal"
53+ release_terminal _t = return ()
54+ reserve_display _t = return ()
55+ release_display _t = return ()
56+ display_bounds t = return $ debug_terminal_bounds t
57+ display_terminal_instance t r c = return $ c (DebugDisplay r)
58+ output_byte_buffer t out_buffer buffer_size
59+ = liftIO $ do
60+ putStrLn $ "output_byte_buffer ?? " ++ show buffer_size
61+ peekArray (fromEnum buffer_size) out_buffer
62+ >>= return . UTF8.fromRep . BSCore.pack
63+ >>= writeIORef (debug_terminal_last_output t)
64+
65+data DebugDisplay = DebugDisplay
66+ { debug_display_bounds :: DisplayRegion
67+ }
68+
69+terminal_instance :: ( Applicative m, MonadIO m ) => DisplayRegion -> m TerminalHandle
70+terminal_instance r = do
71+ output_ref <- liftIO $ newIORef undefined
72+ new_terminal_handle $ DebugTerminal output_ref r
73+
74+dehandle :: TerminalHandle -> DebugTerminal
75+dehandle (TerminalHandle t _) = unsafeCoerce t
76+
77+instance DisplayTerminal DebugDisplay where
78+ -- | Provide the current bounds of the output terminal.
79+ context_region d = debug_display_bounds d
80+
81+ -- | A cursor move is always visualized as the single character 'M'
82+ move_cursor_required_bytes _d _x _y = 1
83+
84+ -- | A cursor move is always visualized as the single character 'M'
85+ serialize_move_cursor _d _x _y ptr = do
86+ liftIO $ poke ptr (toEnum $ fromEnum 'M')
87+ return $ ptr `plusPtr` 1
88+
89+ -- | Show cursor is always visualized as the single character 'S'
90+ show_cursor_required_bytes _d = 1
91+
92+ -- | Show cursor is always visualized as the single character 'S'
93+ serialize_show_cursor _d ptr = do
94+ liftIO $ poke ptr (toEnum $ fromEnum 'S')
95+ return $ ptr `plusPtr` 1
96+
97+ -- | Hide cursor is always visualized as the single character 'H'
98+ hide_cursor_required_bytes _d = 1
99+
100+ -- | Hide cursor is always visualized as the single character 'H'
101+ serialize_hide_cursor _d ptr = do
102+ liftIO $ poke ptr (toEnum $ fromEnum 'H')
103+ return $ ptr `plusPtr` 1
104+
105+ -- | An attr change is always visualized as the single character 'A'
106+ attr_required_bytes _d _fattr _diffs _attr = 1
107+
108+ -- | An attr change is always visualized as the single character 'A'
109+ serialize_set_attr _d _fattr _diffs _attr ptr = do
110+ liftIO $ poke ptr (toEnum $ fromEnum 'A')
111+ return $ ptr `plusPtr` 1
112+
113+ default_attr_required_bytes _d = 1
114+ serialize_default_attr _d ptr = do
115+ liftIO $ poke ptr (toEnum $ fromEnum 'D')
116+ return $ ptr `plusPtr` 1
117+
src/Graphics/Vty/Terminal/Generic.hsnew file+405
@@ -0,0 +1,405 @@
1+-- Copyright 2009 Corey O'Connor
2+{-# LANGUAGE ScopedTypeVariables #-}
3+{-# LANGUAGE Rank2Types #-}
4+{-# LANGUAGE ExistentialQuantification #-}
5+{-# LANGUAGE GADTs #-}
6+{-# LANGUAGE FlexibleContexts #-}
7+module Graphics.Vty.Terminal.Generic ( module Graphics.Vty.Terminal.Generic
8+ , OutputBuffer
9+ )
10+ where
11+
12+import Data.Marshalling
13+
14+import Graphics.Vty.Picture
15+import Graphics.Vty.Span
16+import Graphics.Vty.DisplayRegion
17+
18+import Graphics.Vty.DisplayAttributes
19+
20+import Control.Monad ( liftM )
21+import Control.Monad.Trans
22+
23+import Data.Array
24+import qualified Data.ByteString.Internal as BSCore
25+import Data.Foldable
26+import Data.IORef
27+import Data.Word
28+import Data.String.UTF8 hiding ( foldl )
29+
30+data TerminalHandle where
31+ TerminalHandle :: Terminal t => t -> IORef TerminalState -> TerminalHandle
32+
33+state_ref :: TerminalHandle -> IORef TerminalState
34+state_ref (TerminalHandle _ s_ref) = s_ref
35+
36+new_terminal_handle :: forall m t. ( MonadIO m, Terminal t ) => t -> m TerminalHandle
37+new_terminal_handle t = do
38+ s_ref <- liftIO $ newIORef initial_terminal_state
39+ return $ TerminalHandle t s_ref
40+
41+data TerminalState = TerminalState
42+ { -- | The current terminal display attributes or Nothing if they are not known.
43+ known_fattr :: Maybe FixedAttr
44+ }
45+
46+initial_terminal_state :: TerminalState
47+initial_terminal_state = TerminalState Nothing
48+
49+class Terminal t where
50+ -- | Text identifier for the terminal. Used for debugging.
51+ terminal_ID :: t -> String
52+ -- |
53+ release_terminal :: MonadIO m => t -> m ()
54+ -- | Clear the display and initialize the terminal to some initial display state.
55+ --
56+ -- The expectation of a program is that the display starts in some initial state.
57+ -- The initial state would consist of fixed values:
58+ -- - cursor at top left
59+ -- - UTF-8 character encoding
60+ -- - drawing characteristics are the default
61+ -- The abstract operation I think all these behaviors are instances of is reserving exclusive
62+ -- access to a display such that:
63+ -- - The previous state cannot be determined
64+ -- - When exclusive access to a display is release the display returns to the previous state.
65+ reserve_display :: MonadIO m => t -> m ()
66+
67+ -- | Return the display to the state before reserve_display
68+ -- If no previous state then set the display state to the initial state.
69+ release_display :: MonadIO m => t -> m ()
70+
71+ -- | Returns the current display bounds.
72+ display_bounds :: MonadIO m => t -> m DisplayRegion
73+
74+ -- Internal method used to provide the DisplayTerminal instance to the DisplayHandle
75+ -- constructor.
76+ display_terminal_instance :: MonadIO m
77+ => t
78+ -> DisplayRegion
79+ -> (forall d. DisplayTerminal d => d -> DisplayHandle)
80+ -> m DisplayHandle
81+
82+ -- | Output the byte buffer of the specified size to the terminal device. The size is equal to
83+ -- end_ptr - start_ptr
84+ output_byte_buffer :: MonadIO m => t -> OutputBuffer -> Word -> m ()
85+
86+instance Terminal TerminalHandle where
87+ terminal_ID (TerminalHandle t _) = terminal_ID t
88+ release_terminal (TerminalHandle t _) = release_terminal t
89+ reserve_display (TerminalHandle t _) = reserve_display t
90+ release_display (TerminalHandle t _) = release_display t
91+ display_bounds (TerminalHandle t _) = display_bounds t
92+ display_terminal_instance (TerminalHandle t _) = display_terminal_instance t
93+ output_byte_buffer (TerminalHandle t _) = output_byte_buffer t
94+
95+data DisplayHandle where
96+ DisplayHandle :: DisplayTerminal d => d -> TerminalHandle -> DisplayState -> DisplayHandle
97+
98+-- | Acquire display access to the given region of the display.
99+-- Currently all regions have the upper left corner of (0,0) and the lower right corner at
100+-- (max display_width provided_width, max display_height provided_height)
101+display_context :: MonadIO m => TerminalHandle -> DisplayRegion -> m DisplayHandle
102+display_context t b = do
103+ s <- initial_display_state
104+ let c d = DisplayHandle d t s
105+ display_terminal_instance t b c
106+
107+data DisplayState = DisplayState
108+ { previous_output_ref :: IORef (Maybe SpanOpSequence)
109+ }
110+
111+initial_display_state :: MonadIO m => m DisplayState
112+initial_display_state = liftM DisplayState $ liftIO $ newIORef Nothing
113+
114+class DisplayTerminal d where
115+ -- | Provide the bounds of the display context.
116+ context_region :: d -> DisplayRegion
117+
118+ -- | Maximum number of colors supported by the context.
119+ context_color_count :: d -> Word
120+
121+ -- | sets the output position to the specified row and column. Where the number of bytes
122+ -- required for the control codes can be specified seperate from the actual byte sequence.
123+ move_cursor_required_bytes :: d -> Word -> Word -> Word
124+ serialize_move_cursor :: MonadIO m => d -> Word -> Word -> OutputBuffer -> m OutputBuffer
125+
126+ show_cursor_required_bytes :: d -> Word
127+ serialize_show_cursor :: MonadIO m => d -> OutputBuffer -> m OutputBuffer
128+
129+ hide_cursor_required_bytes :: d -> Word
130+ serialize_hide_cursor :: MonadIO m => d -> OutputBuffer -> m OutputBuffer
131+
132+ -- | Assure the specified output attributes will be applied to all the following text until the
133+ -- next output attribute change. Where the number of bytes required for the control codes can
134+ -- be specified seperate from the actual byte sequence. The required number of bytes must be
135+ -- at least the maximum number of bytes required by any attribute changes. The serialization
136+ -- equations must provide the ptr to the next byte to be specified in the output buffer.
137+ --
138+ -- The currently applied display attributes are provided as well. The Attr data type can
139+ -- specify the style or color should not be changed from the currently applied display
140+ -- attributes. In order to support this the currently applied display attributes are required.
141+ -- In addition it may be possible to optimize the state changes based off the currently applied
142+ -- display attributes.
143+ attr_required_bytes :: d -> FixedAttr -> Attr -> DisplayAttrDiff -> Word
144+ serialize_set_attr :: MonadIO m => d -> FixedAttr -> Attr -> DisplayAttrDiff -> OutputBuffer -> m OutputBuffer
145+
146+ -- | Reset the display attributes to the default display attributes
147+ default_attr_required_bytes :: d -> Word
148+ serialize_default_attr :: MonadIO m => d -> OutputBuffer -> m OutputBuffer
149+
150+ -- | See Graphics.Vty.Terminal.XTermColor.inline_hack
151+ inline_hack :: MonadIO m => d -> m ()
152+ inline_hack _d = return ()
153+
154+
155+instance DisplayTerminal DisplayHandle where
156+ context_region (DisplayHandle d _ _) = context_region d
157+ context_color_count (DisplayHandle d _ _) = context_color_count d
158+ move_cursor_required_bytes (DisplayHandle d _ _) = move_cursor_required_bytes d
159+ serialize_move_cursor (DisplayHandle d _ _) = serialize_move_cursor d
160+ show_cursor_required_bytes (DisplayHandle d _ _) = show_cursor_required_bytes d
161+ serialize_show_cursor (DisplayHandle d _ _) = serialize_show_cursor d
162+ hide_cursor_required_bytes (DisplayHandle d _ _) = hide_cursor_required_bytes d
163+ serialize_hide_cursor (DisplayHandle d _ _) = serialize_hide_cursor d
164+ attr_required_bytes (DisplayHandle d _ _) = attr_required_bytes d
165+ serialize_set_attr (DisplayHandle d _ _) = serialize_set_attr d
166+ default_attr_required_bytes (DisplayHandle d _ _) = default_attr_required_bytes d
167+ serialize_default_attr (DisplayHandle d _ _) = serialize_default_attr d
168+ inline_hack (DisplayHandle d _ _) = inline_hack d
169+
170+-- | All terminals serialize UTF8 text to the terminal device exactly as serialized in memory.
171+utf8_text_required_bytes :: UTF8 BSCore.ByteString -> Word
172+utf8_text_required_bytes str =
173+ let (_, _, src_bytes_length) = BSCore.toForeignPtr (toRep str)
174+ in toEnum src_bytes_length
175+
176+-- | All terminals serialize UTF8 text to the terminal device exactly as serialized in memory.
177+serialize_utf8_text :: MonadIO m => UTF8 BSCore.ByteString -> OutputBuffer -> m OutputBuffer
178+serialize_utf8_text str dest_ptr =
179+ let (src_fptr, src_ptr_offset, src_bytes_length) = BSCore.toForeignPtr (toRep str)
180+ in liftIO $ withForeignPtr src_fptr $ \src_ptr -> do
181+ let src_ptr' = src_ptr `plusPtr` src_ptr_offset
182+ BSCore.memcpy dest_ptr src_ptr' (toEnum src_bytes_length)
183+ return (dest_ptr `plusPtr` src_bytes_length)
184+
185+
186+-- | Displays the given `Picture`.
187+--
188+-- 0. The image is cropped to the display size.
189+--
190+-- 1. Converted into a sequence of attribute changes and text spans.
191+--
192+-- 2. The cursor is hidden.
193+--
194+-- 3. Serialized to the display.
195+--
196+-- 4. The cursor is then shown and positioned or kept hidden.
197+--
198+--
199+-- todo: specify possible IO exceptions.
200+-- abstract from IO monad to a MonadIO instance.
201+output_picture :: MonadIO m => DisplayHandle -> Picture -> m ()
202+output_picture (DisplayHandle d t s) pic = do
203+ let !r = context_region d
204+ let !ops = spans_for_pic pic r
205+ let !initial_attr = FixedAttr default_style_mask Nothing Nothing
206+ -- Diff the previous output against the requested output. Differences are currently on a per-row
207+ -- basis.
208+ diffs :: [Bool]
209+ <- liftIO ( readIORef (previous_output_ref s) )
210+ >>= \mprevious_ops -> case mprevious_ops of
211+ Nothing
212+ -> return $ replicate ( fromEnum $ region_height $ effected_region ops )
213+ True
214+ Just previous_ops
215+ -> if effected_region previous_ops /= effected_region ops
216+ then return $ replicate ( fromEnum $ region_height $ effected_region ops )
217+ True
218+ else return $ zipWith (/=) ( elems $ row_ops previous_ops )
219+ ( elems $ row_ops ops )
220+
221+ -- determine the number of bytes required to completely serialize the output ops.
222+ let total = hide_cursor_required_bytes d
223+ + default_attr_required_bytes d
224+ + required_bytes d initial_attr diffs ops
225+ + case pic_cursor pic of
226+ NoCursor -> 0
227+ Cursor x y -> show_cursor_required_bytes d
228+ + move_cursor_required_bytes d x y
229+
230+ -- ... then serialize
231+ liftIO $ allocaBytes (fromEnum total) $ \start_ptr -> do
232+ ptr <- serialize_hide_cursor d start_ptr
233+ ptr' <- serialize_default_attr d ptr
234+ ptr'' <- serialize_output_ops d ptr' initial_attr diffs ops
235+ end_ptr <- case pic_cursor pic of
236+ NoCursor -> return ptr''
237+ Cursor x y -> do
238+ let m = cursor_output_map ops $ pic_cursor pic
239+ (ox, oy) = char_to_output_pos m (x,y)
240+ serialize_show_cursor d ptr'' >>= serialize_move_cursor d ox oy
241+ -- todo: How to handle exceptions?
242+ case end_ptr `minusPtr` start_ptr of
243+ count | count < 0 -> fail "End pointer before start of buffer."
244+ | toEnum count > total -> fail $ "End pointer past end of buffer by " ++ show (toEnum count - total)
245+ | otherwise -> output_byte_buffer t start_ptr (toEnum count)
246+ -- Cache the output spans.
247+ liftIO $ writeIORef (previous_output_ref s) (Just ops)
248+ return ()
249+
250+required_bytes :: DisplayTerminal d => d -> FixedAttr -> [Bool] -> SpanOpSequence -> Word
251+required_bytes d in_fattr diffs ops =
252+ let (_, n, _, _) = foldl' required_bytes' (0, 0, in_fattr, diffs) ( row_ops ops )
253+ in n
254+ where
255+ required_bytes' (y, current_sum, fattr, True : diffs') span_ops
256+ = let (s, fattr') = span_ops_required_bytes d y fattr span_ops
257+ in ( y + 1, s + current_sum, fattr', diffs' )
258+ required_bytes' (y, current_sum, fattr, False : diffs') _span_ops
259+ = ( y + 1, current_sum, fattr, diffs' )
260+ required_bytes' (_y, _current_sum, _fattr, [] ) _span_ops
261+ = error "shouldn't be possible"
262+
263+span_ops_required_bytes :: DisplayTerminal d => d -> Word -> FixedAttr -> SpanOps -> (Word, FixedAttr)
264+span_ops_required_bytes d y in_fattr span_ops =
265+ -- The first operation is to set the cursor to the start of the row
266+ let header_required_bytes = move_cursor_required_bytes d 0 y
267+ -- then the span ops are serialized in the order specified
268+ in foldl' ( \(current_sum, fattr) op -> let (c, fattr') = span_op_required_bytes d fattr op
269+ in (c + current_sum, fattr')
270+ )
271+ (header_required_bytes, in_fattr)
272+ span_ops
273+
274+span_op_required_bytes :: DisplayTerminal d => d -> FixedAttr -> SpanOp -> (Word, FixedAttr)
275+span_op_required_bytes d fattr (AttributeChange attr) =
276+ let attr' = limit_attr_for_display d attr
277+ diffs = display_attr_diffs fattr fattr'
278+ c = attr_required_bytes d fattr attr' diffs
279+ fattr' = fix_display_attr fattr attr'
280+ in (c, fattr')
281+span_op_required_bytes _d fattr (TextSpan _ _ str) = (utf8_text_required_bytes str, fattr)
282+
283+serialize_output_ops :: ( MonadIO m, DisplayTerminal d )
284+ => d
285+ -> OutputBuffer
286+ -> FixedAttr
287+ -> [Bool]
288+ -> SpanOpSequence
289+ -> m OutputBuffer
290+serialize_output_ops d start_ptr in_fattr diffs ops = do
291+ (_, end_ptr, _, _) <- foldlM serialize_output_ops'
292+ ( 0, start_ptr, in_fattr, diffs )
293+ ( row_ops ops )
294+ return end_ptr
295+ where
296+ serialize_output_ops' ( y, out_ptr, fattr, True : diffs' ) span_ops
297+ = serialize_span_ops d y out_ptr fattr span_ops
298+ >>= return . ( \(out_ptr', fattr') -> ( y + 1, out_ptr', fattr', diffs' ) )
299+ serialize_output_ops' ( y, out_ptr, fattr, False : diffs' ) _span_ops
300+ = return ( y + 1, out_ptr, fattr, diffs' )
301+ serialize_output_ops' (_y, _out_ptr, _fattr, [] ) _span_ops
302+ = error "shouldn't be possible"
303+
304+serialize_span_ops :: ( MonadIO m, DisplayTerminal d )
305+ => d
306+ -> Word
307+ -> OutputBuffer
308+ -> FixedAttr
309+ -> SpanOps
310+ -> m (OutputBuffer, FixedAttr)
311+serialize_span_ops d y out_ptr in_fattr span_ops = do
312+ -- The first operation is to set the cursor to the start of the row
313+ out_ptr' <- serialize_move_cursor d 0 y out_ptr
314+ -- then the span ops are serialized in the order specified
315+ foldlM ( \(out_ptr'', fattr) op -> serialize_span_op d op out_ptr'' fattr )
316+ (out_ptr', in_fattr)
317+ span_ops
318+
319+serialize_span_op :: ( MonadIO m, DisplayTerminal d )
320+ => d
321+ -> SpanOp
322+ -> OutputBuffer
323+ -> FixedAttr
324+ -> m (OutputBuffer, FixedAttr)
325+serialize_span_op d (AttributeChange attr) out_ptr fattr = do
326+ let attr' = limit_attr_for_display d attr
327+ fattr' = fix_display_attr fattr attr'
328+ diffs = display_attr_diffs fattr fattr'
329+ out_ptr' <- serialize_set_attr d fattr attr' diffs out_ptr
330+ return (out_ptr', fattr')
331+serialize_span_op _d (TextSpan _ _ str) out_ptr fattr = do
332+ out_ptr' <- serialize_utf8_text str out_ptr
333+ return (out_ptr', fattr)
334+
335+marshall_to_terminal :: ( MonadIO m, Terminal t )
336+ => t -> Word -> (Ptr Word8 -> m (Ptr Word8)) -> m ()
337+marshall_to_terminal t c f = do
338+ start_ptr <- liftIO $ mallocBytes (fromEnum c)
339+ --
340+ -- todo: capture exceptions?
341+ end_ptr <- f start_ptr
342+ case end_ptr `minusPtr` start_ptr of
343+ count | count < 0 -> fail "End pointer before start pointer."
344+ | toEnum count > c -> fail $ "End pointer past end of buffer by " ++ show (toEnum count - c)
345+ | otherwise -> output_byte_buffer t start_ptr (toEnum count)
346+ liftIO $ free start_ptr
347+ return ()
348+
349+data CursorOutputMap = CursorOutputMap
350+ { char_to_output_pos :: (Word, Word) -> (Word, Word)
351+ }
352+
353+cursor_output_map :: SpanOpSequence -> Cursor -> CursorOutputMap
354+cursor_output_map span_ops _cursor = CursorOutputMap
355+ { char_to_output_pos = \(cx, cy) -> (cursor_column_offset span_ops cx cy, cy)
356+ }
357+
358+cursor_column_offset :: SpanOpSequence -> Word -> Word -> Word
359+cursor_column_offset span_ops cx cy =
360+ let cursor_row_ops = row_ops span_ops ! cy
361+ (out_offset, _, _)
362+ = foldl' ( \(d, current_cx, done) op ->
363+ if done then (d, current_cx, done) else case span_op_has_width op of
364+ Nothing -> (d, current_cx, False)
365+ Just (cw, ow) -> case compare cx (current_cx + cw) of
366+ GT -> ( d + ow
367+ , current_cx + cw
368+ , False
369+ )
370+ EQ -> ( d + ow
371+ , current_cx + cw
372+ , True
373+ )
374+ LT -> ( d + columns_to_char_offset (cx - current_cx) op
375+ , current_cx + cw
376+ , True
377+ )
378+ )
379+ (0, 0, False)
380+ cursor_row_ops
381+ in out_offset
382+
383+limit_attr_for_display :: DisplayTerminal d => d -> Attr -> Attr
384+limit_attr_for_display d attr
385+ = attr { attr_fore_color = clamp_color $ attr_fore_color attr
386+ , attr_back_color = clamp_color $ attr_back_color attr
387+ }
388+ where
389+ clamp_color Default = Default
390+ clamp_color KeepCurrent = KeepCurrent
391+ clamp_color (SetTo c) = clamp_color' c
392+ clamp_color' (ISOColor v)
393+ | context_color_count d < 8 = Default
394+ | context_color_count d < 16 && v >= 8 = SetTo $ ISOColor (v - 8)
395+ | otherwise = SetTo $ ISOColor v
396+ clamp_color' (Color240 v)
397+ -- TODO: Choose closes ISO color?
398+ | context_color_count d < 8 = Default
399+ | context_color_count d < 16 = Default
400+ | context_color_count d == 240 = SetTo $ Color240 v
401+ | otherwise
402+ = let p :: Double = fromIntegral v / 240.0
403+ v' = floor $ p * (fromIntegral $ context_color_count d)
404+ in SetTo $ Color240 v'
405+
src/Graphics/Vty/Terminal/MacOSX.hsnew file+95
@@ -0,0 +1,95 @@
1+-- Copyright 2009 Corey O'Connor
2+-- The standard Mac OS X terminals Terminal.app and iTerm both declare themselves to be
3+-- "xterm-color" by default. However the terminfo database for xterm-color included with OS X is
4+-- incomplete.
5+--
6+-- This terminal implementation modifies the standard terminfo terminal as required for complete OS
7+-- X support.
8+{-# LANGUAGE ForeignFunctionInterface #-}
9+{-# LANGUAGE ScopedTypeVariables #-}
10+{-# LANGUAGE MagicHash #-}
11+{-# LANGUAGE FlexibleInstances #-}
12+{-# LANGUAGE TypeFamilies #-}
13+module Graphics.Vty.Terminal.MacOSX ( terminal_instance
14+ )
15+ where
16+
17+import Graphics.Vty.Terminal.Generic
18+import qualified Graphics.Vty.Terminal.TerminfoBased as TerminfoBased
19+
20+import Control.Applicative
21+import Control.Monad.Trans
22+
23+import System.IO
24+
25+data Term = Term
26+ { super_term :: TerminalHandle
27+ , term_app :: String
28+ }
29+
30+-- for Terminal.app use "xterm". For iTerm.app use "xterm-256color"
31+terminal_instance :: ( Applicative m, MonadIO m ) => String -> m Term
32+terminal_instance v = do
33+ let base_term "iTerm.app" = "xterm-256color"
34+ base_term _ = "xterm"
35+ t <- TerminfoBased.terminal_instance (base_term v) >>= new_terminal_handle
36+ return $ Term t v
37+
38+flushed_put :: MonadIO m => String -> m ()
39+flushed_put str = do
40+ liftIO $ hPutStr stdout str
41+ liftIO $ hFlush stdout
42+
43+-- Terminal.app really does want the xterm-color smcup and rmcup caps. Not the generic xterm ones.
44+smcup_str, rmcup_str :: String
45+smcup_str = "\ESC7\ESC[?47h"
46+rmcup_str = "\ESC[2J\ESC[?47l\ESC8"
47+
48+-- iTerm needs a clear screen after smcup as well?
49+clear_screen_str :: String
50+clear_screen_str = "\ESC[H\ESC[2J"
51+
52+instance Terminal Term where
53+ terminal_ID t = term_app t ++ " :: MacOSX"
54+
55+ release_terminal t = do
56+ release_terminal $ super_term t
57+
58+ reserve_display _t = do
59+ flushed_put smcup_str
60+ flushed_put clear_screen_str
61+
62+ release_display _t = do
63+ flushed_put rmcup_str
64+
65+ display_terminal_instance t b c = do
66+ d <- display_context (super_term t) b
67+ return $ c (DisplayContext d)
68+
69+ display_bounds t = display_bounds (super_term t)
70+
71+ output_byte_buffer t = output_byte_buffer (super_term t)
72+
73+data DisplayContext = DisplayContext
74+ { super_display :: DisplayHandle
75+ }
76+
77+instance DisplayTerminal DisplayContext where
78+ context_region d = context_region (super_display d)
79+ context_color_count d = context_color_count (super_display d)
80+
81+ move_cursor_required_bytes d = move_cursor_required_bytes (super_display d)
82+ serialize_move_cursor d = serialize_move_cursor (super_display d)
83+
84+ show_cursor_required_bytes d = show_cursor_required_bytes (super_display d)
85+ serialize_show_cursor d = serialize_show_cursor (super_display d)
86+
87+ hide_cursor_required_bytes d = hide_cursor_required_bytes (super_display d)
88+ serialize_hide_cursor d = serialize_hide_cursor (super_display d)
89+
90+ attr_required_bytes d = attr_required_bytes (super_display d)
91+ serialize_set_attr d = serialize_set_attr (super_display d)
92+
93+ default_attr_required_bytes d = default_attr_required_bytes (super_display d)
94+ serialize_default_attr d = serialize_default_attr (super_display d)
95+
src/Graphics/Vty/Terminal/TerminfoBased.hsnew file+412
@@ -0,0 +1,412 @@
1+-- Copyright 2009 Corey O'Connor
2+{-# LANGUAGE ForeignFunctionInterface #-}
3+{-# LANGUAGE ScopedTypeVariables #-}
4+{-# LANGUAGE FlexibleInstances #-}
5+module Graphics.Vty.Terminal.TerminfoBased ( terminal_instance
6+ )
7+ where
8+
9+import Data.Terminfo.Parse
10+import Data.Terminfo.Eval
11+
12+import Graphics.Vty.Attributes
13+import Graphics.Vty.DisplayAttributes
14+import Graphics.Vty.Terminal.Generic
15+import Graphics.Vty.DisplayRegion
16+
17+import Control.Applicative
18+import Control.Monad ( foldM )
19+import Control.Monad.Trans
20+
21+import Data.Bits ( (.&.) )
22+import Data.Maybe ( isJust, isNothing, fromJust )
23+import Data.Word
24+
25+import Foreign.C.Types ( CLong )
26+
27+import qualified System.Console.Terminfo as Terminfo
28+import System.IO
29+
30+data Term = Term
31+ { term_info_ID :: String
32+ , term_info :: Terminfo.Terminal
33+ , smcup :: Maybe CapExpression
34+ , rmcup :: Maybe CapExpression
35+ , cup :: CapExpression
36+ , cnorm :: CapExpression
37+ , civis :: CapExpression
38+ , set_fore_color :: CapExpression
39+ , set_back_color :: CapExpression
40+ , set_default_attr :: CapExpression
41+ , clear_screen :: CapExpression
42+ , display_attr_caps :: DisplayAttrCaps
43+ }
44+
45+data DisplayAttrCaps = DisplayAttrCaps
46+ { set_attr_states :: Maybe CapExpression
47+ , enter_standout :: Maybe CapExpression
48+ , exit_standout :: Maybe CapExpression
49+ , enter_underline :: Maybe CapExpression
50+ , exit_underline :: Maybe CapExpression
51+ , enter_reverse_video :: Maybe CapExpression
52+ , enter_dim_mode :: Maybe CapExpression
53+ , enter_bold_mode :: Maybe CapExpression
54+ }
55+
56+marshall_cap_to_terminal :: MonadIO m => Term -> (Term -> CapExpression) -> [CapParam] -> m ()
57+marshall_cap_to_terminal t cap_selector cap_params = do
58+ marshall_to_terminal t ( cap_expression_required_bytes (cap_selector t) cap_params )
59+ ( serialize_cap_expression (cap_selector t) cap_params )
60+ return ()
61+
62+{- | Uses terminfo for all control codes. While this should provide the most compatible terminal
63+ - terminfo does not support some features that would increase efficiency and improve compatibility:
64+ - * determine the character encoding supported by the terminal. Should this be taken from the LANG
65+ - environment variable?
66+ - * Provide independent string capabilities for all display attributes.
67+ -
68+ -
69+ - todo: Some display attributes like underline and bold have independent string capabilities that
70+ - should be used instead of the generic "sgr" string capability.
71+ -}
72+terminal_instance :: ( Applicative m, MonadIO m ) => String -> m Term
73+terminal_instance in_ID = do
74+ ti <- liftIO $ Terminfo.setupTerm in_ID
75+ let require_cap str
76+ = case Terminfo.getCapability ti (Terminfo.tiGetStr str) of
77+ Nothing -> fail $ "Terminal does not define required capability \"" ++ str ++ "\""
78+ Just cap_str -> case parse_cap_expression cap_str of
79+ Left e -> fail $ show e
80+ Right cap -> return cap
81+ probe_cap cap_name
82+ = case Terminfo.getCapability ti (Terminfo.tiGetStr cap_name) of
83+ Nothing -> return Nothing
84+ Just cap_str -> case parse_cap_expression cap_str of
85+ Left e -> fail $ show e
86+ Right cap -> return $ Just cap
87+ pure Term
88+ <*> pure in_ID
89+ <*> pure ti
90+ <*> probe_cap "smcup"
91+ <*> probe_cap "rmcup"
92+ <*> require_cap "cup"
93+ <*> require_cap "cnorm"
94+ <*> require_cap "civis"
95+ <*> require_cap "setaf"
96+ <*> require_cap "setab"
97+ <*> require_cap "sgr0"
98+ <*> require_cap "clear"
99+ <*> current_display_attr_caps ti
100+
101+current_display_attr_caps :: ( Applicative m, MonadIO m )
102+ => Terminfo.Terminal
103+ -> m DisplayAttrCaps
104+current_display_attr_caps ti
105+ = pure DisplayAttrCaps
106+ <*> probe_cap "sgr"
107+ <*> probe_cap "smso"
108+ <*> probe_cap "rmso"
109+ <*> probe_cap "smul"
110+ <*> probe_cap "rmul"
111+ <*> probe_cap "rev"
112+ <*> probe_cap "dim"
113+ <*> probe_cap "bold"
114+ where probe_cap cap_name
115+ = case Terminfo.getCapability ti (Terminfo.tiGetStr cap_name) of
116+ Nothing -> return Nothing
117+ Just cap_str -> case parse_cap_expression cap_str of
118+ Left e -> fail $ show e
119+ Right cap -> return $ Just cap
120+
121+instance Terminal Term where
122+ terminal_ID t = term_info_ID t ++ " :: TerminfoBased"
123+
124+ release_terminal t = do
125+ marshall_cap_to_terminal t set_default_attr []
126+ marshall_cap_to_terminal t cnorm []
127+ return ()
128+
129+ reserve_display t = do
130+ if (isJust $ smcup t)
131+ then marshall_cap_to_terminal t (fromJust . smcup) []
132+ else return ()
133+ -- Screen on OS X does not appear to support smcup?
134+ -- To approximate the expected behavior: clear the screen and then move the mouse to the
135+ -- home position.
136+ marshall_cap_to_terminal t clear_screen []
137+ return ()
138+
139+ release_display t = do
140+ if (isJust $ rmcup t)
141+ then marshall_cap_to_terminal t (fromJust . rmcup) []
142+ else return ()
143+ marshall_cap_to_terminal t cnorm []
144+ return ()
145+
146+ display_terminal_instance t b c = do
147+ let color_count
148+ = case Terminfo.getCapability (term_info t) (Terminfo.tiGetNum "colors" ) of
149+ Nothing -> 8
150+ Just v -> toEnum v
151+ return $ c (DisplayContext b t color_count)
152+
153+ display_bounds _t = do
154+ raw_size <- liftIO $ get_window_size
155+ case raw_size of
156+ ( w, h ) | w < 0 || h < 0 -> fail $ "getwinsize returned < 0 : " ++ show raw_size
157+ | otherwise -> return $ DisplayRegion (toEnum w) (toEnum h)
158+
159+ -- Output the byte buffer of the specified size to the terminal device.
160+ output_byte_buffer _t out_ptr out_byte_count = do
161+ if out_byte_count == 0
162+ then return ()
163+ else liftIO $ hPutBuf stdout out_ptr (fromEnum out_byte_count)
164+ liftIO $ hFlush stdout
165+
166+foreign import ccall "gwinsz.h c_get_window_size" c_get_window_size :: IO CLong
167+
168+get_window_size :: IO (Int,Int)
169+get_window_size = do
170+ (a,b) <- (`divMod` 65536) `fmap` c_get_window_size
171+ return (fromIntegral b, fromIntegral a)
172+
173+data DisplayContext = DisplayContext
174+ { bounds :: DisplayRegion
175+ , term :: Term
176+ , supported_colors :: Word
177+ }
178+
179+instance DisplayTerminal DisplayContext where
180+ context_region d = bounds d
181+
182+ context_color_count d = supported_colors d
183+
184+ move_cursor_required_bytes d x y
185+ = cap_expression_required_bytes (cup $ term d) [y, x]
186+ serialize_move_cursor d x y out_ptr
187+ = serialize_cap_expression (cup $ term d) [y, x] out_ptr
188+
189+ show_cursor_required_bytes d
190+ = cap_expression_required_bytes (cnorm $ term d) []
191+ serialize_show_cursor d out_ptr
192+ = serialize_cap_expression (cnorm $ term d) [] out_ptr
193+
194+ hide_cursor_required_bytes d
195+ = cap_expression_required_bytes (civis $ term d) []
196+ serialize_hide_cursor d out_ptr
197+ = serialize_cap_expression (civis $ term d) [] out_ptr
198+
199+ -- Instead of evaluating all the rules related to setting display attributes twice (once in
200+ -- required bytes and again in serialize) or some memoization scheme just return a size
201+ -- requirement as big the longest possible control string.
202+ --
203+ -- Which is assumed to the be less than 512 for now.
204+ --
205+ -- \todo Not verified as safe and wastes memory.
206+ attr_required_bytes _d _prev_attr _req_attr _diffs = 512
207+
208+ -- Portably setting the display attributes is a giant pain in the ass.
209+ -- If the terminal supports the sgr capability (which sets the on/off state of each style
210+ -- directly ; and, for no good reason, resets the colors to the default) this always works:
211+ -- 0. set the style attributes. This resets the fore and back color.
212+ -- 1, If a foreground color is to be set then set the foreground color
213+ -- 2. likewise with the background color
214+ --
215+ -- If the terminal does not support the sgr then
216+ -- if there is a change from an applied color to the default (in either the fore or back color)
217+ -- then:
218+ -- 0. reset all display attributes (sgr0)
219+ -- 1. enter required style modes
220+ -- 2. set the fore color if required
221+ -- 3. set the back color if required
222+ --
223+ -- Entering the required style modes could require a reset of the display attributes. If this is
224+ -- the case then the back and fore colors always need to be set if not default.
225+ --
226+ -- All this (I think) is satisfied by the following logic:
227+ serialize_set_attr d prev_attr req_attr diffs out_ptr = do
228+ case (fore_color_diff diffs == ColorToDefault) || (back_color_diff diffs == ColorToDefault) of
229+ -- The only way to reset either color, portably, to the default is to use either the set
230+ -- state capability or the set default capability.
231+ True -> do
232+ case req_display_cap_seq_for ( display_attr_caps $ term d )
233+ ( fixed_style attr )
234+ ( style_to_apply_seq $ fixed_style attr )
235+ of
236+ EnterExitSeq caps
237+ -- only way to reset a color to the defaults
238+ -> serialize_default_attr d out_ptr
239+ >>= (\out_ptr' -> foldM (\ptr cap -> serialize_cap_expression cap [] ptr) out_ptr' caps)
240+ >>= set_colors
241+ SetState state
242+ -- implicitly resets the colors to the defaults
243+ -> serialize_cap_expression ( fromJust $ set_attr_states
244+ $ display_attr_caps
245+ $ term d
246+ )
247+ ( sgr_args_for_state state )
248+ out_ptr
249+ >>= set_colors
250+ -- Otherwise the display colors are not changing or changing between two non-default
251+ -- points.
252+ False -> do
253+ -- Still, it could be the case that the change in display attributes requires the
254+ -- colors to be reset because the required capability was not available.
255+ case req_display_cap_seq_for ( display_attr_caps $ term d )
256+ ( fixed_style attr )
257+ ( style_diffs diffs )
258+ of
259+ -- Really, if terminals were re-implemented with modern concepts instead of
260+ -- bowing down to 40 yr old dumb terminal requirements this would be the
261+ -- only case ever reached!
262+ -- Changes the style and color states according to the differences with the
263+ -- currently applied states.
264+ EnterExitSeq caps
265+ -> foldM (\ptr cap -> serialize_cap_expression cap [] ptr) out_ptr caps
266+ >>= apply_color_diff set_fore_color ( fore_color_diff diffs )
267+ >>= apply_color_diff set_back_color ( back_color_diff diffs )
268+ SetState state
269+ -- implicitly resets the colors to the defaults
270+ -> serialize_cap_expression ( fromJust $ set_attr_states
271+ $ display_attr_caps
272+ $ term d
273+ )
274+ ( sgr_args_for_state state )
275+ out_ptr
276+ >>= set_colors
277+ where
278+ attr = fix_display_attr prev_attr req_attr
279+ set_colors ptr = do
280+ ptr' <- case fixed_fore_color attr of
281+ Just c -> serialize_cap_expression ( set_fore_color $ term d )
282+ [ ansi_color_index c ]
283+ ptr
284+ Nothing -> return ptr
285+ ptr'' <- case fixed_back_color attr of
286+ Just c -> serialize_cap_expression ( set_back_color $ term d )
287+ [ ansi_color_index c ]
288+ ptr'
289+ Nothing -> return ptr'
290+ return ptr''
291+ apply_color_diff _f NoColorChange ptr
292+ = return ptr
293+ apply_color_diff _f ColorToDefault _ptr
294+ = fail "ColorToDefault is not a possible case for apply_color_diffs"
295+ apply_color_diff f ( SetColor c ) ptr
296+ = serialize_cap_expression ( f $ term d )
297+ [ ansi_color_index c ]
298+ ptr
299+
300+ default_attr_required_bytes d
301+ = cap_expression_required_bytes (set_default_attr $ term d) []
302+ serialize_default_attr d out_ptr = do
303+ serialize_cap_expression ( set_default_attr $ term d ) [] out_ptr
304+
305+ansi_color_index :: Color -> Word
306+ansi_color_index (ISOColor v) = toEnum $ fromEnum v
307+ansi_color_index (Color240 v) = 16 + ( toEnum $ fromEnum v )
308+
309+{- The sequence of terminfo caps to apply a given style are determined according to these rules.
310+ -
311+ - 1. The assumption is that it's preferable to use the simpler enter/exit mode capabilities than
312+ - the full set display attribute state capability.
313+ -
314+ - 2. If a mode is supposed to be removed but there is not an exit capability defined then the
315+ - display attributes are reset to defaults then the display attribute state is set.
316+ -
317+ - 3. If a mode is supposed to be applied but there is not an enter capability defined then then
318+ - display attribute state is set if possible. Otherwise the mode is not applied.
319+ -
320+ - 4. If the display attribute state is being set then just update the arguments to that for any
321+ - apply/remove.
322+ -
323+ -}
324+
325+data DisplayAttrSeq
326+ = EnterExitSeq [CapExpression]
327+ | SetState DisplayAttrState
328+
329+data DisplayAttrState = DisplayAttrState
330+ { apply_standout :: Bool
331+ , apply_underline :: Bool
332+ , apply_reverse_video :: Bool
333+ , apply_blink :: Bool
334+ , apply_dim :: Bool
335+ , apply_bold :: Bool
336+ }
337+
338+sgr_args_for_state :: DisplayAttrState -> [CapParam]
339+sgr_args_for_state attr_state = map (\b -> if b then 1 else 0)
340+ [ apply_standout attr_state
341+ , apply_underline attr_state
342+ , apply_reverse_video attr_state
343+ , apply_blink attr_state
344+ , apply_dim attr_state
345+ , apply_bold attr_state
346+ , False -- invis
347+ , False -- protect
348+ , False -- alt char set
349+ ]
350+
351+req_display_cap_seq_for :: DisplayAttrCaps -> Style -> [StyleStateChange] -> DisplayAttrSeq
352+req_display_cap_seq_for caps s diffs
353+ -- if the state transition implied by any diff cannot be supported with an enter/exit mode cap
354+ -- then either the state needs to be set or the attribute change ignored.
355+ = case (any no_enter_exit_cap diffs, isJust $ set_attr_states caps) of
356+ -- If all the diffs have an enter-exit cap then just use those
357+ ( False, _ ) -> EnterExitSeq $ map enter_exit_cap diffs
358+ -- If not all the diffs have an enter-exit cap and there is no set state cap then filter out
359+ -- all unsupported diffs and just apply the rest
360+ ( True, False ) -> EnterExitSeq $ map enter_exit_cap
361+ $ filter (not . no_enter_exit_cap) diffs
362+ -- if not all the diffs have an enter-exit can and there is a set state cap then just use
363+ -- the set state cap.
364+ ( True, True ) -> SetState $ state_for_style s
365+ where
366+ no_enter_exit_cap ApplyStandout = isNothing $ enter_standout caps
367+ no_enter_exit_cap RemoveStandout = isNothing $ exit_standout caps
368+ no_enter_exit_cap ApplyUnderline = isNothing $ enter_underline caps
369+ no_enter_exit_cap RemoveUnderline = isNothing $ exit_underline caps
370+ no_enter_exit_cap ApplyReverseVideo = isNothing $ enter_reverse_video caps
371+ no_enter_exit_cap RemoveReverseVideo = True
372+ no_enter_exit_cap ApplyBlink = True
373+ no_enter_exit_cap RemoveBlink = True
374+ no_enter_exit_cap ApplyDim = isNothing $ enter_dim_mode caps
375+ no_enter_exit_cap RemoveDim = True
376+ no_enter_exit_cap ApplyBold = isNothing $ enter_bold_mode caps
377+ no_enter_exit_cap RemoveBold = True
378+ enter_exit_cap ApplyStandout = fromJust $ enter_standout caps
379+ enter_exit_cap RemoveStandout = fromJust $ exit_standout caps
380+ enter_exit_cap ApplyUnderline = fromJust $ enter_underline caps
381+ enter_exit_cap RemoveUnderline = fromJust $ exit_underline caps
382+ enter_exit_cap ApplyReverseVideo = fromJust $ enter_reverse_video caps
383+ enter_exit_cap ApplyDim = fromJust $ enter_dim_mode caps
384+ enter_exit_cap ApplyBold = fromJust $ enter_bold_mode caps
385+ enter_exit_cap _ = error "enter_exit_cap applied to diff that was known not to have one."
386+
387+state_for_style :: Style -> DisplayAttrState
388+state_for_style s = DisplayAttrState
389+ { apply_standout = is_style_set standout
390+ , apply_underline = is_style_set underline
391+ , apply_reverse_video = is_style_set reverse_video
392+ , apply_blink = is_style_set blink
393+ , apply_dim = is_style_set dim
394+ , apply_bold = is_style_set bold
395+ }
396+ where is_style_set = has_style s
397+
398+style_to_apply_seq :: Style -> [StyleStateChange]
399+style_to_apply_seq s = concat
400+ [ apply_if_required ApplyStandout standout
401+ , apply_if_required ApplyUnderline underline
402+ , apply_if_required ApplyReverseVideo reverse_video
403+ , apply_if_required ApplyBlink blink
404+ , apply_if_required ApplyDim dim
405+ , apply_if_required ApplyBlink bold
406+ ]
407+ where
408+ apply_if_required ap flag
409+ = if 0 == ( flag .&. s )
410+ then []
411+ else [ ap ]
412+
src/Graphics/Vty/Terminal/XTermColor.hsnew file+94
@@ -0,0 +1,94 @@
1+-- Copyright 2009 Corey O'Connor
2+{-# LANGUAGE ForeignFunctionInterface #-}
3+{-# LANGUAGE ScopedTypeVariables #-}
4+{-# LANGUAGE MagicHash #-}
5+{-# LANGUAGE FlexibleInstances #-}
6+{-# LANGUAGE TypeFamilies #-}
7+module Graphics.Vty.Terminal.XTermColor ( terminal_instance
8+ )
9+ where
10+
11+import Graphics.Vty.Terminal.Generic
12+import qualified Graphics.Vty.Terminal.TerminfoBased as TerminfoBased
13+
14+import Control.Applicative
15+import Control.Monad.Trans
16+
17+import System.IO
18+
19+data XTermColor = XTermColor
20+ { xterm_variant :: String
21+ , super_term :: TerminalHandle
22+ }
23+
24+-- Initialize the display to UTF-8
25+-- Regardless of what is output the text encoding is assumed to be UTF-8
26+terminal_instance :: ( Applicative m, MonadIO m ) => String -> m XTermColor
27+terminal_instance variant = do
28+ -- If the terminal variant is xterm-color use xterm instead since, more often than not,
29+ -- xterm-color is broken.
30+ let variant' = if variant == "xterm-color" then "xterm" else variant
31+ flushed_put set_utf8_char_set
32+ t <- TerminfoBased.terminal_instance variant' >>= new_terminal_handle
33+ return $ XTermColor variant' t
34+
35+flushed_put :: MonadIO m => String -> m ()
36+flushed_put str = do
37+ liftIO $ hPutStr stdout str
38+ liftIO $ hFlush stdout
39+
40+-- Since I don't know of a terminfo string cap that produces these strings these are hardcoded.
41+set_utf8_char_set, set_default_char_set :: String
42+set_utf8_char_set = "\ESC%G"
43+set_default_char_set = "\ESC%@"
44+
45+instance Terminal XTermColor where
46+ terminal_ID t = (show $ xterm_variant t) ++ " :: XTermColor"
47+
48+ release_terminal t = do
49+ flushed_put set_default_char_set
50+ release_terminal $ super_term t
51+
52+ reserve_display t = reserve_display (super_term t)
53+
54+ release_display t = release_display (super_term t)
55+
56+ display_terminal_instance t b c = do
57+ d <- display_context (super_term t) b
58+ return $ c (DisplayContext d)
59+
60+ display_bounds t = display_bounds (super_term t)
61+
62+ output_byte_buffer t = output_byte_buffer (super_term t)
63+
64+data DisplayContext = DisplayContext
65+ { super_display :: DisplayHandle
66+ }
67+
68+instance DisplayTerminal DisplayContext where
69+ context_region d = context_region (super_display d)
70+
71+ context_color_count d = context_color_count (super_display d)
72+
73+ move_cursor_required_bytes d = move_cursor_required_bytes (super_display d)
74+ serialize_move_cursor d = serialize_move_cursor (super_display d)
75+
76+ show_cursor_required_bytes d = show_cursor_required_bytes (super_display d)
77+ serialize_show_cursor d = serialize_show_cursor (super_display d)
78+
79+ hide_cursor_required_bytes d = hide_cursor_required_bytes (super_display d)
80+ serialize_hide_cursor d = serialize_hide_cursor (super_display d)
81+
82+ attr_required_bytes d = attr_required_bytes (super_display d)
83+ serialize_set_attr d = serialize_set_attr (super_display d)
84+
85+ default_attr_required_bytes d = default_attr_required_bytes (super_display d)
86+ serialize_default_attr d = serialize_default_attr (super_display d)
87+
88+ -- I think xterm is broken: Reseting the background color as the first bytes serialized on a new
89+ -- line does not effect the background color xterm uses to clear the line. Which is used *after*
90+ -- the next newline.
91+ inline_hack _d = do
92+ liftIO $ hPutStr stdout "\ESC[K"
93+ liftIO $ hFlush stdout
94+
src/Graphics/Vty/UnicodeWidthTable/IO.hsdeleted−105
@@ -1,105 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−module Graphics.Vty.UnicodeWidthTable.IO
3− ( readUnicodeWidthTable
4− , parseUnicodeWidthTable
5− , writeUnicodeWidthTable
6− )
7−where
8−
9−import Control.Monad (when, forM)
10−import Data.Binary
11−import Data.Binary.Get
12−import Data.Binary.Put
13−import qualified Data.ByteString.Lazy as BSL
14−#if !(MIN_VERSION_base(4,11,0))
15−import Data.Semigroup ((<>))
16−#endif
17−
18−import Graphics.Vty.UnicodeWidthTable.Types
19−
20−-- | Load a binary unicode width table from the specified file.
21−--
22−-- This either returns a successfully parsed table or a table parsing
23−-- error message. This does not handle I/O exceptions.
24−readUnicodeWidthTable :: FilePath -> IO (Either String UnicodeWidthTable)
25−readUnicodeWidthTable path = parseUnicodeWidthTable <$> BSL.readFile path
26−
27−-- | Parse a binary unicode width table.
28−parseUnicodeWidthTable :: BSL.ByteString -> Either String UnicodeWidthTable
29−parseUnicodeWidthTable bs =
30− case runGetOrFail tableParser bs of
31− Left (_, _, msg) ->
32− Left msg
33−
34− -- Even if we parsed a table, leftover bytes indicate something
35− -- could be wrong.
36− Right (remainingBytes, _, _) | not (BSL.null remainingBytes) ->
37− Left $ "Error: " <> show (BSL.length remainingBytes) <>
38− " byte(s) left unconsumed"
39−
40− Right (_, _, table) ->
41− Right table
42−
43−-- | Write the unicode width table to the specified path.
44−--
45−-- This does not handle I/O exceptions.
46−writeUnicodeWidthTable :: FilePath -> UnicodeWidthTable -> IO ()
47−writeUnicodeWidthTable path table = do
48− let body = runPut (tableV1Writer table)
49− BSL.writeFile path body
50−
51−-- | Width table magic bytes for use in the binary format.
52−widthTableMagic :: Word32
53−widthTableMagic = 0xc1a9f7e0
54−
55−tableParser :: Get UnicodeWidthTable
56−tableParser = do
57− magic <- getWord32le
58−
59− when (magic /= widthTableMagic) $
60− fail "Table magic number invalid"
61−
62− version <- getWord8
63−
64− case version of
65− 1 -> tableV1Parser
66− _ -> fail "Table version invalid"
67−
68−tableV1Parser :: Get UnicodeWidthTable
69−tableV1Parser = do
70− numRanges <- getWord32le
71−
72− let parseRange = do
73− start <- getWord32le
74− size <- getWord32le
75− cols <- getWord8
76− return WidthTableRange { rangeStart = start
77− , rangeSize = size
78− , rangeColumns = cols
79− }
80−
81− ranges <- forM [1..numRanges] $ const parseRange
82−
83− return UnicodeWidthTable { unicodeWidthTableRanges = ranges
84− }
85−
86−tableV1Writer :: UnicodeWidthTable -> Put
87−tableV1Writer table = do
88− -- Magic bytes
89− putWord32le widthTableMagic
90−
91− -- Version
92− putWord8 1
93−
94− -- Number of ranges
95− let ranges = unicodeWidthTableRanges table
96− let numRanges = length ranges
97− putWord32le (fromIntegral numRanges)
98−
99− -- Ranges
100− let putRange r = do
101− putWord32le $ rangeStart r
102− putWord32le $ rangeSize r
103− putWord8 $ rangeColumns r
104−
105− mapM_ putRange ranges
src/Graphics/Vty/UnicodeWidthTable/Install.hsdeleted−129
@@ -1,129 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE ForeignFunctionInterface #-}
3−module Graphics.Vty.UnicodeWidthTable.Install
4− ( TableInstallException(..)
5− , installUnicodeWidthTable
6− , isCustomTableReady
7− )
8−where
9−
10−import Control.Monad (when, forM_)
11−import qualified Control.Exception as E
12−import GHC.Conc.Sync (withMVar)
13−import Control.Concurrent.MVar (MVar, newMVar)
14−import Data.Word (Word8, Word32)
15−#if !(MIN_VERSION_base(4,11,0))
16−import Data.Semigroup ((<>))
17−#endif
18−import System.IO.Unsafe (unsafePerformIO)
19−
20−import Graphics.Vty.UnicodeWidthTable.Types
21−
22−-- | The lock used to make functions in this module thread-safe.
23−installLock :: MVar ()
24−{-# NOINLINE installLock #-}
25−installLock = unsafePerformIO $ newMVar ()
26−
27−-- | Returns True if and only if a custom table has been allocated and
28−-- marked as ready for use.
29−--
30−-- This function is thread-safe.
31−isCustomTableReady :: IO Bool
32−isCustomTableReady = withInstallLock $ (== 1) <$> c_isCustomTableReady
33−
34−withInstallLock :: IO a -> IO a
35−withInstallLock act = withMVar installLock $ const act
36−
37−-- This is the size of the allocated custom character width table, in
38−-- character slots. It's important that this be large enough to hold all
39−-- possible Unicode character values. At the time of this writing, the
40−-- valid Unicode range is 0 - 0x10ffff, hence this value.
41−tableSize :: Int
42−tableSize = 0x110000
43−
44−-- | Exception type raised by 'installUnicodeWidthTable'.
45−data TableInstallException =
46− TableInitFailure Int Int
47− -- ^ The width table could not be initialized. Args: failure status,
48− -- requested table size.
49− | TableRangeFailure Int WidthTableRange
50− -- ^ A code point range could not be configured. Args: failure
51− -- status, offending range.
52− | TableActivationFailure Int
53− -- ^ The table could not be activated. Args: failure status.
54− deriving (Eq, Show)
55−
56−instance E.Exception TableInstallException
57−
58−-- | Install a custom unicode character width
59−-- table. Such tables are obtained with
60−-- 'Graphics.Vty.UnicodeWidthTable.Query.buildUnicodeWidthTable' and
61−-- 'Graphics.Vty.UnicodeWidthTable.IO.readUnicodeWidthTable'.
62−--
63−-- ALERT! This function is probably not what you want to use because
64−-- it is automatically called by 'Graphics.Vty.mkVty'. You will only
65−-- ever need to call this function if you want to use functions
66−-- in 'Graphics.Text.Width' without controlling the terminal with
67−-- 'Graphics.Vty.mkVty'.
68−--
69−-- This affects the behavior of the 'Graphics.Vty.Image.wcwidth'
70−-- function and functions that call it. It does so by
71−-- changing global state available to the C implementation
72−-- of 'Graphics.Vty.Image.wcwidth'. To ensure that your
73−-- program gets consistent results from evaluating calls to
74−-- 'Graphics.Vty.Image.wcwidth', the installation of a custom table
75−-- should be performed before you call 'Graphics.Vty.Image.wcwidth' in
76−-- your program.
77−--
78−-- This is best done at Vty startup, and if you use
79−-- 'Graphics.Vty.mkVty', that function calls this automatically based on
80−-- the Vty configuration's declared width tables. It is exposed as part
81−-- of the public API so that applications can call this as needed if
82−-- they don't want to control the terminal with 'mkVty' but do want to
83−-- make calls to 'Graphics.Vty.Image.wcwidth'.
84−--
85−-- It's also important to note that once a custom table has been
86−-- installed, it is permanent for the life of the process. No new table
87−-- can be installed, and the new custom table cannot be removed.
88−--
89−-- If this function fails for any reason -- if the table cannot be
90−-- installed or is invalid, or if a custom table already exists -- this
91−-- will raise a 'TableInstallException' exception.
92−--
93−-- This function is thread-safe.
94−installUnicodeWidthTable :: UnicodeWidthTable -> IO ()
95−installUnicodeWidthTable table = withInstallLock $ do
96− initResult <- initCustomTable tableSize
97− when (initResult /= 0) $
98− E.throwIO $ TableInitFailure initResult tableSize
99−
100− forM_ (unicodeWidthTableRanges table) $ \r -> do
101− result <- setCustomTableRange (rangeStart r)
102− (rangeSize r)
103− (rangeColumns r)
104−
105− when (result /= 0) $ do
106− deallocateCustomTable
107− E.throwIO $ TableRangeFailure result r
108−
109− actResult <- activateCustomTable
110− when (actResult /= 0) $
111− E.throwIO $ TableActivationFailure actResult
112−
113−------------------------------------------------------------------------
114−-- C imports
115−
116−foreign import ccall unsafe "vty_init_custom_table"
117− initCustomTable :: Int -> IO Int
118−
119−foreign import ccall unsafe "vty_set_custom_table_range"
120− setCustomTableRange :: Word32 -> Word32 -> Word8 -> IO Int
121−
122−foreign import ccall unsafe "vty_activate_custom_table"
123− activateCustomTable :: IO Int
124−
125−foreign import ccall unsafe "vty_custom_table_ready"
126− c_isCustomTableReady :: IO Int
127−
128−foreign import ccall unsafe "vty_deallocate_custom_table"
129− deallocateCustomTable :: IO ()
src/Graphics/Vty/UnicodeWidthTable/Query.hsdeleted−79
@@ -1,79 +0,0 @@
1−{-# LANGUAGE TupleSections #-}
2−module Graphics.Vty.UnicodeWidthTable.Query
3− ( buildUnicodeWidthTable
4− , defaultUnicodeTableUpperBound
5− )
6−where
7−
8−import Control.Monad (forM)
9−import Data.Char (generalCategory, GeneralCategory(..))
10−import System.Console.ANSI (getCursorPosition)
11−import Text.Printf (printf)
12−
13−import Graphics.Vty.UnicodeWidthTable.Types
14−
15−shouldConsider :: Char -> Bool
16−shouldConsider c =
17− case generalCategory c of
18− Control -> False
19− NotAssigned -> False
20− Surrogate -> False
21− _ -> True
22−
23−charWidth :: Char -> IO Int
24−charWidth c = do
25− printf "\r"
26− putChar c
27− Just (_, col) <- getCursorPosition
28− return col
29−
30−-- | Convert a sequence of character/width pairs into a list of
31−-- run-length encoded ranges. This function assumes the pairs come
32−-- sorted by character ordinal value. It does not require that the
33−-- character range is fully covered by the sequence.
34−--
35−-- The result of this function is a list of ranges in reverse order
36−-- relative to the input sequence.
37−mkRanges :: [(Char, Int)] -> [WidthTableRange]
38−mkRanges pairs =
39− let convertedPairs = convert <$> pairs
40− convert (c, i) = (fromIntegral $ fromEnum c, fromIntegral i)
41−
42− go Nothing finishedRanges [] = finishedRanges
43− go (Just r) finishedRanges [] = r:finishedRanges
44−
45− go Nothing finishedRanges ((c, width):rest) =
46− go (Just $ WidthTableRange c 1 width) finishedRanges rest
47−
48− go (Just r@(WidthTableRange prevCh sz prevWidth)) finishedRanges ((c, width):rest) =
49− if c == prevCh + sz && prevWidth == width
50− then go (Just (WidthTableRange prevCh (sz + 1) prevWidth)) finishedRanges rest
51− else go (Just (WidthTableRange c 1 width)) (r:finishedRanges) rest
52−
53− in go Nothing [] convertedPairs
54−
55−-- The uppermost code point to consider when building Unicode width
56−-- tables.
57−defaultUnicodeTableUpperBound :: Char
58−defaultUnicodeTableUpperBound = '\xe0000'
59−
60−-- | Construct a unicode character width table by querying the terminal
61−-- connected to stdout. This works by emitting characters to stdout
62−-- and then querying the terminal to determine the resulting cursor
63−-- position in order to measure character widths. Consequently this will
64−-- generate a lot of output and may take a while, depending on your
65−-- system performance. This should not be run in a terminal while it is
66−-- controlled by Vty.
67−--
68−-- The argument specifies the upper bound code point to test when
69−-- building the table. This allows callers to decide how much of the
70−-- Unicode code point space to scan when building the table.
71−--
72−-- This does not handle exceptions.
73−buildUnicodeWidthTable :: Char -> IO UnicodeWidthTable
74−buildUnicodeWidthTable tableUpperBound = do
75− pairs <- forM (filter shouldConsider ['\0'..tableUpperBound]) $ \i ->
76− (i,) <$> charWidth i
77−
78− return UnicodeWidthTable { unicodeWidthTableRanges = reverse $ mkRanges pairs
79− }
src/Graphics/Vty/UnicodeWidthTable/Types.hsdeleted−29
@@ -1,29 +0,0 @@
1−module Graphics.Vty.UnicodeWidthTable.Types
2− ( UnicodeWidthTable(..)
3− , WidthTableRange(..)
4− )
5−where
6−
7−import Data.Word (Word8, Word32)
8−
9−-- | A range of code points in a width table.
10−data WidthTableRange =
11− WidthTableRange { rangeStart :: Word32
12− -- ^ The range's starting code point.
13− , rangeSize :: Word32
14− -- ^ The number of code points in the contiguous
15− -- range, beginning with the starting code point
16− -- ('rangeStart').
17− , rangeColumns :: Word8
18− -- ^ The terminal width, in columns, of all of the
19− -- characters corresponding to the code points in
20− -- this range.
21− }
22− deriving (Eq, Show)
23−
24−-- | A run-length-encoded table of Unicode character widths.
25−data UnicodeWidthTable =
26− UnicodeWidthTable { unicodeWidthTableRanges :: [WidthTableRange]
27− -- ^ The ranges in the table.
28− }
29− deriving (Show)
test/Bench.hsnew file+62
@@ -0,0 +1,62 @@
1+module Main where
2+
3+import Graphics.Vty
4+
5+import Control.Concurrent( threadDelay )
6+import Control.Monad( liftM2 )
7+
8+import Data.List
9+import Data.Word
10+
11+import System.Environment( getArgs )
12+import System.IO
13+import System.Random
14+
15+
16+main = do
17+ let fixed_gen = mkStdGen 0
18+ setStdGen fixed_gen
19+ args <- getArgs
20+ case args of
21+ [] -> mkVty >>= liftM2 (>>) (run $ Just 0) shutdown
22+ ["--delay"] -> mkVty >>= liftM2 (>>) (run $ Just 1000000) shutdown
23+ ["--slow"] -> mkVty >>= liftM2 (>>) (run Nothing ) shutdown
24+ _ -> fail "usage: ./Bench [--slow|--delay]"
25+
26+run (Just delay) vt = mapM_ (\p -> update vt p >> threadDelay delay) . benchgen =<< display_bounds (terminal vt)
27+run Nothing vt = mapM_ (update vt) (benchgen $ DisplayRegion 200 100)
28+
29+-- Currently, we just do scrolling.
30+takem :: (a -> Word) -> Word -> [a] -> ([a],[a])
31+takem len n [] = ([],[])
32+takem len n (x:xs) | lx > n = ([], x:xs)
33+ | True = let (tk,dp) = takem len (n - lx) xs in (x:tk,dp)
34+ where lx = len x
35+
36+fold :: (a -> Word) -> [Word] -> [a] -> [[a]]
37+fold len [] xs = []
38+fold len (ll:lls) xs = let (tk,dp) = takem len ll xs in tk : fold len lls dp
39+
40+lengths :: Word -> StdGen -> [Word]
41+lengths ml g =
42+ let (x,g2) = randomR (0,fromEnum ml) g
43+ (y,g3) = randomR (0,x) g2
44+ in (toEnum y) : lengths ml g3
45+
46+nums :: StdGen -> [(Attr, String)]
47+nums g = let (x,g2) = (random g :: (Int, StdGen))
48+ (c,g3) = random g2
49+ in ( if c then def_attr `with_fore_color` red else def_attr
50+ , shows x " "
51+ ) : nums g3
52+
53+pad :: Word -> Image -> Image
54+pad ml img = img <|> char_fill def_attr ' ' (ml - image_width img) 1
55+
56+clines :: StdGen -> Word -> [Image]
57+clines g maxll = map (pad maxll . horiz_cat . map (uncurry string)) $ fold (toEnum . length . snd) (lengths maxll g1) (nums g2)
58+ where (g1,g2) = split g
59+
60+benchgen :: DisplayRegion -> [Picture]
61+benchgen (DisplayRegion w h) = take 500 $ map ((\i -> pic_for_image i) . vert_cat . take (fromEnum h)) $ tails $ clines (mkStdGen 42) w
62+
test/Bench2.hsnew file+30
@@ -0,0 +1,30 @@
1+{-# LANGUAGE BangPatterns #-}
2+import Graphics.Vty
3+import System.IO
4+import System.Environment( getArgs )
5+import Control.Concurrent( threadDelay )
6+import System.Random
7+import Data.List
8+import Control.Monad( liftM2 )
9+
10+import qualified Data.ByteString.Char8 as B
11+
12+main = do
13+ let fixed_gen = mkStdGen 0
14+ setStdGen fixed_gen
15+ args <- getArgs
16+ vt <- mkVty
17+ (w, h) <- getSize vt
18+ let !image_0 = renderFill attr 'X' w h
19+ let !image_1 = renderFill attr '0' w h
20+ run vt image_0 image_1
21+ shutdown vt
22+
23+run vt image_0 image_1 = run' vt 300 image_0 image_1
24+
25+run' vt 0 image_0 image_1 = return ()
26+run' vt n image_0 image_1 = do
27+ let p = pic { pImage = image_0 }
28+ update vt p
29+ run' vt (n - 1) image_1 image_0
30+
test/BenchImageFuzz.hsdeleted−29
@@ -1,29 +0,0 @@
1−module BenchImageFuzz where
2−import Graphics.Vty
3−import Graphics.Vty.Debug
4−
5−import Verify.Graphics.Vty.Image
6−import Verify
7−
8−import Control.Applicative
9−import Control.Monad
10−
11−import System.Random
12−
13−rand :: Arbitrary a => IO a
14−rand = head <$> sample' arbitrary
15−
16−randomImage :: IO Image
17−randomImage = rand
18−
19−randomPicture = picForImage <$> randomImage
20−
21−bench0 = do
22− vty <- mkVty defaultConfig
23− (w,h) <- displayBounds $ outputIface vty
24− let pictures = replicateM 3000 randomPicture
25− bench ps = do
26− forM ps (update vty)
27− shutdown vty
28− return $ Bench pictures bench
29−
test/BenchNoDiffOpt.hsdeleted−38
@@ -1,38 +0,0 @@
1−{-# LANGUAGE BangPatterns #-}
2−module BenchNoDiffOpt where
3−
4−{-# LANGUAGE BangPatterns #-}
5−import Graphics.Vty
6−import Verify
7−
8−import Control.Concurrent( threadDelay )
9−import Control.Monad( liftM2 )
10−
11−import qualified Data.ByteString.Char8 as B
12−import Data.List
13−
14−import System.Environment( getArgs )
15−import System.IO
16−import System.Random
17−
18−bench0 = do
19− let fixedGen = mkStdGen 0
20− setStdGen fixedGen
21− vty <- mkVty defaultConfig
22− (w,h) <- displayBounds $ outputIface vty
23− let images = return $ (image0, image1)
24− image0 = charFill defAttr 'X' w h
25− image1 = charFill defAttr '0' w h
26− bench d = do
27− flipOut vty 300 image0 image1
28− shutdown vty
29− return $ Bench images bench
30−
31−flipOut vty n image0 image1 =
32− let !pLeft = picForImage image0
33− !pRight = picForImage image1
34− wLeft 0 = return ()
35− wLeft n = update vty pLeft >> wRight (n-1)
36− wRight 0 = return ()
37− wRight n = update vty pRight >> wLeft (n-1)
38− in wLeft n
test/BenchRenderChar.hs+26−32
@@ -1,38 +1,32 @@
1−-- benchmarks composing images using the renderChar operation. This is
2−-- what Yi uses in Yi.UI.Vty.drawText. Ideally a sequence of renderChar
3−-- images horizontally composed should provide no worse performance than
4−-- a fill render op.
5−module BenchRenderChar where
6−
1+{- benchmarks composing images using the renderChar operation.
2+ - This is what Yi uses in Yi.UI.Vty.drawText. Ideally a sequence of renderChar images horizontally
3+ - composed should provide no worse performance than a fill render op.
4+ -}
75import Graphics.Vty
8−import Verify
96
107import Control.Monad ( forM_ )
118
12−bench0 = do
13− vty <- mkVty defaultConfig
14− (w,h) <- displayBounds $ outputIface vty
15− let testChars = return $ take 500 $ cycle $ [ c | c <- ['a'..'z']]
16− bench d = do
17− forM_ d $ \testChar -> do
18− let testImage = testImageUsingChar testChar w h
19− outPic = picForImage testImage
20− update vty outPic
21− shutdown vty
22− return $ Bench testChars bench
9+import System.Time
10+
11+main = do
12+ vty <- mkVty
13+ (w, h) <- getSize vty
14+ let test_chars = take 500 $ cycle $ [ c | c <- ['a'..'z']]
15+ start_time_0 <- getClockTime
16+ forM_ test_chars $ \test_char -> do
17+ let test_image = test_image_using_renderChar test_char w h
18+ out_pic = pic { pImage = test_image }
19+ update vty out_pic
20+ end_time_0 <- getClockTime
21+ let start_time_1 = end_time_0
22+ forM_ test_chars $ \test_char -> do
23+ let test_image = renderFill attr test_char w h
24+ out_pic = pic { pImage = test_image }
25+ update vty out_pic
26+ end_time_1 <- getClockTime
27+ shutdown vty
28+ putStrLn $ timeDiffToString $ diffClockTimes end_time_0 start_time_0
29+ putStrLn $ timeDiffToString $ diffClockTimes end_time_1 start_time_1
2330
24−testImageUsingChar c w h
25− = vertCat $ replicate (fromIntegral h)
26− $ horizCat $ map (char defAttr) (replicate (fromIntegral w) c)
31+test_image_using_renderChar c w h = vertcat $ replicate h $ horzcat $ map (renderChar attr) (replicate w c)
2732
28−bench1 = do
29− vty <- mkVty defaultConfig
30− (w,h) <- displayBounds $ outputIface vty
31− let testChars = return $ take 500 $ cycle $ [ c | c <- ['a'..'z']]
32− bench d = do
33− forM_ d $ \testChar -> do
34− let testImage = charFill defAttr testChar w h
35− outPic = picForImage testImage
36− update vty outPic
37− shutdown vty
38− return $ Bench testChars bench
test/BenchVerticalScroll.hsdeleted−59
@@ -1,59 +0,0 @@
1−module BenchVerticalScroll where
2−
3−import Graphics.Vty hiding ( pad )
4−import Verify
5−
6−import Control.Concurrent( threadDelay )
7−import Control.Monad( liftM2 )
8−
9−import Data.List
10−import Data.Word
11−
12−import System.Environment( getArgs )
13−import System.IO
14−import System.Random
15−
16−bench0 = do
17− let fixedGen = mkStdGen 0
18− setStdGen fixedGen
19− return $ Bench (return ()) (\() -> mkVty defaultConfig >>= liftM2 (>>) run shutdown)
20−
21−run vt = mapM_ (\p -> update vt p) . benchgen =<< displayBounds (outputIface vt)
22−
23−-- Currently, we just do scrolling.
24−takem :: (a -> Int) -> Int -> [a] -> ([a],[a])
25−takem len n [] = ([],[])
26−takem len n (x:xs) | lx > n = ([], x:xs)
27− | True = let (tk,dp) = takem len (n - lx) xs in (x:tk,dp)
28− where lx = len x
29−
30−fold :: (a -> Int) -> [Int] -> [a] -> [[a]]
31−fold len [] xs = []
32−fold len (ll:lls) xs = let (tk,dp) = takem len ll xs in tk : fold len lls dp
33−
34−lengths :: Int -> StdGen -> [Int]
35−lengths ml g =
36− let (x,g2) = randomR (0,ml) g
37− (y,g3) = randomR (0,x) g2
38− in y : lengths ml g3
39−
40−nums :: StdGen -> [(Attr, String)]
41−nums g = let (x,g2) = (random g :: (Int, StdGen))
42− (c,g3) = random g2
43− in ( if c then defAttr `withForeColor` red else defAttr
44− , shows x " "
45− ) : nums g3
46−
47−pad :: Int -> Image -> Image
48−pad ml img = img <|> charFill defAttr ' ' (ml - imageWidth img) 1
49−
50−clines :: StdGen -> Int -> [Image]
51−clines g maxll = map (pad maxll . horizCat . map (uncurry string))
52− $ fold (length . snd) (lengths maxll g1) (nums g2)
53− where (g1,g2) = split g
54−
55−benchgen :: DisplayRegion -> [Picture]
56−benchgen (w,h)
57− = take 2000 $ map ((\i -> picForImage i) . vertCat . take (fromEnum h))
58− $ tails
59− $ clines (mkStdGen 80) w
test/ControlTable.hs+4−4
@@ -5,10 +5,10 @@ import Graphics.Vty.ControlStrings
55import System.Console.Terminfo
66
77main = do
8− terminal <- setupTermFromEnv
9− controlTable <- init_controlTable terminal
8+ terminal <- setupTermFromEnv
9+ control_table <- init_control_table terminal
1010 putStrLn $ "ANSI terminal show cursor string: " ++ show cvis
11− putStrLn $ "Current terminal show cursor string: " ++ show (showCursorStr controlTable)
11+ putStrLn $ "Current terminal show cursor string: " ++ show (show_cursor_str control_table)
1212 putStrLn $ "ANSI terminal hide cursor string: " ++ show civis
13− putStrLn $ "Current terminal hide cursor string: " ++ show (hideCursorStr controlTable)
13+ putStrLn $ "Current terminal hide cursor string: " ++ show (hide_cursor_str control_table)
1414
test/EventEcho.hsdeleted−40
@@ -1,40 +0,0 @@
1−module Main where
2−
3−import Graphics.Vty
4−
5−import Control.Applicative
6−import Control.Arrow
7−import Control.Monad.RWS
8−
9−import Data.Sequence (Seq, (<|) )
10−import qualified Data.Sequence as Seq
11−import Data.Foldable
12−
13−eventBufferSize = 1000
14−
15−type App = RWST Vty () (Seq String) IO
16−
17−main = do
18− vty <- mkVty defaultConfig
19− _ <- execRWST (vtyInteract False) vty Seq.empty
20− shutdown vty
21−
22−vtyInteract :: Bool -> App ()
23−vtyInteract shouldExit = do
24− updateDisplay
25− unless shouldExit $ handleNextEvent >>= vtyInteract
26−
27−updateDisplay :: App ()
28−updateDisplay = do
29− let info = string defAttr "Press ESC to exit."
30− eventLog <- foldMap (string defAttr) <$> get
31− let pic = picForImage $ info <-> eventLog
32− vty <- ask
33− liftIO $ update vty pic
34−
35−handleNextEvent = ask >>= liftIO . nextEvent >>= handleEvent
36− where
37− handleEvent e = do
38− modify $ (<|) (show e) >>> Seq.take eventBufferSize
39− return $ e == EvKey KEsc []
40−
test/HereDoc.hs+5−3
@@ -1,7 +1,8 @@
11{-# LANGUAGE QuasiQuotes #-}
22{-# LANGUAGE TemplateHaskell #-}
3−-- From http://www.reddit.com/r/haskell/comments/8ereh/a_here_document_syntax/
4−-- copyright unknown?
3+{- From http://www.reddit.com/r/haskell/comments/8ereh/a_here_document_syntax/
4+ - copyright unknown?
5+ -}
56module HereDoc (heredoc) where
67
78import Language.Haskell.TH.Quote
@@ -9,4 +10,5 @@ import Language.Haskell.TH.Syntax
910import Language.Haskell.TH.Lib
1011
1112heredoc :: QuasiQuoter
12−heredoc = QuasiQuoter (litE . stringL) (litP . stringL) undefined undefined
13+heredoc = QuasiQuoter (litE . stringL) (litP . stringL)
14+
test/Issue76.hsdeleted−17
@@ -1,17 +0,0 @@
1−module Main where
2−
3−import Control.Monad (void)
4−import Graphics.Vty
5−
6−main :: IO ()
7−main = do
8− cfg <- standardIOConfig
9− vty <- mkVty cfg
10− let line1 = charFill (defAttr `withBackColor` blue) ' ' 10 1
11− line2 = charFill (defAttr `withBackColor` green) ' ' 10 1
12− img = translate 10 5 (line1 `vertJoin` line2)
13− pic = picForImage img
14−
15− update vty pic
16− void $ nextEvent vty
17− shutdown vty
test/LICENSEdeleted−31
@@ -1,31 +0,0 @@
1−Copyright Stefan O'Rear 2006, Corey O'Connor 2008, Corey O'Connor 2009
2−
3−All rights reserved.
4−
5−Redistribution and use in source and binary forms, with or without
6−modification, are permitted provided that the following conditions are met:
7−
8− * Redistributions of source code must retain the above copyright
9− notice, this list of conditions and the following disclaimer.
10−
11− * Redistributions in binary form must reproduce the above
12− copyright notice, this list of conditions and the following
13− disclaimer in the documentation and/or other materials provided
14− with the distribution.
15−
16− * Neither the name of Stefan O'Rear nor the names of other
17− contributors may be used to endorse or promote products derived
18− from this software without specific prior written permission.
19−
20−THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21−"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22−LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23−A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24−OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25−SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26−LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27−DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28−THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29−(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30−OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31−
test/Makefile+8−20
@@ -4,17 +4,19 @@ verify_display_attributes \
44verify_parse_terminfo_caps \
55verify_eval_terminfo_caps \
66verify_utf8_width \
7−VerifyImageOps \
7+verify_image_ops \
88verify_image_trans \
99verify_empty_image_props \
1010verify_picture_ops \
11−VerifySpanOps \
11+verify_span_ops \
1212verify_debug_terminal \
1313verify_inline \
1414
1515
1616TESTS :=\
17−benchmark \
17+Bench \
18+Bench2 \
19+BenchRenderChar \
1820Test \
1921Test2 \
2022yi_issue_264 \
@@ -24,17 +26,7 @@ $(VERIF_TESTS)
2426$(shell mkdir -p objects )
2527
2628# TODO: Tests should also be buildable referencing the currently installed vty
27−GHC_ARGS=--make -i../src \
28− -package parallel \
29− -package deepseq-1.3.0.1 \
30− -hide-package transformers \
31− -hide-package monads-tf \
32− -package QuickCheck \
33− -ignore-package vty \
34− ../cbits/gwinsz.c ../cbits/set_term_timing.c ../cbits/mk_wcwidth.c \
35− -O2 -funbox-strict-fields -Wall -threaded -fno-full-laziness -fspec-constr -fspec-constr-count=10 \
36− -rtsopts \
37− -odir objects -hidir objects
29+GHC_ARGS=--make -i../src -package parallel-1.1.0.1 -hide-package transformers -hide-package monads-fd -hide-package monads-tf -package QuickCheck-2.1.0.2 -ignore-package vty ../cbits/gwinsz.c ../cbits/set_term_timing.c ../cbits/mk_wcwidth.c -O -funbox-strict-fields -Wall -threaded -fno-full-laziness -fspec-constr -odir objects -hidir objects
3830
3931GHC_PROF_ARGS=-prof -auto-all $(GHC_ARGS)
4032
@@ -45,7 +37,7 @@ all : $(VERIF_TESTS)
4537
4638.PHONY: $(TESTS)
4739.SECONDEXPANSION:
48−$(TESTS) :
40+$(TESTS) :
4941 @echo running test $@
5042 @mkdir -p results/$@
5143 ( ghc $(GHC_PROF_ARGS) $@ \
@@ -54,10 +46,6 @@ $(TESTS) :
5446 )
5547
5648.PHONY: interactive_terminal_test
57−interactive_terminal_test :
49+interactive_terminal_test :
5850 ghc $(GHC_ARGS) $@ && ./$@
5951
60−.PHONY: core
61−core :
62− ghc-core --no-asm --no-cast -- $(GHC_ARGS) benchmark
63−
test/MinimalExample.hsdeleted−14
@@ -1,14 +0,0 @@
1−module Main where
2−
3−import Graphics.Vty
4−
5−main = do
6− vty <- mkVty defaultConfig
7− let line0 = string (def `withForeColor` green) "first line"
8− line1 = string (def `withBackColor` blue) "second line"
9− img = line0 <-> line1
10− pic = picForImage img
11− update vty pic
12− e <- nextEvent vty
13− shutdown vty
14− print $ "Last event was: " ++ show e
test/Rogue.hsdeleted−175
@@ -1,175 +0,0 @@
1−module Main where
2−
3−import Graphics.Vty
4−
5−import Data.Array
6−
7−import Control.Applicative
8−import Control.Monad
9−import Control.Monad.RWS
10−
11−import System.Random
12−
13−data Player = Player
14− { playerCoord :: Coord
15− } deriving (Show,Eq)
16−
17−data World = World
18− { player :: Player
19− , level :: Level
20− }
21− deriving (Show,Eq)
22−
23−data Level = Level
24− { levelStart :: Coord
25− , levelEnd :: Coord
26− , levelGeo :: Geo
27− -- building the geo image is expensive. Cache it. Though VTY should go
28− -- through greater lengths to avoid the need to cache images.
29− , levelGeoImage :: Image
30− }
31− deriving (Show,Eq)
32−
33−data LevelPiece
34− = EmptySpace
35− | Rock
36− deriving (Show, Eq)
37−
38−type Game = RWST Vty () World IO
39−type Geo = Array Coord LevelPiece
40−type Coord = (Int, Int)
41−
42−main :: IO ()
43−main = do
44− vty <- mkVty defaultConfig
45− level0 <- mkLevel 1
46− let world0 = World (Player (levelStart level0)) level0
47− (_finalWorld, ()) <- execRWST play vty world0
48− shutdown vty
49−
50−-- |Generate a level randomly using the specified difficulty. Higher
51−-- difficulty means the level will have more rooms and cover a larger area.
52−mkLevel :: Int -> IO Level
53−mkLevel difficulty = do
54− let size = 80 * difficulty
55− [levelWidth, levelHeight] <- replicateM 2 $ randomRIO (size,size)
56− let randomP = (,) <$> randomRIO (2, levelWidth-3) <*> randomRIO (2, levelHeight-3)
57− start <- randomP
58− end <- randomP
59− -- first the base geography: all rocks
60− let baseGeo = array ((0,0), (levelWidth-1, levelHeight-1))
61− [((x,y),Rock) | x <- [0..levelWidth-1], y <- [0..levelHeight-1]]
62− -- next the empty spaces that make the rooms
63− -- for this we generate a number of center points
64− centers <- replicateM (2 ^ difficulty + difficulty) randomP
65− -- generate rooms for all those points, plus the start and end
66− geo <- foldM (addRoom levelWidth levelHeight) baseGeo (start : end : centers)
67− return $ Level start end geo (buildGeoImage geo)
68−
69−-- |Add a room to a geography and return a new geography. Adds a
70−-- randomly-sized room centered at the specified coordinates.
71−addRoom :: Int
72− -> Int
73− -- ^The width and height of the geographical area
74− -> Geo
75− -- ^The geographical area to which a new room should be added
76− -> Coord
77− -- ^The desired center of the new room.
78− -> IO Geo
79−addRoom levelWidth levelHeight geo (centerX, centerY) = do
80− size <- randomRIO (5,15)
81− let xMin = max 1 (centerX - size)
82− xMax = min (levelWidth - 1) (centerX + size)
83− yMin = max 1 (centerY - size)
84− yMax = min (levelHeight - 1) (centerY + size)
85− let room = [((x,y), EmptySpace) | x <- [xMin..xMax - 1], y <- [yMin..yMax - 1]]
86− return (geo // room)
87−
88−pieceA, dumpA :: Attr
89−pieceA = defAttr `withForeColor` blue `withBackColor` green
90−dumpA = defAttr `withStyle` reverseVideo
91−
92−play :: Game ()
93−play = do
94− updateDisplay
95− done <- processEvent
96− unless done play
97−
98−processEvent :: Game Bool
99−processEvent = do
100− k <- ask >>= liftIO . nextEvent
101− if k == EvKey KEsc []
102− then return True
103− else do
104− case k of
105− EvKey (KChar 'r') [MCtrl] -> ask >>= liftIO . refresh
106− EvKey KLeft [] -> movePlayer (-1) 0
107− EvKey KRight [] -> movePlayer 1 0
108− EvKey KUp [] -> movePlayer 0 (-1)
109− EvKey KDown [] -> movePlayer 0 1
110− _ -> return ()
111− return False
112−
113−movePlayer :: Int -> Int -> Game ()
114−movePlayer dx dy = do
115− world <- get
116− let Player (x, y) = player world
117− let x' = x + dx
118− y' = y + dy
119− -- this is only valid because the level generation assures the border is
120− -- always Rock
121− case levelGeo (level world) ! (x',y') of
122− EmptySpace -> put $ world { player = Player (x',y') }
123− _ -> return ()
124−
125−updateDisplay :: Game ()
126−updateDisplay = do
127− let info = string defAttr "Move with the arrows keys. Press ESC to exit."
128− -- determine offsets to place the player in the center of the level.
129− (w,h) <- asks outputIface >>= liftIO . displayBounds
130− thePlayer <- gets player
131− let ox = (w `div` 2) - playerX thePlayer
132− oy = (h `div` 2) - playerY thePlayer
133− -- translate the world images to place the player in the center of the
134− -- level.
135− world' <- map (translate ox oy) <$> worldImages
136− let pic = picForLayers $ info : world'
137− vty <- ask
138− liftIO $ update vty pic
139−
140−--
141−-- Image-generation functions
142−--
143−
144−worldImages :: Game [Image]
145−worldImages = do
146− thePlayer <- gets player
147− theLevel <- gets level
148− let playerImage = translate (playerX thePlayer) (playerY thePlayer) (char pieceA '@')
149− return [playerImage, levelGeoImage theLevel]
150−
151−imageForGeo :: LevelPiece -> Image
152−imageForGeo EmptySpace = char (defAttr `withBackColor` green) ' '
153−imageForGeo Rock = char defAttr 'X'
154−
155−buildGeoImage :: Geo -> Image
156−buildGeoImage geo =
157− let (geoWidth, geoHeight) = snd $ bounds geo
158− -- seems like a the repeated index operation should be removable. This is
159− -- not performing random access but (presumably) access in order of index.
160− in vertCat [ geoRow
161− | y <- [0..geoHeight-1]
162− , let geoRow = horizCat [ i
163− | x <- [0..geoWidth-1]
164− , let i = imageForGeo (geo ! (x,y))
165− ]
166− ]
167−
168−--
169−-- Miscellaneous
170−--
171−playerX :: Player -> Int
172−playerX = fst . playerCoord
173−
174−playerY :: Player -> Int
175−playerY = snd . playerCoord
test/Setup.lhsdeleted−3
@@ -1,3 +0,0 @@
1−#!/usr/bin/env runhaskell
2−> import Distribution.Simple
3−> main = defaultMain
test/Test.hsnew file+44
@@ -0,0 +1,44 @@
1+{-# LANGUAGE ScopedTypeVariables #-}
2+module Main where
3+
4+import Graphics.Vty
5+
6+import qualified Data.ByteString as B
7+import Data.Word
8+
9+import System.IO
10+
11+
12+main = do
13+ vt <- mkVty
14+ DisplayRegion w h <- display_bounds $ terminal vt
15+ putStrLn $ show $ DisplayRegion w h
16+ play vt 0 1 w h ""
17+
18+pieceA = def_attr `with_fore_color` red
19+dumpA = def_attr `with_style` reverse_video
20+
21+play :: Vty -> Word -> Word -> Word -> Word -> String -> IO ()
22+play vt x y sx sy btl = do update vt (current_pic x y sx sy btl)
23+ k <- next_event vt
24+ case k of EvKey (KASCII 'r') [MCtrl] -> refresh vt >> play vt x y sx sy btl
25+ EvKey KLeft [] | x /= 0 -> play vt (x-1) y sx sy btl
26+ EvKey KRight [] | x /= (sx-1) -> play vt (x+1) y sx sy btl
27+ EvKey KUp [] | y /= 1 -> play vt x (y-1) sx sy btl
28+ EvKey KDown [] | y /= (sy-2) -> play vt x (y+1) sx sy btl
29+ EvKey KEsc [] -> shutdown vt >> return ()
30+ EvResize nx ny -> play vt (min x (toEnum nx - 1))
31+ (min y (toEnum ny - 2))
32+ (toEnum nx)
33+ (toEnum ny)
34+ btl
35+ _ -> play vt x y sx sy (take (fromEnum sx) (show k ++ btl))
36+
37+
38+current_pic :: Word -> Word -> Word -> Word -> String -> Picture
39+current_pic x y sx sy btl = pic_for_image i
40+ where i = string def_attr "Move the @ character around with the arrow keys. Escape exits."
41+ <-> char_fill pieceA ' ' sx (y - 1)
42+ <-> char_fill pieceA ' ' x 1 <|> char pieceA '@' <|> char_fill pieceA ' ' (sx - x - 1) 1
43+ <-> char_fill pieceA ' ' sx (sy - y - 2)
44+ <-> iso_10646_string dumpA btl
test/Test2.hs−1
@@ -1,5 +1,4 @@
11module Main where
2−
32import Graphics.Vty
43
54main = do
test/Verify.hs+55−59
@@ -1,69 +1,68 @@
1−{-# LANGUAGE RankNTypes #-}
2−{-# LANGUAGE GADTs #-}
3−{-# LANGUAGE RecordWildCards #-}
41{-# LANGUAGE TypeSynonymInstances #-}
52{-# LANGUAGE FlexibleInstances #-}
63{-# LANGUAGE StandaloneDeriving #-}
74{-# LANGUAGE DisambiguateRecordFields #-}
85{-# LANGUAGE NamedFieldPuns #-}
9−module Verify
10− ( module Verify
11− , module Control.Applicative
12− , module Control.DeepSeq
13− , module Control.Exception
14− , module Control.Monad
15− , module Test.QuickCheck
16− , module Test.QuickCheck.Modifiers
17− , module Text.Printf
18− , succeeded
19− , failed
20− , monadicIO
21− , liftIO
22− , liftBool
23− , Test(..)
24− , Prop.Result(..)
25− )
26−where
27−
28−import Control.Exception ( bracket, try, SomeException(..) )
29−
30−import Distribution.TestSuite hiding ( Result(..) )
31−import qualified Distribution.TestSuite as TS
6+module Verify ( module Verify
7+ , module Test.QuickCheck
8+ , succeeded
9+ , failed
10+ , Result(..)
11+ , monadicIO
12+ , liftIO
13+ , liftIOResult
14+ , liftResult
15+ , liftBool
16+ )
17+ where
3218
3319import Test.QuickCheck hiding ( Result(..) )
3420import qualified Test.QuickCheck as QC
35−import Test.QuickCheck.Modifiers
36−import Test.QuickCheck.Property hiding ( Result(..) )
37−import qualified Test.QuickCheck.Property as Prop
38−import Test.QuickCheck.Monadic ( monadicIO )
39−
40−import Text.Printf
21+import Test.QuickCheck.Property
22+import Test.QuickCheck.Monadic ( monadicIO )
4123
4224import qualified Codec.Binary.UTF8.String as UTF8
4325
44−import Control.Applicative hiding ( (<|>) )
45−import Control.DeepSeq
46−import Control.Monad ( forM, mapM, mapM_, forM_ )
26+import Control.Applicative
4727import Control.Monad.State.Strict
28+import Control.Monad.Trans ( liftIO )
29+
30+import Data.IORef
31+import Data.Word
4832
4933import Numeric ( showHex )
5034
51−verify :: Testable t => String -> t -> Test
52−verify testName p = Test $ TestInstance
53− { name = testName
54− , run = do
55− qcResult <- quickCheckWithResult (stdArgs {chatty = False}) p
56− case qcResult of
57− QC.Success {..} -> return $ Finished TS.Pass
58− QC.Failure {numShrinks,reason} -> return $ Finished
59− $ TS.Fail $ "After "
60− ++ show numShrinks ++ " shrinks determined failure to be: "
61− ++ show reason
62− _ -> return $ Finished $ TS.Fail "TODO(corey): add failure message"
63− , tags = []
64− , options = []
65− , setOption = \_ _ -> Left "no options supported"
66− }
35+import System.IO
36+
37+type Test = StateT TestState IO
38+
39+instance Applicative Test where
40+ pure = return
41+ ( <*> ) = ap
42+
43+data TestState = TestState
44+ { results_ref :: IORef [QC.Result]
45+ }
46+
47+run_test :: Test () -> IO ()
48+run_test t = do
49+ s <- newIORef [] >>= return . TestState
50+ s' <- runStateT t s >>= return . snd
51+ results <- readIORef $ results_ref s'
52+ let fail_results = [ fail_result | fail_result@(QC.Failure {}) <- results ]
53+ case fail_results of
54+ [] -> putStrLn "state: PASS"
55+ rs -> do
56+ putStrLn "state: FAIL"
57+ putStrLn $ "fail_count: " ++ show (length rs)
58+
59+verify :: Testable prop => String -> prop -> Test QC.Result
60+verify prop_name prop = do
61+ liftIO $ putStrLn $ "verify " ++ prop_name
62+ get >>= \s -> do
63+ r <- liftIO $ quickCheckResult prop
64+ liftIO $ modifyIORef (results_ref s) (\rs -> r : rs)
65+ return r
6766
6867data SingleColumnChar = SingleColumnChar Char
6968 deriving (Show, Eq)
@@ -78,12 +77,9 @@ instance Show DoubleColumnChar where
7877 show (DoubleColumnChar c) = "(0x" ++ showHex (fromEnum c) "" ++ ") ->" ++ UTF8.encodeString [c]
7978
8079instance Arbitrary DoubleColumnChar where
81− arbitrary = elements $ map DoubleColumnChar $
82− [ toEnum 0x3040 .. toEnum 0x3098 ]
83− ++ [ toEnum 0x309B .. toEnum 0xA4CF ]
84−
85−liftIOResult :: Testable prop => IO prop -> Property
86−liftIOResult = ioProperty
80+ arbitrary = elements $ map DoubleColumnChar $
81+ [ toEnum 0x3040 .. toEnum 0x3098 ]
82+ ++ [ toEnum 0x309B .. toEnum 0xA4CF]
8783
88−data Bench where
89− Bench :: forall v . NFData v => IO v -> (v -> IO ()) -> Bench
84+instance Arbitrary Word where
85+ arbitrary = choose (0, 1024) >>= return . toEnum
test/Verify/Data/Terminfo/Parse.hs+63−61
@@ -1,41 +1,47 @@
1−module Verify.Data.Terminfo.Parse
2− ( module Verify.Data.Terminfo.Parse
3− , module Data.Terminfo.Parse
4− )
5−where
1+{-# LANGUAGE ScopedTypeVariables #-}
2+module Verify.Data.Terminfo.Parse ( module Verify.Data.Terminfo.Parse
3+ , module Data.Terminfo.Parse
4+ )
5+ where
66
77import Data.Terminfo.Parse
88import Data.Terminfo.Eval
99import Verify
1010
11+import Data.Array.Unboxed
1112import Data.Word
12−import qualified Data.Vector.Unboxed as Vector
1313
1414import Numeric
1515
16−hexDump :: [Word8] -> String
17−hexDump bytes = foldr (\b s -> showHex b s) "" bytes
16+instance Show CapExpression where
17+ show c
18+ = "CapExpression { " ++ show (cap_ops c) ++ " }"
19+ ++ " <- [" ++ hex_dump (cap_bytes c) ++ "]"
20+ ++ " <= " ++ show (source_string c)
21+
22+hex_dump :: CapBytes -> String
23+hex_dump bytes = foldr (\b s -> showHex b s) "" $ elems bytes
1824
1925data NonParamCapString = NonParamCapString String
2026 deriving Show
2127
2228instance Arbitrary NonParamCapString where
23− arbitrary
29+ arbitrary
2430 = ( do
2531 s <- listOf1 $ (choose (0, 255) >>= return . toEnum) `suchThat` (/= '%')
2632 return $ NonParamCapString s
27− ) `suchThat` ( \(NonParamCapString str) -> length str < 255 )
33+ ) `suchThat` ( \(NonParamCapString str) -> length str < 255 )
2834
2935data LiteralPercentCap = LiteralPercentCap String [Word8]
3036 deriving ( Show )
3137
3238instance Arbitrary LiteralPercentCap where
33− arbitrary
39+ arbitrary
3440 = ( do
3541 NonParamCapString s <- arbitrary
36− (s', bytes) <- insertEscapeOp "%" [toEnum $ fromEnum '%'] s
42+ (s', bytes) <- insert_escape_op "%" [toEnum $ fromEnum '%'] s
3743 return $ LiteralPercentCap s' bytes
38− ) `suchThat` ( \(LiteralPercentCap str _) -> length str < 255 )
44+ ) `suchThat` ( \(LiteralPercentCap str _) -> length str < 255 )
3945
4046data IncFirstTwoCap = IncFirstTwoCap String [Word8]
4147 deriving ( Show )
@@ -44,69 +50,65 @@ instance Arbitrary IncFirstTwoCap where
4450 arbitrary
4551 = ( do
4652 NonParamCapString s <- arbitrary
47− (s', bytes) <- insertEscapeOp "i" [] s
53+ (s', bytes) <- insert_escape_op "i" [] s
4854 return $ IncFirstTwoCap s' bytes
49− ) `suchThat` ( \(IncFirstTwoCap str _) -> length str < 255 )
55+ ) `suchThat` ( \(IncFirstTwoCap str _) -> length str < 255 )
5056
51−data PushParamCap = PushParamCap String Int [Word8]
57+data PushParamCap = PushParamCap String Word [Word8]
5258 deriving ( Show )
5359
5460instance Arbitrary PushParamCap where
5561 arbitrary
5662 = ( do
5763 NonParamCapString s <- arbitrary
58− n <- choose (1,9)
59− (s', bytes) <- insertEscapeOp ("p" ++ show n) [] s
64+ n :: Word <- choose (1,9) >>= return . toEnum
65+ (s', bytes) <- insert_escape_op ("p" ++ show n) [] s
6066 return $ PushParamCap s' n bytes
61− ) `suchThat` ( \(PushParamCap str _ _) -> length str < 255 )
67+ ) `suchThat` ( \(PushParamCap str _ _) -> length str < 255 )
6268
63−data DecPrintCap = DecPrintCap String Int [Word8]
69+data DecPrintCap = DecPrintCap String Word [Word8]
6470 deriving ( Show )
6571
6672instance Arbitrary DecPrintCap where
6773 arbitrary
6874 = ( do
6975 NonParamCapString s <- arbitrary
70− n <- choose (1,9)
71− (s', bytes) <- insertEscapeOp ("p" ++ show n ++ "%d") [] s
76+ n :: Word <- choose (1,9) >>= return . toEnum
77+ (s', bytes) <- insert_escape_op ("p" ++ show n ++ "%d") [] s
7278 return $ DecPrintCap s' n bytes
73− ) `suchThat` ( \(DecPrintCap str _ _) -> length str < 255 )
74−
75−insertEscapeOp opStr replBytes s = do
76− insertPoints <- listOf1 $ elements [0 .. length s - 1]
77− let s' = f s ('%' : opStr)
78− remainingBytes = f (map (toEnum . fromEnum) s) replBytes
79− f inVs out_v = concat [ vs
80− | vi <- zip inVs [0 .. length s - 1]
81− , let vs = fst vi : ( if snd vi `elem` insertPoints
82− then out_v
83− else []
84− )
85− ]
86− return (s', remainingBytes)
87−
88−isBytesOp :: CapOp -> Bool
89−isBytesOp (Bytes {}) = True
90−-- isBytesOp _ = False
91−
92−bytesForRange cap offset count
93− = Vector.toList $ Vector.take count $ Vector.drop offset $ capBytes cap
94−
95−collectBytes :: CapExpression -> [Word8]
96−collectBytes e = concat [ bytes
97− | Bytes offset count <- capOps e
98− , let bytes = bytesForRange e offset count
99− ]
100−
101−
102−verifyBytesEqual :: [Word8] -> [Word8] -> Result
103−verifyBytesEqual outBytes expectedBytes
104− = if outBytes == expectedBytes
105− then succeeded
106− else failed
107− { reason = "outBytes ["
108− ++ hexDump outBytes
109− ++ "] /= expectedBytes ["
110− ++ hexDump expectedBytes
111− ++ "]"
79+ ) `suchThat` ( \(DecPrintCap str _ _) -> length str < 255 )
80+
81+insert_escape_op op_str repl_bytes s = do
82+ insert_points <- listOf1 $ elements [0 .. length s - 1]
83+ let s' = f s ('%' : op_str)
84+ remaining_bytes = f (map (toEnum . fromEnum) s) repl_bytes
85+ f in_vs out_v = concat [ vs
86+ | vi <- zip in_vs [0 .. length s - 1]
87+ , let vs = fst vi : ( if snd vi `elem` insert_points
88+ then out_v
89+ else []
90+ )
91+ ]
92+ return (s', remaining_bytes)
93+
94+is_bytes_op :: CapOp -> Bool
95+is_bytes_op (Bytes {}) = True
96+-- is_bytes_op _ = False
97+
98+collect_bytes :: CapExpression -> [Word8]
99+collect_bytes e = concat [ bytes
100+ | Bytes offset c <- cap_ops e
101+ , let bytes = bytes_for_range e offset c
102+ ]
103+
104+verify_bytes_equal out_bytes expected_bytes
105+ = if out_bytes == expected_bytes
106+ then liftResult succeeded
107+ else liftResult $ failed
108+ { reason = "out_bytes ["
109+ ++ hex_dump ( listArray (0, toEnum $ length out_bytes - 1) out_bytes )
110+ ++ "] /= expected_bytes ["
111+ ++ hex_dump ( listArray (0, toEnum $ length expected_bytes - 1) expected_bytes )
112+ ++ "]"
112113 }
114+
test/Verify/Graphics/Vty/Attributes.hs+19−52
@@ -1,73 +1,40 @@
1−module Verify.Graphics.Vty.Attributes
2− ( module Verify.Graphics.Vty.Attributes
3− , module Graphics.Vty.Attributes
4− )
5−where
1+module Verify.Graphics.Vty.Attributes ( module Verify.Graphics.Vty.Attributes
2+ , module Graphics.Vty.Attributes
3+ )
4+ where
65
76import Graphics.Vty.Attributes
87import Verify
98
109import Data.List ( delete )
1110
12−allColors :: [Color]
13−allColors =
14− [ black
15− , red
16− , green
17− , yellow
18− , blue
19− , magenta
20− , cyan
21− , white
22− , brightBlack
23− , brightRed
24− , brightGreen
25− , brightYellow
26− , brightBlue
27− , brightMagenta
28− , brightCyan
29− , brightWhite
30− ] ++ map Color240 [0..239]
31−
32−allStyles :: [Style]
33−allStyles =
34− [ standout
35− , underline
36− , reverseVideo
37− , blink
38− , dim
39− , bold
40− ]
41−
4211-- Limit the possible attributes to just a few for now.
43−possibleAttrMods :: [ AttrOp ]
44−possibleAttrMods =
45− [ idOp
46− ] ++ map setForeColorOp allColors
47− ++ map setBackColorOp allColors
48− ++ map setStyleOp allStyles
12+possible_attr_mods :: [ AttrOp ]
13+possible_attr_mods =
14+ [ set_bold_op
15+ , id_op
16+ ]
4917
5018instance Arbitrary Attr where
51− arbitrary = elements possibleAttrMods >>= return . flip applyOp defAttr
19+ arbitrary = elements possible_attr_mods >>= return . flip apply_op def_attr
5220
5321data DiffAttr = DiffAttr Attr Attr
5422
5523instance Arbitrary DiffAttr where
5624 arbitrary = do
57− op0 <- elements possibleAttrMods
58− let possibleAttrMods' = delete op0 possibleAttrMods
59− op1 <- elements possibleAttrMods'
60− return $ DiffAttr (applyOp op0 defAttr) (applyOp op1 defAttr)
25+ op0 <- elements possible_attr_mods
26+ let possible_attr_mods' = delete op0 possible_attr_mods
27+ op1 <- elements possible_attr_mods'
28+ return $ DiffAttr (apply_op op0 def_attr) (apply_op op1 def_attr)
6129
6230data AttrOp = AttrOp String (Attr -> Attr)
6331
6432instance Eq AttrOp where
6533 AttrOp n0 _ == AttrOp n1 _ = n0 == n1
6634
67−setStyleOp s = AttrOp "set_style" (flip withStyle s)
68−setForeColorOp c = AttrOp "set_fore_color" (flip withForeColor c)
69−setBackColorOp c = AttrOp "set_back_color" (flip withBackColor c)
70−idOp = AttrOp "id" id
35+set_bold_op = AttrOp "set_bold" (flip with_style bold)
36+id_op = AttrOp "id" id
37+
38+apply_op :: AttrOp -> Attr -> Attr
39+apply_op (AttrOp _ f) a = f a
7140
72−applyOp :: AttrOp -> Attr -> Attr
73−applyOp (AttrOp _ f) a = f a
test/Verify/Graphics/Vty/DisplayAttributes.hs+5−5
@@ -1,8 +1,8 @@
1−module Verify.Graphics.Vty.DisplayAttributes
2− ( module Verify.Graphics.Vty.DisplayAttributes
3− , module Graphics.Vty.DisplayAttributes
4− )
5−where
1+module Verify.Graphics.Vty.DisplayAttributes ( module Verify.Graphics.Vty.DisplayAttributes
2+ , module Graphics.Vty.DisplayAttributes
3+ )
4+ where
65
76import Graphics.Vty.DisplayAttributes
87import Verify
8+
test/Verify/Graphics/Vty/DisplayRegion.hsnew file+27
@@ -0,0 +1,27 @@
1+{-# LANGUAGE ScopedTypeVariables #-}
2+module Verify.Graphics.Vty.DisplayRegion ( module Verify.Graphics.Vty.DisplayRegion
3+ , module Graphics.Vty.DisplayRegion
4+ )
5+ where
6+
7+import Graphics.Vty.Debug
8+import Graphics.Vty.DisplayRegion
9+
10+import Verify
11+
12+import Data.Word
13+
14+data EmptyWindow = EmptyWindow DebugWindow
15+
16+instance Arbitrary EmptyWindow where
17+ arbitrary = return $ EmptyWindow (DebugWindow (0 :: Word) (0 :: Word))
18+
19+instance Show EmptyWindow where
20+ show (EmptyWindow _) = "EmptyWindow"
21+
22+instance Arbitrary DebugWindow where
23+ arbitrary = do
24+ w <- suchThat arbitrary (/= 0)
25+ h <- suchThat arbitrary (/= 0)
26+ return $ DebugWindow w h
27+
test/Verify/Graphics/Vty/Image.hs+51−178
@@ -1,218 +1,91 @@
1−{-# LANGUAGE BangPatterns #-}
21{-# LANGUAGE NamedFieldPuns #-}
32{-# LANGUAGE DisambiguateRecordFields #-}
4−module Verify.Graphics.Vty.Image
5− ( module Verify.Graphics.Vty.Image
6− , module Graphics.Vty.Image
7− )
8−where
3+module Verify.Graphics.Vty.Image ( module Verify.Graphics.Vty.Image
4+ , module Graphics.Vty.Image
5+ )
6+ where
97
10−import Verify.Graphics.Vty.Attributes
8+import Graphics.Vty.Debug
119import Graphics.Vty.Image
12−import Graphics.Vty.Image.Internal
10+import Graphics.Vty.DisplayRegion
1311
1412import Verify
13+import Verify.Graphics.Vty.Attributes
14+
15+import Data.Word
1516
1617data UnitImage = UnitImage Char Image
1718
1819instance Arbitrary UnitImage where
1920 arbitrary = do
2021 SingleColumnChar c <- arbitrary
21− a <- arbitrary
22− return $ UnitImage c (char a c)
22+ return $ UnitImage c (char def_attr c)
2323
2424instance Show UnitImage where
2525 show (UnitImage c _) = "UnitImage " ++ show c
2626
27−data DefaultImage = DefaultImage Image
27+data DefaultImage = DefaultImage Image ImageConstructLog
2828
2929instance Show DefaultImage where
30− show (DefaultImage i)
31− = "DefaultImage (" ++ show i ++ ") " ++ show (imageWidth i, imageHeight i)
30+ show (DefaultImage i image_log)
31+ = "DefaultImage (" ++ show i ++ ") " ++ show (image_width i, image_height i) ++ " " ++ show image_log
3232
3333instance Arbitrary DefaultImage where
3434 arbitrary = do
35− i <- return $ char defAttr 'X'
36− return $ DefaultImage i
37−
38−data SingleRowSingleAttrImage
39− = SingleRowSingleAttrImage
40− { expectedAttr :: Attr
41− , expectedColumns :: Int
42− , rowImage :: Image
35+ i <- return $ char def_attr 'X' -- elements forward_image_ops >>= return . (\op -> op empty_image)
36+ return $ DefaultImage i []
37+
38+data SingleRowSingleAttrImage
39+ = SingleRowSingleAttrImage
40+ { expected_attr :: Attr
41+ , expected_columns :: Word
42+ , row_image :: Image
4343 }
4444
4545instance Show SingleRowSingleAttrImage where
46− show (SingleRowSingleAttrImage attr columns image)
46+ show (SingleRowSingleAttrImage attr columns image)
4747 = "SingleRowSingleAttrImage (" ++ show attr ++ ") " ++ show columns ++ " ( " ++ show image ++ " )"
4848
49−newtype WidthResize = WidthResize (Image -> (Image, Int))
50−
51−instance Arbitrary WidthResize where
52− arbitrary = do
53− WidthResize f <- arbitrary
54− w <- choose (1,64)
55− oneof $ map (return . WidthResize)
56− [ \i -> (i, imageWidth i)
57− , \i -> (resizeWidth w $ fst $ f i, w)
58− , \i -> let i' = fst $ f i in (cropLeft w i', min (imageWidth i') w)
59− , \i -> let i' = fst $ f i in (cropRight w i', min (imageWidth i') w)
60− ]
61−
62−newtype HeightResize = HeightResize (Image -> (Image, Int))
63−
64−instance Arbitrary HeightResize where
65− arbitrary = do
66− HeightResize f <- arbitrary
67− h <- choose (1,64)
68− oneof $ map (return . HeightResize)
69− [ \i -> (i, imageHeight i)
70− , \i -> (resizeHeight h $ fst $ f i, h)
71− , \i -> let i' = fst $ f i in (cropTop h i', min (imageHeight i') h)
72− , \i -> let i' = fst $ f i in (cropBottom h i', min (imageHeight i') h)
73− ]
74−
75−newtype ImageResize = ImageResize (Image -> (Image, (Int, Int)))
76−
77−instance Arbitrary ImageResize where
78− arbitrary = oneof
79− [ return $! ImageResize $! \i -> (i, (imageWidth i, imageHeight i))
80− , return $! ImageResize $! \i -> (i, (imageWidth i, imageHeight i))
81− , return $! ImageResize $! \i -> (i, (imageWidth i, imageHeight i))
82− , return $! ImageResize $! \i -> (i, (imageWidth i, imageHeight i))
83− , return $! ImageResize $! \i -> (i, (imageWidth i, imageHeight i))
84− , return $! ImageResize $! \i -> (i, (imageWidth i, imageHeight i))
85− , do
86− ImageResize f <- arbitrary
87− WidthResize g <- arbitrary
88− return $! ImageResize $! \i ->
89− let (i0, (_, outHeight)) = f i
90− gI = g i0
91− in (fst gI, (snd gI, outHeight))
92− , do
93− ImageResize f <- arbitrary
94− HeightResize g <- arbitrary
95− return $! ImageResize $! \i ->
96− let (i0, (outWidth, _)) = f i
97− gI = g i0
98− in (fst gI, (outWidth, snd gI))
99− ]
100−
101−
10249instance Arbitrary SingleRowSingleAttrImage where
10350 arbitrary = do
104− -- The text must contain at least one character. Otherwise the
105− -- image simplifies to the IdImage which has a height of 0. If
106− -- this is to represent a single row then the height must be 1
107− singleColumnRowText <- Verify.resize 16 (listOf1 arbitrary)
108− a <- arbitrary
109− let outImage = horizCat $ [char a c | SingleColumnChar c <- singleColumnRowText]
110− outWidth = length singleColumnRowText
111− return $ SingleRowSingleAttrImage a outWidth outImage
112−
113−data SingleRowTwoAttrImage
114− = SingleRowTwoAttrImage
115− { part0 :: SingleRowSingleAttrImage
116− , part1 :: SingleRowSingleAttrImage
117− , joinImage :: Image
51+ -- The text must contain at least one character. Otherwise the image simplifies to the
52+ -- IdImage which has a height of 0. If this is to represent a single row then the height
53+ -- must be 1
54+ single_column_row_text <- listOf1 arbitrary
55+ attr <- arbitrary
56+ return $ SingleRowSingleAttrImage
57+ attr
58+ ( fromIntegral $ length single_column_row_text )
59+ ( horiz_cat $ [ char attr c | SingleColumnChar c <- single_column_row_text ] )
60+
61+data SingleRowTwoAttrImage
62+ = SingleRowTwoAttrImage
63+ { part_0 :: SingleRowSingleAttrImage
64+ , part_1 :: SingleRowSingleAttrImage
65+ , join_image :: Image
11866 } deriving Show
11967
12068instance Arbitrary SingleRowTwoAttrImage where
12169 arbitrary = do
12270 p0 <- arbitrary
12371 p1 <- arbitrary
124− return $ SingleRowTwoAttrImage p0 p1 (rowImage p0 <|> rowImage p1)
72+ return $ SingleRowTwoAttrImage p0 p1 (row_image p0 <|> row_image p1)
12573
126−data SingleAttrSingleSpanStack = SingleAttrSingleSpanStack
127− { stackImage :: Image
128− , stackSourceImages :: [SingleRowSingleAttrImage]
129− , stackWidth :: Int
130− , stackHeight :: Int
74+data SingleAttrSingleSpanStack = SingleAttrSingleSpanStack
75+ { stack_image :: Image
76+ , stack_source_images :: [SingleRowSingleAttrImage]
77+ , stack_width :: Word
78+ , stack_height :: Word
13179 }
13280 deriving Show
13381
13482instance Arbitrary SingleAttrSingleSpanStack where
13583 arbitrary = do
136− imageList <- Verify.resize 16 (listOf1 arbitrary)
137− return $ mkSingleAttrSingleSpanStack imageList
138− shrink s = do
139− imageList <- shrink $ stackSourceImages s
140− if null imageList
141− then []
142− else return $ mkSingleAttrSingleSpanStack imageList
143−
144−mkSingleAttrSingleSpanStack imageList =
145− let image = vertCat [ i | SingleRowSingleAttrImage { rowImage = i } <- imageList ]
146− in SingleAttrSingleSpanStack image imageList (maximum $ map expectedColumns imageList)
147− (toEnum $ length imageList)
148−
149−instance Arbitrary Image where
150− arbitrary = oneof
151− [ return EmptyImage
152− , do
153− SingleAttrSingleSpanStack {stackImage} <- Verify.resize 8 arbitrary
154− ImageResize f <- Verify.resize 2 arbitrary
155− return $! fst $! f stackImage
156− , do
157− SingleAttrSingleSpanStack {stackImage} <- Verify.resize 8 arbitrary
158− ImageResize f <- Verify.resize 2 arbitrary
159− return $! fst $! f stackImage
160− , do
161− i0 <- arbitrary
162− i1 <- arbitrary
163− let i = i0 <|> i1
164− ImageResize f <- Verify.resize 2 arbitrary
165− return $! fst $! f i
166− , do
167− i0 <- arbitrary
168− i1 <- arbitrary
169− let i = i0 <-> i1
170− ImageResize f <- Verify.resize 2 arbitrary
171− return $! fst $! f i
172− ]
173− {-
174− shrink i@(HorizJoin {partLeft, partRight}) = do
175− let !i_alt = backgroundFill (imageWidth i) (imageHeight i)
176− !partLeft' <- shrink partLeft
177− !partRight' <- shrink partRight
178− [i_alt, partLeft' <|> partRight']
179− shrink i@(VertJoin {partTop, partBottom}) = do
180− let !i_alt = backgroundFill (imageWidth i) (imageHeight i)
181− !partTop' <- shrink partTop
182− !partBottom' <- shrink partBottom
183− [i_alt, partTop' <-> partBottom']
184− shrink i@(CropRight {croppedImage, outputWidth}) = do
185− let !i_alt = backgroundFill (imageWidth i) (imageHeight i)
186− [i_alt, croppedImage]
187− shrink i@(CropLeft {croppedImage, leftSkip, outputWidth}) = do
188− let !i_alt = backgroundFill (imageWidth i) (imageHeight i)
189− [i_alt, croppedImage]
190− shrink i@(CropBottom {croppedImage, outputHeight}) = do
191− let !i_alt = backgroundFill (imageWidth i) (imageHeight i)
192− [i_alt, croppedImage]
193− shrink i@(CropTop {croppedImage, topSkip, outputHeight}) = do
194− let !i_alt = backgroundFill (imageWidth i) (imageHeight i)
195− [i_alt, croppedImage]
196− shrink i = [emptyImage, backgroundFill (imageWidth i) (imageHeight i)]
197− -}
198−
199−data CropOperation
200− = CropFromLeft
201− | CropFromRight
202− | CropFromTop
203− | CropFromBottom
204− deriving (Eq, Show)
205−
206−instance Arbitrary CropOperation where
207− arbitrary = oneof $ map return [CropFromLeft, CropFromRight, CropFromTop, CropFromBottom]
208−
209−data Translation = Translation Image (Int, Int) Image
210− deriving (Eq, Show)
211−
212−instance Arbitrary Translation where
213− arbitrary = do
214− i <- arbitrary
215− x <- arbitrary `suchThat` (> 0)
216− y <- arbitrary `suchThat` (> 0)
217− let i' = translate x y i
218− return $ Translation i (x,y) i'
84+ NonEmpty image_list <- arbitrary
85+ let image = vert_cat [ i | SingleRowSingleAttrImage { row_image = i } <- image_list ]
86+ return $ SingleAttrSingleSpanStack
87+ image
88+ image_list
89+ ( maximum $ map expected_columns image_list )
90+ ( toEnum $ length image_list )
91+
test/Verify/Graphics/Vty/Output.hsdeleted−59
@@ -1,59 +0,0 @@
1−module Verify.Graphics.Vty.Output where
2−
3−import Control.Applicative ((<$>))
4−
5−import Graphics.Vty.Output.Mock
6−
7−import Data.IORef
8−import qualified Data.String.UTF8 as UTF8
9−
10−import Test.QuickCheck.Property
11−
12−-- A list of terminals that should be supported. This started with a
13−-- list of terminals ubuntu supported. Then those terminals that really
14−-- could not be supported were removed. Then a few more were pruned
15−-- until a reasonable looking set was made.
16−terminalsOfInterest :: [String]
17−terminalsOfInterest =
18− [ "vt100"
19− , "vt220"
20− , "vt102"
21− , "xterm-r5"
22− , "xterm-xfree86"
23− , "xterm-r6"
24− , "xterm-256color"
25− , "xterm-vt220"
26− , "xterm-debian"
27− , "xterm-mono"
28− , "xterm-color"
29− , "xterm"
30− , "mach"
31− , "mach-bold"
32− , "mach-color"
33− , "linux"
34− , "ansi"
35− , "hurd"
36− , "Eterm"
37− , "pcansi"
38− , "screen-256color"
39− , "screen-bce"
40− , "screen-s"
41− , "screen-w"
42− , "screen"
43− , "screen-256color-bce"
44− , "sun"
45− , "rxvt"
46− , "rxvt-unicode"
47− , "rxvt-basic"
48− , "cygwin"
49− ]
50−
51−compareMockOutput :: MockData -> String -> IO Result
52−compareMockOutput mockData expectedStr = do
53− outStr <- UTF8.toString <$> readIORef mockData
54− if outStr /= expectedStr
55− then return $ failed { reason = "bytes\n" ++ outStr
56− ++ "\nare not the expected bytes\n"
57− ++ expectedStr
58− }
59− else return succeeded
test/Verify/Graphics/Vty/Picture.hs+28−30
@@ -1,52 +1,50 @@
11{-# LANGUAGE DisambiguateRecordFields #-}
2−module Verify.Graphics.Vty.Picture
3− ( module Verify.Graphics.Vty.Picture
4− , module Graphics.Vty.Picture
5− )
6−where
7−
8−import Verify.Graphics.Vty.Prelude
2+module Verify.Graphics.Vty.Picture ( module Verify.Graphics.Vty.Picture
3+ , module Graphics.Vty.Picture
4+ )
5+ where
96
107import Graphics.Vty.Picture
118import Graphics.Vty.Debug
129
1310import Verify.Graphics.Vty.Attributes
1411import Verify.Graphics.Vty.Image
12+import Verify.Graphics.Vty.DisplayRegion
1513
1614import Verify
1715
18−data DefaultPic = DefaultPic
19− { defaultPic :: Picture
20− , defaultWin :: MockWindow
16+data DefaultPic = DefaultPic
17+ { default_pic :: Picture
18+ , default_win :: DebugWindow
19+ , default_construct_log :: ImageConstructLog
2120 }
2221
2322instance Show DefaultPic where
24− show (DefaultPic pic win)
25− = "DefaultPic\n\t( " ++ show pic ++ ")\n\t" ++ show win ++ "\n"
23+ show (DefaultPic pic win image_log)
24+ = "DefaultPic\n\t( " ++ show pic ++ ")\n\t" ++ show win ++ "\n\t" ++ show image_log ++ "\n"
2625
2726instance Arbitrary DefaultPic where
2827 arbitrary = do
29− DefaultImage image <- arbitrary
30− let win = MockWindow (imageWidth image) (imageHeight image)
31− return $ DefaultPic (picForImage image)
32− win
33−
34−data PicWithBGAttr = PicWithBGAttr
35− { withAttrPic :: Picture
36− , withAttrWin :: MockWindow
37− , withAttrSpecifiedAttr :: Attr
28+ DefaultImage image image_construct_events <- arbitrary
29+ let win = DebugWindow (image_width image) (image_height image)
30+ return $ DefaultPic (pic_for_image image)
31+ win
32+ image_construct_events
33+
34+data PicWithBGAttr = PicWithBGAttr
35+ { with_attr_pic :: Picture
36+ , with_attr_win :: DebugWindow
37+ , with_attr_construct_log :: ImageConstructLog
38+ , with_attr_specified_attr :: Attr
3839 } deriving ( Show )
3940
4041instance Arbitrary PicWithBGAttr where
4142 arbitrary = do
42− DefaultImage image <- arbitrary
43− let win = MockWindow (imageWidth image) (imageHeight image)
43+ DefaultImage image image_construct_events <- arbitrary
44+ let win = DebugWindow (image_width image) (image_height image)
4445 attr <- arbitrary
45− return $ PicWithBGAttr (picForImage image)
46− win
46+ return $ PicWithBGAttr (pic_for_image image)
47+ win
48+ image_construct_events
4749 attr
48−
49−instance Arbitrary Picture where
50− arbitrary = do
51− layers <- Verify.resize 20 (listOf1 arbitrary)
52− return $ picForLayers layers
50+
test/Verify/Graphics/Vty/Prelude.hsdeleted−24
@@ -1,24 +0,0 @@
1−module Verify.Graphics.Vty.Prelude
2− ( module Verify.Graphics.Vty.Prelude
3− , MockWindow(..)
4− )
5−where
6−
7−import Graphics.Vty.Debug
8−
9−import Verify
10−
11−data EmptyWindow = EmptyWindow MockWindow
12−
13−instance Arbitrary EmptyWindow where
14− arbitrary = return $ EmptyWindow (MockWindow (0 :: Int) (0 :: Int))
15−
16−instance Show EmptyWindow where
17− show (EmptyWindow _) = "EmptyWindow"
18−
19−instance Arbitrary MockWindow where
20− arbitrary = do
21− w <- choose (1,1024)
22− h <- choose (1,1024)
23− return $ MockWindow w h
24−
test/Verify/Graphics/Vty/Span.hs+17−25
@@ -1,37 +1,29 @@
11{-# LANGUAGE FlexibleInstances #-}
2−module Verify.Graphics.Vty.Span
3− ( module Verify.Graphics.Vty.Span
4− , module Graphics.Vty.Span
5− )
6−where
2+module Verify.Graphics.Vty.Span ( module Verify.Graphics.Vty.Span
3+ , module Graphics.Vty.Span
4+ )
5+ where
76
87import Graphics.Vty.Debug
98import Graphics.Vty.Span
109
1110import Verify.Graphics.Vty.Picture
1211
13−import qualified Data.Vector as Vector
1412import Data.Word
1513
1614import Verify
1715
18−isAttrSpanOp :: SpanOp -> Bool
19−isAttrSpanOp TextSpan {} = True
20−isAttrSpanOp _ = False
16+is_attr_span_op :: SpanOp -> Bool
17+is_attr_span_op AttributeChange {} = True
18+is_attr_span_op _ = False
2119
22−verifyAllSpansHaveWidth i spans w
23− = let v = map (\s -> (spanOpsAffectedColumns s /= w, s)) (Vector.toList spans)
24− in case any ((== True) . fst) v of
25− False -> succeeded
26− True -> failed { reason = "Not all spans contained operations defining exactly "
27− ++ show w
28− ++ " columns of output - \n"
29− ++ (concatMap ((++ "\n") . show)) v
30− }
31−
32−verifyOpsEquality i_ops i_alt_ops =
33− if i_ops == i_alt_ops
34− then succeeded
35− else failed { reason = "ops for alternate image " ++ show i_alt_ops
36− ++ " are not the same as " ++ show i_ops
37− }
20+verify_all_spans_have_width i spans w
21+ = case all_spans_have_width spans w of
22+ True -> succeeded
23+ False -> failed { reason = "Not all spans contained operations defining exactly "
24+ ++ show w
25+ ++ " columns of output -\n"
26+ ++ show i
27+ ++ "\n->\n"
28+ ++ show spans
29+ }
test/VerifyColor240.hsdeleted−269
@@ -1,269 +0,0 @@
1−module VerifyColor240 where
2−
3−import Data.Word(Word8)
4−import Verify
5−
6−import Graphics.Vty.Attributes.Color240(rgbColorToColor240)
7−
8−oldRgbColorToColor240 :: Integral i => i -> i -> i -> Word8
9−oldRgbColorToColor240 r g b
10− | r < 0 && g < 0 && b < 0 = error "rgbColor with negative color component intensity"
11− | r == 8 && g == 8 && b == 8 = 216
12− | r == 18 && g == 18 && b == 18 = 217
13− | r == 28 && g == 28 && b == 28 = 218
14− | r == 38 && g == 38 && b == 38 = 219
15− | r == 48 && g == 48 && b == 48 = 220
16− | r == 58 && g == 58 && b == 58 = 221
17− | r == 68 && g == 68 && b == 68 = 222
18− | r == 78 && g == 78 && b == 78 = 223
19− | r == 88 && g == 88 && b == 88 = 224
20− | r == 98 && g == 98 && b == 98 = 225
21− | r == 108 && g == 108 && b == 108 = 226
22− | r == 118 && g == 118 && b == 118 = 227
23− | r == 128 && g == 128 && b == 128 = 228
24− | r == 138 && g == 138 && b == 138 = 229
25− | r == 148 && g == 148 && b == 148 = 230
26− | r == 158 && g == 158 && b == 158 = 231
27− | r == 168 && g == 168 && b == 168 = 232
28− | r == 178 && g == 178 && b == 178 = 233
29− | r == 188 && g == 188 && b == 188 = 234
30− | r == 198 && g == 198 && b == 198 = 235
31− | r == 208 && g == 208 && b == 208 = 236
32− | r == 218 && g == 218 && b == 218 = 237
33− | r == 228 && g == 228 && b == 228 = 238
34− | r == 238 && g == 238 && b == 238 = 239
35− | r <= 0 && g <= 0 && b <= 0 = 0
36− | r <= 0 && g <= 0 && b <= 95 = 1
37− | r <= 0 && g <= 0 && b <= 135 = 2
38− | r <= 0 && g <= 0 && b <= 175 = 3
39− | r <= 0 && g <= 0 && b <= 215 = 4
40− | r <= 0 && g <= 0 && b <= 255 = 5
41− | r <= 0 && g <= 95 && b <= 0 = 6
42− | r <= 0 && g <= 95 && b <= 95 = 7
43− | r <= 0 && g <= 95 && b <= 135 = 8
44− | r <= 0 && g <= 95 && b <= 175 = 9
45− | r <= 0 && g <= 95 && b <= 215 = 10
46− | r <= 0 && g <= 95 && b <= 255 = 11
47− | r <= 0 && g <= 135 && b <= 0 = 12
48− | r <= 0 && g <= 135 && b <= 95 = 13
49− | r <= 0 && g <= 135 && b <= 135 = 14
50− | r <= 0 && g <= 135 && b <= 175 = 15
51− | r <= 0 && g <= 135 && b <= 215 = 16
52− | r <= 0 && g <= 135 && b <= 255 = 17
53− | r <= 0 && g <= 175 && b <= 0 = 18
54− | r <= 0 && g <= 175 && b <= 95 = 19
55− | r <= 0 && g <= 175 && b <= 135 = 20
56− | r <= 0 && g <= 175 && b <= 175 = 21
57− | r <= 0 && g <= 175 && b <= 215 = 22
58− | r <= 0 && g <= 175 && b <= 255 = 23
59− | r <= 0 && g <= 215 && b <= 0 = 24
60− | r <= 0 && g <= 215 && b <= 95 = 25
61− | r <= 0 && g <= 215 && b <= 135 = 26
62− | r <= 0 && g <= 215 && b <= 175 = 27
63− | r <= 0 && g <= 215 && b <= 215 = 28
64− | r <= 0 && g <= 215 && b <= 255 = 29
65− | r <= 0 && g <= 255 && b <= 0 = 30
66− | r <= 0 && g <= 255 && b <= 95 = 31
67− | r <= 0 && g <= 255 && b <= 135 = 32
68− | r <= 0 && g <= 255 && b <= 175 = 33
69− | r <= 0 && g <= 255 && b <= 215 = 34
70− | r <= 0 && g <= 255 && b <= 255 = 35
71− | r <= 95 && g <= 0 && b <= 0 = 36
72− | r <= 95 && g <= 0 && b <= 95 = 37
73− | r <= 95 && g <= 0 && b <= 135 = 38
74− | r <= 95 && g <= 0 && b <= 175 = 39
75− | r <= 95 && g <= 0 && b <= 215 = 40
76− | r <= 95 && g <= 0 && b <= 255 = 41
77− | r <= 95 && g <= 95 && b <= 0 = 42
78− | r <= 95 && g <= 95 && b <= 95 = 43
79− | r <= 95 && g <= 95 && b <= 135 = 44
80− | r <= 95 && g <= 95 && b <= 175 = 45
81− | r <= 95 && g <= 95 && b <= 215 = 46
82− | r <= 95 && g <= 95 && b <= 255 = 47
83− | r <= 95 && g <= 135 && b <= 0 = 48
84− | r <= 95 && g <= 135 && b <= 95 = 49
85− | r <= 95 && g <= 135 && b <= 135 = 50
86− | r <= 95 && g <= 135 && b <= 175 = 51
87− | r <= 95 && g <= 135 && b <= 215 = 52
88− | r <= 95 && g <= 135 && b <= 255 = 53
89− | r <= 95 && g <= 175 && b <= 0 = 54
90− | r <= 95 && g <= 175 && b <= 95 = 55
91− | r <= 95 && g <= 175 && b <= 135 = 56
92− | r <= 95 && g <= 175 && b <= 175 = 57
93− | r <= 95 && g <= 175 && b <= 215 = 58
94− | r <= 95 && g <= 175 && b <= 255 = 59
95− | r <= 95 && g <= 215 && b <= 0 = 60
96− | r <= 95 && g <= 215 && b <= 95 = 61
97− | r <= 95 && g <= 215 && b <= 135 = 62
98− | r <= 95 && g <= 215 && b <= 175 = 63
99− | r <= 95 && g <= 215 && b <= 215 = 64
100− | r <= 95 && g <= 215 && b <= 255 = 65
101− | r <= 95 && g <= 255 && b <= 0 = 66
102− | r <= 95 && g <= 255 && b <= 95 = 67
103− | r <= 95 && g <= 255 && b <= 135 = 68
104− | r <= 95 && g <= 255 && b <= 175 = 69
105− | r <= 95 && g <= 255 && b <= 215 = 70
106− | r <= 95 && g <= 255 && b <= 255 = 71
107− | r <= 135 && g <= 0 && b <= 0 = 72
108− | r <= 135 && g <= 0 && b <= 95 = 73
109− | r <= 135 && g <= 0 && b <= 135 = 74
110− | r <= 135 && g <= 0 && b <= 175 = 75
111− | r <= 135 && g <= 0 && b <= 215 = 76
112− | r <= 135 && g <= 0 && b <= 255 = 77
113− | r <= 135 && g <= 95 && b <= 0 = 78
114− | r <= 135 && g <= 95 && b <= 95 = 79
115− | r <= 135 && g <= 95 && b <= 135 = 80
116− | r <= 135 && g <= 95 && b <= 175 = 81
117− | r <= 135 && g <= 95 && b <= 215 = 82
118− | r <= 135 && g <= 95 && b <= 255 = 83
119− | r <= 135 && g <= 135 && b <= 0 = 84
120− | r <= 135 && g <= 135 && b <= 95 = 85
121− | r <= 135 && g <= 135 && b <= 135 = 86
122− | r <= 135 && g <= 135 && b <= 175 = 87
123− | r <= 135 && g <= 135 && b <= 215 = 88
124− | r <= 135 && g <= 135 && b <= 255 = 89
125− | r <= 135 && g <= 175 && b <= 0 = 90
126− | r <= 135 && g <= 175 && b <= 95 = 91
127− | r <= 135 && g <= 175 && b <= 135 = 92
128− | r <= 135 && g <= 175 && b <= 175 = 93
129− | r <= 135 && g <= 175 && b <= 215 = 94
130− | r <= 135 && g <= 175 && b <= 255 = 95
131− | r <= 135 && g <= 215 && b <= 0 = 96
132− | r <= 135 && g <= 215 && b <= 95 = 97
133− | r <= 135 && g <= 215 && b <= 135 = 98
134− | r <= 135 && g <= 215 && b <= 175 = 99
135− | r <= 135 && g <= 215 && b <= 215 = 100
136− | r <= 135 && g <= 215 && b <= 255 = 101
137− | r <= 135 && g <= 255 && b <= 0 = 102
138− | r <= 135 && g <= 255 && b <= 95 = 103
139− | r <= 135 && g <= 255 && b <= 135 = 104
140− | r <= 135 && g <= 255 && b <= 175 = 105
141− | r <= 135 && g <= 255 && b <= 215 = 106
142− | r <= 135 && g <= 255 && b <= 255 = 107
143− | r <= 175 && g <= 0 && b <= 0 = 108
144− | r <= 175 && g <= 0 && b <= 95 = 109
145− | r <= 175 && g <= 0 && b <= 135 = 110
146− | r <= 175 && g <= 0 && b <= 175 = 111
147− | r <= 175 && g <= 0 && b <= 215 = 112
148− | r <= 175 && g <= 0 && b <= 255 = 113
149− | r <= 175 && g <= 95 && b <= 0 = 114
150− | r <= 175 && g <= 95 && b <= 95 = 115
151− | r <= 175 && g <= 95 && b <= 135 = 116
152− | r <= 175 && g <= 95 && b <= 175 = 117
153− | r <= 175 && g <= 95 && b <= 215 = 118
154− | r <= 175 && g <= 95 && b <= 255 = 119
155− | r <= 175 && g <= 135 && b <= 0 = 120
156− | r <= 175 && g <= 135 && b <= 95 = 121
157− | r <= 175 && g <= 135 && b <= 135 = 122
158− | r <= 175 && g <= 135 && b <= 175 = 123
159− | r <= 175 && g <= 135 && b <= 215 = 124
160− | r <= 175 && g <= 135 && b <= 255 = 125
161− | r <= 175 && g <= 175 && b <= 0 = 126
162− | r <= 175 && g <= 175 && b <= 95 = 127
163− | r <= 175 && g <= 175 && b <= 135 = 128
164− | r <= 175 && g <= 175 && b <= 175 = 129
165− | r <= 175 && g <= 175 && b <= 215 = 130
166− | r <= 175 && g <= 175 && b <= 255 = 131
167− | r <= 175 && g <= 215 && b <= 0 = 132
168− | r <= 175 && g <= 215 && b <= 95 = 133
169− | r <= 175 && g <= 215 && b <= 135 = 134
170− | r <= 175 && g <= 215 && b <= 175 = 135
171− | r <= 175 && g <= 215 && b <= 215 = 136
172− | r <= 175 && g <= 215 && b <= 255 = 137
173− | r <= 175 && g <= 255 && b <= 0 = 138
174− | r <= 175 && g <= 255 && b <= 95 = 139
175− | r <= 175 && g <= 255 && b <= 135 = 140
176− | r <= 175 && g <= 255 && b <= 175 = 141
177− | r <= 175 && g <= 255 && b <= 215 = 142
178− | r <= 175 && g <= 255 && b <= 255 = 143
179− | r <= 215 && g <= 0 && b <= 0 = 144
180− | r <= 215 && g <= 0 && b <= 95 = 145
181− | r <= 215 && g <= 0 && b <= 135 = 146
182− | r <= 215 && g <= 0 && b <= 175 = 147
183− | r <= 215 && g <= 0 && b <= 215 = 148
184− | r <= 215 && g <= 0 && b <= 255 = 149
185− | r <= 215 && g <= 95 && b <= 0 = 150
186− | r <= 215 && g <= 95 && b <= 95 = 151
187− | r <= 215 && g <= 95 && b <= 135 = 152
188− | r <= 215 && g <= 95 && b <= 175 = 153
189− | r <= 215 && g <= 95 && b <= 215 = 154
190− | r <= 215 && g <= 95 && b <= 255 = 155
191− | r <= 215 && g <= 135 && b <= 0 = 156
192− | r <= 215 && g <= 135 && b <= 95 = 157
193− | r <= 215 && g <= 135 && b <= 135 = 158
194− | r <= 215 && g <= 135 && b <= 175 = 159
195− | r <= 215 && g <= 135 && b <= 215 = 160
196− | r <= 215 && g <= 135 && b <= 255 = 161
197− | r <= 215 && g <= 175 && b <= 0 = 162
198− | r <= 215 && g <= 175 && b <= 95 = 163
199− | r <= 215 && g <= 175 && b <= 135 = 164
200− | r <= 215 && g <= 175 && b <= 175 = 165
201− | r <= 215 && g <= 175 && b <= 215 = 166
202− | r <= 215 && g <= 175 && b <= 255 = 167
203− | r <= 215 && g <= 215 && b <= 0 = 168
204− | r <= 215 && g <= 215 && b <= 95 = 169
205− | r <= 215 && g <= 215 && b <= 135 = 170
206− | r <= 215 && g <= 215 && b <= 175 = 171
207− | r <= 215 && g <= 215 && b <= 215 = 172
208− | r <= 215 && g <= 215 && b <= 255 = 173
209− | r <= 215 && g <= 255 && b <= 0 = 174
210− | r <= 215 && g <= 255 && b <= 95 = 175
211− | r <= 215 && g <= 255 && b <= 135 = 176
212− | r <= 215 && g <= 255 && b <= 175 = 177
213− | r <= 215 && g <= 255 && b <= 215 = 178
214− | r <= 215 && g <= 255 && b <= 255 = 179
215− | r <= 255 && g <= 0 && b <= 0 = 180
216− | r <= 255 && g <= 0 && b <= 95 = 181
217− | r <= 255 && g <= 0 && b <= 135 = 182
218− | r <= 255 && g <= 0 && b <= 175 = 183
219− | r <= 255 && g <= 0 && b <= 215 = 184
220− | r <= 255 && g <= 0 && b <= 255 = 185
221− | r <= 255 && g <= 95 && b <= 0 = 186
222− | r <= 255 && g <= 95 && b <= 95 = 187
223− | r <= 255 && g <= 95 && b <= 135 = 188
224− | r <= 255 && g <= 95 && b <= 175 = 189
225− | r <= 255 && g <= 95 && b <= 215 = 190
226− | r <= 255 && g <= 95 && b <= 255 = 191
227− | r <= 255 && g <= 135 && b <= 0 = 192
228− | r <= 255 && g <= 135 && b <= 95 = 193
229− | r <= 255 && g <= 135 && b <= 135 = 194
230− | r <= 255 && g <= 135 && b <= 175 = 195
231− | r <= 255 && g <= 135 && b <= 215 = 196
232− | r <= 255 && g <= 135 && b <= 255 = 197
233− | r <= 255 && g <= 175 && b <= 0 = 198
234− | r <= 255 && g <= 175 && b <= 95 = 199
235− | r <= 255 && g <= 175 && b <= 135 = 200
236− | r <= 255 && g <= 175 && b <= 175 = 201
237− | r <= 255 && g <= 175 && b <= 215 = 202
238− | r <= 255 && g <= 175 && b <= 255 = 203
239− | r <= 255 && g <= 215 && b <= 0 = 204
240− | r <= 255 && g <= 215 && b <= 95 = 205
241− | r <= 255 && g <= 215 && b <= 135 = 206
242− | r <= 255 && g <= 215 && b <= 175 = 207
243− | r <= 255 && g <= 215 && b <= 215 = 208
244− | r <= 255 && g <= 215 && b <= 255 = 209
245− | r <= 255 && g <= 255 && b <= 0 = 210
246− | r <= 255 && g <= 255 && b <= 95 = 211
247− | r <= 255 && g <= 255 && b <= 135 = 212
248− | r <= 255 && g <= 255 && b <= 175 = 213
249− | r <= 255 && g <= 255 && b <= 215 = 214
250− | r <= 255 && g <= 255 && b <= 255 = 215
251− | otherwise = error (printf "RGB color %d %d %d does not map to 240 palette."
252− (fromIntegral r :: Int)
253− (fromIntegral g :: Int)
254− (fromIntegral b :: Int))
255−
256−sameColor :: Int -> Int -> Int -> Bool
257−sameColor r g b = oldRgbColorToColor240 r g b == rgbColorToColor240 r g b
258−
259−genVal :: Gen Int
260−genVal = chooseInt (0, 255)
261−
262−sameColorCheck :: Property
263−sameColorCheck = forAll genVal (\r -> forAll genVal (\g -> forAll genVal (sameColor r g)))
264−
265−tests :: IO [Test]
266−tests = return
267− [ verify "check rgbColorToColor240 optimization is correct" sameColorCheck
268− ]
269−
test/VerifyConfig.hsdeleted−44
@@ -1,44 +0,0 @@
1−{-# LANGUAGE QuasiQuotes #-}
2−module Main where
3−
4−import Graphics.Vty.Config
5−import Graphics.Vty.Input.Events
6−
7−import Data.String.QQ
8−import qualified Data.ByteString.Char8 as B
9−
10−import Test.Framework
11−import Test.Framework.Providers.HUnit
12−import Test.HUnit hiding (Test)
13−
14−exampleConfig :: B.ByteString
15−exampleConfig = B.pack [s|
16−-- comments should be ignored.
17−map _ "\ESC[B" KUp []
18−askfjla dfasjdflk jasdlkfj asdfj -- lines failing parse should be ignored
19−map _ "\ESC[1;3B" KDown [MAlt]
20−map "xterm" "\ESC[1;3B" KDown [MAlt]
21−map "xterm-256-color" "\ESC[1;3B" KDown [MAlt]
22−debugLog "/tmp/vty-debug.txt"
23−|]
24−
25−exampleConfigConfig :: Config
26−exampleConfigConfig = defaultConfig
27− { debugLog = Just "/tmp/vty-debug.txt"
28− , inputMap = [ (Nothing, "\ESC[B", EvKey KUp [])
29− , (Nothing, "\ESC[1;3B", EvKey KDown [MAlt])
30− , (Just "xterm", "\ESC[1;3B", EvKey KDown [MAlt])
31− , (Just "xterm-256-color", "\ESC[1;3B", EvKey KDown [MAlt])
32− ]
33− }
34−
35−exampleConfigParses :: IO ()
36−exampleConfigParses =
37− assertEqual "example config parses as expected"
38− exampleConfigConfig
39− (runParseConfig "exampleConfig" exampleConfig)
40−
41−main :: IO ()
42−main = defaultMain
43− [ testCase "example config parses" $ exampleConfigParses
44− ]
test/VerifyCropSpanGeneration.hsdeleted−92
@@ -1,92 +0,0 @@
1−{-# LANGUAGE DisambiguateRecordFields #-}
2−{-# LANGUAGE NamedFieldPuns #-}
3−module VerifyCropSpanGeneration where
4−
5−import Verify.Graphics.Vty.Prelude
6−
7−import Verify.Graphics.Vty.Image
8−import Verify.Graphics.Vty.Picture
9−import Verify.Graphics.Vty.Span
10−
11−import Graphics.Vty.Debug
12−import Graphics.Vty.PictureToSpans
13−
14−import Verify
15−
16−import qualified Data.Vector as Vector
17−
18−cropOpDisplayOps :: (Int -> Image -> Image) ->
19− Int -> Image -> (DisplayOps, Image)
20−cropOpDisplayOps cropOp v i =
21− let iOut = cropOp v i
22− p = picForImage iOut
23− w = MockWindow (imageWidth iOut) (imageHeight iOut)
24− in (displayOpsForPic p (regionForWindow w), iOut)
25−
26−widthCropOutputColumns :: (Int -> Image -> Image) ->
27− SingleAttrSingleSpanStack ->
28− NonNegative Int ->
29− Property
30−widthCropOutputColumns cropOp s (NonNegative w) = stackWidth s > w ==>
31− let (ops, iOut) = cropOpDisplayOps cropOp w (stackImage s)
32− in verifyAllSpansHaveWidth iOut ops w
33−
34−heightCropOutputColumns :: (Int -> Image -> Image) ->
35− SingleAttrSingleSpanStack ->
36− NonNegative Int ->
37− Property
38−heightCropOutputColumns cropOp s (NonNegative h) = stackHeight s > h ==>
39− let (ops, _) = cropOpDisplayOps cropOp h (stackImage s)
40− in displayOpsRows ops == h
41−
42−cropRightOutputColumns :: SingleAttrSingleSpanStack -> NonNegative Int -> Property
43−cropRightOutputColumns = widthCropOutputColumns cropRight
44−
45−cropLeftOutputColumns :: SingleAttrSingleSpanStack -> NonNegative Int -> Property
46−cropLeftOutputColumns = widthCropOutputColumns cropLeft
47−
48−cropTopOutputRows :: SingleAttrSingleSpanStack -> NonNegative Int -> Property
49−cropTopOutputRows = heightCropOutputColumns cropTop
50−
51−cropBottomOutputRows :: SingleAttrSingleSpanStack -> NonNegative Int -> Property
52−cropBottomOutputRows = heightCropOutputColumns cropBottom
53−
54−-- TODO: known benign failure.
55−cropRightAndLeftRejoinedEquivalence :: SingleAttrSingleSpanStack -> Property
56−cropRightAndLeftRejoinedEquivalence stack = imageWidth (stackImage stack) `mod` 2 == 0 ==>
57− let i = stackImage stack
58− -- the right part is made by cropping the image from the left.
59− iR = cropLeft (imageWidth i `div` 2) i
60− -- the left part is made by cropping the image from the right
61− iL = cropRight (imageWidth i `div` 2) i
62− iAlt = iL <|> iR
63− iOps = displayOpsForImage i
64− iAltOps = displayOpsForImage iAlt
65− in verifyOpsEquality iOps iAltOps
66−
67−cropTopAndBottomRejoinedEquivalence :: SingleAttrSingleSpanStack -> Property
68−cropTopAndBottomRejoinedEquivalence stack = imageHeight (stackImage stack) `mod` 2 == 0 ==>
69− let i = stackImage stack
70− -- the top part is made by cropping the image from the bottom.
71− iT = cropBottom (imageHeight i `div` 2) i
72− -- the bottom part is made by cropping the image from the top.
73− iB = cropTop (imageHeight i `div` 2) i
74− iAlt = iT <-> iB
75− in displayOpsForImage i == displayOpsForImage iAlt
76−
77−tests :: IO [Test]
78−tests = return
79− [ verify "cropping from the bottom produces display operations covering the expected rows"
80− cropBottomOutputRows
81− , verify "cropping from the top produces display operations covering the expected rows"
82− cropTopOutputRows
83− , verify "cropping from the left produces display operations covering the expected columns"
84− cropLeftOutputColumns
85− , verify "cropping from the right produces display operations covering the expected columns"
86− cropRightOutputColumns
87− -- TODO: known benign failure.
88− -- , verify "the output of a stack is the same as that stack cropped left & right and joined together"
89− -- cropRightAndLeftRejoinedEquivalence
90− , verify "the output of a stack is the same as that stack cropped top & bottom and joined together"
91− cropTopAndBottomRejoinedEquivalence
92− ]
test/VerifyEmptyImageProps.hsdeleted−14
@@ -1,14 +0,0 @@
1−{-# LANGUAGE ScopedTypeVariables #-}
2−module VerifyEmptyImageProps where
3−
4−import Verify
5−
6−-- should be exported by Graphics.Vty.Picture
7−import Graphics.Vty.Image ( Image, emptyImage )
8−
9−tests :: IO [Test]
10−tests = do
11− -- should provide an image type.
12− let _ :: Image = emptyImage
13− return []
14−
test/VerifyEvalTerminfoCaps.hsdeleted−92
@@ -1,92 +0,0 @@
1−{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE NamedFieldPuns #-}
3−{-# LANGUAGE ScopedTypeVariables #-}
4−module VerifyEvalTerminfoCaps where
5−
6−import Blaze.ByteString.Builder.Internal.Write (runWrite, getBound)
7−import Data.Terminfo.Eval
8−import Data.Terminfo.Parse
9−import Control.DeepSeq
10−
11−import qualified System.Console.Terminfo as Terminfo
12−
13−import Verify
14−import Verify.Graphics.Vty.Output
15−
16−import Control.Applicative ( (<$>) )
17−import Control.Exception ( try, SomeException(..) )
18−
19−import Control.Monad ( mapM_, forM, forM_ )
20−
21−import Data.Maybe ( fromJust )
22−import Data.Word
23−
24−import Foreign.Marshal.Alloc (mallocBytes)
25−import Foreign.Ptr (Ptr, minusPtr)
26−import Numeric
27−
28−-- If a terminal defines one of the caps then it's expected to be
29−-- parsable.
30−capsOfInterest =
31− [ "cup"
32− , "sc"
33− , "rc"
34− , "setf"
35− , "setb"
36− , "setaf"
37− , "setab"
38− , "op"
39− , "cnorm"
40− , "civis"
41− , "smcup"
42− , "rmcup"
43− , "clear"
44− , "hpa"
45− , "vpa"
46− , "sgr"
47− , "sgr0"
48− ]
49−
50−fromCapname ti name = fromJust $ Terminfo.getCapability ti (Terminfo.tiGetStr name)
51−
52−tests :: IO [Test]
53−tests = do
54− -- 1 MB should be big enough for any termcaps ;-)
55− evalBuffer :: Ptr Word8 <- mallocBytes (1024 * 1024)
56− fmap concat $ forM terminalsOfInterest $ \termName -> do
57− putStrLn $ "adding tests for terminal: " ++ termName
58− mti <- try $ Terminfo.setupTerm termName
59− case mti of
60− Left (_e :: SomeException)
61− -> return []
62− Right ti -> do
63− fmap concat $ forM capsOfInterest $ \capName -> do
64− case Terminfo.getCapability ti (Terminfo.tiGetStr capName) of
65− Just capDef -> do
66− putStrLn $ "\tadding test for cap: " ++ capName
67− let testName = termName ++ "(" ++ capName ++ ")"
68− case parseCapExpression capDef of
69− Left error -> return [verify testName (failed {reason = "parse error " ++ show error})]
70− Right !cap_expr -> return [verify testName (verifyEvalCap evalBuffer cap_expr)]
71− Nothing -> do
72− return []
73−
74−{-# NOINLINE verifyEvalCap #-}
75−verifyEvalCap :: Ptr Word8 -> CapExpression -> Int -> Property
76−verifyEvalCap evalBuffer expr !junkInt = do
77− forAll (vector 9) $ \inputValues ->
78− let write = writeCapExpr expr inputValues
79− !byteCount = getBound write
80− in liftIOResult $ do
81− let startPtr :: Ptr Word8 = evalBuffer
82− forM_ [0..100] $ \i -> runWrite write startPtr
83− endPtr <- runWrite write startPtr
84− case endPtr `minusPtr` startPtr of
85− count | count < 0 ->
86− return $ failed { reason = "End pointer before start pointer." }
87− | toEnum count > byteCount ->
88− return $ failed { reason = "End pointer past end of buffer by "
89− ++ show (toEnum count - byteCount)
90− }
91− | otherwise ->
92− return succeeded
test/VerifyImageOps.hsdeleted−174
@@ -1,174 +0,0 @@
1−{-# LANGUAGE ScopedTypeVariables #-}
2−module VerifyImageOps where
3−
4−import Graphics.Vty.Attributes
5−import Graphics.Vty.Image.Internal
6−import Verify.Graphics.Vty.Image
7−
8−import Verify
9−
10−import Control.DeepSeq
11−
12−twoSwHorizConcat :: SingleColumnChar -> SingleColumnChar -> Bool
13−twoSwHorizConcat (SingleColumnChar c1) (SingleColumnChar c2) =
14− imageWidth (char defAttr c1 <|> char defAttr c2) == 2
15−
16−manySwHorizConcat :: [SingleColumnChar] -> Bool
17−manySwHorizConcat cs =
18− let chars = [ char | SingleColumnChar char <- cs ]
19− l = fromIntegral $ length cs
20− in imageWidth ( horizCat $ map (char defAttr) chars ) == l
21−
22−twoSwVertConcat :: SingleColumnChar -> SingleColumnChar -> Bool
23−twoSwVertConcat (SingleColumnChar c1) (SingleColumnChar c2) =
24− imageHeight (char defAttr c1 <-> char defAttr c2) == 2
25−
26−horizConcatSwAssoc :: SingleColumnChar -> SingleColumnChar -> SingleColumnChar -> Bool
27−horizConcatSwAssoc (SingleColumnChar c0) (SingleColumnChar c1) (SingleColumnChar c2) =
28− (char defAttr c0 <|> char defAttr c1) <|> char defAttr c2
29− ==
30− char defAttr c0 <|> (char defAttr c1 <|> char defAttr c2)
31−
32−twoDwHorizConcat :: DoubleColumnChar -> DoubleColumnChar -> Bool
33−twoDwHorizConcat (DoubleColumnChar c1) (DoubleColumnChar c2) =
34− imageWidth (char defAttr c1 <|> char defAttr c2) == 4
35−
36−manyDwHorizConcat :: [DoubleColumnChar] -> Bool
37−manyDwHorizConcat cs =
38− let chars = [ char | DoubleColumnChar char <- cs ]
39− l = fromIntegral $ length cs
40− in imageWidth ( horizCat $ map (char defAttr) chars ) == l * 2
41−
42−twoDwVertConcat :: DoubleColumnChar -> DoubleColumnChar -> Bool
43−twoDwVertConcat (DoubleColumnChar c1) (DoubleColumnChar c2) =
44− imageHeight (char defAttr c1 <-> char defAttr c2) == 2
45−
46−horizConcatDwAssoc :: DoubleColumnChar -> DoubleColumnChar -> DoubleColumnChar -> Bool
47−horizConcatDwAssoc (DoubleColumnChar c0) (DoubleColumnChar c1) (DoubleColumnChar c2) =
48− (char defAttr c0 <|> char defAttr c1) <|> char defAttr c2
49− ==
50− char defAttr c0 <|> (char defAttr c1 <|> char defAttr c2)
51−
52−vertContatSingleRow :: NonEmptyList SingleRowSingleAttrImage -> Bool
53−vertContatSingleRow (NonEmpty stack) =
54− let expectedHeight :: Int = length stack
55− stackImage = vertCat [ i | SingleRowSingleAttrImage { rowImage = i } <- stack ]
56− in imageHeight stackImage == expectedHeight
57−
58−disjointHeightHorizJoin :: NonEmptyList SingleRowSingleAttrImage
59− -> NonEmptyList SingleRowSingleAttrImage
60− -> Bool
61−disjointHeightHorizJoin (NonEmpty stack0) (NonEmpty stack1) =
62− let expectedHeight :: Int = max (length stack0) (length stack1)
63− stackImage0 = vertCat [ i | SingleRowSingleAttrImage { rowImage = i } <- stack0 ]
64− stackImage1 = vertCat [ i | SingleRowSingleAttrImage { rowImage = i } <- stack1 ]
65− in imageHeight (stackImage0 <|> stackImage1) == expectedHeight
66−
67−
68−disjointHeightHorizJoinBgFill :: NonEmptyList SingleRowSingleAttrImage
69− -> NonEmptyList SingleRowSingleAttrImage
70− -> Bool
71−disjointHeightHorizJoinBgFill (NonEmpty stack0) (NonEmpty stack1) =
72− let stackImage0 = vertCat [ i | SingleRowSingleAttrImage { rowImage = i } <- stack0 ]
73− stackImage1 = vertCat [ i | SingleRowSingleAttrImage { rowImage = i } <- stack1 ]
74− image = stackImage0 <|> stackImage1
75− expectedHeight = imageHeight image
76− in case image of
77− HorizJoin {} -> ( expectedHeight == (imageHeight $ partLeft image) )
78− &&
79− ( expectedHeight == (imageHeight $ partRight image) )
80− _ -> True
81−
82−disjointWidthVertJoin :: NonEmptyList SingleRowSingleAttrImage
83− -> NonEmptyList SingleRowSingleAttrImage
84− -> Bool
85−disjointWidthVertJoin (NonEmpty stack0) (NonEmpty stack1) =
86− let expectedWidth = maximum $ map imageWidth (stack0Images ++ stack1Images)
87− stack0Images = [ i | SingleRowSingleAttrImage { rowImage = i } <- stack0 ]
88− stack1Images = [ i | SingleRowSingleAttrImage { rowImage = i } <- stack1 ]
89− stack0Image = vertCat stack0Images
90− stack1Image = vertCat stack1Images
91− image = stack0Image <-> stack1Image
92− in imageWidth image == expectedWidth
93−
94−disjointWidthVertJoinBgFill :: NonEmptyList SingleRowSingleAttrImage
95− -> NonEmptyList SingleRowSingleAttrImage
96− -> Bool
97−disjointWidthVertJoinBgFill (NonEmpty stack0) (NonEmpty stack1) =
98− let expectedWidth = maximum $ map imageWidth (stack0Images ++ stack1Images)
99− stack0Images = [ i | SingleRowSingleAttrImage { rowImage = i } <- stack0 ]
100− stack1Images = [ i | SingleRowSingleAttrImage { rowImage = i } <- stack1 ]
101− stack0Image = vertCat stack0Images
102− stack1Image = vertCat stack1Images
103− image = stack0Image <-> stack1Image
104− in case image of
105− VertJoin {} -> ( expectedWidth == (imageWidth $ partTop image) )
106− &&
107− ( expectedWidth == (imageWidth $ partBottom image) )
108− _ -> True
109−
110−translationIsLinearOnOutSize :: Translation -> Bool
111−translationIsLinearOnOutSize (Translation i (x,y) i') =
112− imageWidth i' == imageWidth i + x && imageHeight i' == imageHeight i + y
113−
114−paddingIsLinearOnOutSize :: Image -> Gen Bool
115−paddingIsLinearOnOutSize i = do
116− l <- offset
117− t <- offset
118− r <- offset
119− b <- offset
120− let i' = pad l t r b i
121− return $ imageWidth i' == imageWidth i + l + r && imageHeight i' == imageHeight i + t + b
122− where offset = choose (1,1024)
123−
124−cropLeftLimitsWidth :: Image -> Int -> Property
125−cropLeftLimitsWidth i v = v >= 0 ==>
126− v >= imageWidth (cropLeft v i)
127−
128−cropRightLimitsWidth :: Image -> Int -> Property
129−cropRightLimitsWidth i v = v >= 0 ==>
130− v >= imageWidth (cropRight v i)
131−
132−cropTopLimitsHeight :: Image -> Int -> Property
133−cropTopLimitsHeight i v = v >= 0 ==>
134− v >= imageHeight (cropTop v i)
135−
136−cropBottomLimitsHeight :: Image -> Int -> Property
137−cropBottomLimitsHeight i v = v >= 0 ==>
138− v >= imageHeight (cropBottom v i)
139−
140−-- ridiculous tests just to satisfy my desire for nice code coverage :-P
141−canShowImage :: Image -> Bool
142−canShowImage i = length (show i) > 0
143−
144−canRnfImage :: Image -> Bool
145−canRnfImage i = rnf i == ()
146−
147−canPpImage :: Image -> Bool
148−canPpImage i = length (ppImageStructure i) > 0
149−
150−tests :: IO [Test]
151−tests = return
152− [ verify "twoSwHorizConcat" twoSwHorizConcat
153− , verify "manySwHorizConcat" manySwHorizConcat
154− , verify "twoSwVertConcat" twoSwVertConcat
155− , verify "horizConcatSwAssoc" horizConcatSwAssoc
156− , verify "manyDwHorizConcat" manyDwHorizConcat
157− , verify "twoDwHorizConcat" twoDwHorizConcat
158− , verify "twoDwVertConcat" twoDwVertConcat
159− , verify "horizConcatDwAssoc" horizConcatDwAssoc
160− , verify "single row vert concats to correct height" vertContatSingleRow
161− , verify "disjointHeightHorizJoin" disjointHeightHorizJoin
162− , verify "disjointHeightHorizJoin BG fill" disjointHeightHorizJoinBgFill
163− , verify "disjointWidthVertJoin" disjointWidthVertJoin
164− , verify "disjointWidthVertJoin BG fill" disjointWidthVertJoinBgFill
165− , verify "translation effects output dimensions linearly" translationIsLinearOnOutSize
166− , verify "padding effects output dimensions linearly" paddingIsLinearOnOutSize
167− , verify "crop left limits width" cropLeftLimitsWidth
168− , verify "crop right limits width" cropRightLimitsWidth
169− , verify "crop top limits height" cropTopLimitsHeight
170− , verify "crop bottom limits height" cropBottomLimitsHeight
171− , verify "can show image" canShowImage
172− , verify "can rnf image" canRnfImage
173− , verify "can pp image" canPpImage
174− ]
test/VerifyImageTrans.hsdeleted−34
@@ -1,34 +0,0 @@
1−{-# LANGUAGE NamedFieldPuns #-}
2−module VerifyImageTrans where
3−
4−import Verify.Graphics.Vty.Image
5−
6−import Graphics.Vty.Image.Internal
7−
8−import Verify
9−
10−import Data.Word
11−
12−isHorizTextOfColumns :: Image -> Int -> Bool
13−isHorizTextOfColumns (HorizText { outputWidth = inW }) expectedW = inW == expectedW
14−isHorizTextOfColumns (BGFill { outputWidth = inW }) expectedW = inW == expectedW
15−isHorizTextOfColumns _image _expectedW = False
16−
17−verifyHorizContatWoAttrChangeSimplifies :: SingleRowSingleAttrImage -> Bool
18−verifyHorizContatWoAttrChangeSimplifies (SingleRowSingleAttrImage _attr charCount image) =
19− isHorizTextOfColumns image charCount
20−
21−verifyHorizContatWAttrChangeSimplifies :: SingleRowTwoAttrImage -> Bool
22−verifyHorizContatWAttrChangeSimplifies ( SingleRowTwoAttrImage (SingleRowSingleAttrImage attr0 charCount0 _image0)
23− (SingleRowSingleAttrImage attr1 charCount1 _image1)
24− i
25− )
26− | charCount0 == 0 || charCount1 == 0 || attr0 == attr1 = isHorizTextOfColumns i (charCount0 + charCount1)
27− | otherwise = False == isHorizTextOfColumns i (charCount0 + charCount1)
28−
29−tests :: IO [Test]
30−tests = return
31− [ verify "verifyHorizContatWoAttrChangeSimplifies" verifyHorizContatWoAttrChangeSimplifies
32− , verify "verifyHorizContatWAttrChangeSimplifies" verifyHorizContatWAttrChangeSimplifies
33− ]
34−
test/VerifyInline.hsdeleted−31
@@ -1,31 +0,0 @@
1−module VerifyInline where
2−
3−import Graphics.Vty.Inline
4−import Graphics.Vty.Output
5−import Graphics.Vty.Output.TerminfoBased as TerminfoBased
6−
7−import Verify.Graphics.Vty.Output
8−
9−import Verify
10−
11−import Distribution.TestSuite
12−
13−import System.IO
14−
15−tests :: IO [Test]
16−tests = concat <$> forM terminalsOfInterest (\termName -> return $
17− [ Test $ TestInstance
18− { name = "verify vty inline"
19− , run = do
20− {- disabled because I cannot get useful output out of cabal why this fails.
21− nullOut <- openFile "/dev/null" WriteMode
22− t <- TerminfoBased.reserveTerminal termName nullOut
23− putAttrChange t $ default_all
24− releaseTerminal t
25− -}
26− return $ Finished Pass
27− , tags = []
28− , options = []
29− , setOption = \_ _ -> Left "no options supported"
30− }
31− ])
test/VerifyLayersSpanGeneration.hsdeleted−130
@@ -1,130 +0,0 @@
1−{-# LANGUAGE DisambiguateRecordFields #-}
2−{-# LANGUAGE NamedFieldPuns #-}
3−module VerifyLayersSpanGeneration where
4−
5−import Verify.Graphics.Vty.Prelude
6−
7−import Verify.Graphics.Vty.Attributes
8−import Verify.Graphics.Vty.Image
9−import Verify.Graphics.Vty.Picture
10−import Verify.Graphics.Vty.Span
11−
12−import Graphics.Vty.Debug
13−import Graphics.Vty.PictureToSpans
14−
15−import Verify
16−
17−import qualified Data.Vector as Vector
18−
19−largerHorizSpanOcclusion :: SingleRowSingleAttrImage -> SingleRowSingleAttrImage -> Result
20−largerHorizSpanOcclusion row0 row1 =
21− let i0 = rowImage row0
22− i1 = rowImage row1
23− (iLarger, iSmaller) = if imageWidth i0 > imageWidth i1 then (i0, i1) else (i1, i0)
24− expectedOps = displayOpsForImage iLarger
25− p = picForLayers [iLarger, iSmaller]
26− ops = displayOpsForPic p (imageWidth iLarger,imageHeight iLarger)
27− in verifyOpsEquality expectedOps ops
28−
29−-- | Two rows stacked vertical is equivalent to the first row rendered
30−-- as the top layer and the second row rendered as a bottom layer with a
31−-- background fill where the first row would be.
32−vertStackLayerEquivalence0 :: SingleRowSingleAttrImage -> SingleRowSingleAttrImage -> Result
33−vertStackLayerEquivalence0 row0 row1 =
34− let i0 = rowImage row0
35− i1 = rowImage row1
36− i = i0 <-> i1
37− p = picForImage i
38− iLower = backgroundFill (imageWidth i0) 1 <-> i1
39− pLayered = picForLayers [i0, iLower]
40− expectedOps = displayOpsForImage i
41− opsLayered = displayOpsForPic pLayered (imageWidth iLower,imageHeight iLower)
42− in verifyOpsEquality expectedOps opsLayered
43−
44−vertStackLayerEquivalence1 :: SingleRowSingleAttrImage -> SingleRowSingleAttrImage -> Result
45−vertStackLayerEquivalence1 row0 row1 =
46− let i0 = rowImage row0
47− i1 = rowImage row1
48− i = i0 <-> i1
49− p = picForImage i
50− iLower = i0 <-> backgroundFill (imageWidth i1) 1
51− iUpper = backgroundFill (imageWidth i0) 1 <-> i1
52− pLayered = picForLayers [iUpper, iLower]
53− expectedOps = displayOpsForImage i
54− opsLayered = displayOpsForPic pLayered (imageWidth iLower,imageHeight iLower)
55− in verifyOpsEquality expectedOps opsLayered
56−
57−-- | Two rows horiz joined is equivalent to the first row rendered as
58−-- the top layer and the second row rendered as a bottom layer with a
59−-- background fill where the first row would be.
60−horizJoinLayerEquivalence0 :: SingleRowSingleAttrImage -> SingleRowSingleAttrImage -> Result
61−horizJoinLayerEquivalence0 row0 row1 =
62− let i0 = rowImage row0
63− i1 = rowImage row1
64− i = i0 <|> i1
65− p = picForImage i
66− iLower = backgroundFill (imageWidth i0) 1 <|> i1
67− pLayered = picForLayers [i0, iLower]
68− expectedOps = displayOpsForImage i
69− opsLayered = displayOpsForPic pLayered (imageWidth iLower,imageHeight iLower)
70− in verifyOpsEquality expectedOps opsLayered
71−
72−horizJoinLayerEquivalence1 :: SingleRowSingleAttrImage -> SingleRowSingleAttrImage -> Result
73−horizJoinLayerEquivalence1 row0 row1 =
74− let i0 = rowImage row0
75− i1 = rowImage row1
76− i = i0 <|> i1
77− p = picForImage i
78− iLower = i0 <|> backgroundFill (imageWidth i1) 1
79− iUpper = backgroundFill (imageWidth i0) 1 <|> i1
80− pLayered = picForLayers [iUpper, iLower]
81− expectedOps = displayOpsForImage i
82− opsLayered = displayOpsForPic pLayered (imageWidth iLower,imageHeight iLower)
83− in verifyOpsEquality expectedOps opsLayered
84−
85−horizJoinAlternate0 :: Result
86−horizJoinAlternate0 =
87− let size = 4
88− str0 = replicate size 'a'
89− str1 = replicate size 'b'
90− i0 = string defAttr str0
91− i1 = string defAttr str1
92− i = horizCat $ zipWith horizJoin (replicate size i0) (replicate size i1)
93− layer0 = horizCat $ replicate size $ i0 <|> backgroundFill size 1
94− layer1 = horizCat $ replicate size $ backgroundFill size 1 <|> i1
95− expectedOps = displayOpsForImage i
96− opsLayered = displayOpsForPic (picForLayers [layer0, layer1])
97− (imageWidth i,imageHeight i)
98− in verifyOpsEquality expectedOps opsLayered
99−
100−horizJoinAlternate1 :: Result
101−horizJoinAlternate1 =
102− let size = 4
103− str0 = replicate size 'a'
104− str1 = replicate size 'b'
105− i0 = string defAttr str0
106− i1 = string defAttr str1
107− i = horizCat $ zipWith horizJoin (replicate size i0) (replicate size i1)
108− layers = [l | b <- take 4 [0,size*2..], let l = backgroundFill b 1 <|> i0 <|> i1]
109− expectedOps = displayOpsForImage i
110− opsLayered = displayOpsForPic (picForLayers layers)
111− (imageWidth i,imageHeight i)
112− in verifyOpsEquality expectedOps opsLayered
113−
114−tests :: IO [Test]
115−tests = return
116− [ verify "a larger horiz span occludes a smaller span on a lower layer"
117− largerHorizSpanOcclusion
118− , verify "two rows stack vertical equiv to first image layered on top of second with padding (0)"
119− vertStackLayerEquivalence0
120− , verify "two rows stack vertical equiv to first image layered on top of second with padding (1)"
121− vertStackLayerEquivalence1
122− -- , verify "two rows horiz joined equiv to first image layered on top of second with padding (0)"
123− -- horizJoinLayerEquivalence0
124− -- , verify "two rows horiz joined equiv to first image layered on top of second with padding (1)"
125− -- horizJoinLayerEquivalence1
126− -- , verify "alternating images using joins is the same as alternating images using layers (0)"
127− -- horizJoinAlternate0
128− -- , verify "alternating images using joins is the same as alternating images using layers (1)"
129− -- horizJoinAlternate1
130− ]
test/VerifyOutput.hsdeleted−58
@@ -1,58 +0,0 @@
1−-- We setup the environment to invoke certain terminals of interest.
2−-- This assumes appropriate definitions exist in the current environment
3−-- for the terminals of interest.
4−{-# LANGUAGE ScopedTypeVariables #-}
5−module VerifyOutput where
6−
7−import Verify
8−
9−import Graphics.Vty
10−
11−import Verify.Graphics.Vty.Image
12−import Verify.Graphics.Vty.Output
13−
14−import Control.Monad
15−
16−import qualified System.Console.Terminfo as Terminfo
17−import System.Posix.IO
18−
19−tests :: IO [Test]
20−tests = concat <$> forM terminalsOfInterest (\termName -> do
21− -- check if that terminfo exists
22− -- putStrLn $ "testing end to end for terminal: " ++ termName
23− mti <- try $ Terminfo.setupTerm termName
24− case mti of
25− Left (_ :: SomeException) -> return []
26− Right _ -> return [ verify ("verify " ++ termName ++ " could output a picture")
27− (smokeTestTermNonMac termName)
28− ]
29− )
30−
31−smokeTestTermNonMac :: String -> Image -> Property
32−smokeTestTermNonMac termName i = liftIOResult $ do
33− smokeTestTerm termName i
34−
35−smokeTestTerm :: String -> Image -> IO Result
36−smokeTestTerm termName i = do
37− nullOut <- openFd "/dev/null" WriteOnly Nothing defaultFileFlags
38− t <- outputForConfig $ defaultConfig
39− { outputFd = Just nullOut
40− , termName = Just termName
41− , colorMode = Just NoColor
42− }
43− -- putStrLn $ "context color count: " ++ show (contextColorCount t)
44− reserveDisplay t
45− dc <- displayContext t (100,100)
46− -- always show the cursor to produce tests for terminals with no
47− -- cursor support.
48− let pic = (picForImage i) { picCursor = Cursor 0 0 }
49− outputPicture dc pic
50− setCursorPos t 0 0
51− when (supportsCursorVisibility t) $ do
52− hideCursor t
53− showCursor t
54− releaseDisplay t
55− releaseTerminal t
56− closeFd nullOut
57− return succeeded
58−
test/VerifyParseTerminfoCaps.hsdeleted−101
@@ -1,101 +0,0 @@
1−{-# LANGUAGE NamedFieldPuns #-}
2−{-# LANGUAGE ScopedTypeVariables #-}
3−module VerifyParseTerminfoCaps where
4−
5−import Prelude hiding ( catch )
6−
7−import qualified System.Console.Terminfo as Terminfo
8−
9−import Verify.Data.Terminfo.Parse
10−import Verify.Graphics.Vty.Output
11−import Verify
12−
13−import Data.Maybe ( catMaybes, fromJust )
14−import Data.Word
15−
16−import Numeric
17−
18−-- If a terminal defines one of the caps then it's expected to be parsable.
19−-- TODO: reduce duplication with terminfo terminal implementation.
20−capsOfInterest =
21− [ "cup"
22− , "sc"
23− , "rc"
24− , "setf"
25− , "setb"
26− , "setaf"
27− , "setab"
28− , "op"
29− , "cnorm"
30− , "civis"
31− , "smcup"
32− , "rmcup"
33− , "clear"
34− , "hpa"
35− , "vpa"
36− , "sgr"
37− , "sgr0"
38− ]
39−
40−fromCapname ti name = fromJust $ Terminfo.getCapability ti (Terminfo.tiGetStr name)
41−
42−tests :: IO [Test]
43−tests = do
44− parseTests <- concat <$> forM terminalsOfInterest (\termName ->
45− liftIO (try $ Terminfo.setupTerm termName)
46− >>= either (\(_e :: SomeException) -> return [])
47− (\ti -> concat <$> forM capsOfInterest (\capName -> do
48− let caseName = "\tparsing cap: " ++ capName
49− liftIO $ putStrLn caseName
50− return $ case Terminfo.getCapability ti (Terminfo.tiGetStr capName) of
51− Just capDef -> [verify (caseName ++ " -> " ++ show capDef)
52− (verifyParseCap capDef $ const succeeded)]
53− Nothing -> []
54− )
55− )
56− )
57− return $ [ verify "parse_nonParamaterizedCaps" nonParamaterizedCaps
58− , verify "parse cap string with literal %" literalPercentCaps
59− , verify "parse cap string with %i op" incFirstTwoCaps
60− , verify "parse cap string with %pN op" pushParamCaps
61− ] ++ parseTests
62−
63−verifyParseCap capString onParse =
64− case parseCapExpression capString of
65− Left error -> failed { reason = "parse error " ++ show error }
66− Right e -> onParse e
67−
68−nonParamaterizedCaps (NonParamCapString cap) = do
69− verifyParseCap cap $ \e ->
70− let expectedBytes = map (toEnum . fromEnum) cap
71− outBytes = bytesForRange e 0 (length cap)
72− in verifyBytesEqual outBytes expectedBytes
73−
74−literalPercentCaps (LiteralPercentCap capString expectedBytes) = do
75− verifyParseCap capString $ \e -> verifyBytesEqual (collectBytes e) expectedBytes
76−
77−incFirstTwoCaps (IncFirstTwoCap capString expectedBytes) = do
78− verifyParseCap capString $ \e -> verifyBytesEqual (collectBytes e) expectedBytes
79−
80−pushParamCaps (PushParamCap capString expectedParamCount expectedBytes) = do
81− verifyParseCap capString $ \e ->
82− let outBytes = collectBytes e
83− outParamCount = paramCount e
84− in if outParamCount == expectedParamCount
85− then verifyBytesEqual outBytes expectedBytes
86− else failed { reason = "out param count /= expected param count" }
87−
88−decPrintParamCaps (DecPrintCap capString expectedParamCount expectedBytes) = do
89− verifyParseCap capString $ \e ->
90− let outBytes = collectBytes e
91− outParamCount = paramCount e
92− in if outParamCount == expectedParamCount
93− then verifyBytesEqual outBytes expectedBytes
94− else failed { reason = "out param count /= expected param count" }
95−
96−printCap ti capName = do
97− putStrLn $ capName ++ ": " ++ show (fromCapname ti capName)
98−
99−printExpression ti capName = do
100− let parseResult = parseCapExpression $ fromCapname ti capName
101− putStrLn $ capName ++ ": " ++ show parseResult
test/VerifySimpleSpanGeneration.hsdeleted−188
@@ -1,188 +0,0 @@
1−{-# LANGUAGE DisambiguateRecordFields #-}
2−{-# LANGUAGE NamedFieldPuns #-}
3−module VerifySimpleSpanGeneration where
4−
5−import Verify.Graphics.Vty.Prelude
6−
7−import Verify.Graphics.Vty.Image
8−import Verify.Graphics.Vty.Picture
9−import Verify.Graphics.Vty.Span
10−
11−import Graphics.Vty.Debug
12−import Graphics.Vty.PictureToSpans
13−
14−import Verify
15−
16−import qualified Data.Vector as Vector
17−
18−unitImageAndZeroWindow0 :: UnitImage -> EmptyWindow -> Bool
19−unitImageAndZeroWindow0 (UnitImage _ i) (EmptyWindow w) =
20− let p = picForImage i
21− ops = displayOpsForPic p (regionForWindow w)
22− in displayOpsColumns ops == 0 && displayOpsRows ops == 0
23−
24−unitImageAndZeroWindow1 :: UnitImage -> EmptyWindow -> Bool
25−unitImageAndZeroWindow1 (UnitImage _ i) (EmptyWindow w) =
26− let p = picForImage i
27− ops = displayOpsForPic p (regionForWindow w)
28− in ( spanOpsAffectedRows ops == 0 ) && ( allSpansHaveWidth ops 0 )
29−
30−horizSpanImageAndZeroWindow0 :: SingleRowSingleAttrImage -> EmptyWindow -> Bool
31−horizSpanImageAndZeroWindow0 (SingleRowSingleAttrImage { rowImage = i }) (EmptyWindow w) =
32− let p = picForImage i
33− ops = displayOpsForPic p (regionForWindow w)
34− in displayOpsColumns ops == 0 && displayOpsRows ops == 0
35−
36−horizSpanImageAndZeroWindow1 :: SingleRowSingleAttrImage -> EmptyWindow -> Bool
37−horizSpanImageAndZeroWindow1 (SingleRowSingleAttrImage { rowImage = i }) (EmptyWindow w) =
38− let p = picForImage i
39− ops = displayOpsForPic p (regionForWindow w)
40− in ( spanOpsAffectedRows ops == 0 ) && ( allSpansHaveWidth ops 0 )
41−
42−horizSpanImageAndEqualWindow0 :: SingleRowSingleAttrImage -> Result
43−horizSpanImageAndEqualWindow0 (SingleRowSingleAttrImage { rowImage = i, expectedColumns = c }) =
44− let p = picForImage i
45− w = MockWindow c 1
46− ops = displayOpsForPic p (regionForWindow w)
47− in verifyAllSpansHaveWidth i ops c
48−
49−horizSpanImageAndEqualWindow1 :: SingleRowSingleAttrImage -> Bool
50−horizSpanImageAndEqualWindow1 (SingleRowSingleAttrImage { rowImage = i, expectedColumns = c }) =
51− let p = picForImage i
52− w = MockWindow c 1
53− ops = displayOpsForPic p (regionForWindow w)
54− in spanOpsAffectedRows ops == 1
55−
56−horizSpanImageAndLesserWindow0 :: SingleRowSingleAttrImage -> Result
57−horizSpanImageAndLesserWindow0 (SingleRowSingleAttrImage { rowImage = i, expectedColumns = c }) =
58− let p = picForImage i
59− lesserWidth = c `div` 2
60− w = MockWindow lesserWidth 1
61− ops = displayOpsForPic p (regionForWindow w)
62− in verifyAllSpansHaveWidth i ops lesserWidth
63−
64−singleAttrSingleSpanStackCropped0 :: SingleAttrSingleSpanStack -> Result
65−singleAttrSingleSpanStackCropped0 stack =
66− let p = picForImage (stackImage stack)
67− w = MockWindow (stackWidth stack `div` 2) (stackHeight stack)
68− ops = displayOpsForPic p (regionForWindow w)
69− in verifyAllSpansHaveWidth (stackImage stack) ops (stackWidth stack `div` 2)
70−
71−singleAttrSingleSpanStackCropped1 :: SingleAttrSingleSpanStack -> Bool
72−singleAttrSingleSpanStackCropped1 stack =
73− let p = picForImage (stackImage stack)
74− expectedRowCount = stackHeight stack `div` 2
75− w = MockWindow (stackWidth stack) expectedRowCount
76− ops = displayOpsForPic p (regionForWindow w)
77− actualRowCount = spanOpsAffectedRows ops
78− in expectedRowCount == actualRowCount
79−
80−singleAttrSingleSpanStackCropped2 :: SingleAttrSingleSpanStack -> SingleAttrSingleSpanStack -> Result
81−singleAttrSingleSpanStackCropped2 stack0 stack1 =
82− let p = picForImage (stackImage stack0 <|> stackImage stack1)
83− w = MockWindow (stackWidth stack0) (imageHeight (picImage p))
84− ops = displayOpsForPic p (regionForWindow w)
85− in verifyAllSpansHaveWidth (picImage p) ops (stackWidth stack0)
86−
87−singleAttrSingleSpanStackCropped3 :: SingleAttrSingleSpanStack -> SingleAttrSingleSpanStack -> Bool
88−singleAttrSingleSpanStackCropped3 stack0 stack1 =
89− let p = picForImage (stackImage stack0 <|> stackImage stack1)
90− w = MockWindow (imageWidth (picImage p)) expectedRowCount
91− ops = displayOpsForPic p (regionForWindow w)
92− expectedRowCount = imageHeight (picImage p) `div` 2
93− actualRowCount = spanOpsAffectedRows ops
94− in expectedRowCount == actualRowCount
95−
96−singleAttrSingleSpanStackCropped4 :: SingleAttrSingleSpanStack -> SingleAttrSingleSpanStack -> Result
97−singleAttrSingleSpanStackCropped4 stack0 stack1 =
98− let p = picForImage (stackImage stack0 <-> stackImage stack1)
99− w = MockWindow expectedWidth (imageHeight (picImage p))
100− ops = displayOpsForPic p (regionForWindow w)
101− expectedWidth = imageWidth (picImage p) `div` 2
102− in verifyAllSpansHaveWidth (picImage p) ops expectedWidth
103−
104−singleAttrSingleSpanStackCropped5 :: SingleAttrSingleSpanStack -> SingleAttrSingleSpanStack -> Bool
105−singleAttrSingleSpanStackCropped5 stack0 stack1 =
106− let p = picForImage (stackImage stack0 <-> stackImage stack1)
107− w = MockWindow (imageWidth (picImage p)) (stackHeight stack0)
108− ops = displayOpsForPic p (regionForWindow w)
109− expectedRowCount = stackHeight stack0
110− actualRowCount = spanOpsAffectedRows ops
111− in expectedRowCount == actualRowCount
112−
113−horizSpanImageAndGreaterWindow0 :: SingleRowSingleAttrImage -> Result
114−horizSpanImageAndGreaterWindow0 (SingleRowSingleAttrImage { rowImage = i, expectedColumns = c }) =
115− let p = picForImage i
116− -- SingleRowSingleAttrImage always has width >= 1
117− greaterWidth = c * 2
118− w = MockWindow greaterWidth 1
119− ops = displayOpsForPic p (regionForWindow w)
120− in verifyAllSpansHaveWidth i ops greaterWidth
121−
122−arbImageIsCropped :: DefaultImage -> MockWindow -> Bool
123−arbImageIsCropped (DefaultImage image) win@(MockWindow w h) =
124− let pic = picForImage image
125− ops = displayOpsForPic pic (regionForWindow win)
126− in ( spanOpsAffectedRows ops == h ) && ( allSpansHaveWidth ops w )
127−
128−spanOpsActuallyFillRows :: DefaultPic -> Bool
129−spanOpsActuallyFillRows (DefaultPic pic win) =
130− let ops = displayOpsForPic pic (regionForWindow win)
131− expectedRowCount = regionHeight (regionForWindow win)
132− actualRowCount = spanOpsAffectedRows ops
133− in expectedRowCount == actualRowCount
134−
135−spanOpsActuallyFillColumns :: DefaultPic -> Bool
136−spanOpsActuallyFillColumns (DefaultPic pic win) =
137− let ops = displayOpsForPic pic (regionForWindow win)
138− expectedColumnCount = regionWidth (regionForWindow win)
139− in allSpansHaveWidth ops expectedColumnCount
140−
141−firstSpanOpSetsAttr :: DefaultPic -> Bool
142−firstSpanOpSetsAttr DefaultPic { defaultPic = pic, defaultWin = win } =
143− let ops = displayOpsForPic pic (regionForWindow win)
144− in all ( isAttrSpanOp . Vector.head ) ( Vector.toList ops )
145−
146−singleAttrSingleSpanStackOpCoverage :: SingleAttrSingleSpanStack -> Result
147−singleAttrSingleSpanStackOpCoverage stack =
148− let p = picForImage (stackImage stack)
149− w = MockWindow (stackWidth stack) (stackHeight stack)
150− ops = displayOpsForPic p (regionForWindow w)
151− in verifyAllSpansHaveWidth (stackImage stack) ops (stackWidth stack)
152−
153−imageCoverageMatchesBounds :: Image -> Result
154−imageCoverageMatchesBounds i =
155− let p = picForImage i
156− r = (imageWidth i,imageHeight i)
157− ops = displayOpsForPic p r
158− in verifyAllSpansHaveWidth i ops (imageWidth i)
159−
160−tests :: IO [Test]
161−tests = return
162− [ verify "unit image is cropped when window size == (0,0) [0]" unitImageAndZeroWindow0
163− , verify "unit image is cropped when window size == (0,0) [1]" unitImageAndZeroWindow1
164− , verify "horiz span image is cropped when window size == (0,0) [0]" horizSpanImageAndZeroWindow0
165− , verify "horiz span image is cropped when window size == (0,0) [1]" horizSpanImageAndZeroWindow1
166− , verify "horiz span image is not cropped when window size == size of image [width]" horizSpanImageAndEqualWindow0
167− , verify "horiz span image is not cropped when window size == size of image [height]" horizSpanImageAndEqualWindow1
168− , verify "horiz span image is not cropped when window size < size of image [width]" horizSpanImageAndLesserWindow0
169− , verify "horiz span image is not cropped when window size > size of image [width]" horizSpanImageAndGreaterWindow0
170− , verify "first span op is always to set the text attribute" firstSpanOpSetsAttr
171− , verify "a stack of single attr text spans should define content for all the columns [output region == size of stack]"
172− singleAttrSingleSpanStackOpCoverage
173− , verify "a single attr text span is cropped when window size < size of stack image [width]"
174− singleAttrSingleSpanStackCropped0
175− , verify "a single attr text span is cropped when window size < size of stack image [height]"
176− singleAttrSingleSpanStackCropped1
177− , verify "single attr text span <|> single attr text span display cropped. [width]"
178− singleAttrSingleSpanStackCropped2
179− , verify "single attr text span <|> single attr text span display cropped. [height]"
180− singleAttrSingleSpanStackCropped3
181− , verify "single attr text span <-> single attr text span display cropped. [width]"
182− singleAttrSingleSpanStackCropped4
183− , verify "single attr text span <-> single attr text span display cropped. [height]"
184− singleAttrSingleSpanStackCropped5
185− , verify "an arbitrary image when rendered to a window of the same size will cover the entire window"
186− imageCoverageMatchesBounds
187− ]
188−
test/VerifyUsingMockInput.hsdeleted−223
@@ -1,223 +0,0 @@
1−{-# LANGUAGE MultiParamTypeClasses #-}
2−{-# LANGUAGE FlexibleInstances #-}
3−{-# LANGUAGE ScopedTypeVariables #-}
4−-- Generate some input bytes and delays between blocks of input bytes.
5−-- Verify the events produced are as expected.
6−module Main where
7−
8−import Verify.Graphics.Vty.Output
9−
10−import Graphics.Vty hiding (resize)
11−import Graphics.Vty.Input.Events
12−import Graphics.Vty.Input.Loop
13−import Graphics.Vty.Input.Terminfo
14−
15−import Control.Concurrent
16−import Control.Concurrent.STM
17−import Control.Exception
18−import Lens.Micro ((^.))
19−import Control.Monad
20−
21−import Data.IORef
22−import Data.List (intersperse, reverse, nubBy)
23−
24−import System.Console.Terminfo
25−import System.Posix.IO
26−import System.Posix.Terminal (openPseudoTerminal)
27−import System.Posix.Types
28−import System.Timeout
29−
30−import Test.Framework.Providers.SmallCheck
31−import Test.Framework
32−import Test.SmallCheck
33−import Test.SmallCheck.Series
34−
35−import Text.Printf
36−
37−-- processing a block of 16 chars is the largest I can do without taking
38−-- too long to run the test.
39−maxBlockSize :: Int
40−maxBlockSize = 16
41−
42−maxTableSize :: Int
43−maxTableSize = 28
44−
45−forEachOf :: (Show a, Testable m b) => [a] -> (a -> b) -> Property m
46−forEachOf l = over (generate (\n -> take n l))
47−
48−data InputEvent
49− = Bytes String
50− -- ^ Input sequence encoded as a string. Regardless, the input is
51− -- read a byte at a time.
52− | Delay Int
53− -- ^ Microsecond delay
54− deriving Show
55−
56−type InputSpec = [InputEvent]
57−
58−type ExpectedSpec = [Event]
59−
60−synthesizeInput :: InputSpec -> Fd -> IO ()
61−synthesizeInput input outHandle = forM_ input f >> (void $ fdWrite outHandle "\xFFFD")
62− where
63− f (Bytes str) = void $ fdWrite outHandle str
64− f (Delay t) = threadDelay t
65−
66−minDetectableDelay :: Int
67−minDetectableDelay = 4000
68−
69−minTimout :: Int
70−minTimout = 4000000
71−
72−testKeyDelay :: Int
73−testKeyDelay = minDetectableDelay * 4
74−
75−testEscSampleDelay :: Int
76−testEscSampleDelay = minDetectableDelay * 2
77−
78−genEventsUsingIoActions :: Int -> IO () -> IO () -> IO ()
79−genEventsUsingIoActions maxDuration inputAction outputAction = do
80− let maxDuration' = max minTimout maxDuration
81− readComplete <- newEmptyMVar
82− writeComplete <- newEmptyMVar
83− _ <- forkOS $ inputAction `finally` putMVar writeComplete ()
84− _ <- forkOS $ outputAction `finally` putMVar readComplete ()
85− Just () <- timeout maxDuration' $ takeMVar writeComplete
86− Just () <- timeout maxDuration' $ takeMVar readComplete
87− return ()
88−
89−compareEvents :: (Show a1, Show a, Eq a1) => a -> [a1] -> [a1] -> IO Bool
90−compareEvents inputSpec expectedEvents outEvents = compareEvents' expectedEvents outEvents
91− where
92− compareEvents' [] [] = return True
93− compareEvents' [] outEvents' = do
94− printf "extra events %s\n" (show outEvents') :: IO ()
95− return False
96− compareEvents' expectedEvents' [] = do
97− printf "events %s were not produced for input %s\n" (show expectedEvents') (show inputSpec) :: IO ()
98− printf "expected events %s\n" (show expectedEvents) :: IO ()
99− printf "received events %s\n" (show outEvents) :: IO ()
100− return False
101− compareEvents' (e : expectedEvents') (o : outEvents')
102− | e == o = compareEvents' expectedEvents' outEvents'
103− | otherwise = do
104− printf "%s expected not %s for input %s\n" (show e) (show o) (show inputSpec) :: IO ()
105− printf "expected events %s\n" (show expectedEvents) :: IO ()
106− printf "received events %s\n" (show outEvents) :: IO ()
107− return False
108−
109−assertEventsFromSynInput :: ClassifyMap -> InputSpec -> ExpectedSpec -> IO Bool
110−assertEventsFromSynInput table inputSpec expectedEvents = do
111− let maxDuration = sum [t | Delay t <- inputSpec] + minDetectableDelay
112− eventCount = length expectedEvents
113− (writeFd, readFd) <- openPseudoTerminal
114− (setTermAttr,_) <- attributeControl readFd
115− setTermAttr
116− let testConfig = defaultConfig { inputFd = Just readFd
117− , termName = Just "dummy"
118− , vmin = Just 1
119− , vtime = Just 100
120− }
121− input <- initInput testConfig table
122− eventsRef <- newIORef []
123− let writeWaitClose = do
124− synthesizeInput inputSpec writeFd
125− threadDelay minDetectableDelay
126− shutdownInput input
127− threadDelay minDetectableDelay
128− closeFd writeFd
129− closeFd readFd
130− -- drain output pipe
131− let readEvents = readLoop eventCount
132− readLoop 0 = return ()
133− readLoop n = do
134− e <- atomically $ readTChan $ input^.eventChannel
135− case e of
136− InputEvent ev -> modifyIORef eventsRef ((:) ev)
137− ResumeAfterSignal -> return ()
138− readLoop (n - 1)
139− genEventsUsingIoActions maxDuration writeWaitClose readEvents
140− outEvents <- reverse <$> readIORef eventsRef
141− compareEvents inputSpec expectedEvents outEvents
142−
143−newtype InputBlocksUsingTable event
144− = InputBlocksUsingTable ([(String,event)] -> [(String, event)])
145−
146−instance Show (InputBlocksUsingTable event) where
147− show (InputBlocksUsingTable _g) = "InputBlocksUsingTable"
148−
149−instance Monad m => Serial m (InputBlocksUsingTable event) where
150− series = do
151− n :: Int <- localDepth (const maxTableSize) series
152− return $ InputBlocksUsingTable $ \raw_table ->
153− let table = reverse $ nubBy (\(s0,_) (s1,_) -> s0 == s1) $ reverse raw_table
154− in concat (take n (selections table))
155− where
156− selections [] = []
157− selections (x:xs) = let z = selections xs in [x] : (z ++ map ((:) x) z)
158−
159−verifyVisibleSynInputToEvent :: Property IO
160−verifyVisibleSynInputToEvent = forAll $ \(InputBlocksUsingTable gen) -> monadic $ do
161− let table = visibleChars
162− inputSeq = gen table
163− events = map snd inputSeq
164− keydowns = map (Bytes . fst) inputSeq
165− input = intersperse (Delay testKeyDelay) keydowns ++ [Delay testKeyDelay]
166− assertEventsFromSynInput universalTable input events
167−
168−verifyCapsSynInputToEvent :: Property IO
169−verifyCapsSynInputToEvent = forAll $ \(InputBlocksUsingTable gen) ->
170− forEachOf terminalsOfInterest $ \terminalName -> monadic $ do
171− term <- setupTerm terminalName
172− let table = capsClassifyMap term keysFromCapsTable
173− inputSeq = gen table
174− events = map snd inputSeq
175− keydowns = map (Bytes . fst) inputSeq
176− input = intersperse (Delay testKeyDelay) keydowns ++ [Delay testKeyDelay]
177− assertEventsFromSynInput table input events
178−
179−verifySpecialSynInputToEvent :: Property IO
180−verifySpecialSynInputToEvent = forAll $ \(InputBlocksUsingTable gen) -> monadic $ do
181− let table = specialSupportKeys
182− inputSeq = gen table
183− events = map snd inputSeq
184− keydowns = map (Bytes . fst) inputSeq
185− input = intersperse (Delay testKeyDelay) keydowns ++ [Delay testKeyDelay]
186− assertEventsFromSynInput universalTable input events
187−
188−verifyFullSynInputToEvent :: Property IO
189−verifyFullSynInputToEvent = forAll $ \(InputBlocksUsingTable gen) ->
190− forEachOf terminalsOfInterest $ \terminalName -> monadic $ do
191− term <- setupTerm terminalName
192− let table = classifyMapForTerm terminalName term
193− inputSeq = gen table
194− events = map snd inputSeq
195− keydowns = map (Bytes . fst) inputSeq
196− input = intersperse (Delay testKeyDelay) keydowns ++ [Delay testKeyDelay]
197− assertEventsFromSynInput table input events
198−
199−verifyFullSynInputToEvent_2x :: Property IO
200−verifyFullSynInputToEvent_2x = forAll $ \(InputBlocksUsingTable gen) ->
201− forEachOf terminalsOfInterest $ \terminalName -> monadic $ do
202− term <- setupTerm terminalName
203− let table = classifyMapForTerm terminalName term
204− inputSeq = gen table
205− events = concatMap ((\s -> [s,s]) . snd) inputSeq
206− keydowns = map (Bytes . (\s -> s ++ s) . fst) inputSeq
207− input = intersperse (Delay testKeyDelay) keydowns ++ [Delay testKeyDelay]
208− assertEventsFromSynInput table input events
209−
210−main :: IO ()
211−main = defaultMain
212− [ testProperty "synthesized typing of single visible chars translates to expected events"
213− verifyVisibleSynInputToEvent
214− , testProperty "synthesized typing of keys from capabilities tables translates to expected events"
215− verifyCapsSynInputToEvent
216− , testProperty "synthesized typing of hard coded special keys translates to expected events"
217− verifySpecialSynInputToEvent
218− , testProperty "synthesized typing of any key in the table translates to its paired event"
219− verifyFullSynInputToEvent
220− , testProperty "synthesized typing of 2x any key in the table translates to 2x paired event"
221− verifyFullSynInputToEvent_2x
222− ]
223−
test/VerifyUsingMockTerminal.hsdeleted−108
@@ -1,108 +0,0 @@
1−module VerifyUsingMockTerminal where
2−
3−import Verify.Graphics.Vty.Prelude
4−
5−import Verify.Graphics.Vty.Picture
6−import Verify.Graphics.Vty.Image
7−import Verify.Graphics.Vty.Attributes
8−import Verify.Graphics.Vty.Span
9−import Verify.Graphics.Vty.Output
10−import Graphics.Vty.Output
11−import Graphics.Vty.Output.Interface
12−import Graphics.Vty.Output.Mock
13−
14−import Graphics.Vty.Debug
15−
16−import Verify
17−
18−import qualified Data.ByteString as BS
19−import Data.IORef
20−import qualified Data.String.UTF8 as UTF8
21−
22−import System.IO
23−
24−unitImageUnitBounds :: UnitImage -> Property
25−unitImageUnitBounds (UnitImage _ i) = liftIOResult $ do
26− (_,t) <- mockTerminal (1,1)
27− dc <- displayBounds t >>= displayContext t
28− let pic = picForImage i
29− outputPicture dc pic
30− return succeeded
31−
32−unitImageArbBounds :: UnitImage -> MockWindow -> Property
33−unitImageArbBounds (UnitImage _ i) (MockWindow w h) = liftIOResult $ do
34− (_,t) <- mockTerminal (w,h)
35− dc <- displayBounds t >>= displayContext t
36− let pic = picForImage i
37− outputPicture dc pic
38− return succeeded
39−
40−singleTRow :: MockWindow -> Property
41−singleTRow (MockWindow w h) = liftIOResult $ do
42− (mockData,t) <- mockTerminal (w,h)
43− dc <- displayBounds t >>= displayContext t
44− -- create an image that contains just the character T repeated for a
45− -- single row
46− let i = horizCat $ replicate (fromEnum w) (char defAttr 'T')
47− pic = (picForImage i) { picBackground = Background 'B' defAttr }
48− outputPicture dc pic
49− -- The mock output string that represents the output bytes a single
50− -- line containing the T string: Followed by h - 1 lines of a change
51− -- to the background attribute and then the background character
52− let expected = "H" ++ "MDA" ++ replicate (fromEnum w) 'T'
53− ++ concat (replicate (fromEnum h - 1) $ "MDA" ++ replicate (fromEnum w) 'B')
54− compareMockOutput mockData expected
55−
56−manyTRows :: MockWindow -> Property
57−manyTRows (MockWindow w h) = liftIOResult $ do
58− (mockData, t) <- mockTerminal (w,h)
59− dc <- displayBounds t >>= displayContext t
60− -- create an image that contains the character 'T' repeated for all
61− -- the rows
62− let i = vertCat $ replicate (fromEnum h) $ horizCat $ replicate (fromEnum w) (char defAttr 'T')
63− pic = (picForImage i) { picBackground = Background 'B' defAttr }
64− outputPicture dc pic
65− -- The UTF8 string that represents the output bytes is h repeats of
66− -- a move, 'M', followed by an attribute change. 'A', followed by w
67− -- 'T's
68− let expected = "H" ++ concat (replicate (fromEnum h) $ "MDA" ++ replicate (fromEnum w) 'T')
69− compareMockOutput mockData expected
70−
71−manyTRowsCroppedWidth :: MockWindow -> Property
72−manyTRowsCroppedWidth (MockWindow w h) = liftIOResult $ do
73− (mockData,t) <- mockTerminal (w,h)
74− dc <- displayBounds t >>= displayContext t
75− -- create an image that contains the character 'T' repeated for all
76− -- the rows
77− let i = vertCat $ replicate (fromEnum h) $ horizCat $ replicate (fromEnum w * 2) (char defAttr 'T')
78− pic = (picForImage i) { picBackground = Background 'B' defAttr }
79− outputPicture dc pic
80− -- The UTF8 string that represents the output bytes is h repeats of
81− -- a move, 'M', followed by an attribute change. 'A', followed by w
82− -- 'T's
83− let expected = "H" ++ concat (replicate (fromEnum h) $ "MDA" ++ replicate (fromEnum w) 'T')
84− compareMockOutput mockData expected
85−
86−manyTRowsCroppedHeight :: MockWindow -> Property
87−manyTRowsCroppedHeight (MockWindow w h) = liftIOResult $ do
88− (mockData,t) <- mockTerminal (w,h)
89− dc <- displayBounds t >>= displayContext t
90− -- create an image that contains the character 'T' repeated for all
91− -- the rows
92− let i = vertCat $ replicate (fromEnum h * 2) $ horizCat $ replicate (fromEnum w) (char defAttr 'T')
93− pic = (picForImage i) { picBackground = Background 'B' defAttr }
94− outputPicture dc pic
95− -- The UTF8 string that represents the output bytes is h repeats of
96− -- a move, 'M', followed by an attribute change. 'A', followed by w
97− -- count 'T's
98− let expected = "H" ++ concat (replicate (fromEnum h) $ "MDA" ++ replicate (fromEnum w) 'T')
99− compareMockOutput mockData expected
100−
101−tests :: IO [Test]
102−tests = return [ verify "unitImageUnitBounds" unitImageUnitBounds
103− , verify "unitImageArbBounds" unitImageArbBounds
104− , verify "singleTRow" singleTRow
105− , verify "manyTRows" manyTRows
106− , verify "manyTRowsCroppedWidth" manyTRowsCroppedWidth
107− , verify "manyTRowsCroppedHeight" manyTRowsCroppedHeight
108− ]
test/VerifyUtf8Width.hsdeleted−30
@@ -1,30 +0,0 @@
1−module VerifyUtf8Width where
2−
3−import Verify
4−
5−import Graphics.Text.Width
6−import Graphics.Vty.Attributes
7−import Graphics.Vty.Picture
8−import Graphics.Vty.Image
9−
10−swIs1Column :: SingleColumnChar -> Bool
11−swIs1Column (SingleColumnChar c) = imageWidth (char defAttr c) == 1
12−
13−dwIs2Column :: DoubleColumnChar -> Bool
14−dwIs2Column (DoubleColumnChar c) = imageWidth (char defAttr c) == 2
15−
16−dcStringIsEven :: NonEmptyList DoubleColumnChar -> Bool
17−dcStringIsEven (NonEmpty dw_list) =
18− even $ safeWcswidth [ c | DoubleColumnChar c <- dw_list ]
19−
20−safeWcwidthForControlChars :: Bool
21−safeWcwidthForControlChars = 0 == safeWcwidth '\NUL'
22−
23−tests :: IO [Test]
24−tests = return
25− [ verify "swIs1Column" swIs1Column
26− , verify "dwIs2Column" dwIs2Column
27− , verify "a string of double characters is an even width" dcStringIsEven
28− , verify "safeWcwidth provides a width of 0 for chars without widths" safeWcwidthForControlChars
29− ]
30−
test/benchmark.hsdeleted−56
@@ -1,56 +0,0 @@
1−{-# LANGUAGE QuasiQuotes #-}
2−module Main where
3−
4−import Graphics.Vty
5−
6−import qualified BenchImageFuzz
7−import qualified BenchNoDiffOpt
8−import qualified BenchRenderChar
9−import qualified BenchVerticalScroll
10−
11−import Control.Monad
12−
13−import Data.Maybe
14−import Data.List
15−
16−import System.Environment
17−import System.Posix.Process
18−
19−import Verify
20−
21−main = do
22− args <- getArgs
23− let benches = [ ("no-diff-opt-0", BenchNoDiffOpt.bench0)
24− , ("render-char-0", BenchRenderChar.bench0)
25− , ("render-char-1", BenchRenderChar.bench1)
26− , ("vertical-scroll-0", BenchVerticalScroll.bench0)
27− , ("image-fuzz-0", BenchImageFuzz.bench0) ]
28− help = forM_ benches $ \(b,_) -> putStrLn $ "--" ++ b
29− case args of
30− ["--help"] -> help
31− ["-h" ] -> help
32− _ -> do
33− let args' = if args /= []
34− then map (drop 2) args
35− else map fst benches
36− -- drop the dash-dash "--"
37− results <- forM args' $ \bName -> do
38− case lookup bName benches of
39− Just b -> bench bName b
40− Nothing -> fail $ "No benchmark named " ++ bName
41− print results
42− return ()
43−
44−bench bName b = do
45− putStrLn $ "starting " ++ bName
46− Bench bDataGen bProc <- b
47− bData <- bDataGen
48− startTimes <- bData `deepseq` getProcessTimes
49− bProc bData
50− endTimes <- getProcessTimes
51− let ut = userTime endTimes - userTime startTimes
52− st = systemTime endTimes - systemTime startTimes
53− putStrLn $ "user time: " ++ show ut
54− putStrLn $ "system time: " ++ show st
55− return (bName, ut, st)
56−
test/current_benchmark_results.txtdeleted−40
@@ -1,40 +0,0 @@
1−post-blaze-builder
2−Phenom II X4, GHC 7.6.3 on linux, Konsole
3− [("no-diff-opt-0",81,6),("render-char-0",318,10),("render-char-1",126,9),("vertical-scroll-0",745,39),("image-fuzz-0",182,7)]
4− [("no-diff-opt-0",94,6),("render-char-0",322,10),("render-char-1",137,7),("vertical-scroll-0",770,36),("image-fuzz-0",186,8)]
5−
6−2 ghz core i7, GHC 7.6.3 on Mac using iTerm
7− [("no-diff-opt-0",30,3),("render-char-0",118,8),("render-char-1",49,5),("vertical-scroll-0",307,27),("image-fuzz-0",194,13)]
8− [("no-diff-opt-0",29,3),("render-char-0",119,9),("render-char-1",49,5),("vertical-scroll-0",307,27),("image-fuzz-0",192,13)]
9−
10−2 ghz core i7. GHC 7.6.3 on NixOS over screen + ssh using Mac iTerm
11− [("no-diff-opt-0",18,26),("render-char-0",93,140),("render-char-1",26,46),("vertical-scroll-0",190,279),("image-fuzz-0",117,107)]
12−
13−pre-blaze-builder
14−
15−Phenom II X4, GHC 7.6.3 on linux, Konsole
16− [("no-diff-opt-0",79,6),("render-char-0",474,10),("render-char-1",154,12),("vertical-scroll-0",702,37),("image-fuzz-0",144,6)]
17− [("no-diff-opt-0",76,6),("render-char-0",485,11),("render-char-1",152,13),("vertical-scroll-0",701,37),("image-fuzz-0",149,7)]
18−
19−2.4 ghz intel core duo. Mac iTerm
20− [("no-diff-opt-0",43,9),("render-char-0",345,28),("render-char-1",76,14),("vertical-scroll-0",431,62),("image-fuzz-0",169,21)]
21−2 ghz core i7. NixOS over screen + ssh using Mac iTerm
22− [("no-diff-opt-0",16,23),("render-char-0",119,182),("render-char-1",32,44),("vertical-scroll-0",164,245),("image-fuzz-0",94,75)]
23−
24−no unclipped opt 5.0
25−on toast
26− [("no-diff-opt-0",147,8),("render-char-0",332,18),("render-char-1",277,16),("vertical-scroll-0",1503,61),("image-fuzz-0",46,3)]
27− [("no-diff-opt-0",137,11),("render-char-0",470,18),("render-char-1",254,18),("vertical-scroll-0",1240,56),("image-fuzz-0",46,3)]
28−
29−on ooyala, no screen, iTerm:
30−
31− * 5.0 initial:
32− * [("no-diff-opt-0",24,3),("render-char-0",102,8),("render-char-1",41,4),("vertical-scroll-0",226,24),("image-fuzz-0",17,1)]
33−
34− * post background span replacement:
35− * [("no-diff-opt-0",26,3),("render-char-0",109,8),("render-char-1",44,5),("vertical-scroll-0",244,26),("image-fuzz-0",17,2)]
36− * [("no-diff-opt-0",25,4),("render-char-0",110,7),("render-char-1",43,6),("vertical-scroll-0",239,24),("image-fuzz-0",17,2)]
37−
38−release 4.7
39−on toast
40− [("no-diff-opt-0",226,13),("render-char-0",411,22),("render-char-1",386,21),("vertical-scroll-0",1930,96),("image-fuzz-0",1225,52)]
test/interactive_terminal_test.hs+450−739
@@ -2,42 +2,41 @@
22{-# LANGUAGE ScopedTypeVariables #-}
33module Main where
44
5−import Graphics.Vty
65import Graphics.Vty.Attributes
6+import Graphics.Vty.Image
77import Graphics.Vty.Inline
88import Graphics.Vty.Picture
9−import Graphics.Vty.Output
9+import Graphics.Vty.Terminal
10+import Graphics.Vty.DisplayRegion
1011
11−import Control.Concurrent (threadDelay)
12−import Control.Exception
1312import Control.Monad
1413
1514import Data.List ( lookup )
1615import Data.Maybe ( isJust, fromJust )
1716import Data.Monoid
18−import Data.String.QQ
1917import Data.Word
2018
21−import Foreign.Marshal.Array
19+import Foreign.Marshal.Array
20+import HereDoc
2221
2322import qualified System.Environment as Env
2423
2524import System.IO ( hFlush, hPutStr, hPutBuf, stdout )
2625
2726main = do
28− printIntro
27+ print_intro
2928
30−outputFilePath = "test_results.list"
29+output_file_path = "test_results.list"
3130
32−printIntro = do
33− putStr $ [s|
31+print_intro = do
32+ putStr $ [$heredoc|
3433This is an interactive verification program for the terminal input and output
3534support of the VTY library. This will ask a series of questions about what you
3635see on screen. The goal is to verify that VTY's output and input support
3736performs as expected with your terminal.
3837
3938This program produces a file named
40− |] ++ outputFilePath ++ [s|
39+ |] ++ output_file_path ++ [$heredoc|
4140in the current directory that contains the results for each test assertion. This
4241can be emailed to coreyoconnor@gmail.com and used by the VTY authors to improve
4342support for your terminal. No personal information is contained in the report.
@@ -63,43 +62,42 @@ All the tests assume the following about the terminal display:
6362 a good range of the unicode characters. Each test involving unicode display
6463 describes the expected appearance of each glyph.
6564
66−Thanks for the help! :-D
67−To exit the test early enter "q" anytime at the following menu screen.
68−
69−If any test fails then please post an issue to
70− https://github.com/coreyoconnor/vty/issues
71−with the test_results.list file pasted into the issue. A suitable summary is:
72−"interactive terminal test failure".
73−|]
74− waitForReturn
75− results <- doTestMenu 1
76− envAttributes <- mapM ( \envName -> Control.Exception.catch ( Env.getEnv envName >>= return . (,) envName )
77− ( \ (_ :: SomeException) -> return (envName, "") )
78− )
79− [ "TERM", "COLORTERM", "LANG", "TERM_PROGRAM", "XTERM_VERSION" ]
80− t <- standardIOConfig >>= outputForConfig
81− let resultsTxt = show envAttributes ++ "\n"
82− ++ terminalID t ++ "\n"
83− ++ show results ++ "\n"
84− releaseTerminal t
85− writeFile outputFilePath resultsTxt
86−
87−waitForReturn = do
65+Thanks for the help! :-D
66+To exit the test early enter "q" anytime at the following menu screen. Even if
67+you exit the test early please email the test_results.list file to
68+coreyoconnor@gmail.com. The results file will still contain information useful
69+to debug terminal support.
70+
71+|]
72+ wait_for_return
73+ results <- do_test_menu 1
74+ env_attributes <- mapM ( \env_name -> catch ( Env.getEnv env_name >>= return . (,) env_name )
75+ ( const $ return (env_name, "") )
76+ )
77+ [ "TERM", "COLORTERM", "LANG", "TERM_PROGRAM", "XTERM_VERSION" ]
78+ t <- terminal_handle
79+ let results_txt = show env_attributes ++ "\n"
80+ ++ terminal_ID t ++ "\n"
81+ ++ show results ++ "\n"
82+ release_terminal t
83+ writeFile output_file_path results_txt
84+
85+wait_for_return = do
8886 putStr "\n(press return to continue)"
8987 hFlush stdout
9088 getLine
9189
92−testMenu :: [(String, Test)]
93−testMenu = zip (map show [1..]) allTests
90+test_menu :: [(String, Test)]
91+test_menu = zip (map show [1..]) all_tests
9492
95−doTestMenu :: Int -> IO [(String, Bool)]
96−doTestMenu nextID
97− | nextID > length allTests = do
98− putStrLn $ "Done! Please email the " ++ outputFilePath ++ " file to coreyoconnor@gmail.com"
93+do_test_menu :: Int -> IO [(String, Bool)]
94+do_test_menu next_ID
95+ | next_ID > length all_tests = do
96+ putStrLn $ "Done! Please email the " ++ output_file_path ++ " file to coreyoconnor@gmail.com"
9997 return []
10098 | otherwise = do
101− displayTestMenu
102− putStrLn $ "Press return to start with #" ++ show nextID ++ "."
99+ display_test_menu
100+ putStrLn $ "Press return to start with #" ++ show next_ID ++ "."
103101 putStrLn "Enter a test number to perform only that test."
104102 putStrLn "q (or control-C) to quit."
105103 putStr "> "
@@ -107,102 +105,90 @@ doTestMenu nextID
107105 s <- getLine >>= return . filter (/= '\n')
108106 case s of
109107 "q" -> return mempty
110− "" -> do
111− r <- runTest $ show nextID
112− rs <- doTestMenu ( nextID + 1 )
108+ "" -> do
109+ r <- run_test $ show next_ID
110+ rs <- do_test_menu ( next_ID + 1 )
113111 return $ r : rs
114− i | isJust ( lookup i testMenu ) -> do
115− r <- runTest i
116− rs <- doTestMenu ( read i + 1 )
112+ i | isJust ( lookup i test_menu ) -> do
113+ r <- run_test i
114+ rs <- do_test_menu ( read i + 1 )
117115 return $ r : rs
118116 where
119− displayTestMenu
120− = mapM_ displayTestMenu' testMenu
121− displayTestMenu' ( i, t )
122− = putStrLn $ ( if i == show nextID
123− then "> "
117+ display_test_menu
118+ = mapM_ display_test_menu' test_menu
119+ display_test_menu' ( i, t )
120+ = putStrLn $ ( if i == show next_ID
121+ then "> "
124122 else " "
125− ) ++ i ++ ". " ++ testName t
126−
127−runTest :: String -> IO (String, Bool)
128−runTest i = do
129− let t = fromJust $ lookup i testMenu
130− printSummary t
131− waitForReturn
132− testAction t
133− r <- confirmResults t
134− return (testID t, r)
135−
136−defaultSuccessConfirmResults = do
123+ ) ++ i ++ ". " ++ test_name t
124+
125+run_test :: String -> IO (String, Bool)
126+run_test i = do
127+ let t = fromJust $ lookup i test_menu
128+ print_summary t
129+ wait_for_return
130+ test_action t
131+ r <- confirm_results t
132+ return (test_ID t, r)
133+
134+default_success_confirm_results = do
137135 putStr "\n"
138136 putStr "[Y/n] "
139137 hFlush stdout
140138 r <- getLine
141139 case r of
142− "" -> return True
140+ "" -> return True
143141 "y" -> return True
144142 "Y" -> return True
145143 "n" -> return False
146144
147145data Test = Test
148− { testName :: String
149− , testID :: String
150− , testAction :: IO ()
151− , printSummary :: IO ()
152− , confirmResults :: IO Bool
146+ { test_name :: String
147+ , test_ID :: String
148+ , test_action :: IO ()
149+ , print_summary :: IO ()
150+ , confirm_results :: IO Bool
153151 }
154152
155−allTests
156− = [ reserveOutputTest
157− , displayBoundsTest0
158− , displayBoundsTest1
159− , displayBoundsTest2
160− , displayBoundsTest3
161− , unicodeSingleWidth0
162− , unicodeSingleWidth1
163− , unicodeDoubleWidth0
164− , unicodeDoubleWidth1
165− , attributesTest0
166− , attributesTest1
167− , attributesTest2
168− , attributesTest3
169− , attributesTest4
170− , attributesTest5
171− , inlineTest0
172− , inlineTest1
173− , inlineTest2
174− , cursorHideTest0
175− , vertCropTest0
176− , vertCropTest1
177− , vertCropTest2
178− , vertCropTest3
179− , horizCropTest0
180− , horizCropTest1
181− , horizCropTest2
182− , horizCropTest3
183− , layer0
184− , layer1
153+all_tests
154+ = [ reserve_output_test
155+ , display_bounds_test_0
156+ , display_bounds_test_1
157+ , display_bounds_test_2
158+ , display_bounds_test_3
159+ , unicode_single_width_0
160+ , unicode_single_width_1
161+ , unicode_double_width_0
162+ , unicode_double_width_1
163+ , attributes_test_0
164+ , attributes_test_1
165+ , attributes_test_2
166+ , attributes_test_3
167+ , attributes_test_4
168+ , attributes_test_5
169+ , inline_test_0
170+ , inline_test_1
185171 ]
186172
187−reserveOutputTest = Test
188− { testName = "Initialize and reserve terminal output then restore previous state."
189− , testID = "reserveOutputTest"
190− , testAction = do
191− t <- standardIOConfig >>= outputForConfig
192− reserveDisplay t
173+reserve_output_test = Test
174+ { test_name = "Initialize and reserve terminal output then restore previous state."
175+ , test_ID = "reserve_output_test"
176+ , test_action = do
177+ t <- terminal_handle
178+ reserve_display t
193179 putStrLn "Line 1"
194180 putStrLn "Line 2"
195181 putStrLn "Line 3"
196182 putStrLn "Line 4 (press return)"
197183 hFlush stdout
198184 getLine
199− releaseDisplay t
200− releaseTerminal t
185+ release_display t
186+ release_terminal t
201187 return ()
202− , printSummary = do
203− putStr $ [s|
188+ , print_summary = do
189+ putStr $ [$heredoc|
204190Once return is pressed:
205− 0. The screen will be cleared.
191+ 0. The screen will be cleared.
206192 1. Four lines of text should be visible.
207193 1. The cursor should be visible and at the start of the fifth line.
208194
@@ -210,143 +196,135 @@ After return is pressed for the second time this test then:
210196 * The screen containing the test summary should be restored;
211197 * The cursor is visible.
212198|]
213− , confirmResults = do
214− putStr $ [s|
199+ , confirm_results = do
200+ putStr $ [$heredoc|
215201Did the test output match the description?
216202|]
217− defaultSuccessConfirmResults
203+ default_success_confirm_results
218204 }
219205
220−displayBoundsTest0 = Test
221− { testName = "Verify display bounds are correct test 0: Using spaces."
222− , testID = "displayBoundsTest0"
223− , testAction = do
224− t <- standardIOConfig >>= outputForConfig
225− reserveDisplay t
226− (w,h) <- displayBounds t
227− let row0 = replicate (fromEnum w) 'X' ++ "\n"
228− rowH = replicate (fromEnum w - 1) 'X'
229− rowN = "X" ++ replicate (fromEnum w - 2) ' ' ++ "X\n"
230− image = row0 ++ (concat $ replicate (fromEnum h - 2) rowN) ++ rowH
206+display_bounds_test_0 = Test
207+ { test_name = "Verify display bounds are correct test 0: Using spaces."
208+ , test_ID = "display_bounds_test_0"
209+ , test_action = do
210+ t <- terminal_handle
211+ reserve_display t
212+ DisplayRegion w h <- display_bounds t
213+ let row_0 = replicate (fromEnum w) 'X' ++ "\n"
214+ row_h = replicate (fromEnum w - 1) 'X'
215+ row_n = "X" ++ replicate (fromEnum w - 2) ' ' ++ "X\n"
216+ image = row_0 ++ (concat $ replicate (fromEnum h - 2) row_n) ++ row_h
231217 putStr image
232218 hFlush stdout
233219 getLine
234− releaseDisplay t
235− releaseTerminal t
220+ release_display t
221+ release_terminal t
236222 return ()
237− , printSummary = displayBoundsTestSummary True
238− , confirmResults = genericOutputMatchConfirm
223+ , print_summary = display_bounds_test_summary True
224+ , confirm_results = generic_output_match_confirm
239225 }
240226
241−displayBoundsTest1 = Test
242− { testName = "Verify display bounds are correct test 0: Using cursor movement."
243− , testID = "displayBoundsTest1"
244− , testAction = do
245− t <- standardIOConfig >>= outputForConfig
246− reserveDisplay t
247− (w,h) <- displayBounds t
248− setCursorPos t 0 0
249− let row0 = replicate (fromEnum w) 'X' ++ "\n"
250− putStr row0
227+display_bounds_test_1 = Test
228+ { test_name = "Verify display bounds are correct test 0: Using cursor movement."
229+ , test_ID = "display_bounds_test_1"
230+ , test_action = do
231+ t <- terminal_handle
232+ reserve_display t
233+ DisplayRegion w h <- display_bounds t
234+ set_cursor_pos t 0 0
235+ let row_0 = replicate (fromEnum w) 'X' ++ "\n"
236+ putStr row_0
251237 forM_ [1 .. h - 2] $ \y -> do
252− setCursorPos t 0 y
238+ set_cursor_pos t 0 y
253239 putStr "X"
254− hFlush stdout
255− setCursorPos t (w - 1) y
240+ set_cursor_pos t (w - 1) y
256241 putStr "X"
257− hFlush stdout
258− setCursorPos t 0 (h - 1)
259− let rowH = replicate (fromEnum w - 1) 'X'
260− putStr rowH
242+ set_cursor_pos t 0 (h - 1)
243+ let row_h = replicate (fromEnum w - 1) 'X'
244+ putStr row_h
261245 hFlush stdout
262246 getLine
263− releaseDisplay t
264− releaseTerminal t
247+ release_display t
248+ release_terminal t
265249 return ()
266− , printSummary = displayBoundsTestSummary True
267− , confirmResults = genericOutputMatchConfirm
250+ , print_summary = display_bounds_test_summary True
251+ , confirm_results = generic_output_match_confirm
268252 }
269253
270−displayBoundsTest2 = Test
271− { testName = "Verify display bounds are correct test 0: Using Image ops."
272− , testID = "displayBoundsTest2"
273− , testAction = do
274− t <- standardIOConfig >>= outputForConfig
275− reserveDisplay t
276− bounds@(w,h) <- displayBounds t
277− let firstRow = horizCat $ replicate (fromEnum w) (char defAttr 'X')
278− middleRows = vertCat $ replicate (fromEnum h - 2) middleRow
279− middleRow = (char defAttr 'X') <|> backgroundFill (w - 2) 1 <|> (char defAttr 'X')
280− endRow = firstRow
281− image = firstRow <-> middleRows <-> endRow
282− pic = (picForImage image) { picCursor = Cursor (w - 1) (h - 1) }
283− d <- displayContext t bounds
284− outputPicture d pic
254+display_bounds_test_2 = Test
255+ { test_name = "Verify display bounds are correct test 0: Using Image ops."
256+ , test_ID = "display_bounds_test_2"
257+ , test_action = do
258+ t <- terminal_handle
259+ reserve_display t
260+ bounds@(DisplayRegion w h) <- display_bounds t
261+ let first_row = horiz_cat $ replicate (fromEnum w) (char def_attr 'X')
262+ middle_rows = vert_cat $ replicate (fromEnum h - 2) middle_row
263+ middle_row = (char def_attr 'X') <|> background_fill (w - 2) 1 <|> (char def_attr 'X')
264+ end_row = first_row
265+ image = first_row <-> middle_rows <-> end_row
266+ pic = (pic_for_image image) { pic_cursor = Cursor (w - 1) (h - 1) }
267+ d <- display_context t bounds
268+ output_picture d pic
285269 getLine
286− releaseDisplay t
287− releaseTerminal t
270+ release_display t
271+ release_terminal t
288272 return ()
289− , printSummary = displayBoundsTestSummary True
290− , confirmResults = genericOutputMatchConfirm
273+ , print_summary = display_bounds_test_summary True
274+ , confirm_results = generic_output_match_confirm
291275 }
292276
293−displayBoundsTest3 = Test
294− { testName = "Verify display bounds are correct test 0: Hide cursor; Set cursor pos."
295− , testID = "displayBoundsTest3"
296− , testAction = do
297− t <- standardIOConfig >>= outputForConfig
298− reserveDisplay t
299− (w,h) <- displayBounds t
300− hideCursor t
301− setCursorPos t 0 0
302− let row0 = replicate (fromEnum w) 'X'
303− putStrLn row0
277+display_bounds_test_3 = Test
278+ { test_name = "Verify display bounds are correct test 0: Hide cursor; Set cursor pos."
279+ , test_ID = "display_bounds_test_3"
280+ , test_action = do
281+ t <- terminal_handle
282+ reserve_display t
283+ DisplayRegion w h <- display_bounds t
284+ hide_cursor t
285+ set_cursor_pos t 0 0
286+ let row_0 = replicate (fromEnum w) 'X'
287+ putStrLn row_0
304288 forM_ [1 .. h - 2] $ \y -> do
305− setCursorPos t 0 y
289+ set_cursor_pos t 0 y
306290 putStr "X"
307− hFlush stdout
308− setCursorPos t (w - 1) y
291+ set_cursor_pos t (w - 1) y
309292 putStr "X"
310− hFlush stdout
311− setCursorPos t 0 (h - 1)
312− let rowH = row0
313− putStr rowH
293+ set_cursor_pos t 0 (h - 1)
294+ let row_h = row_0
295+ putStr row_h
314296 hFlush stdout
315297 getLine
316− showCursor t
317− releaseDisplay t
318− releaseTerminal t
298+ show_cursor t
299+ release_display t
300+ release_terminal t
319301 return ()
320− , printSummary = displayBoundsTestSummary False
321− , confirmResults = genericOutputMatchConfirm
302+ , print_summary = display_bounds_test_summary False
303+ , confirm_results = generic_output_match_confirm
322304 }
323305
324−displayBoundsTestSummary hasCursor = do
325− putStr $ [s|
306+display_bounds_test_summary has_cursor = do
307+ putStr $ [$heredoc|
326308Once return is pressed:
327309 0. The screen will be cleared.
328310|]
329− if hasCursor
311+ if has_cursor
330312 then putStr " 1. The cursor will be visible."
331313 else putStr " 1. The cursor will NOT be visible."
332− putStr [s|
333−
314+ putStr [$heredoc|
334315 2. The border of the display will be outlined in Xs.
335316 So if - and | represented the edge of the terminal window:
336317 |-------------|
337318 |XXXXXXXXXXXXX|
338319 |X X||]
339320
340− if hasCursor
341− then putStr $ [s|
342−
321+ if has_cursor
322+ then putStr $ [$heredoc|
343323 |XXXXXXXXXXXXC| |]
344− else putStr $ [s|
345−
324+ else putStr $ [$heredoc|
346325 |XXXXXXXXXXXXX| |]
347326
348− putStr $ [s|
349−
327+ putStr $ [$heredoc|
350328 |-------------|
351329
352330 ( Where C is the final position of the cursor. There may be an X drawn
@@ -358,13 +336,13 @@ After return is pressed for the second time:
358336 1. The cursor should be visible.
359337|]
360338
361−genericOutputMatchConfirm = do
362− putStr $ [s|
339+generic_output_match_confirm = do
340+ putStr $ [$heredoc|
363341Did the test output match the description?
364342|]
365− defaultSuccessConfirmResults
343+ default_success_confirm_results
366344
367−-- Explicitly define the bytes that encode each example text.
345+-- Explicitely definethe bytes that encode each example text.
368346-- This avoids any issues with how the compiler represents string literals.
369347--
370348-- This document is UTF-8 encoded so the UTF-8 string is still included for
@@ -373,24 +351,24 @@ Did the test output match the description?
373351-- It's assumed the compiler will at least not barf on UTF-8 encoded text in
374352-- comments ;-)
375353--
376−-- txt0 = ↑↑↓↓←→←→BA
377−
378−utf8Txt0 :: [[Word8]]
379−utf8Txt0 = [ [ 0xe2 , 0x86 , 0x91 ]
380− , [ 0xe2 , 0x86 , 0x91 ]
381− , [ 0xe2 , 0x86 , 0x93 ]
382− , [ 0xe2 , 0x86 , 0x93 ]
383− , [ 0xe2 , 0x86 , 0x90 ]
384− , [ 0xe2 , 0x86 , 0x92 ]
385− , [ 0xe2 , 0x86 , 0x90 ]
386− , [ 0xe2 , 0x86 , 0x92 ]
387− , [ 0x42 ]
388− , [ 0x41 ]
389− ]
390−
391−iso10646Txt0 :: String
392−iso10646Txt0 = map toEnum
393− [ 8593
354+-- txt_0 = ↑↑↓↓←→←→BA
355+
356+utf8_txt_0 :: [[Word8]]
357+utf8_txt_0 = [ [ 0xe2 , 0x86 , 0x91 ]
358+ , [ 0xe2 , 0x86 , 0x91 ]
359+ , [ 0xe2 , 0x86 , 0x93 ]
360+ , [ 0xe2 , 0x86 , 0x93 ]
361+ , [ 0xe2 , 0x86 , 0x90 ]
362+ , [ 0xe2 , 0x86 , 0x92 ]
363+ , [ 0xe2 , 0x86 , 0x90 ]
364+ , [ 0xe2 , 0x86 , 0x92 ]
365+ , [ 0x42 ]
366+ , [ 0x41 ]
367+ ]
368+
369+iso_10646_txt_0 :: String
370+iso_10646_txt_0 = map toEnum
371+ [ 8593
394372 , 8593
395373 , 8595
396374 , 8595
@@ -402,46 +380,46 @@ iso10646Txt0 = map toEnum
402380 , 65
403381 ]
404382
405−unicodeSingleWidth0 = Test
406− { testName = "Verify terminal can display unicode single-width characters. (Direct UTF-8)"
407− , testID = "unicodeSingleWidth0"
408− , testAction = do
409− t <- standardIOConfig >>= outputForConfig
410− reserveDisplay t
411− hideCursor t
412− withArrayLen (concat utf8Txt0) (flip $ hPutBuf stdout)
383+unicode_single_width_0 = Test
384+ { test_name = "Verify terminal can display unicode single-width characters. (Direct UTF-8)"
385+ , test_ID = "unicode_single_width_0"
386+ , test_action = do
387+ t <- terminal_handle
388+ reserve_display t
389+ hide_cursor t
390+ withArrayLen (concat utf8_txt_0) (flip $ hPutBuf stdout)
413391 hPutStr stdout "\n"
414392 hPutStr stdout "0123456789\n"
415393 hFlush stdout
416394 getLine
417− releaseDisplay t
418− releaseTerminal t
395+ release_display t
396+ release_terminal t
419397 return ()
420− , printSummary = unicodeSingleWidthSummary
421− , confirmResults = genericOutputMatchConfirm
398+ , print_summary = unicode_single_width_summary
399+ , confirm_results = generic_output_match_confirm
422400 }
423401
424−unicodeSingleWidth1 = Test
425− { testName = "Verify terminal can display unicode single-width characters. (Image ops)"
426− , testID = "unicodeSingleWidth1"
427− , testAction = do
428− t <- standardIOConfig >>= outputForConfig
429− reserveDisplay t
430− let pic = picForImage image
431− image = line0 <-> line1
432− line0 = iso10646String defAttr iso10646Txt0
433− line1 = string defAttr "0123456789"
434− d <- displayBounds t >>= displayContext t
435− outputPicture d pic
402+unicode_single_width_1 = Test
403+ { test_name = "Verify terminal can display unicode single-width characters. (Image ops)"
404+ , test_ID = "unicode_single_width_1"
405+ , test_action = do
406+ t <- terminal_handle
407+ reserve_display t
408+ let pic = pic_for_image image
409+ image = line_0 <-> line_1
410+ line_0 = iso_10646_string def_attr iso_10646_txt_0
411+ line_1 = string def_attr "0123456789"
412+ d <- display_bounds t >>= display_context t
413+ output_picture d pic
436414 getLine
437− releaseDisplay t
438− releaseTerminal t
415+ release_display t
416+ release_terminal t
439417 return ()
440− , printSummary = unicodeSingleWidthSummary
441− , confirmResults = genericOutputMatchConfirm
418+ , print_summary = unicode_single_width_summary
419+ , confirm_results = generic_output_match_confirm
442420 }
443421
444−unicodeSingleWidthSummary = putStr [s|
422+unicode_single_width_summary = putStr [$heredoc|
445423Once return is pressed:
446424 0. The screen will be cleared.
447425 1. The cursor will be hidden.
@@ -475,55 +453,55 @@ After return is pressed for the second time:
475453
476454-- The second example is a unicode string containing double-width glyphs
477455-- 你好吗
478−utf8Txt1 :: [[Word8]]
479−utf8Txt1 = [ [0xe4,0xbd,0xa0]
480− , [0xe5,0xa5,0xbd]
481− , [0xe5,0x90,0x97]
482− ]
483−
484−iso10646Txt1 :: String
485−iso10646Txt1 = map toEnum [20320,22909,21527]
486−
487−unicodeDoubleWidth0 = Test
488− { testName = "Verify terminal can display unicode double-width characters. (Direct UTF-8)"
489− , testID = "unicodeDoubleWidth0"
490− , testAction = do
491− t <- standardIOConfig >>= outputForConfig
492− reserveDisplay t
493− hideCursor t
494− withArrayLen (concat utf8Txt1) (flip $ hPutBuf stdout)
456+utf8_txt_1 :: [[Word8]]
457+utf8_txt_1 = [ [0xe4,0xbd,0xa0]
458+ , [0xe5,0xa5,0xbd]
459+ , [0xe5,0x90,0x97]
460+ ]
461+
462+iso_10646_txt_1 :: String
463+iso_10646_txt_1 = map toEnum [20320,22909,21527]
464+
465+unicode_double_width_0 = Test
466+ { test_name = "Verify terminal can display unicode double-width characters. (Direct UTF-8)"
467+ , test_ID = "unicode_double_width_0"
468+ , test_action = do
469+ t <- terminal_handle
470+ reserve_display t
471+ hide_cursor t
472+ withArrayLen (concat utf8_txt_1) (flip $ hPutBuf stdout)
495473 hPutStr stdout "\n"
496474 hPutStr stdout "012345\n"
497475 hFlush stdout
498476 getLine
499− releaseDisplay t
500− releaseTerminal t
477+ release_display t
478+ release_terminal t
501479 return ()
502− , printSummary = unicodeDoubleWidthSummary
503− , confirmResults = genericOutputMatchConfirm
480+ , print_summary = unicode_double_width_summary
481+ , confirm_results = generic_output_match_confirm
504482 }
505483
506−unicodeDoubleWidth1 = Test
507− { testName = "Verify terminal can display unicode double-width characters. (Image ops)"
508− , testID = "unicodeDoubleWidth1"
509− , testAction = do
510− t <- standardIOConfig >>= outputForConfig
511− reserveDisplay t
512− let pic = picForImage image
513− image = line0 <-> line1
514− line0 = iso10646String defAttr iso10646Txt1
515− line1 = string defAttr "012345"
516− d <- displayBounds t >>= displayContext t
517− outputPicture d pic
484+unicode_double_width_1 = Test
485+ { test_name = "Verify terminal can display unicode double-width characters. (Image ops)"
486+ , test_ID = "unicode_double_width_1"
487+ , test_action = do
488+ t <- terminal_handle
489+ reserve_display t
490+ let pic = pic_for_image image
491+ image = line_0 <-> line_1
492+ line_0 = iso_10646_string def_attr iso_10646_txt_1
493+ line_1 = string def_attr "012345"
494+ d <- display_bounds t >>= display_context t
495+ output_picture d pic
518496 getLine
519− releaseDisplay t
520− releaseTerminal t
497+ release_display t
498+ release_terminal t
521499 return ()
522− , printSummary = unicodeDoubleWidthSummary
523− , confirmResults = genericOutputMatchConfirm
500+ , print_summary = unicode_double_width_summary
501+ , confirm_results = generic_output_match_confirm
524502 }
525503
526−unicodeDoubleWidthSummary = putStr [s|
504+unicode_double_width_summary = putStr [$heredoc|
527505Once return is pressed:
528506 0. The screen will be cleared.
529507 1. The cursor will be hidden.
@@ -550,33 +528,33 @@ After return is pressed for the second time:
550528 1. The cursor should be visible.
551529|]
552530
553−allColors = zip [ black, red, green, yellow, blue, magenta, cyan, white ]
554− [ "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white" ]
531+all_colors = zip [ black, red, green, yellow, blue, magenta, cyan, white ]
532+ [ "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white" ]
555533
556−allBrightColors
557− = zip [ brightBlack, brightRed, brightGreen, brightYellow, brightBlue, brightMagenta, brightCyan, brightWhite ]
534+all_bright_colors
535+ = zip [ bright_black, bright_red, bright_green, bright_yellow, bright_blue, bright_magenta, bright_cyan, bright_white ]
558536 [ "bright black", "bright red", "bright green", "bright yellow", "bright blue", "bright magenta", "bright cyan", "bright white" ]
559537
560−attributesTest0 = Test
561− { testName = "Character attributes: foreground colors."
562− , testID = "attributesTest0"
563− , testAction = do
564− t <- standardIOConfig >>= outputForConfig
565− reserveDisplay t
566− let pic = picForImage image
567− image = border <|> column0 <|> border <|> column1 <|> border
568− column0 = vertCat $ map lineWithColor allColors
569− border = vertCat $ replicate (length allColors) $ string defAttr " | "
570− column1 = vertCat $ map (string defAttr . snd) allColors
571− lineWithColor (c, cName) = string (defAttr `withForeColor` c) cName
572− d <- displayBounds t >>= displayContext t
573− outputPicture d pic
538+attributes_test_0 = Test
539+ { test_name = "Character attributes: foreground colors."
540+ , test_ID = "attributes_test_0"
541+ , test_action = do
542+ t <- terminal_handle
543+ reserve_display t
544+ let pic = pic_for_image image
545+ image = border <|> column_0 <|> border <|> column_1 <|> border
546+ column_0 = vert_cat $ map line_with_color all_colors
547+ border = vert_cat $ replicate (length all_colors) $ string def_attr " | "
548+ column_1 = vert_cat $ map (string def_attr . snd) all_colors
549+ line_with_color (c, c_name) = string (def_attr `with_fore_color` c) c_name
550+ d <- display_bounds t >>= display_context t
551+ output_picture d pic
574552 getLine
575− releaseDisplay t
576− releaseTerminal t
553+ release_display t
554+ release_terminal t
577555 return ()
578− , printSummary = do
579− putStr $ [s|
556+ , print_summary = do
557+ putStr $ [$heredoc|
580558Once return is pressed:
581559 0. The screen will be cleared.
582560 1. The cursor will be hidden.
@@ -596,33 +574,33 @@ After return is pressed for the second time:
596574 0. The screen containing the test summary should be restored.
597575 1. The cursor should be visible.
598576|]
599− , confirmResults = do
600− putStr $ [s|
577+ , confirm_results = do
578+ putStr $ [$heredoc|
601579Did the test output match the description?
602580|]
603− defaultSuccessConfirmResults
581+ default_success_confirm_results
604582 }
605583
606−attributesTest1 = Test
607− { testName = "Character attributes: background colors."
608− , testID = "attributesTest1"
609− , testAction = do
610− t <- standardIOConfig >>= outputForConfig
611− reserveDisplay t
612− let pic = picForImage image
613− image = border <|> column0 <|> border <|> column1 <|> border
614− column0 = vertCat $ map lineWithColor allColors
615− border = vertCat $ replicate (length allColors) $ string defAttr " | "
616− column1 = vertCat $ map (string defAttr . snd) allColors
617− lineWithColor (c, cName) = string (defAttr `withBackColor` c) cName
618− d <- displayBounds t >>= displayContext t
619− outputPicture d pic
584+attributes_test_1 = Test
585+ { test_name = "Character attributes: background colors."
586+ , test_ID = "attributes_test_1"
587+ , test_action = do
588+ t <- terminal_handle
589+ reserve_display t
590+ let pic = pic_for_image image
591+ image = border <|> column_0 <|> border <|> column_1 <|> border
592+ column_0 = vert_cat $ map line_with_color all_colors
593+ border = vert_cat $ replicate (length all_colors) $ string def_attr " | "
594+ column_1 = vert_cat $ map (string def_attr . snd) all_colors
595+ line_with_color (c, c_name) = string (def_attr `with_back_color` c) c_name
596+ d <- display_bounds t >>= display_context t
597+ output_picture d pic
620598 getLine
621− releaseDisplay t
622− releaseTerminal t
599+ release_display t
600+ release_terminal t
623601 return ()
624− , printSummary = do
625− putStr $ [s|
602+ , print_summary = do
603+ putStr $ [$heredoc|
626604Once return is pressed:
627605 0. The screen will be cleared.
628606 1. The cursor will be hidden.
@@ -651,57 +629,57 @@ After return is pressed for the second time:
651629 0. The screen containing the test summary should be restored.
652630 1. The cursor should be visible.
653631|]
654− , confirmResults = do
655− putStr $ [s|
632+ , confirm_results = do
633+ putStr $ [$heredoc|
656634Did the test output match the description?
657635|]
658− defaultSuccessConfirmResults
636+ default_success_confirm_results
659637 }
660638
661−attributesTest2 = Test
662− { testName = "Character attributes: Vivid foreground colors."
663− , testID = "attributesTest2"
664− , testAction = do
665− t <- standardIOConfig >>= outputForConfig
666− reserveDisplay t
667− let pic = picForImage image
668− image = horizCat [border, column0, border, column1, border, column2, border]
669− border = vertCat $ replicate (length allColors) $ string defAttr " | "
670− column0 = vertCat $ map lineWithColor0 allColors
671− column1 = vertCat $ map lineWithColor1 allBrightColors
672− column2 = vertCat $ map (string defAttr . snd) allColors
673− lineWithColor0 (c, cName) = string (defAttr `withForeColor` c) cName
674− lineWithColor1 (c, cName) = string (defAttr `withForeColor` c) cName
675− d <- displayBounds t >>= displayContext t
676− outputPicture d pic
639+attributes_test_2 = Test
640+ { test_name = "Character attributes: Vivid foreground colors."
641+ , test_ID = "attributes_test_2"
642+ , test_action = do
643+ t <- terminal_handle
644+ reserve_display t
645+ let pic = pic_for_image image
646+ image = horiz_cat [border, column_0, border, column_1, border, column_2, border]
647+ border = vert_cat $ replicate (length all_colors) $ string def_attr " | "
648+ column_0 = vert_cat $ map line_with_color_0 all_colors
649+ column_1 = vert_cat $ map line_with_color_1 all_bright_colors
650+ column_2 = vert_cat $ map (string def_attr . snd) all_colors
651+ line_with_color_0 (c, c_name) = string (def_attr `with_fore_color` c) c_name
652+ line_with_color_1 (c, c_name) = string (def_attr `with_fore_color` c) c_name
653+ d <- display_bounds t >>= display_context t
654+ output_picture d pic
677655 getLine
678− releaseDisplay t
679− releaseTerminal t
656+ release_display t
657+ release_terminal t
680658 return ()
681− , printSummary = do
682− putStr $ [s|
659+ , print_summary = do
660+ putStr $ [$heredoc|
683661Once return is pressed:
684662 0. The screen will be cleared.
685663 1. The cursor will be hidden.
686664 2. 9 lines of text in three columns will be drawn:
687665 a. The first column will be a name of a standard color (for an 8 color
688− terminal) rendered with that color as the foreground color.
666+ terminal) rendered with that color as the foreground color.
689667 b. The next column will be also be the name of a standard color rendered
690668 with that color as the foreground color but the shade used should be
691− more vivid than the shade used in the first column.
669+ more vivid than the shade used in the first column.
692670 c. The final column will be the name of a color rendered with the
693671 default attributes.
694672
695673For instance, one line will be the word "magenta" and that word should be
696−rendered in the magenta color.
674+rendered in the magenta color.
697675
698676I'm not actually sure exactly what "vivid" means in this context. For xterm the
699−vivid colors are brighter.
677+vivid colors are brighter.
700678
701−Verify:
679+Verify:
702680 * The first column: The foreground color matches the named color.
703681 * The second column: The foreground color matches the named color but is
704− more vivid than the color used in the first column.
682+ more vivid than the color used in the first column.
705683 * The third column: All text is rendered with the default attributes.
706684 * The vertical bars used in each line to mark the border of a column are
707685 lined up.
@@ -710,58 +688,58 @@ After return is pressed for the second time:
710688 0. The screen containing the test summary should be restored.
711689 1. The cursor should be visible.
712690|]
713− , confirmResults = do
714− putStr $ [s|
691+ , confirm_results = do
692+ putStr $ [$heredoc|
715693Did the test output match the description?
716694|]
717− defaultSuccessConfirmResults
695+ default_success_confirm_results
718696 }
719697
720−attributesTest3 = Test
721− { testName = "Character attributes: Vivid background colors."
722− , testID = "attributesTest3"
723− , testAction = do
724− t <- standardIOConfig >>= outputForConfig
725− reserveDisplay t
726− let pic = picForImage image
727− image = horizCat [border, column0, border, column1, border, column2, border]
728− border = vertCat $ replicate (length allColors) $ string defAttr " | "
729− column0 = vertCat $ map lineWithColor0 allColors
730− column1 = vertCat $ map lineWithColor1 allBrightColors
731− column2 = vertCat $ map (string defAttr . snd) allColors
732− lineWithColor0 (c, cName) = string (defAttr `withBackColor` c) cName
733− lineWithColor1 (c, cName) = string (defAttr `withBackColor` c) cName
734− d <- displayBounds t >>= displayContext t
735− outputPicture d pic
698+attributes_test_3 = Test
699+ { test_name = "Character attributes: Vivid background colors."
700+ , test_ID = "attributes_test_3"
701+ , test_action = do
702+ t <- terminal_handle
703+ reserve_display t
704+ let pic = pic_for_image image
705+ image = horiz_cat [border, column_0, border, column_1, border, column_2, border]
706+ border = vert_cat $ replicate (length all_colors) $ string def_attr " | "
707+ column_0 = vert_cat $ map line_with_color_0 all_colors
708+ column_1 = vert_cat $ map line_with_color_1 all_bright_colors
709+ column_2 = vert_cat $ map (string def_attr . snd) all_colors
710+ line_with_color_0 (c, c_name) = string (def_attr `with_back_color` c) c_name
711+ line_with_color_1 (c, c_name) = string (def_attr `with_back_color` c) c_name
712+ d <- display_bounds t >>= display_context t
713+ output_picture d pic
736714 getLine
737− releaseDisplay t
738− releaseTerminal t
715+ release_display t
716+ release_terminal t
739717 return ()
740− , printSummary = do
741− putStr $ [s|
718+ , print_summary = do
719+ putStr $ [$heredoc|
742720Once return is pressed:
743721 0. The screen will be cleared.
744722 1. The cursor will be hidden.
745723 2. 9 lines of text in three columns will be drawn:
746724 a. The first column will contain be a name of a standard color for an 8
747725 color terminal rendered with the default foreground color with a
748− background the named color.
726+ background the named color.
749727 b. The first column will contain be a name of a standard color for an 8
750728 color terminal rendered with the default foreground color with the
751− background a vivid version of the named color.
729+ background a vivid version of the named color.
752730 c. The third column will be the name of a standard color rendered with
753731 the default attributes.
754−
732+
755733For instance, one line will contain be the word "magenta" and the word should
756−be rendered in the default foreground color over a magenta background.
734+be rendered in the default foreground color over a magenta background.
757735
758736I'm not actually sure exactly what "vivid" means in this context. For xterm the
759737vivid colors are brighter.
760738
761−Verify:
739+Verify:
762740 * The first column: The background color matches the named color.
763741 * The second column: The background color matches the named color and is
764− more vivid than the color used in the first column.
742+ more vivid than the color used in the first column.
765743 * The third column column: All text is rendered with the default attributes.
766744 * The vertical bars used in each line to mark the border of a column are
767745 lined up.
@@ -777,51 +755,51 @@ After return is pressed for the second time:
777755 0. The screen containing the test summary should be restored.
778756 1. The cursor should be visible.
779757|]
780− , confirmResults = do
781− putStr $ [s|
758+ , confirm_results = do
759+ putStr $ [$heredoc|
782760Did the test output match the description?
783761|]
784− defaultSuccessConfirmResults
762+ default_success_confirm_results
785763 }
786764
787−attrCombos =
765+attr_combos =
788766 [ ( "default", id )
789− , ( "bold", flip withStyle bold )
790− , ( "blink", flip withStyle blink )
791− , ( "underline", flip withStyle underline )
792− , ( "bold + blink", flip withStyle (bold + blink) )
793− , ( "bold + underline", flip withStyle (bold + underline) )
794− , ( "underline + blink", flip withStyle (underline + blink) )
795− , ( "bold + blink + underline", flip withStyle (bold + blink + underline) )
767+ , ( "bold", flip with_style bold )
768+ , ( "blink", flip with_style blink )
769+ , ( "underline", flip with_style underline )
770+ , ( "bold + blink", flip with_style (bold + blink) )
771+ , ( "bold + underline", flip with_style (bold + underline) )
772+ , ( "underline + blink", flip with_style (underline + blink) )
773+ , ( "bold + blink + underline", flip with_style (bold + blink + underline) )
796774 ]
797775
798−attributesTest4 = Test
799− { testName = "Character attributes: Bold; Blink; Underline."
800− , testID = "attributesTest4"
801− , testAction = do
802− t <- standardIOConfig >>= outputForConfig
803− reserveDisplay t
804− let pic = picForImage image
805− image = horizCat [border, column0, border, column1, border]
806− border = vertCat $ replicate (length attrCombos) $ string defAttr " | "
807− column0 = vertCat $ map lineWithAttrs attrCombos
808− column1 = vertCat $ map (string defAttr . fst) attrCombos
809− lineWithAttrs (desc, attrF) = string (attrF defAttr) desc
810− d <- displayBounds t >>= displayContext t
811− outputPicture d pic
776+attributes_test_4 = Test
777+ { test_name = "Character attributes: Bold; Blink; Underline."
778+ , test_ID = "attributes_test_4"
779+ , test_action = do
780+ t <- terminal_handle
781+ reserve_display t
782+ let pic = pic_for_image image
783+ image = horiz_cat [border, column_0, border, column_1, border]
784+ border = vert_cat $ replicate (length attr_combos) $ string def_attr " | "
785+ column_0 = vert_cat $ map line_with_attrs attr_combos
786+ column_1 = vert_cat $ map (string def_attr . fst) attr_combos
787+ line_with_attrs (desc, attr_f) = string (attr_f def_attr) desc
788+ d <- display_bounds t >>= display_context t
789+ output_picture d pic
812790 getLine
813− releaseDisplay t
814− releaseTerminal t
791+ release_display t
792+ release_terminal t
815793 return ()
816− , printSummary = do
817− putStr $ [s|
794+ , print_summary = do
795+ putStr $ [$heredoc|
818796Once return is pressed:
819797 0. The screen will be cleared.
820798 1. The cursor will be hidden.
821− 2. 8 rows of text in two columns.
799+ 2. 8 rows of text in two columns.
822800 The rows will contain the following text:
823801 default
824− bold
802+ bold
825803 blink
826804 underline
827805 bold + blink
@@ -830,8 +808,8 @@ Once return is pressed:
830808 bold + blink + underline
831809 The first column will be rendered with the described attributes. The second
832810 column will be rendered with the default attributes.
833−
834−Verify:
811+
812+Verify:
835813 * The vertical bars used in each line to mark the border of a column are
836814 lined up.
837815 * The text in the first column is rendered as described.
@@ -840,353 +818,86 @@ After return is pressed for the second time:
840818 0. The screen containing the test summary should be restored.
841819 1. The cursor should be visible.
842820|]
843− , confirmResults = do
844− putStr $ [s|
821+ , confirm_results = do
822+ putStr $ [$heredoc|
845823Did the test output match the description?
846824|]
847− defaultSuccessConfirmResults
825+ default_success_confirm_results
848826 }
849827
850−attributesTest5 = Test
851− { testName = "Character attributes: 240 color palette"
852− , testID = "attributesTest5"
853− , testAction = do
854− t <- standardIOConfig >>= outputForConfig
855− reserveDisplay t
856− let pic = picForImage image
857− image = vertCat $ map horizCat $ splitColorImages colorImages
858− colorImages = map (\i -> string (currentAttr `withBackColor` Color240 i) " ") [0..239]
859− splitColorImages [] = []
860− splitColorImages is = (take 20 is ++ [string defAttr " "]) : (splitColorImages (drop 20 is))
861− d <- displayBounds t >>= displayContext t
862− outputPicture d pic
828+attributes_test_5 = Test
829+ { test_name = "Character attributes: 240 color palette"
830+ , test_ID = "attributes_test_5"
831+ , test_action = do
832+ t <- terminal_handle
833+ reserve_display t
834+ let pic = pic_for_image image
835+ image = vert_cat $ map horiz_cat $ split_color_images color_images
836+ color_images = map (\i -> string (current_attr `with_back_color` Color240 i) " ") [0..239]
837+ split_color_images [] = []
838+ split_color_images is = (take 20 is ++ [string def_attr " "]) : (split_color_images (drop 20 is))
839+ d <- display_bounds t >>= display_context t
840+ output_picture d pic
863841 getLine
864− releaseDisplay t
865− releaseTerminal t
842+ release_display t
843+ release_terminal t
866844 return ()
867− , printSummary = do
868− putStr $ [s|
845+ , print_summary = do
846+ putStr $ [$heredoc|
869847Once return is pressed:
870848 0. The screen will be cleared.
871849 1. The cursor will be hidden.
872850 2. A 20 character wide and 12 row high block of color squares. This should look like a palette
873851 of some sort. I'm not exactly sure if all color terminals use the same palette. I doubt it...
874852
875−Verify:
853+Verify:
876854
877855After return is pressed for the second time:
878856 0. The screen containing the test summary should be restored.
879857 1. The cursor should be visible.
880858|]
881− , confirmResults = do
882− putStr $ [s|
859+ , confirm_results = do
860+ putStr $ [$heredoc|
883861Did the test output match the description?
884862|]
885− defaultSuccessConfirmResults
863+ default_success_confirm_results
886864 }
887865
888−inlineTest0 = Test
889− { testName = "Verify styled output can be performed without clearing the screen."
890− , testID = "inlineTest0"
891− , testAction = do
866+inline_test_0 = Test
867+ { test_name = "Verify styled output can be performed without clearing the screen."
868+ , test_ID = "inline_test_0"
869+ , test_action = do
870+ t <- terminal_handle
892871 putStrLn "line 1."
893− putAttrChange_ $ backColor red >> applyStyle underline
872+ put_attr_change t $ back_color red >> apply_style underline
894873 putStrLn "line 2."
895− putAttrChange_ $ defaultAll
874+ put_attr_change t $ default_all
896875 putStrLn "line 3."
897− , printSummary = putStr $ [s|
876+ release_terminal t
877+ return ()
878+ , print_summary = putStr $ [$heredoc|
898879lines are in order.
899880The second line "line 2" should have a red background and the text underline.
900881The third line "line 3" should be drawn in the same style as the first line.
901882|]
902883
903− , confirmResults = genericOutputMatchConfirm
884+ , confirm_results = generic_output_match_confirm
904885 }
905886
906−inlineTest1 = Test
907− { testName = "Verify styled output can be performed without clearing the screen."
908− , testID = "inlineTest1"
909− , testAction = do
887+inline_test_1 = Test
888+ { test_name = "Verify styled output can be performed without clearing the screen."
889+ , test_ID = "inline_test_1"
890+ , test_action = do
891+ t <- terminal_handle
910892 putStr "Not styled. "
911− putAttrChange_ $ backColor red >> applyStyle underline
893+ put_attr_change t $ back_color red >> apply_style underline
912894 putStr " Styled! "
913− putAttrChange_ $ defaultAll
895+ put_attr_change t $ default_all
914896 putStrLn "Not styled."
915− , printSummary = putStr $ [s|
916−|]
917−
918− , confirmResults = genericOutputMatchConfirm
919− }
920−
921−inlineTest2 = Test
922− { testName = "Verify styled output can be performed without clearing the screen."
923− , testID = "inlineTest1"
924− , testAction = do
925− putStr "Not styled. "
926− putAttrChange_ $ backColor red >> applyStyle underline
927− putStr " Styled! "
928− putAttrChange_ $ defaultAll
929− putStr "Not styled.\n"
930− , printSummary = putStr $ [s|
931−|]
932− , confirmResults = genericOutputMatchConfirm
933− }
934−
935−cursorHideTest0 :: Test
936−cursorHideTest0 = Test
937− { testName = "Verify the cursor is hid and re-shown. issue #7"
938− , testID = "cursorHideTest0"
939− , testAction = do
940− vty <- mkVty defaultConfig
941− showCursor $ outputIface vty
942− setCursorPos (outputIface vty) 5 5
943− nextEvent vty
944− hideCursor $ outputIface vty
945− nextEvent vty
946− shutdown vty
897+ release_terminal t
947898 return ()
948− , printSummary = putStr $ [s|
949− 1. verify the cursor is displayed.
950− 2. press enter
951− 3. verify the cursor is hid.
952− 4. press enter.
953− 5. the display should return to the state before the test.
899+ , print_summary = putStr $ [$heredoc|
954900|]
955− , confirmResults = genericOutputMatchConfirm
956− }
957901
958−outputImageAndWait :: Image -> IO ()
959−outputImageAndWait image = do
960− let pic = picForImage image
961− outputPicAndWait pic
962−
963−outputPicAndWait :: Picture -> IO ()
964−outputPicAndWait pic = do
965− t <- standardIOConfig >>= outputForConfig
966− reserveDisplay t
967− d <- displayBounds t >>= displayContext t
968− outputPicture d pic
969− getLine
970− releaseDisplay t
971− releaseTerminal t
972− return ()
973−
974−vertCropTest0 :: Test
975−vertCropTest0 = Test
976− { testName = "Verify bottom cropping works as expected with single column chars"
977− , testID = "cropTest0"
978− , testAction = do
979− let block0 = cropBottom 2 $ vertCat $ map (string defAttr) lorumIpsum
980− block1 = vertCat $ map (string defAttr) $ take 2 lorumIpsum
981− image = block0 <-> backgroundFill 10 2 <-> block1
982− outputImageAndWait image
983− , printSummary = putStr $ [s|
984− 1. Verify the two text blocks are identical.
985− 2. press enter.
986− 3. the display should return to the state before the test.
987−|]
988− , confirmResults = genericOutputMatchConfirm
989− }
990−
991−vertCropTest1 :: Test
992−vertCropTest1 = Test
993− { testName = "Verify bottom cropping works as expected with double column chars"
994− , testID = "cropTest0"
995− , testAction = do
996− let block0 = cropBottom 2 $ vertCat $ map (string defAttr) lorumIpsumChinese
997− block1 = vertCat $ map (string defAttr) $ take 2 lorumIpsumChinese
998− image = block0 <-> backgroundFill 10 2 <-> block1
999− outputImageAndWait image
1000− , printSummary = putStr $ [s|
1001− 1. Verify the two text blocks are identical.
1002− 2. press enter.
1003− 3. the display should return to the state before the test.
1004−|]
1005− , confirmResults = genericOutputMatchConfirm
902+ , confirm_results = generic_output_match_confirm
1006903 }
1007−
1008−vertCropTest2 :: Test
1009−vertCropTest2 = Test
1010− { testName = "Verify top cropping works as expected with single column chars"
1011− , testID = "cropTest2"
1012− , testAction = do
1013− let block0 = cropTop 2 $ vertCat $ map (string defAttr) lorumIpsum
1014− block1 = vertCat $ map (string defAttr) $ drop (length lorumIpsum - 2) lorumIpsum
1015− image = block0 <-> backgroundFill 10 2 <-> block1
1016− outputImageAndWait image
1017− , printSummary = putStr $ [s|
1018− 1. Verify the two text blocks are identical.
1019− 2. press enter.
1020− 3. the display should return to the state before the test.
1021−|]
1022− , confirmResults = genericOutputMatchConfirm
1023− }
1024−
1025−vertCropTest3 :: Test
1026−vertCropTest3 = Test
1027− { testName = "Verify top cropping works as expected with double column chars"
1028− , testID = "cropTest0"
1029− , testAction = do
1030− let block0 = cropTop 2 $ vertCat $ map (string defAttr) lorumIpsumChinese
1031− block1 = vertCat $ map (string defAttr) $ drop (length lorumIpsumChinese - 2 ) lorumIpsumChinese
1032− image = block0 <-> backgroundFill 10 2 <-> block1
1033− outputImageAndWait image
1034− , printSummary = putStr $ [s|
1035− 1. Verify the two text blocks are identical.
1036− 2. press enter.
1037− 3. the display should return to the state before the test.
1038−|]
1039− , confirmResults = genericOutputMatchConfirm
1040− }
1041−
1042−horizCropTest0 :: Test
1043−horizCropTest0 = Test
1044− { testName = "Verify right cropping works as expected with single column chars"
1045− , testID = "cropTest0"
1046− , testAction = do
1047− let baseImage = vertCat $ map (string defAttr) lorumIpsum
1048− croppedImage = cropRight (imageWidth baseImage `div` 2) baseImage
1049− image = baseImage <-> backgroundFill 10 2 <-> croppedImage
1050− outputImageAndWait image
1051− , printSummary = putStr $ [s|
1052− 1. Verify the bottom text block is about half the width of the top text block.
1053− 2. press enter.
1054− 3. the display should return to the state before the test.
1055−|]
1056− , confirmResults = genericOutputMatchConfirm
1057− }
1058−
1059−horizCropTest1 :: Test
1060−horizCropTest1 = Test
1061− { testName = "Verify right cropping works as expected with double column chars"
1062− , testID = "cropTest0"
1063− , testAction = do
1064− let baseImage = vertCat $ map (string defAttr) lorumIpsumChinese
1065− croppedImage = cropRight (imageWidth baseImage `div` 2) baseImage
1066− image = baseImage <-> backgroundFill 10 2 <-> croppedImage
1067− outputImageAndWait image
1068− , printSummary = putStr $ [s|
1069− 1. Verify the bottom text block is the left half of the top block. Ellipses on the right edge are OK.
1070− 2. press enter.
1071− 3. the display should return to the state before the test.
1072−|]
1073− , confirmResults = genericOutputMatchConfirm
1074− }
1075−
1076−horizCropTest2 :: Test
1077−horizCropTest2 = Test
1078− { testName = "Verify left cropping works as expected with single column chars"
1079− , testID = "cropTest0"
1080− , testAction = do
1081− let baseImage = vertCat $ map (string defAttr) lorumIpsum
1082− croppedImage = cropLeft (imageWidth baseImage `div` 2) baseImage
1083− image = baseImage <-> backgroundFill 10 2 <-> croppedImage
1084− outputImageAndWait image
1085− , printSummary = putStr $ [s|
1086− 1. Verify the bottom text block is the right half of the top text block.
1087− 2. press enter.
1088− 3. the display should return to the state before the test.
1089−|]
1090− , confirmResults = genericOutputMatchConfirm
1091− }
1092−
1093−horizCropTest3 :: Test
1094−horizCropTest3 = Test
1095− { testName = "Verify right cropping works as expected with double column chars"
1096− , testID = "cropTest0"
1097− , testAction = do
1098− let baseImage = vertCat $ map (string defAttr) lorumIpsumChinese
1099− croppedImage = cropLeft (imageWidth baseImage `div` 2) baseImage
1100− image = baseImage <-> backgroundFill 10 2 <-> croppedImage
1101− outputImageAndWait image
1102− , printSummary = putStr $ [s|
1103− 1. Verify the bottom text block is the right half of the top block. Ellipses on the left edge are OK.
1104− 2. press enter.
1105− 3. the display should return to the state before the test.
1106−|]
1107− , confirmResults = genericOutputMatchConfirm
1108− }
1109−
1110−layer0 :: Test
1111−layer0 = Test
1112− { testName = "verify layer 0"
1113− , testID = "layer0"
1114− , testAction = do
1115− let upperImage = vertCat $ map (string defAttr) lorumIpsumChinese
1116− lowerImage = vertCat $ map (string defAttr) lorumIpsum
1117− p = picForLayers [upperImage, lowerImage]
1118− outputPicAndWait p
1119− , printSummary = putStr $ [s|
1120− 1. Verify the text block appears to be Chinese text placed on top Latin text.
1121− 2. press enter.
1122− 3. the display should return to the state before the test.
1123−|]
1124− , confirmResults = genericOutputMatchConfirm
1125− }
1126−
1127−layer1 :: Test
1128−layer1 = Test
1129− { testName = "verify layer 1"
1130− , testID = "layer1"
1131− , testAction = do
1132− let upperImage = vertCat $ map (string defAttr) lorumIpsumChinese
1133− block = resize 10 10 upperImage
1134− layer0 = vertCat $ map (string defAttr) lorumIpsum
1135− layer1 = charFill (defAttr `withBackColor` blue) '#' 1000 1000
1136− cheesyAnim0 block [layer0, layer1]
1137− , printSummary = putStr $ [s|
1138− 1. Verify the text block appears to be Chinese text moving on top a Latin text.
1139− Which is all on a background of '#' characters over blue.
1140− 2. press enter.
1141− 3. the display should return to the state before the test.
1142−|]
1143− , confirmResults = genericOutputMatchConfirm
1144− }
1145−
1146−cheesyAnim0 :: Image -> [Image] -> IO ()
1147−cheesyAnim0 i background = do
1148− t <- standardIOConfig >>= outputForConfig
1149− reserveDisplay t
1150− bounds <- displayBounds t
1151− d <- displayContext t bounds
1152− forM_ [0..2] $ \t -> do
1153− forM_ [0..100] $ \t -> do
1154− let i_offset = translate (t `mod` fst bounds)
1155− (t `div` 2 `mod` snd bounds)
1156− i
1157− let pic = picForLayers $ i_offset : background
1158− outputPicture d pic
1159− threadDelay 50000
1160− forM_ [0..100] $ \t -> do
1161− let i_offset = translate (t * (-1) `mod` fst bounds)
1162− (t * (-1) `div` 2 `mod` snd bounds)
1163− i
1164− let pic = picForLayers $ i_offset : background
1165− outputPicture d pic
1166− threadDelay 50000
1167− releaseDisplay t
1168− releaseTerminal t
1169− return ()
1170−
1171−lorumIpsum :: [String]
1172−lorumIpsum = lines [s|
1173−Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium,
1174−totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae
1175−dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit,
1176−sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam
1177−est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius
1178−modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima
1179−veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea
1180−commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil
1181−molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?
1182−|]
1183−
1184−lorumIpsumChinese :: [String]
1185−lorumIpsumChinese = lines [s|
1186−輐銛 螷蟞覮 裌覅詵 暕 鴅噮 槶 惝掭掝 婸媥媕 耏胠臿, 汫汭沎 忕汌卣 蚡袀 僣 蒮 瀁瀎瀊 渮湸湤 緌翢,
1187−腠腶舝 糲蘥蠩 樏殣氀 蒮 蹢鎒 滍 鸄齴 櫧櫋瀩 鬄鵊鵙 莃荶衒, 毸溠 橀 簎艜薤 莃荶衒 翣聜蒢
1188−斔櫅檷 晛桼桾 拻敁柧 犿玒 膣, 墐 笓粊紒 bacon 鼀齕, 蔝蓶蓨 顊顃餭 姴怤 骱 暕 蹢鎒鎛 藒襓謥 鄻鎟霣
1189−鬎鯪, 鐩闤 硻禂稢 谾踘遳 撱 赲 迡 箷 蛃袚觙 萇雊蜩 壿嫷 鋡 縢羱聬 跐鉠鉣 蔝蓶蓨 匢奾灱 溮煡煟 雥齆犪
1190−蔰 虈觿, 腷腯葹 鍹餳駷 蛚袲褁蜸 皯竻 瀁瀎 蜭蜸覟 梪涫湴 揗斝湁 毼
1191−|]
1192−
test/verify_attribute_ops.hsnew file+8
@@ -0,0 +1,8 @@
1+module Main where
2+
3+import Verify.Graphics.Vty.Attributes
4+
5+import Verify
6+
7+main = run_test $ do
8+ return ()
test/verify_debug_terminal.hsnew file+115
@@ -0,0 +1,115 @@
1+{-# LANGUAGE ScopedTypeVariables #-}
2+module Main where
3+
4+import Verify.Graphics.Vty.DisplayRegion
5+import Verify.Graphics.Vty.Picture
6+import Verify.Graphics.Vty.Image
7+import Verify.Graphics.Vty.Span
8+import Graphics.Vty.Terminal
9+import Graphics.Vty.Terminal.Debug
10+
11+import Graphics.Vty.Debug
12+
13+import Verify
14+
15+import qualified Data.ByteString as BS
16+import Data.IORef
17+import qualified Data.String.UTF8 as UTF8
18+
19+import System.IO
20+
21+unit_image_unit_bounds :: UnitImage -> Property
22+unit_image_unit_bounds (UnitImage _ i) = liftIOResult $ do
23+ t <- terminal_instance (DisplayRegion 1 1)
24+ d <- display_bounds t >>= display_context t
25+ let pic = pic_for_image i
26+ output_picture d pic
27+ return succeeded
28+
29+unit_image_arb_bounds :: UnitImage -> DebugWindow -> Property
30+unit_image_arb_bounds (UnitImage _ i) (DebugWindow w h) = liftIOResult $ do
31+ t <- terminal_instance (DisplayRegion w h)
32+ d <- display_bounds t >>= display_context t
33+ let pic = pic_for_image i
34+ output_picture d pic
35+ return succeeded
36+
37+single_T_row :: DebugWindow -> Property
38+single_T_row (DebugWindow w h) = liftIOResult $ do
39+ t <- terminal_instance (DisplayRegion w h)
40+ d <- display_bounds t >>= display_context t
41+ -- create an image that contains just the character T repeated for a single row
42+ let i = horiz_cat $ replicate (fromEnum w) (char def_attr 'T')
43+ pic = (pic_for_image i) { pic_background = Background 'B' def_attr }
44+ output_picture d pic
45+ out_bytes <- readIORef (debug_terminal_last_output $ dehandle t) >>= return . UTF8.toRep
46+ -- The UTF8 string that represents the output bytes a single line containing the T string:
47+ let expected = "HD" ++ "MA" ++ replicate (fromEnum w) 'T'
48+ -- Followed by h - 1 lines of a change to the background attribute and then the background
49+ -- character
50+ ++ concat (replicate (fromEnum h - 1) $ "MA" ++ replicate (fromEnum w) 'B')
51+ expected_bytes :: BS.ByteString = UTF8.toRep $ UTF8.fromString expected
52+ if out_bytes /= expected_bytes
53+ then return $ failed { reason = "\n" ++ show out_bytes ++ "\n\n" ++ show expected_bytes }
54+ else return succeeded
55+
56+many_T_rows :: DebugWindow -> Property
57+many_T_rows (DebugWindow w h) = liftIOResult $ do
58+ t <- terminal_instance (DisplayRegion w h)
59+ d <- display_bounds t >>= display_context t
60+ -- create an image that contains the character 'T' repeated for all the rows
61+ let i = vert_cat $ replicate (fromEnum h) $ horiz_cat $ replicate (fromEnum w) (char def_attr 'T')
62+ pic = (pic_for_image i) { pic_background = Background 'B' def_attr }
63+ output_picture d pic
64+ out_bytes <- readIORef (debug_terminal_last_output $ dehandle t) >>= return . UTF8.toRep
65+ -- The UTF8 string that represents the output bytes is h repeats of a move, 'M', followed by an
66+ -- attribute change. 'A', followed by w 'T's
67+ let expected = "HD" ++ concat (replicate (fromEnum h) $ "MA" ++ replicate (fromEnum w) 'T')
68+ expected_bytes :: BS.ByteString = UTF8.toRep $ UTF8.fromString expected
69+ if out_bytes /= expected_bytes
70+ then return $ failed { reason = "\n" ++ show out_bytes ++ "\n\n" ++ show expected_bytes }
71+ else return succeeded
72+
73+many_T_rows_cropped_width :: DebugWindow -> Property
74+many_T_rows_cropped_width (DebugWindow w h) = liftIOResult $ do
75+ t <- terminal_instance (DisplayRegion w h)
76+ d <- display_bounds t >>= display_context t
77+ -- create an image that contains the character 'T' repeated for all the rows
78+ let i = vert_cat $ replicate (fromEnum h) $ horiz_cat $ replicate (fromEnum w * 2) (char def_attr 'T')
79+ pic = (pic_for_image i) { pic_background = Background 'B' def_attr }
80+ output_picture d pic
81+ out_bytes <- readIORef (debug_terminal_last_output $ dehandle t) >>= return . UTF8.toRep
82+ -- The UTF8 string that represents the output bytes is h repeats of a move, 'M', followed by an
83+ -- attribute change. 'A', followed by w 'T's
84+ let expected = "HD" ++ concat (replicate (fromEnum h) $ "MA" ++ replicate (fromEnum w) 'T')
85+ expected_bytes :: BS.ByteString = UTF8.toRep $ UTF8.fromString expected
86+ if out_bytes /= expected_bytes
87+ then return $ failed { reason = "\n" ++ show out_bytes ++ "\n\n" ++ show expected_bytes }
88+ else return succeeded
89+
90+many_T_rows_cropped_height :: DebugWindow -> Property
91+many_T_rows_cropped_height (DebugWindow w h) = liftIOResult $ do
92+ t <- terminal_instance (DisplayRegion w h)
93+ d <- display_bounds t >>= display_context t
94+ -- create an image that contains the character 'T' repeated for all the rows
95+ let i = vert_cat $ replicate (fromEnum h * 2) $ horiz_cat $ replicate (fromEnum w) (char def_attr 'T')
96+ pic = (pic_for_image i) { pic_background = Background 'B' def_attr }
97+ output_picture d pic
98+ out_bytes <- readIORef (debug_terminal_last_output $ dehandle t) >>= return . UTF8.toRep
99+ -- The UTF8 string that represents the output bytes is h repeats of a move, 'M', followed by an
100+ -- attribute change. 'A', followed by w count 'T's
101+ let expected = "HD" ++ concat (replicate (fromEnum h) $ "MA" ++ replicate (fromEnum w) 'T')
102+ expected_bytes :: BS.ByteString = UTF8.toRep $ UTF8.fromString expected
103+ if out_bytes /= expected_bytes
104+ then return $ failed { reason = "\n" ++ show out_bytes ++ "\n\n" ++ show expected_bytes }
105+ else return succeeded
106+
107+main = run_test $ do
108+ verify "unit_image_unit_bounds" unit_image_unit_bounds
109+ verify "unit_image_arb_bounds" unit_image_arb_bounds
110+ verify "single_T_row" single_T_row
111+ verify "many_T_rows" many_T_rows
112+ verify "many_T_rows_cropped_width" many_T_rows_cropped_width
113+ verify "many_T_rows_cropped_height" many_T_rows_cropped_height
114+ return ()
115+
test/VerifyDisplayAttributes.hs → test/verify_display_attributes.hsrenamed+3−3
@@ -1,9 +1,9 @@
1−module VerifyDisplayAttributes where
1+module Main where
22
33import Verify.Graphics.Vty.DisplayAttributes
44import Verify.Graphics.Vty.Attributes
55
66import Verify
77
8−tests :: IO [Test]
9−tests = return []
8+main = run_test $ do
9+ return ()
test/verify_empty_image_props.hsnew file+13
@@ -0,0 +1,13 @@
1+{-# LANGUAGE ScopedTypeVariables #-}
2+module Main where
3+
4+import Verify
5+
6+-- should be exported by Graphics.Vty.Picture
7+import Graphics.Vty.Picture ( Image, empty_image )
8+
9+main = run_test $ do
10+ -- should provide an image type.
11+ let _ :: Image = empty_image
12+ return ()
13+
test/verify_eval_terminfo_caps.hsnew file+129
@@ -0,0 +1,129 @@
1+{-# LANGUAGE ScopedTypeVariables #-}
2+{-# LANGUAGE NamedFieldPuns #-}
3+module Main where
4+
5+import Data.Marshalling
6+
7+import Data.Terminfo.Eval
8+import Data.Terminfo.Parse
9+
10+import qualified System.Console.Terminfo as Terminfo
11+
12+import Verify
13+
14+import Control.Exception ( try, SomeException(..) )
15+
16+import Control.Monad ( mapM_, forM_ )
17+
18+import Data.Array.Unboxed
19+import Data.Maybe ( fromJust )
20+import Data.Word
21+
22+import Numeric
23+
24+-- A list of terminals that ubuntu includes a terminfo cap file for.
25+-- Assuming that is a good place to start.
26+terminals_of_interest =
27+ [ "wsvt25"
28+ , "wsvt25m"
29+ , "vt52"
30+ , "vt100"
31+ , "vt220"
32+ , "vt102"
33+ , "xterm-r5"
34+ , "xterm-xfree86"
35+ , "xterm-r6"
36+ , "xterm-256color"
37+ , "xterm-vt220"
38+ , "xterm-debian"
39+ , "xterm-mono"
40+ , "xterm-color"
41+ , "xterm"
42+ , "mach"
43+ , "mach-bold"
44+ , "mach-color"
45+ , "linux"
46+ , "ansi"
47+ , "hurd"
48+ , "Eterm"
49+ , "pcansi"
50+ , "screen-256color"
51+ , "screen-bce"
52+ , "screen-s"
53+ , "screen-w"
54+ , "screen"
55+ , "screen-256color-bce"
56+ , "sun"
57+ , "rxvt"
58+ , "rxvt-unicode"
59+ , "rxvt-basic"
60+ , "cygwin"
61+ , "cons25"
62+ , "dumb"
63+ ]
64+
65+-- If a terminal defines one of the caps then it's expected to be parsable.
66+caps_of_interest =
67+ [ "cup"
68+ , "sc"
69+ , "rc"
70+ , "setf"
71+ , "setb"
72+ , "setaf"
73+ , "setab"
74+ , "op"
75+ , "cnorm"
76+ , "civis"
77+ , "smcup"
78+ , "rmcup"
79+ , "clear"
80+ , "hpa"
81+ , "vpa"
82+ , "sgr"
83+ , "sgr0"
84+ ]
85+
86+from_capname ti name = fromJust $ Terminfo.getCapability ti (Terminfo.tiGetStr name)
87+
88+main = do
89+ run_test $ do
90+ forM_ terminals_of_interest $ \term_name -> do
91+ liftIO $ putStrLn $ "testing parsing of caps for terminal: " ++ term_name
92+ mti <- liftIO $ try $ Terminfo.setupTerm term_name
93+ case mti of
94+ Left (_e :: SomeException)
95+ -> return ()
96+ Right ti -> do
97+ forM_ caps_of_interest $ \cap_name -> do
98+ liftIO $ putStrLn $ "\tevaluating cap: " ++ cap_name
99+ case Terminfo.getCapability ti (Terminfo.tiGetStr cap_name) of
100+ Just cap_def -> do
101+ verify ( "\teval cap " ++ cap_name ++ " -> " ++ show cap_def )
102+ ( verify_eval_cap cap_def )
103+ return ()
104+ Nothing -> do
105+ return ()
106+ return ()
107+ return ()
108+
109+verify_eval_cap cap_string
110+ = case parse_cap_expression cap_string of
111+ Left error ->
112+ liftResult $ failed { reason = "parse error " ++ show error }
113+ Right expr ->
114+ forAll (vector 9) $ \input_values ->
115+ let byte_count = cap_expression_required_bytes expr input_values
116+ in liftIOResult $ do
117+ start_ptr :: Ptr Word8 <- mallocBytes (fromEnum byte_count)
118+ end_ptr <- serialize_cap_expression expr input_values start_ptr
119+ free start_ptr
120+ case end_ptr `minusPtr` start_ptr of
121+ count | count < 0 ->
122+ return $ failed { reason = "End pointer before start pointer." }
123+ | toEnum count > byte_count ->
124+ return $ failed { reason = "End pointer past end of buffer by "
125+ ++ show (toEnum count - byte_count)
126+ }
127+ | otherwise ->
128+ return succeeded
129+
test/verify_image_ops.hsnew file+124
@@ -0,0 +1,124 @@
1+{-# LANGUAGE ScopedTypeVariables #-}
2+module Main where
3+import Graphics.Vty.Attributes
4+import Verify.Graphics.Vty.Image
5+
6+import Verify
7+
8+import Data.Word
9+
10+two_sw_horiz_concat :: SingleColumnChar -> SingleColumnChar -> Bool
11+two_sw_horiz_concat (SingleColumnChar c1) (SingleColumnChar c2) =
12+ image_width (char def_attr c1 <|> char def_attr c2) == 2
13+
14+many_sw_horiz_concat :: [SingleColumnChar] -> Bool
15+many_sw_horiz_concat cs =
16+ let chars = [ char | SingleColumnChar char <- cs ]
17+ l = fromIntegral $ length cs
18+ in image_width ( horiz_cat $ map (char def_attr) chars ) == l
19+
20+two_sw_vert_concat :: SingleColumnChar -> SingleColumnChar -> Bool
21+two_sw_vert_concat (SingleColumnChar c1) (SingleColumnChar c2) =
22+ image_height (char def_attr c1 <-> char def_attr c2) == 2
23+
24+horiz_concat_sw_assoc :: SingleColumnChar -> SingleColumnChar -> SingleColumnChar -> Bool
25+horiz_concat_sw_assoc (SingleColumnChar c0) (SingleColumnChar c1) (SingleColumnChar c2) =
26+ (char def_attr c0 <|> char def_attr c1) <|> char def_attr c2
27+ ==
28+ char def_attr c0 <|> (char def_attr c1 <|> char def_attr c2)
29+
30+two_dw_horiz_concat :: DoubleColumnChar -> DoubleColumnChar -> Bool
31+two_dw_horiz_concat (DoubleColumnChar c1) (DoubleColumnChar c2) =
32+ image_width (char def_attr c1 <|> char def_attr c2) == 4
33+
34+many_dw_horiz_concat :: [DoubleColumnChar] -> Bool
35+many_dw_horiz_concat cs =
36+ let chars = [ char | DoubleColumnChar char <- cs ]
37+ l = fromIntegral $ length cs
38+ in image_width ( horiz_cat $ map (char def_attr) chars ) == l * 2
39+
40+two_dw_vert_concat :: DoubleColumnChar -> DoubleColumnChar -> Bool
41+two_dw_vert_concat (DoubleColumnChar c1) (DoubleColumnChar c2) =
42+ image_height (char def_attr c1 <-> char def_attr c2) == 2
43+
44+horiz_concat_dw_assoc :: DoubleColumnChar -> DoubleColumnChar -> DoubleColumnChar -> Bool
45+horiz_concat_dw_assoc (DoubleColumnChar c0) (DoubleColumnChar c1) (DoubleColumnChar c2) =
46+ (char def_attr c0 <|> char def_attr c1) <|> char def_attr c2
47+ ==
48+ char def_attr c0 <|> (char def_attr c1 <|> char def_attr c2)
49+
50+vert_contat_single_row :: NonEmptyList SingleRowSingleAttrImage -> Bool
51+vert_contat_single_row (NonEmpty stack) =
52+ let expected_height :: Word = fromIntegral $ length stack
53+ stack_image = vert_cat [ i | SingleRowSingleAttrImage { row_image = i } <- stack ]
54+ in image_height stack_image == expected_height
55+
56+disjoint_height_horiz_join :: NonEmptyList SingleRowSingleAttrImage
57+ -> NonEmptyList SingleRowSingleAttrImage
58+ -> Bool
59+disjoint_height_horiz_join (NonEmpty stack_0) (NonEmpty stack_1) =
60+ let expected_height :: Word = fromIntegral $ max (length stack_0) (length stack_1)
61+ stack_image_0 = vert_cat [ i | SingleRowSingleAttrImage { row_image = i } <- stack_0 ]
62+ stack_image_1 = vert_cat [ i | SingleRowSingleAttrImage { row_image = i } <- stack_1 ]
63+ in image_height (stack_image_0 <|> stack_image_1) == expected_height
64+
65+
66+disjoint_height_horiz_join_bg_fill :: NonEmptyList SingleRowSingleAttrImage
67+ -> NonEmptyList SingleRowSingleAttrImage
68+ -> Bool
69+disjoint_height_horiz_join_bg_fill (NonEmpty stack_0) (NonEmpty stack_1) =
70+ let stack_image_0 = vert_cat [ i | SingleRowSingleAttrImage { row_image = i } <- stack_0 ]
71+ stack_image_1 = vert_cat [ i | SingleRowSingleAttrImage { row_image = i } <- stack_1 ]
72+ image = stack_image_0 <|> stack_image_1
73+ expected_height = image_height image
74+ in case image of
75+ HorizJoin {} -> ( expected_height == (image_height $ part_left image) )
76+ &&
77+ ( expected_height == (image_height $ part_right image) )
78+ _ -> True
79+
80+disjoint_width_vert_join :: NonEmptyList SingleRowSingleAttrImage
81+ -> NonEmptyList SingleRowSingleAttrImage
82+ -> Bool
83+disjoint_width_vert_join (NonEmpty stack_0) (NonEmpty stack_1) =
84+ let expected_width = maximum $ map image_width (stack_0_images ++ stack_1_images)
85+ stack_0_images = [ i | SingleRowSingleAttrImage { row_image = i } <- stack_0 ]
86+ stack_1_images = [ i | SingleRowSingleAttrImage { row_image = i } <- stack_1 ]
87+ stack_0_image = vert_cat stack_0_images
88+ stack_1_image = vert_cat stack_1_images
89+ image = stack_0_image <-> stack_1_image
90+ in image_width image == expected_width
91+
92+disjoint_width_vert_join_bg_fill :: NonEmptyList SingleRowSingleAttrImage
93+ -> NonEmptyList SingleRowSingleAttrImage
94+ -> Bool
95+disjoint_width_vert_join_bg_fill (NonEmpty stack_0) (NonEmpty stack_1) =
96+ let expected_width = maximum $ map image_width (stack_0_images ++ stack_1_images)
97+ stack_0_images = [ i | SingleRowSingleAttrImage { row_image = i } <- stack_0 ]
98+ stack_1_images = [ i | SingleRowSingleAttrImage { row_image = i } <- stack_1 ]
99+ stack_0_image = vert_cat stack_0_images
100+ stack_1_image = vert_cat stack_1_images
101+ image = stack_0_image <-> stack_1_image
102+ in case image of
103+ VertJoin {} -> ( expected_width == (image_width $ part_top image) )
104+ &&
105+ ( expected_width == (image_width $ part_bottom image) )
106+ _ -> True
107+
108+main = run_test $ do
109+ verify "two_sw_horiz_concat" two_sw_horiz_concat
110+ verify "many_sw_horiz_concat" many_sw_horiz_concat
111+ verify "two_sw_vert_concat" two_sw_vert_concat
112+ verify "horiz_concat_sw_assoc" horiz_concat_sw_assoc
113+ verify "many_dw_horiz_concat" many_dw_horiz_concat
114+ verify "two_dw_horiz_concat" two_dw_horiz_concat
115+ verify "two_dw_vert_concat" two_dw_vert_concat
116+ verify "horiz_concat_dw_assoc" horiz_concat_dw_assoc
117+ liftIO $ putStrLn $ replicate 80 '-'
118+ verify "single row vert concats to correct height" vert_contat_single_row
119+ verify "disjoint_height_horiz_join" disjoint_height_horiz_join
120+ verify "disjoint_height_horiz_join BG fill" disjoint_height_horiz_join_bg_fill
121+ verify "disjoint_width_vert_join" disjoint_width_vert_join
122+ verify "disjoint_width_vert_join BG fill" disjoint_width_vert_join_bg_fill
123+ return ()
124+
test/verify_image_trans.hsnew file+31
@@ -0,0 +1,31 @@
1+{-# LANGUAGE NamedFieldPuns #-}
2+module Main where
3+
4+import Verify.Graphics.Vty.Image
5+import Graphics.Vty.Debug
6+
7+import Verify
8+
9+import Data.Word
10+
11+is_horiz_text_of_columns :: Image -> Word -> Bool
12+is_horiz_text_of_columns (HorizText { output_width = in_w }) expected_w = in_w == expected_w
13+is_horiz_text_of_columns (BGFill { output_width = in_w }) expected_w = in_w == expected_w
14+is_horiz_text_of_columns _image _expected_w = False
15+
16+verify_horiz_contat_wo_attr_change_simplifies :: SingleRowSingleAttrImage -> Bool
17+verify_horiz_contat_wo_attr_change_simplifies (SingleRowSingleAttrImage _attr char_count image) =
18+ is_horiz_text_of_columns image char_count
19+
20+verify_horiz_contat_w_attr_change_simplifies :: SingleRowTwoAttrImage -> Bool
21+verify_horiz_contat_w_attr_change_simplifies ( SingleRowTwoAttrImage (SingleRowSingleAttrImage attr0 char_count0 image0)
22+ (SingleRowSingleAttrImage attr1 char_count1 image1)
23+ i
24+ )
25+ | char_count0 == 0 || char_count1 == 0 || attr0 == attr1 = is_horiz_text_of_columns i (char_count0 + char_count1)
26+ | otherwise = False == is_horiz_text_of_columns i (char_count0 + char_count1)
27+
28+main = run_test $ do
29+ verify "verify_horiz_contat_wo_attr_change_simplifies" verify_horiz_contat_wo_attr_change_simplifies
30+ verify "verify_horiz_contat_w_attr_change_simplifies" verify_horiz_contat_w_attr_change_simplifies
31+ return ()
test/verify_inline.hsnew file+11
@@ -0,0 +1,11 @@
1+module Main where
2+
3+import Graphics.Vty.Inline
4+import Graphics.Vty.Terminal
5+
6+import Verify
7+
8+main = run_test $ do
9+ t <- terminal_handle
10+ put_attr_change t $ default_all
11+ return ()
test/verify_parse_terminfo_caps.hsnew file+158
@@ -0,0 +1,158 @@
1+{-# LANGUAGE ScopedTypeVariables #-}
2+{-# LANGUAGE NamedFieldPuns #-}
3+module Main where
4+
5+import Prelude hiding ( catch )
6+
7+import Data.Terminfo.Eval ( bytes_for_range )
8+
9+import qualified System.Console.Terminfo as Terminfo
10+
11+import Verify.Data.Terminfo.Parse
12+import Verify
13+
14+import Control.Exception ( try, SomeException(..) )
15+import Control.Monad ( mapM_, forM_ )
16+
17+import Data.Array.Unboxed
18+import Data.Maybe ( fromJust )
19+import Data.Word
20+
21+import Numeric
22+
23+-- A list of terminals that ubuntu includes a terminfo cap file for.
24+-- Assuming that is a good place to start.
25+terminals_of_interest =
26+ [ "wsvt25"
27+ , "wsvt25m"
28+ , "vt52"
29+ , "vt100"
30+ , "vt220"
31+ , "vt102"
32+ , "xterm-r5"
33+ , "xterm-xfree86"
34+ , "xterm-r6"
35+ , "xterm-256color"
36+ , "xterm-vt220"
37+ , "xterm-debian"
38+ , "xterm-mono"
39+ , "xterm-color"
40+ , "xterm"
41+ , "mach"
42+ , "mach-bold"
43+ , "mach-color"
44+ , "linux"
45+ , "ansi"
46+ , "hurd"
47+ , "Eterm"
48+ , "pcansi"
49+ , "screen-256color"
50+ , "screen-bce"
51+ , "screen-s"
52+ , "screen-w"
53+ , "screen"
54+ , "screen-256color-bce"
55+ , "sun"
56+ , "rxvt"
57+ , "rxvt-unicode"
58+ , "rxvt-basic"
59+ , "cygwin"
60+ , "cons25"
61+ , "dumb"
62+ ]
63+
64+-- If a terminal defines one of the caps then it's expected to be parsable.
65+caps_of_interest =
66+ [ "cup"
67+ , "sc"
68+ , "rc"
69+ , "setf"
70+ , "setb"
71+ , "setaf"
72+ , "setab"
73+ , "op"
74+ , "cnorm"
75+ , "civis"
76+ , "smcup"
77+ , "rmcup"
78+ , "clear"
79+ , "hpa"
80+ , "vpa"
81+ , "sgr"
82+ , "sgr0"
83+ ]
84+
85+from_capname ti name = fromJust $ Terminfo.getCapability ti (Terminfo.tiGetStr name)
86+
87+main = do
88+ run_test $ do
89+ forM_ terminals_of_interest $ \term_name -> do
90+ liftIO $ putStrLn $ "testing parsing of caps for terminal: " ++ term_name
91+ mti <- liftIO $ try $ Terminfo.setupTerm term_name
92+ case mti of
93+ Left (_e :: SomeException)
94+ -> return ()
95+ Right ti -> do
96+ forM_ caps_of_interest $ \cap_name -> do
97+ liftIO $ putStrLn $ "\tparsing cap: " ++ cap_name
98+ case Terminfo.getCapability ti (Terminfo.tiGetStr cap_name) of
99+ Just cap_def -> do
100+ verify ( "\tparse cap " ++ cap_name ++ " -> " ++ show cap_def )
101+ ( verify_parse_cap cap_def $ const (liftResult succeeded) )
102+ return ()
103+ Nothing -> do
104+ return ()
105+ -- The quickcheck tests
106+ verify "parse_non_paramaterized_caps" non_paramaterized_caps
107+ verify "parse cap string with literal %" literal_percent_caps
108+ verify "parse cap string with %i op" inc_first_two_caps
109+ verify "parse cap string with %pN op" push_param_caps
110+ return ()
111+ return ()
112+
113+verify_parse_cap cap_string on_parse = do
114+ case parse_cap_expression cap_string of
115+ Left error -> liftResult $ failed { reason = "parse error " ++ show error }
116+ Right e -> on_parse e
117+
118+non_paramaterized_caps (NonParamCapString cap) = do
119+ verify_parse_cap cap $ \e ->
120+ let expected_count :: Word8 = toEnum $ length cap
121+ expected_bytes = map (toEnum . fromEnum) cap
122+ out_bytes = bytes_for_range e 0 expected_count
123+ in verify_bytes_equal out_bytes expected_bytes
124+
125+literal_percent_caps (LiteralPercentCap cap_string expected_bytes) = do
126+ verify_parse_cap cap_string $ \e ->
127+ let expected_count :: Word8 = toEnum $ length expected_bytes
128+ out_bytes = collect_bytes e
129+ in verify_bytes_equal out_bytes expected_bytes
130+
131+inc_first_two_caps (IncFirstTwoCap cap_string expected_bytes) = do
132+ verify_parse_cap cap_string $ \e ->
133+ let expected_count :: Word8 = toEnum $ length expected_bytes
134+ out_bytes = collect_bytes e
135+ in verify_bytes_equal out_bytes expected_bytes
136+
137+push_param_caps (PushParamCap cap_string expected_param_count expected_bytes) = do
138+ verify_parse_cap cap_string $ \e ->
139+ let expected_count :: Word8 = toEnum $ length expected_bytes
140+ out_bytes = collect_bytes e
141+ out_param_count = param_count e
142+ in verify_bytes_equal out_bytes expected_bytes
143+ .&. out_param_count == expected_param_count
144+
145+dec_print_param_caps (DecPrintCap cap_string expected_param_count expected_bytes) = do
146+ verify_parse_cap cap_string $ \e ->
147+ let expected_count :: Word8 = toEnum $ length expected_bytes
148+ out_bytes = collect_bytes e
149+ out_param_count = param_count e
150+ in verify_bytes_equal out_bytes expected_bytes
151+ .&. out_param_count == expected_param_count
152+
153+print_cap ti cap_name = do
154+ putStrLn $ cap_name ++ ": " ++ show (from_capname ti cap_name)
155+
156+print_expression ti cap_name = do
157+ putStrLn $ cap_name ++ ": " ++ show (parse_cap_expression $ from_capname ti cap_name)
158+
test/verify_picture_ops.hsnew file+9
@@ -0,0 +1,9 @@
1+module Main where
2+
3+import Graphics.Vty.Picture ( translate )
4+
5+import Verify
6+
7+main = run_test $ do
8+ return ()
9+
test/verify_picture_to_span.hsnew file+10
@@ -0,0 +1,10 @@
1+module Main where
2+
3+import Graphics.Vty.Picture
4+import Graphics.Vty.Span
5+import Graphics.Vty.PictureToSpans
6+
7+import Verify
8+
9+main = run_test $ do
10+ return ()
test/verify_span_ops.hsnew file+179
@@ -0,0 +1,179 @@
1+{-# LANGUAGE DisambiguateRecordFields #-}
2+{-# LANGUAGE NamedFieldPuns #-}
3+module Main where
4+
5+import Verify.Graphics.Vty.Picture
6+import Verify.Graphics.Vty.Image
7+import Verify.Graphics.Vty.Span
8+import Verify.Graphics.Vty.DisplayRegion
9+
10+import Graphics.Vty.Debug
11+
12+import Verify
13+
14+import Data.Array
15+
16+unit_image_and_zero_window_0 :: UnitImage -> EmptyWindow -> Bool
17+unit_image_and_zero_window_0 (UnitImage _ i) (EmptyWindow w) =
18+ let p = pic_for_image i
19+ spans = spans_for_pic p (region_for_window w)
20+ in span_ops_columns spans == 0 && span_ops_rows spans == 0
21+
22+unit_image_and_zero_window_1 :: UnitImage -> EmptyWindow -> Bool
23+unit_image_and_zero_window_1 (UnitImage _ i) (EmptyWindow w) =
24+ let p = pic_for_image i
25+ spans = spans_for_pic p (region_for_window w)
26+ in ( span_ops_effected_rows spans == 0 ) && ( all_spans_have_width spans 0 )
27+
28+horiz_span_image_and_zero_window_0 :: SingleRowSingleAttrImage -> EmptyWindow -> Bool
29+horiz_span_image_and_zero_window_0 (SingleRowSingleAttrImage { row_image = i }) (EmptyWindow w) =
30+ let p = pic_for_image i
31+ spans = spans_for_pic p (region_for_window w)
32+ in span_ops_columns spans == 0 && span_ops_rows spans == 0
33+
34+horiz_span_image_and_zero_window_1 :: SingleRowSingleAttrImage -> EmptyWindow -> Bool
35+horiz_span_image_and_zero_window_1 (SingleRowSingleAttrImage { row_image = i }) (EmptyWindow w) =
36+ let p = pic_for_image i
37+ spans = spans_for_pic p (region_for_window w)
38+ in ( span_ops_effected_rows spans == 0 ) && ( all_spans_have_width spans 0 )
39+
40+horiz_span_image_and_equal_window_0 :: SingleRowSingleAttrImage -> Result
41+horiz_span_image_and_equal_window_0 (SingleRowSingleAttrImage { row_image = i, expected_columns = c }) =
42+ let p = pic_for_image i
43+ w = DebugWindow c 1
44+ spans = spans_for_pic p (region_for_window w)
45+ in verify_all_spans_have_width i spans c
46+
47+horiz_span_image_and_equal_window_1 :: SingleRowSingleAttrImage -> Bool
48+horiz_span_image_and_equal_window_1 (SingleRowSingleAttrImage { row_image = i, expected_columns = c }) =
49+ let p = pic_for_image i
50+ w = DebugWindow c 1
51+ spans = spans_for_pic p (region_for_window w)
52+ in span_ops_effected_rows spans == 1
53+
54+horiz_span_image_and_lesser_window_0 :: SingleRowSingleAttrImage -> Result
55+horiz_span_image_and_lesser_window_0 (SingleRowSingleAttrImage { row_image = i, expected_columns = c }) =
56+ let p = pic_for_image i
57+ lesser_width = c `div` 2
58+ w = DebugWindow lesser_width 1
59+ spans = spans_for_pic p (region_for_window w)
60+ in verify_all_spans_have_width i spans lesser_width
61+
62+single_attr_single_span_stack_cropped_0 :: SingleAttrSingleSpanStack -> Result
63+single_attr_single_span_stack_cropped_0 stack =
64+ let p = pic_for_image (stack_image stack)
65+ w = DebugWindow (stack_width stack `div` 2) (stack_height stack)
66+ spans = spans_for_pic p (region_for_window w)
67+ in verify_all_spans_have_width (stack_image stack) spans (stack_width stack `div` 2)
68+
69+single_attr_single_span_stack_cropped_1 :: SingleAttrSingleSpanStack -> Bool
70+single_attr_single_span_stack_cropped_1 stack =
71+ let p = pic_for_image (stack_image stack)
72+ expected_row_count = stack_height stack `div` 2
73+ w = DebugWindow (stack_width stack) expected_row_count
74+ spans = spans_for_pic p (region_for_window w)
75+ actual_row_count = span_ops_effected_rows spans
76+ in expected_row_count == actual_row_count
77+
78+single_attr_single_span_stack_cropped_2 :: SingleAttrSingleSpanStack -> SingleAttrSingleSpanStack -> Result
79+single_attr_single_span_stack_cropped_2 stack_0 stack_1 =
80+ let p = pic_for_image (stack_image stack_0 <|> stack_image stack_1)
81+ w = DebugWindow (stack_width stack_0) (image_height (pic_image p))
82+ spans = spans_for_pic p (region_for_window w)
83+ in verify_all_spans_have_width (pic_image p) spans (stack_width stack_0)
84+
85+single_attr_single_span_stack_cropped_3 :: SingleAttrSingleSpanStack -> SingleAttrSingleSpanStack -> Bool
86+single_attr_single_span_stack_cropped_3 stack_0 stack_1 =
87+ let p = pic_for_image (stack_image stack_0 <|> stack_image stack_1)
88+ w = DebugWindow (image_width (pic_image p)) expected_row_count
89+ spans = spans_for_pic p (region_for_window w)
90+ expected_row_count = image_height (pic_image p) `div` 2
91+ actual_row_count = span_ops_effected_rows spans
92+ in expected_row_count == actual_row_count
93+
94+single_attr_single_span_stack_cropped_4 :: SingleAttrSingleSpanStack -> SingleAttrSingleSpanStack -> Result
95+single_attr_single_span_stack_cropped_4 stack_0 stack_1 =
96+ let p = pic_for_image (stack_image stack_0 <-> stack_image stack_1)
97+ w = DebugWindow expected_width (image_height (pic_image p))
98+ spans = spans_for_pic p (region_for_window w)
99+ expected_width = image_width (pic_image p) `div` 2
100+ in verify_all_spans_have_width (pic_image p) spans expected_width
101+
102+single_attr_single_span_stack_cropped_5 :: SingleAttrSingleSpanStack -> SingleAttrSingleSpanStack -> Bool
103+single_attr_single_span_stack_cropped_5 stack_0 stack_1 =
104+ let p = pic_for_image (stack_image stack_0 <-> stack_image stack_1)
105+ w = DebugWindow (image_width (pic_image p)) (stack_height stack_0)
106+ spans = spans_for_pic p (region_for_window w)
107+ expected_row_count = stack_height stack_0
108+ actual_row_count = span_ops_effected_rows spans
109+ in expected_row_count == actual_row_count
110+
111+horiz_span_image_and_greater_window_0 :: SingleRowSingleAttrImage -> Result
112+horiz_span_image_and_greater_window_0 (SingleRowSingleAttrImage { row_image = i, expected_columns = c }) =
113+ let p = pic_for_image i
114+ -- SingleRowSingleAttrImage always has width >= 1
115+ greater_width = c * 2
116+ w = DebugWindow greater_width 1
117+ spans = spans_for_pic p (region_for_window w)
118+ in verify_all_spans_have_width i spans greater_width
119+
120+arb_image_is_cropped :: DefaultImage -> DebugWindow -> Bool
121+arb_image_is_cropped (DefaultImage image _) win@(DebugWindow w h) =
122+ let pic = pic_for_image image
123+ spans = spans_for_pic pic (region_for_window win)
124+ in ( span_ops_effected_rows spans == h ) && ( all_spans_have_width spans w )
125+
126+span_ops_actually_fill_rows :: DefaultPic -> Bool
127+span_ops_actually_fill_rows (DefaultPic pic win _) =
128+ let spans = spans_for_pic pic (region_for_window win)
129+ expected_row_count = region_height (region_for_window win)
130+ actual_row_count = span_ops_effected_rows spans
131+ in expected_row_count == actual_row_count
132+
133+span_ops_actually_fill_columns :: DefaultPic -> Bool
134+span_ops_actually_fill_columns (DefaultPic pic win _) =
135+ let spans = spans_for_pic pic (region_for_window win)
136+ expected_column_count = region_width (region_for_window win)
137+ in all_spans_have_width spans expected_column_count
138+
139+first_span_op_sets_attr :: DefaultPic -> Bool
140+first_span_op_sets_attr DefaultPic { default_pic = pic, default_win = win } =
141+ let spans = spans_for_pic pic (region_for_window win)
142+ in all ( is_attr_span_op . head ) ( elems $ row_ops spans )
143+
144+single_attr_single_span_stack_op_coverage :: SingleAttrSingleSpanStack -> Result
145+single_attr_single_span_stack_op_coverage stack =
146+ let p = pic_for_image (stack_image stack)
147+ w = DebugWindow (stack_width stack) (stack_height stack)
148+ spans = spans_for_pic p (region_for_window w)
149+ in verify_all_spans_have_width (stack_image stack) spans (stack_width stack)
150+
151+main :: IO ()
152+main = run_test $ do
153+ verify "unit image is cropped when window size == (0,0) [0]" unit_image_and_zero_window_0
154+ verify "unit image is cropped when window size == (0,0) [1]" unit_image_and_zero_window_1
155+ verify "horiz span image is cropped when window size == (0,0) [0]" horiz_span_image_and_zero_window_0
156+ verify "horiz span image is cropped when window size == (0,0) [1]" horiz_span_image_and_zero_window_1
157+ verify "horiz span image is not cropped when window size == size of image [width]" horiz_span_image_and_equal_window_0
158+ verify "horiz span image is not cropped when window size == size of image [height]" horiz_span_image_and_equal_window_1
159+ verify "horiz span image is not cropped when window size < size of image [width]" horiz_span_image_and_lesser_window_0
160+ verify "horiz span image is not cropped when window size > size of image [width]" horiz_span_image_and_greater_window_0
161+ verify "arbitrary image is padded or cropped" arb_image_is_cropped
162+ verify "The span ops actually define content for all the rows in the output region" span_ops_actually_fill_rows
163+ verify "The span ops actually define content for all the columns in the output region" span_ops_actually_fill_columns
164+ verify "first span op is always to set the text attribute" first_span_op_sets_attr
165+ verify "a stack of single attr text spans should define content for all the columns [output region == size of stack]"
166+ single_attr_single_span_stack_op_coverage
167+ verify "a single attr text span is cropped when window size < size of stack image [width]"
168+ single_attr_single_span_stack_cropped_0
169+ verify "a single attr text span is cropped when window size < size of stack image [height]"
170+ single_attr_single_span_stack_cropped_1
171+ verify "single attr text span <|> single attr text span cropped. [width]"
172+ single_attr_single_span_stack_cropped_2
173+ verify "single attr text span <|> single attr text span cropped. [height]"
174+ single_attr_single_span_stack_cropped_3
175+ verify "single attr text span <-> single attr text span cropped. [width]"
176+ single_attr_single_span_stack_cropped_4
177+ verify "single attr text span <-> single attr text span cropped. [height]"
178+ single_attr_single_span_stack_cropped_5
179+ return ()
test/verify_utf8_width.hsnew file+17
@@ -0,0 +1,17 @@
1+module Main where
2+import Verify
3+
4+import Graphics.Vty.Attributes
5+import Graphics.Vty.Picture
6+
7+sw_is_1_column :: SingleColumnChar -> Bool
8+sw_is_1_column (SingleColumnChar c) = image_width (char def_attr c) == 1
9+
10+dw_is_2_column :: DoubleColumnChar -> Bool
11+dw_is_2_column (DoubleColumnChar c) = image_width (char def_attr c) == 2
12+
13+main = run_test $ do
14+ verify "sw_is_1_column" sw_is_1_column
15+ verify "dw_is_2_column" dw_is_2_column
16+ return ()
17+
test/vty-examples.cabaldeleted−138
@@ -1,138 +0,0 @@
1−name: vty-examples
2−version: 5.7
3−license: BSD3
4−license-file: LICENSE
5−author: AUTHORS
6−maintainer: Jonathan Daugherty (cygnus@foobox.com)
7−homepage: https://github.com/jtdaugherty/vty
8−category: User Interfaces
9−synopsis: Examples programs using the vty library.
10−description:
11− vty is terminal GUI library in the niche of ncurses. It is intended to
12− be easy to use, have no confusing corner cases, and good support for
13− common terminal types.
14− .
15− vty-interactive-terminal-test - interactive test. Useful for building
16− a bug report for vtys author.
17− .
18− vty-event-echo - view a input event log for vty. Example of
19− interacting with user.
20− .
21− vty-rogue - A bad rogue-like game. Go from the entrance to exit.
22− Example of layers
23− .
24− vty-benchmark - benchmarks vty. A series of tests that push random
25− pictures to the terminal.
26− .
27− &#169; Corey O'Connor; BSD3 license.
28− .
29− &#169; Jonathan Daugherty; BSD3 license.
30−cabal-version: >= 1.18.0
31−build-type: Simple
32−
33−source-repository head
34− type: git
35− location: https://github.com/jtdaugherty/vty.git
36−
37−executable vty-interactive-terminal-test
38− main-is: interactive_terminal_test.hs
39−
40− default-language: Haskell2010
41− default-extensions: ScopedTypeVariables
42− ghc-options: -threaded
43−
44− build-depends: vty >= 5.15,
45− base >= 4 && < 5,
46− bytestring,
47− containers,
48− data-default,
49− deepseq,
50− lens,
51− mtl,
52− parallel,
53− parsec,
54− string-qq,
55− terminfo,
56− text,
57− unix,
58− utf8-string,
59− vector
60−
61− other-modules: Verify.Data.Terminfo.Parse
62− Verify
63−
64−executable vty-event-echo
65− main-is: EventEcho.hs
66−
67− default-language: Haskell2010
68− default-extensions: ScopedTypeVariables
69− FlexibleContexts
70− ghc-options: -threaded
71−
72− build-depends: vty >= 5.15,
73− base >= 4 && < 5,
74− array,
75− bytestring,
76− containers,
77− data-default,
78− lens,
79− mtl,
80− parallel,
81− text,
82− utf8-string,
83− vector
84−
85−executable vty-rogue
86− main-is: Rogue.hs
87−
88− default-language: Haskell2010
89− default-extensions: ScopedTypeVariables
90− ghc-options: -threaded
91−
92− build-depends: vty >= 5.15,
93− base >= 4 && < 5,
94− array,
95− bytestring,
96− containers,
97− data-default,
98− lens,
99− mtl,
100− parallel,
101− random,
102− text,
103− utf8-string,
104− vector
105−
106−executable vty-benchmark
107− main-is: benchmark.hs
108−
109− default-language: Haskell2010
110− default-extensions: ScopedTypeVariables
111− ghc-options: -threaded
112−
113− build-depends: vty >= 5.15,
114− base >= 4 && < 5,
115− bytestring,
116− Cabal,
117− containers,
118− data-default,
119− deepseq,
120− lens,
121− mtl,
122− parallel,
123− parsec,
124− QuickCheck,
125− random,
126− string-qq,
127− terminfo,
128− text,
129− unix,
130− utf8-string,
131− vector
132−
133− other-modules: BenchImageFuzz
134− BenchNoDiffOpt
135− BenchRenderChar
136− BenchVerticalScroll
137− Verify.Graphics.Vty.Image
138− Verify.Graphics.Vty.Attributes
test/vty_inline_example.hs+5−2
@@ -2,8 +2,11 @@ import Graphics.Vty
22import Graphics.Vty.Inline
33
44main = do
5+ t <- terminal_handle
56 putStr "Not styled. "
6− putAttrChange_ t $ backColor red >> applyStyle underline
7+ put_attr_change t $ back_color red >> apply_style underline
78 putStr " Styled! "
8− putAttrChange_ t $ defaultAll
9+ put_attr_change t $ default_all
910 putStrLn "Not styled."
11+ release_terminal t
12+ return ()
test/Issue18.hs → test/vty_issue_18.hsrenamed+6−4
@@ -1,4 +1,5 @@
1−module Main where
1+module Main
2+ where
23
34import Graphics.Vty
45import Graphics.Vty.Debug
@@ -12,8 +13,8 @@ main = do
1213 play vty sx sy
1314
1415play :: Vty -> Int -> Int -> IO ()
15−play vty sx sy =
16− let
16+play vty sx sy =
17+ let
1718 testScreen = pic {
1819 pCursor = NoCursor
1920 , pImage = box 10 10 }
@@ -30,5 +31,6 @@ box w h =
3031 vertLine = renderFill attr '|' 1 (h - 2)
3132 horizLine = corner <|> renderHFill attr '-' (w - 2) <|> corner
3233 centerArea = vertLine <|> renderFill attr 'X' (w - 2) (h - 2) <|> vertLine
33− in
34+ in
3435 horizLine <-> centerArea <-> horizLine
36+
test/YiIssue264.hs → test/yi_issue_264.hsrenamed−1
@@ -1,5 +1,4 @@
11module Main where
2−
32import Graphics.Vty
43import Control.Exception
54
tools/BuildWidthTable.hsdeleted−144
@@ -1,144 +0,0 @@
1−{-# LANGUAGE ScopedTypeVariables #-}
2−{-# LANGUAGE CPP #-}
3−module Main where
4−
5−import qualified Control.Exception as E
6−import Control.Monad (when)
7−import Data.Maybe (fromMaybe)
8−#if !(MIN_VERSION_base(4,11,0))
9−import Data.Semigroup ((<>))
10−#endif
11−import System.Directory (createDirectoryIfMissing)
12−import System.Environment (getArgs, getProgName)
13−import System.FilePath (takeDirectory)
14−import System.Exit (exitFailure)
15−import System.Console.GetOpt
16−import Text.Read (readMaybe)
17−
18−import Graphics.Vty.Config ( terminalWidthTablePath, currentTerminalName
19− , vtyConfigPath, addConfigWidthMap
20− , ConfigUpdateResult(..)
21− )
22−import Graphics.Vty.UnicodeWidthTable.IO
23−import Graphics.Vty.UnicodeWidthTable.Query
24−
25−data Arg = Help
26− | OutputPath String
27− | TableUpperBound String
28− | UpdateConfig
29− | VtyConfigPath String
30− deriving (Eq, Show)
31−
32−options :: Config -> [OptDescr Arg]
33−options config =
34− [ Option "h" ["help"] (NoArg Help)
35− "This help output"
36− , Option "b" ["bound"] (ReqArg TableUpperBound "MAX_CHAR")
37− ("The maximum Unicode code point to test when building the table " <>
38− "(default: " <> (show $ fromEnum $ configBound config) <> ")")
39− , Option "p" ["path"] (ReqArg OutputPath "PATH")
40− ("The output path to write to (default: " <>
41− fromMaybe "<none>" (configOutputPath config) <> ")")
42− , Option "u" ["update-config"] (NoArg UpdateConfig)
43− "Create or update the Vty configuration file to use the new table (default: no)"
44− , Option "c" ["config-path"] (ReqArg VtyConfigPath "PATH")
45− ("Update the specified Vty configuration file path when -u is set (default: " <>
46− configPath config <> ")")
47− ]
48−
49−data Config =
50− Config { configOutputPath :: Maybe FilePath
51− , configBound :: Char
52− , configUpdate :: Bool
53− , configPath :: FilePath
54− }
55− deriving (Show)
56−
57−mkDefaultConfig :: IO Config
58−mkDefaultConfig = do
59− Config <$> terminalWidthTablePath
60− <*> pure defaultUnicodeTableUpperBound
61− <*> pure False
62− <*> vtyConfigPath
63−
64−usage :: IO ()
65−usage = do
66− config <- mkDefaultConfig
67− pn <- getProgName
68− putStrLn $ "Usage: " <> pn <> " [options]"
69− putStrLn ""
70− putStrLn "This tool queries the terminal on stdout to determine the widths"
71− putStrLn "of Unicode characters rendered to the terminal. The resulting data"
72− putStrLn "is written to a table at the specified output path for later"
73− putStrLn "loading by Vty-based applications."
74− putStrLn ""
75−
76− putStrLn $ usageInfo pn (options config)
77−
78−updateConfigFromArg :: Arg -> Config -> Config
79−updateConfigFromArg Help c =
80− c
81−updateConfigFromArg UpdateConfig c =
82− c { configUpdate = True }
83−updateConfigFromArg (VtyConfigPath p) c =
84− c { configPath = p }
85−updateConfigFromArg (TableUpperBound s) c =
86− case readMaybe s of
87− Nothing -> error $ "Invalid table upper bound: " <> show s
88− Just v -> c { configBound = toEnum v }
89−updateConfigFromArg (OutputPath p) c =
90− c { configOutputPath = Just p }
91−
92−main :: IO ()
93−main = do
94− defConfig <- mkDefaultConfig
95− strArgs <- getArgs
96− let (args, unused, errors) = getOpt Permute (options defConfig) strArgs
97−
98− when (not $ null errors) $ do
99− mapM_ putStrLn errors
100− exitFailure
101−
102− when ((not $ null unused) || (Help `elem` args)) $ do
103− usage
104− exitFailure
105−
106− let config = foldr updateConfigFromArg defConfig args
107−
108− outputPath <- case configOutputPath config of
109− Nothing -> do
110− putStrLn "Error: could not obtain terminal width table path"
111− exitFailure
112− Just path -> return path
113−
114− putStrLn "Querying terminal:"
115− builtTable <- buildUnicodeWidthTable $ configBound config
116−
117− let dir = takeDirectory outputPath
118− createDirectoryIfMissing True dir
119− writeUnicodeWidthTable outputPath builtTable
120−
121− putStrLn $ "\nOutput table written to " <> outputPath
122−
123− when (configUpdate config) $ do
124− let cPath = configPath config
125− Just tName <- currentTerminalName
126−
127− result <- E.try $ addConfigWidthMap cPath tName outputPath
128−
129− case result of
130− Left (e::E.SomeException) -> do
131− putStrLn $ "Error updating Vty configuration at " <> cPath <> ": " <>
132− show e
133− exitFailure
134− Right ConfigurationCreated -> do
135− putStrLn $ "Configuration file created: " <> cPath
136− Right ConfigurationModified -> do
137− putStrLn $ "Configuration file updated: " <> cPath
138− Right (ConfigurationConflict other) -> do
139− putStrLn $ "Configuration file not updated: uses a different table " <>
140− "for TERM=" <> tName <> ": " <> other
141− Right ConfigurationRedundant -> do
142− putStrLn $ "Configuration file not updated: configuration " <>
143− cPath <> " already uses table " <> outputPath <>
144− " for TERM=" <> tName
vty.cabal+96−627
@@ -1,633 +1,102 @@
1−name: vty
2−version: 5.35.1
3−license: BSD3
4−license-file: LICENSE
5−author: AUTHORS
6−maintainer: Jonathan Daugherty (cygnus@foobox.com)
7−homepage: https://github.com/jtdaugherty/vty
8−category: User Interfaces
9−synopsis: A simple terminal UI library
10−description:
11− vty is terminal GUI library in the niche of ncurses. It is intended to
12− be easy to use, have no confusing corner cases, and good support for
13− common terminal types.
1+Name: vty
2+Version: 4.2.1.0
3+License: BSD3
4+License-file: LICENSE
5+Author: Stefan O'Rear, Corey O'Connor
6+Maintainer: Corey O'Connor (coreyoconnor@gmail.com)
7+Homepage: http://trac.haskell.org/vty/
8+Category: User Interfaces
9+Synopsis: A simple terminal access library
10+Description:
11+ vty is terminal GUI library in the niche of ncurses. It is intended to be easy to use, have no
12+ confusing corner cases, and good support for common terminal types.
1413 .
15− See the @vty-examples@ package as well as the program
16− @test/interactive_terminal_test.hs@ included in the @vty@ package for
17− examples on how to use the library.
14+ Included in the source distribution is a program test/interactive_terminal_test.hs that
15+ demonstrates the various features.
1816 .
19− Import the "Graphics.Vty" convenience module to get access to the core
20− parts of the library.
17+ If your terminal is not behaving as expected the results of the test/interactive_terminal_test.hs
18+ program should be sent to the Vty maintainter to aid in debugging the issue.
2119 .
22− &#169; 2006-2007 Stefan O'Rear; BSD3 license.
20+ Notable infelicities: Sometimes poor efficiency; Assumes UTF-8 character encoding support by the
21+ terminal;
2322 .
24− &#169; Corey O'Connor; BSD3 license.
23+ You can 'darcs get' it from <http://code.haskell.org/vty/>
2524 .
26− &#169; Jonathan Daugherty; BSD3 license.
27−cabal-version: 1.18
28−build-type: Simple
29−extra-doc-files: README.md,
30− AUTHORS,
31− CHANGELOG.md,
32− LICENSE
33−tested-with: GHC==7.10.3, GHC==8.0.2, GHC==8.2.2, GHC==8.4.3, GHC==8.6.5
34−
35−source-repository head
36− type: git
37− location: https://github.com/jtdaugherty/vty.git
38−
39−library
40− default-language: Haskell2010
41− build-depends: base >= 4.8 && < 5,
42− blaze-builder >= 0.3.3.2 && < 0.5,
43− bytestring,
44− containers,
45− deepseq >= 1.1 && < 1.5,
46− directory,
47− filepath >= 1.0 && < 2.0,
48− microlens < 0.4.13,
49− microlens-mtl,
50− microlens-th,
51− hashable >= 1.2,
52− mtl >= 1.1.1.0 && < 2.3,
53− parallel >= 2.2 && < 3.3,
54− parsec >= 2 && < 4,
55− stm,
56− terminfo >= 0.3 && < 0.5,
57− transformers >= 0.3.0.0,
58− text >= 0.11.3,
59− unix,
60− utf8-string >= 0.3 && < 1.1,
61− vector >= 0.7,
62− binary,
63− ansi-terminal >= 0.10.3
64−
65− if !impl(ghc >= 8.0)
66− build-depends: semigroups >= 0.16,
67− fail
68−
69− exposed-modules: Graphics.Vty
70− Graphics.Vty.Attributes
71− Graphics.Vty.Attributes.Color
72− Graphics.Vty.Attributes.Color240
73− Graphics.Vty.Config
74− Graphics.Vty.Error
75− Graphics.Vty.Image
76− Graphics.Vty.Inline
77− Graphics.Vty.Inline.Unsafe
78− Graphics.Vty.Input
79− Graphics.Vty.Input.Events
80− Graphics.Vty.Picture
81− Graphics.Vty.Output
82− Graphics.Text.Width
83− Codec.Binary.UTF8.Debug
84− Data.Terminfo.Parse
85− Data.Terminfo.Eval
86− Graphics.Vty.Debug
87− Graphics.Vty.DisplayAttributes
88− Graphics.Vty.Image.Internal
89− Graphics.Vty.Input.Classify
90− Graphics.Vty.Input.Classify.Types
91− Graphics.Vty.Input.Classify.Parse
92− Graphics.Vty.Input.Loop
93− Graphics.Vty.Input.Mouse
94− Graphics.Vty.Input.Focus
95− Graphics.Vty.Input.Paste
96− Graphics.Vty.Input.Terminfo
97− Graphics.Vty.PictureToSpans
98− Graphics.Vty.Span
99− Graphics.Vty.Output.Mock
100− Graphics.Vty.Output.Interface
101− Graphics.Vty.Output.XTermColor
102− Graphics.Vty.Output.TerminfoBased
103− Graphics.Vty.UnicodeWidthTable.Types
104− Graphics.Vty.UnicodeWidthTable.IO
105− Graphics.Vty.UnicodeWidthTable.Query
106− Graphics.Vty.UnicodeWidthTable.Install
107−
108− other-modules: Graphics.Vty.Debug.Image
109− Graphics.Vty.Input.Terminfo.ANSIVT
110−
111− c-sources: cbits/gwinsz.c
112− cbits/set_term_timing.c
113− cbits/get_tty_erase.c
114− cbits/mk_wcwidth.c
115−
116− include-dirs: cbits
117−
118− hs-source-dirs: src
119−
120− ghc-options: -O2 -funbox-strict-fields -Wall -fspec-constr -fspec-constr-count=10
121−
122− ghc-prof-options: -O2 -funbox-strict-fields -caf-all -Wall -fspec-constr -fspec-constr-count=10
123−
124−executable vty-build-width-table
125− main-is: BuildWidthTable.hs
126− hs-source-dirs: tools
127−
128− default-language: Haskell2010
129− ghc-options: -threaded -Wall
130−
131− if !impl(ghc >= 8.0)
132− build-depends: semigroups >= 0.16
133−
134− build-depends: vty,
135− directory,
136− filepath,
137− base >= 4.8 && < 5
138−
139−executable vty-mode-demo
140− main-is: ModeDemo.hs
141− hs-source-dirs: demos
142−
143− default-language: Haskell2010
144− ghc-options: -threaded
145−
146− build-depends: vty,
147− base >= 4.8 && < 5,
148− containers,
149− microlens,
150− microlens-mtl,
151− mtl >= 1.1.1.0 && < 2.3
152−
153−executable vty-demo
154− main-is: Demo.hs
155− hs-source-dirs: demos
156−
157− default-language: Haskell2010
158− ghc-options: -threaded
159−
160− build-depends: vty,
161− base >= 4.8 && < 5,
162− containers,
163− microlens,
164− microlens-mtl,
165− mtl >= 1.1.1.0 && < 2.3
166−
167−test-suite verify-using-mock-terminal
168− default-language: Haskell2010
169−
170− type: detailed-0.9
171−
172− hs-source-dirs: test
173−
174− test-module: VerifyUsingMockTerminal
175−
176− other-modules: Verify
177− Verify.Graphics.Vty.Attributes
178− Verify.Graphics.Vty.Prelude
179− Verify.Graphics.Vty.Picture
180− Verify.Graphics.Vty.Image
181− Verify.Graphics.Vty.Span
182− Verify.Graphics.Vty.Output
183−
184− build-depends: vty,
185− Cabal >= 1.20,
186− QuickCheck >= 2.7,
187− random >= 1.0 && < 1.3,
188− base >= 4.8 && < 5,
189− bytestring,
190− containers,
191− deepseq >= 1.1 && < 1.5,
192− mtl >= 1.1.1.0 && < 2.3,
193− text >= 0.11.3,
194− terminfo >= 0.3 && < 0.5,
195− unix,
196− utf8-string >= 0.3 && < 1.1,
197− vector >= 0.7
198−
199−test-suite verify-terminal
200− default-language: Haskell2010
201−
202− type: detailed-0.9
203−
204− hs-source-dirs: test
205−
206− test-module: VerifyOutput
207−
208− other-modules: Verify
209− Verify.Graphics.Vty.Attributes
210− Verify.Graphics.Vty.Prelude
211− Verify.Graphics.Vty.Picture
212− Verify.Graphics.Vty.Image
213− Verify.Graphics.Vty.Span
214− Verify.Graphics.Vty.Output
215−
216− build-depends: vty,
217− Cabal >= 1.20,
218− QuickCheck >= 2.7,
219− random >= 1.0 && < 1.3,
220− base >= 4.8 && < 5,
221− bytestring,
222− containers,
223− deepseq >= 1.1 && < 1.5,
224− mtl >= 1.1.1.0 && < 2.3,
225− terminfo >= 0.3 && < 0.5,
226− text >= 0.11.3,
227− unix,
228− utf8-string >= 0.3 && < 1.1,
229− vector >= 0.7
230−
231−test-suite verify-display-attributes
232− default-language: Haskell2010
233−
234− type: detailed-0.9
235−
236− hs-source-dirs: test
237−
238− test-module: VerifyDisplayAttributes
239−
240− other-modules: Verify
241− Verify.Graphics.Vty.Attributes
242− Verify.Graphics.Vty.DisplayAttributes
243− Verify.Graphics.Vty.Prelude
244− Verify.Graphics.Vty.Picture
245− Verify.Graphics.Vty.Image
246− Verify.Graphics.Vty.Span
247−
248− build-depends: vty,
249− Cabal >= 1.20,
250− QuickCheck >= 2.7,
251− random >= 1.0 && < 1.3,
252− base >= 4.8 && < 5,
253− bytestring,
254− containers,
255− deepseq >= 1.1 && < 1.5,
256− mtl >= 1.1.1.0 && < 2.3,
257− text >= 0.11.3,
258− unix,
259− utf8-string >= 0.3 && < 1.1,
260− vector >= 0.7
261−
262−test-suite verify-empty-image-props
263− default-language: Haskell2010
264−
265− type: detailed-0.9
266−
267− hs-source-dirs: test
268−
269− test-module: VerifyEmptyImageProps
270−
271− other-modules: Verify
272−
273− build-depends: vty,
274− Cabal >= 1.20,
275− QuickCheck >= 2.7,
276− random >= 1.0 && < 1.3,
277− base >= 4.8 && < 5,
278− bytestring,
279− containers,
280− deepseq >= 1.1 && < 1.5,
281− mtl >= 1.1.1.0 && < 2.3,
282− text >= 0.11.3,
283− unix,
284− utf8-string >= 0.3 && < 1.1,
285− vector >= 0.7
286−
287−test-suite verify-eval-terminfo-caps
288− default-language: Haskell2010
289−
290− type: detailed-0.9
291−
292− hs-source-dirs: test
293−
294− test-module: VerifyEvalTerminfoCaps
295−
296− other-modules: Verify
297− Verify.Graphics.Vty.Output
298−
299− build-depends: vty,
300− Cabal >= 1.20,
301− QuickCheck >= 2.7,
302− random >= 1.0 && < 1.3,
303− base >= 4.8 && < 5,
304− blaze-builder >= 0.3.3.2 && < 0.5,
305− bytestring,
306− containers,
307− deepseq >= 1.1 && < 1.5,
308− mtl >= 1.1.1.0 && < 2.3,
309− terminfo >= 0.3 && < 0.5,
310− text >= 0.11.3,
311− unix,
312− utf8-string >= 0.3 && < 1.1,
313− vector >= 0.7
314−
315−test-suite verify-image-ops
316− default-language: Haskell2010
317−
318− type: detailed-0.9
319−
320− hs-source-dirs: test
321−
322− test-module: VerifyImageOps
323−
324− other-modules: Verify
325− Verify.Graphics.Vty.Attributes
326− Verify.Graphics.Vty.Image
327−
328− build-depends: vty,
329− Cabal >= 1.20,
330− QuickCheck >= 2.7,
331− random >= 1.0 && < 1.3,
332− base >= 4.8 && < 5,
333− bytestring,
334− containers,
335− deepseq >= 1.1 && < 1.5,
336− mtl >= 1.1.1.0 && < 2.3,
337− text >= 0.11.3,
338− unix,
339− utf8-string >= 0.3 && < 1.1,
340− vector >= 0.7
341−
342−test-suite verify-image-trans
343− default-language: Haskell2010
344−
345− type: detailed-0.9
346−
347− hs-source-dirs: test
348−
349− test-module: VerifyImageTrans
350−
351− other-modules: Verify
352− Verify.Graphics.Vty.Attributes
353− Verify.Graphics.Vty.Image
354−
355− build-depends: vty,
356− Cabal >= 1.20,
357− QuickCheck >= 2.7,
358− random >= 1.0 && < 1.3,
359− base >= 4.8 && < 5,
360− bytestring,
361− containers,
362− deepseq >= 1.1 && < 1.5,
363− mtl >= 1.1.1.0 && < 2.3,
364− text >= 0.11.3,
365− unix,
366− utf8-string >= 0.3 && < 1.1,
367− vector >= 0.7
368−
369−test-suite verify-inline
370− default-language: Haskell2010
371−
372− type: detailed-0.9
373−
374− hs-source-dirs: test
375−
376− test-module: VerifyInline
377−
378− other-modules: Verify
379− Verify.Graphics.Vty.Output
380−
381− build-depends: vty,
382− Cabal >= 1.20,
383− QuickCheck >= 2.7,
384− random >= 1.0 && < 1.3,
385− base >= 4.8 && < 5,
386− bytestring,
387− containers,
388− deepseq >= 1.1 && < 1.5,
389− mtl >= 1.1.1.0 && < 2.3,
390− text >= 0.11.3,
391− unix,
392− utf8-string >= 0.3 && < 1.1,
393− vector >= 0.7
394−
395−test-suite verify-parse-terminfo-caps
396− default-language: Haskell2010
397−
398− type: detailed-0.9
399−
400− hs-source-dirs: test
401−
402− test-module: VerifyParseTerminfoCaps
403−
404− other-modules: Verify
405− Verify.Data.Terminfo.Parse
406− Verify.Graphics.Vty.Output
407−
408− build-depends: vty,
409− Cabal >= 1.20,
410− QuickCheck >= 2.7,
411− random >= 1.0 && < 1.3,
412− base >= 4.8 && < 5,
413− bytestring,
414− containers,
415− deepseq >= 1.1 && < 1.5,
416− mtl >= 1.1.1.0 && < 2.3,
417− terminfo >= 0.3 && < 0.5,
418− text >= 0.11.3,
419− unix,
420− utf8-string >= 0.3 && < 1.1,
421− vector >= 0.7
422−
423−test-suite verify-simple-span-generation
424− default-language: Haskell2010
425−
426− type: detailed-0.9
427−
428− hs-source-dirs: test
429−
430− test-module: VerifySimpleSpanGeneration
431−
432− other-modules: Verify
433− Verify.Graphics.Vty.Attributes
434− Verify.Graphics.Vty.Prelude
435− Verify.Graphics.Vty.Picture
436− Verify.Graphics.Vty.Image
437− Verify.Graphics.Vty.Span
438−
439− build-depends: vty,
440− Cabal >= 1.20,
441− QuickCheck >= 2.7,
442− random >= 1.0 && < 1.3,
443− base >= 4.8 && < 5,
444− bytestring,
445− containers,
446− deepseq >= 1.1 && < 1.5,
447− mtl >= 1.1.1.0 && < 2.3,
448− text >= 0.11.3,
449− unix,
450− utf8-string >= 0.3 && < 1.1,
451− vector >= 0.7
452−
453−
454−test-suite verify-crop-span-generation
455− default-language: Haskell2010
456−
457− type: detailed-0.9
458−
459− hs-source-dirs: test
460−
461− test-module: VerifyCropSpanGeneration
462−
463− other-modules: Verify
464− Verify.Graphics.Vty.Attributes
465− Verify.Graphics.Vty.Prelude
466− Verify.Graphics.Vty.Picture
467− Verify.Graphics.Vty.Image
468− Verify.Graphics.Vty.Span
469−
470− build-depends: vty,
471− Cabal >= 1.20,
472− QuickCheck >= 2.7,
473− random >= 1.0 && < 1.3,
474− base >= 4.8 && < 5,
475− bytestring,
476− containers,
477− deepseq >= 1.1 && < 1.5,
478− mtl >= 1.1.1.0 && < 2.3,
479− text >= 0.11.3,
480− unix,
481− utf8-string >= 0.3 && < 1.1,
482− vector >= 0.7
483−
484−
485−test-suite verify-layers-span-generation
486− default-language: Haskell2010
487−
488− type: detailed-0.9
489−
490− hs-source-dirs: test
491−
492− test-module: VerifyLayersSpanGeneration
493−
494− other-modules: Verify
495− Verify.Graphics.Vty.Attributes
496− Verify.Graphics.Vty.Prelude
497− Verify.Graphics.Vty.Picture
498− Verify.Graphics.Vty.Image
499− Verify.Graphics.Vty.Span
500−
501− build-depends: vty,
502− Cabal >= 1.20,
503− QuickCheck >= 2.7,
504− random >= 1.0 && < 1.3,
505− base >= 4.8 && < 5,
506− bytestring,
507− containers,
508− deepseq >= 1.1 && < 1.5,
509− mtl >= 1.1.1.0 && < 2.3,
510− text >= 0.11.3,
511− unix,
512− utf8-string >= 0.3 && < 1.1,
513− vector >= 0.7
514−
515−test-suite verify-color-mapping
516− default-language: Haskell2010
517−
518− type: detailed-0.9
519−
520− hs-source-dirs: test
521−
522− test-module: VerifyColor240
523−
524− other-modules: Verify
525−
526− build-depends: vty,
527− Cabal >= 1.20,
528− QuickCheck >= 2.7,
529− random >= 1.0 && < 1.3,
530− base >= 4.8 && < 5,
531− bytestring,
532− containers,
533− deepseq >= 1.1 && < 1.5,
534− mtl >= 1.1.1.0 && < 2.3,
535− text >= 0.11.3,
536− unix,
537− utf8-string >= 0.3 && < 1.1,
538− vector >= 0.7
539−
540−test-suite verify-utf8-width
541− default-language: Haskell2010
542−
543− type: detailed-0.9
544−
545− hs-source-dirs: test
546−
547− test-module: VerifyUtf8Width
548−
549− other-modules: Verify
550−
551− build-depends: vty,
552− Cabal >= 1.20,
553− QuickCheck >= 2.7,
554− random >= 1.0 && < 1.3,
555− base >= 4.8 && < 5,
556− bytestring,
557− containers,
558− deepseq >= 1.1 && < 1.5,
559− mtl >= 1.1.1.0 && < 2.3,
560− text >= 0.11.3,
561− unix,
562− utf8-string >= 0.3 && < 1.1,
563− vector >= 0.7
564−
565−test-suite verify-using-mock-input
566− default-language: Haskell2010
567−
568− type: exitcode-stdio-1.0
569−
570− hs-source-dirs: test
571−
572− main-is: VerifyUsingMockInput.hs
573−
574− other-modules: Verify.Graphics.Vty.Output
575−
576− build-depends: vty,
577− Cabal >= 1.20,
578− QuickCheck >= 2.7,
579− smallcheck == 1.*,
580− quickcheck-assertions >= 0.1.1,
581− test-framework == 0.8.*,
582− test-framework-smallcheck == 0.2.*,
583− random >= 1.0 && < 1.3,
584− base >= 4.8 && < 5,
585− bytestring,
586− containers,
587− deepseq >= 1.1 && < 1.5,
588− microlens,
589− microlens-mtl,
590− mtl >= 1.1.1.0 && < 2.3,
591− stm,
592− terminfo >= 0.3 && < 0.5,
593− text >= 0.11.3,
594− unix,
595− utf8-string >= 0.3 && < 1.1,
596− vector >= 0.7
597−
598− ghc-options: -threaded -Wall
599−
600−test-suite verify-config
601− default-language: Haskell2010
602−
603− type: exitcode-stdio-1.0
604−
605− hs-source-dirs: test
606−
607− main-is: VerifyConfig.hs
608−
609− build-depends: vty,
610− Cabal >= 1.20,
611− HUnit,
612− QuickCheck >= 2.7,
613− smallcheck == 1.*,
614− quickcheck-assertions >= 0.1.1,
615− test-framework == 0.8.*,
616− test-framework-smallcheck == 0.2.*,
617− test-framework-hunit,
618− random >= 1.0 && < 1.3,
619− base >= 4.8 && < 5,
620− bytestring,
621− containers,
622− deepseq >= 1.1 && < 1.5,
623− microlens,
624− microlens-mtl,
625− mtl >= 1.1.1.0 && < 2.3,
626− string-qq,
627− terminfo >= 0.3 && < 0.5,
628− text >= 0.11.3,
629− unix,
630− utf8-string >= 0.3 && < 1.1,
631− vector >= 0.7
25+ &#169; 2006-2007 Stefan O'Rear; BSD3 license.
26+ .
27+ &#169; 2008-2009 Corey O'Connor; BSD3 license.
28+
29+Build-Depends: base >= 4 && < 5, bytestring, containers, unix
30+Build-Depends: terminfo >= 0.3 && < 0.4
31+Build-Depends: utf8-string >= 0.3 && < 0.4
32+Build-Depends: mtl >= 1.1.0.0 && < 1.2
33+Build-Depends: ghc-prim, parallel < 2
34+Build-Depends: array
35+Build-Depends: parsec >= 2 && < 4
36+Build-Depends: vector-space >= 0.5 && < 0.6
37+Build-Type: Simple
38+Data-Files: README, TODO
39+Exposed-Modules: Graphics.Vty
40+ Graphics.Vty.Terminal
41+ Graphics.Vty.LLInput
42+ Graphics.Vty.Attributes
43+ Graphics.Vty.Inline
44+ Graphics.Vty.Picture
45+ Graphics.Vty.DisplayRegion
46+
47+other-modules: Codec.Binary.UTF8.Width
48+ Data.Marshalling
49+ Data.Terminfo.Parse
50+ Data.Terminfo.Eval
51+ Graphics.Vty.DisplayAttributes
52+ Graphics.Vty.Image
53+ Graphics.Vty.Span
54+ Graphics.Vty.Terminal.Generic
55+ Graphics.Vty.Terminal.MacOSX
56+ Graphics.Vty.Terminal.XTermColor
57+ Graphics.Vty.Terminal.TerminfoBased
58+
59+C-Sources: cbits/gwinsz.c
60+ cbits/set_term_timing.c
61+ cbits/mk_wcwidth.c
62+Include-Dirs: cbits
63+hs-source-dirs: src
64+Extra-Source-Files: test/Makefile
65+ test/Bench2.hs
66+ test/Bench.hs
67+ test/BenchRenderChar.hs
68+ test/ControlTable.hs
69+ test/HereDoc.hs
70+ test/interactive_terminal_test.hs
71+ test/Test2.hs
72+ test/Test.hs
73+ test/verify_attribute_ops.hs
74+ test/verify_debug_terminal.hs
75+ test/verify_eval_terminfo_caps.hs
76+ test/Verify.hs
77+ test/verify_image_ops.hs
78+ test/verify_image_trans.hs
79+ test/verify_parse_terminfo_caps.hs
80+ test/verify_picture_ops.hs
81+ test/verify_picture_to_span.hs
82+ test/verify_span_ops.hs
83+ test/verify_utf8_width.hs
84+ test/vty_issue_18.hs
85+ test/yi_issue_264.hs
86+ test/Verify/Graphics/Vty/Attributes.hs
87+ test/Verify/Graphics/Vty/Picture.hs
88+ test/Verify/Graphics/Vty/Span.hs
89+ test/Verify/Graphics/Vty/DisplayRegion.hs
90+ test/Verify/Graphics/Vty/Image.hs
91+ test/Verify/Data/Terminfo/Parse.hs
92+ src/Codec/Binary/UTF8/Debug.hs
93+ src/Graphics/Vty/Terminal/Debug.hs
94+ src/Graphics/Vty/Debug.hs
95+ cbits/gwinsz.c
96+ cbits/mk_wcwidth.c
97+ cbits/set_term_timing.c
98+ cbits/gwinsz.h
99+
100+ghc-options: -funbox-strict-fields -Wall -fno-full-laziness -fspec-constr
101+ghc-prof-options: -funbox-strict-fields -caf-all -Wall -fno-full-laziness -fspec-constr
632102
633− ghc-options: -threaded -Wall