on f6c3cbf help sign in
.gitignoredeleted−15
since splitsnapshots
unifiedsplit
@@ -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−
3Copyright Stefan O'Rear 2006, Corey O'Connor 2008, Corey O'Connor 2009
1Copyright Stefan O'Rear 2006, Corey O'Connor 2008, Corey O'Connor 2009
4
2
5All rights reserved.
3All 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 @@
1#include <sys/ioctl.h>
1#include <sys/ioctl.h>
2
2
3−unsigned long vty_c_get_window_size(int fd) {
3+unsigned long c_get_window_size(void) {
4− struct winsize w;
4+ struct winsize w;
5− if (ioctl (fd, TIOCGWINSZ, &w) >= 0)
5+ if (ioctl (0, TIOCGWINSZ, &w) >= 0)
6− return (w.ws_row << 16) + w.ws_col;
6+ return (w.ws_row << 16) + w.ws_col;
7− else
7+ else
8− return 0x190050;
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− }
18}
9}
cbits/gwinsz.h+1−2
@@ -1,2 +1 @@
1−unsigned long vty_c_get_window_size(int fd);
1+unsigned long c_get_window_size(void);
2−unsigned long vty_c_set_window_size(int fd, unsigned long val);
cbits/mk_wcwidth.c+99−128
@@ -59,29 +59,7 @@
59 * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
59 * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
60 */
60 */
61
61
62−#include <HsFFI.h>
62+#include <wchar.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;
85
63
86struct interval {
64struct interval {
87 int first;
65 int first;
@@ -89,7 +67,7 @@ struct interval {
89};
67};
90
68
91/* auxiliary function for binary search in interval table */
69/* 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) {
93 int min = 0;
71 int min = 0;
94 int mid;
72 int mid;
95
73
@@ -141,7 +119,7 @@ static int vty_bisearch(HsChar ucs, const struct interval *table, int max) {
141 * in ISO 10646.
119 * in ISO 10646.
142 */
120 */
143
121
144−static HsInt builtin_wcwidth(HsChar ucs)
122+int mk_wcwidth(wchar_t ucs)
145{
123{
146 /* sorted list of non-overlapping intervals of non-spacing characters */
124 /* sorted list of non-overlapping intervals of non-spacing characters */
147 /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */
125 /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */
@@ -203,13 +181,13 @@ static HsInt builtin_wcwidth(HsChar ucs)
203 return -1;
181 return -1;
204
182
205 /* binary search in table of non-spacing characters */
183 /* binary search in table of non-spacing characters */
206− if (vty_bisearch(ucs, combining,
184+ if (bisearch(ucs, combining,
207− sizeof(combining) / sizeof(struct interval) - 1))
185+ sizeof(combining) / sizeof(struct interval) - 1))
208 return 0;
186 return 0;
209
187
210 /* if we arrive here, ucs is not a combining or C0/C1 control character */
188 /* if we arrive here, ucs is not a combining or C0/C1 control character */
211
189
212− return 1 +
190+ return 1 +
213 (ucs >= 0x1100 &&
191 (ucs >= 0x1100 &&
214 (ucs <= 0x115f || /* Hangul Jamo init. consonants */
192 (ucs <= 0x115f || /* Hangul Jamo init. consonants */
215 ucs == 0x2329 || ucs == 0x232a ||
193 ucs == 0x2329 || ucs == 0x232a ||
@@ -225,114 +203,107 @@ static HsInt builtin_wcwidth(HsChar ucs)
225 (ucs >= 0x30000 && ucs <= 0x3fffd)));
203 (ucs >= 0x30000 && ucs <= 0x3fffd)));
226}
204}
227
205
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−}
246
206
247−// Initialize a custom character width table.
207+int mk_wcswidth(const wchar_t *pwcs, size_t n)
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)
259{
208{
260− if (custom_table == NULL) {
209+ int w, width = 0;
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−}
274
210
275−// Set the specified character range in the custom width table to the
211+ for (;*pwcs && n-- > 0; pwcs++)
276−// specified width.
212+ if ((w = mk_wcwidth(*pwcs)) < 0)
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;
213+ return -1;
294− } else {
214+ else
295− memset(custom_table + start, width, size);
215+ width += w;
296− return 0;
297− }
298−}
299
216
300−// Mark the allocated custom character width table as ready for use.
217+ return width;
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− }
316}
218}
317
219
318−// Returns whether a custom character width table has been marked ready.
220+
319−int vty_custom_table_ready()
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)
320{
231{
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);
322}
295}
323
296
324−// Deallocate the custom width table.
297+
325−//
298+int mk_wcswidth_cjk(const wchar_t *pwcs, size_t n)
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()
332{
299{
333− if ((custom_table != NULL) && (!custom_table_ready)) {
300+ int w, width = 0;
334− free(custom_table);
301+
335− custom_table = NULL;
302+ for (;*pwcs && n-- > 0; pwcs++)
336− custom_table_size = 0;
303+ if ((w = mk_wcwidth_cjk(*pwcs)) < 0)
337− }
304+ return -1;
305+ else
306+ width += w;
307+
308+ return width;
338}
309}
cbits/set_term_timing.c+6−5
@@ -3,11 +3,12 @@
3#include <unistd.h>
3#include <unistd.h>
4#include <stdlib.h>
4#include <stdlib.h>
5
5
6−void vty_set_term_timing(int fd, int vmin, int vtime)
6+void set_term_timing(void)
7{
7{
8 struct termios trm;
8 struct termios trm;
9− tcgetattr(fd, &trm);
9+ tcgetattr(STDIN_FILENO, &trm);
10− trm.c_cc[VMIN] = vmin;
10+ trm.c_cc[VMIN] = 0;
11− trm.c_cc[VTIME] = vtime;
11+ trm.c_cc[VTIME] = 0;
12− tcsetattr(fd, TCSANOW, &trm);
12+ tcsetattr(STDIN_FILENO, TCSANOW, &trm);
13}
13}
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 #-}
2-- Copyright 2009 Corey O'Connor
1-- Copyright 2009 Corey O'Connor
3−module Codec.Binary.UTF8.Debug where
2+module Codec.Binary.UTF8.Debug
3+ where
4
4
5import Codec.Binary.UTF8.String ( encode )
5import Codec.Binary.UTF8.String ( encode )
6
6
@@ -8,10 +8,10 @@ import Data.Word
8
8
9import Numeric
9import Numeric
10
10
11−-- | Converts an array of ISO-10646 characters (Char type) to an array
11+-- Converts an array of ISO-10646 characters (Char type) to an array of Word8 bytes that is the
12−-- of Word8 bytes that is the corresponding UTF8 byte sequence
12+-- corresponding UTF8 byte sequence
13−utf8FromIso :: [Int] -> [Word8]
13+utf8_from_iso :: Integral i => [i] -> [Word8]
14−utf8FromIso = encode . map toEnum
14+utf8_from_iso = encode . map toEnum
15+
16+pp_utf8 = print . map (\f -> f "") . map showHex . utf8_from_iso
15
17
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 #-}
2-- Copyright 2009 Corey O'Connor
1-- Copyright 2009 Corey O'Connor
3module Data.Sequence.Extra where
2module Data.Sequence.Extra where
4
3
@@ -7,7 +6,7 @@ import Data.Sequence
7import Control.Parallel.Strategies
6import Control.Parallel.Strategies
8
7
9instance NFData a => NFData (Seq a) where
8instance NFData a => NFData (Seq a) where
10− rnf = rnf' . viewl
9+ rnf = \v -> rnf' (viewl v)
11− where
10+ where
12 rnf' EmptyL = ()
11 rnf' EmptyL = ()
13 rnf' (a :< r) = rnf a >| rnf' (viewl r)
12 rnf' (a :< r) = rnf a >| rnf' (viewl r)
src/Data/Terminfo/Eval.hs+224−112
@@ -1,132 +1,244 @@
1+{-# LANGUAGE MagicHash #-}
1{-# LANGUAGE BangPatterns #-}
2{-# LANGUAGE BangPatterns #-}
3+{-# LANGUAGE ScopedTypeVariables #-}
4+{-# LANGUAGE RecordWildCards #-}
5+{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE NoMonomorphismRestriction #-}
6{-# LANGUAGE NoMonomorphismRestriction #-}
7+{-# LANGUAGE NamedFieldPuns #-}
3−{-# OPTIONS_GHC -funbox-strict-fields #-}
8+{-# OPTIONS_GHC -funbox-strict-fields -O #-}
4−{-# OPTIONS_HADDOCK hide #-}
5−
6−-- | Evaluates the paramaterized terminfo string capability with the
9+{- Evaluates the paramaterized terminfo string capability with the given parameters.
7−-- given parameters.
10+ -
11+ - todo: This can be greatly simplified.
12+ -}
8−module Data.Terminfo.Eval
13+module Data.Terminfo.Eval ( cap_expression_required_bytes
9− ( writeCapExpr
14+ , serialize_cap_expression
15+ , bytes_for_range
10− )
16+ )
11−where
17+ where
12−
18+
13−import Blaze.ByteString.Builder.Word
19+import Data.Marshalling
14−import Blaze.ByteString.Builder
15import Data.Terminfo.Parse
20import Data.Terminfo.Parse
16
21
17import Control.Monad.Identity
22import Control.Monad.Identity
23+import Control.Monad.Reader
18import Control.Monad.State.Strict
24import Control.Monad.State.Strict
19−import Control.Monad.Writer
25+import Control.Monad.Trans
20−
21−import Data.Bits ((.|.), (.&.), xor)
22−import Data.List
23
26
24−import qualified Data.Vector.Unboxed as Vector
27+import Data.Array.Unboxed
28+import Data.Bits ( (.|.), (.&.), xor )
29+import Data.List
25
30
26−-- | capability evaluator state
31+import GHC.Prim
27−data EvalState = EvalState
32+import GHC.Word
28− { evalStack :: ![CapParam]
29− , evalExpression :: !CapExpression
30− , evalParams :: ![CapParam]
31− }
32
33
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
34
36
35−pop :: Eval CapParam
37+pop :: MonadState [CapParam] m => m CapParam
36pop = do
38pop = do
37− s <- get
38− let v : stack' = evalStack s
39+ v : stack <- get
39− s' = s { evalStack = stack' }
40− put s'
40+ put stack
41 return v
41 return v
42
42
43−readParam :: Word -> Eval CapParam
43+read_param :: MonadReader (CapExpression, [CapParam]) m => Word -> m CapParam
44−readParam pn = do
44+read_param pn = do
45− !params <- evalParams <$> get
45+ (_,params) <- ask
46 return $! genericIndex params pn
46 return $! genericIndex params pn
47
47
48−push :: CapParam -> Eval ()
48+push :: MonadState [CapParam] m => CapParam -> m ()
49push !v = do
49push !v = do
50− s <- get
50+ stack <- get
51− let s' = s { evalStack = v : evalStack s }
52− put s'
51+ put (v : stack)
53−
52+
54−applyParamOps :: CapExpression -> [CapParam] -> [CapParam]
53+apply_param_ops :: CapExpression -> [CapParam] -> [CapParam]
55−applyParamOps cap params = foldl applyParamOp params (paramOps cap)
54+apply_param_ops cap params = foldl apply_param_op params (param_ops cap)
56−
55+
57−applyParamOp :: [CapParam] -> ParamOp -> [CapParam]
56+apply_param_op :: [CapParam] -> ParamOp -> [CapParam]
58−applyParamOp params IncFirstTwo = map (+ 1) params
57+apply_param_op params IncFirstTwo = map (+ 1) params
59−
58+
60−writeCapExpr :: CapExpression -> [CapParam] -> Write
59+-- | range is 0-based offset into cap_bytes and count
61−writeCapExpr cap params =
60+--
62− let params' = applyParamOps cap params
61+-- todo: The returned list is not assured to have a length st. length == count
63− s0 = EvalState [] cap params'
62+bytes_for_range :: CapExpression -> Word8 -> Word8 -> [Word8]
64− in snd $ runWriter (runStateT (writeCapOps (capOps cap)) s0)
63+bytes_for_range cap !offset !count
65−
64+ = take (fromEnum count)
66−writeCapOps :: CapOps -> Eval ()
65+ $ drop (fromEnum offset)
67−writeCapOps = mapM_ writeCapOp
66+ $ elems
68−
67+ $ cap_bytes cap
69−writeCapOp :: CapOp -> Eval ()
68+
70−writeCapOp (Bytes !offset !count) = do
69+cap_expression_required_bytes :: CapExpression -> [CapParam] -> Word
71− !cap <- evalExpression <$> get
70+cap_expression_required_bytes cap params =
72− let bytes = Vector.take count $ Vector.drop offset (capBytes cap)
71+ let params' = apply_param_ops cap params
73− Vector.forM_ bytes $ tell.writeWord8
72+ in fst $! runIdentity $ runStateT (runReaderT (cap_ops_required_bytes $ cap_ops cap) (cap, params')) []
74−writeCapOp DecOut = do
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
75 p <- pop
82 p <- pop
76− forM_ (show p) $ tell.writeWord8.toEnum.fromEnum
83+ return $ toEnum $ length $ show p
77−writeCapOp CharOut = do
84+cap_op_required_bytes CharOut = do
78− pop >>= tell.writeWord8.toEnum.fromEnum
85+ pop
86+ return 1
79−writeCapOp (PushParam pn) = do
87+cap_op_required_bytes (PushParam pn) = do
80− readParam pn >>= push
88+ read_param pn >>= push
89+ return 0
81−writeCapOp (PushValue v) = do
90+cap_op_required_bytes (PushValue v) = do
82 push v
91 push v
92+ return 0
83−writeCapOp (Conditional expr parts) = do
93+cap_op_required_bytes (Conditional expr parts) = do
84− writeCapOps expr
94+ c_expr <- cap_ops_required_bytes expr
85− writeContitionalParts parts
95+ c_parts <- cond_parts_required_bytes parts
96+ return $ c_expr + c_parts
86− where
97+ where
87− writeContitionalParts [] = return ()
98+ cond_parts_required_bytes [] = return 0
88− writeContitionalParts ((trueOps, falseOps) : falseParts) = do
99+ cond_parts_required_bytes ( (true_ops, false_ops) : false_parts ) = do
89 -- (man 5 terminfo)
100 -- (man 5 terminfo)
90− -- Usually the %? expr part pushes a value onto the stack,
101+ -- Usually the %? expr part pushes a value onto the stack, and %t pops it from the
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
102+ -- stack, testing if it is nonzero (true). If it is zero (false), control
93− -- (else) part.
103+ -- passes to the %e (else) part.
94 v <- pop
104 v <- pop
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
95− if v /= 0
197+ ptr'' <- if v /= 0
96− then writeCapOps trueOps
198+ then serialize_cap_ops ptr true_ops
97− else do
199+ else do
98− writeCapOps falseOps
200+ ptr' <- serialize_cap_ops ptr false_ops
99− writeContitionalParts falseParts
201+ serialize_cond_parts ptr' false_parts
100−
202+ return ptr''
203+
101−writeCapOp BitwiseOr = do
204+serialize_cap_op out_ptr BitwiseOr = do
102− v0 <- pop
205+ v_0 <- pop
103− v1 <- pop
206+ v_1 <- pop
104− push $ v0 .|. v1
207+ push $ v_0 .|. v_1
208+ return out_ptr
105−writeCapOp BitwiseAnd = do
209+serialize_cap_op out_ptr BitwiseAnd = do
106− v0 <- pop
210+ v_0 <- pop
107− v1 <- pop
211+ v_1 <- pop
108− push $ v0 .&. v1
212+ push $ v_0 .&. v_1
213+ return out_ptr
109−writeCapOp BitwiseXOr = do
214+serialize_cap_op out_ptr BitwiseXOr = do
110− v1 <- pop
215+ v_1 <- pop
111− v0 <- pop
216+ v_0 <- pop
112− push $ v0 `xor` v1
217+ push $ v_0 `xor` v_1
218+ return out_ptr
113−writeCapOp ArithPlus = do
219+serialize_cap_op out_ptr ArithPlus = do
114− v1 <- pop
220+ v_1 <- pop
115− v0 <- pop
221+ v_0 <- pop
116− push $ v0 + v1
222+ push $ v_0 + v_1
223+ return out_ptr
117−writeCapOp ArithMinus = do
224+serialize_cap_op out_ptr ArithMinus = do
118− v1 <- pop
225+ v_1 <- pop
119− v0 <- pop
226+ v_0 <- pop
120− push $ v0 - v1
227+ push $ v_0 - v_1
228+ return out_ptr
121−writeCapOp CompareEq = do
229+serialize_cap_op out_ptr CompareEq = do
122− v1 <- pop
230+ v_1 <- pop
123− v0 <- pop
231+ v_0 <- pop
124− push $ if v0 == v1 then 1 else 0
232+ push $ if v_0 == v_1 then 1 else 0
233+ return out_ptr
125−writeCapOp CompareLt = do
234+serialize_cap_op out_ptr CompareLt = do
126− v1 <- pop
235+ v_1 <- pop
127− v0 <- pop
236+ v_0 <- pop
128− push $ if v0 < v1 then 1 else 0
237+ push $ if v_0 < v_1 then 1 else 0
238+ return out_ptr
129−writeCapOp CompareGt = do
239+serialize_cap_op out_ptr CompareGt = do
130− v1 <- pop
240+ v_1 <- pop
131− v0 <- pop
241+ v_0 <- pop
132− push $ if v0 > v1 then 1 else 0
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 #-}
1{-# LANGUAGE BangPatterns #-}
2{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE CPP #-}
3+{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE NoMonomorphismRestriction #-}
4{-# LANGUAGE NoMonomorphismRestriction #-}
4−{-# LANGUAGE ScopedTypeVariables #-}
5+{-# LANGUAGE NamedFieldPuns #-}
5{-# OPTIONS_GHC -funbox-strict-fields -O #-}
6{-# OPTIONS_GHC -funbox-strict-fields -O #-}
6−{-# OPTIONS_HADDOCK hide #-}
7−
8−module Data.Terminfo.Parse
7+module Data.Terminfo.Parse ( module Data.Terminfo.Parse
9− ( module Data.Terminfo.Parse
10− , Text.Parsec.ParseError
8+ , Text.ParserCombinators.Parsec.ParseError
11− )
9+ )
12−where
10+ where
13
11
14import Control.Monad ( liftM )
12import Control.Monad ( liftM )
15−import Control.DeepSeq
13+import Control.Parallel.Strategies
16
14
17−#if !(MIN_VERSION_base(4,11,0))
15+import Data.Array.Unboxed
18−import Data.Semigroup (Semigroup(..))
16+import Data.Monoid
19−#endif
20import Data.Word
17import Data.Word
21−import qualified Data.Vector.Unboxed as Vector
22
18
23−import Numeric (showHex)
19+import Text.ParserCombinators.Parsec
24
20
25−import Text.Parsec
21+type BytesLength = Word8
22+type BytesOffset = Word8
23+type CapBytes = UArray Word8 Word8
26
24
27data CapExpression = CapExpression
25data CapExpression = CapExpression
28− { capOps :: !CapOps
26+ { cap_ops :: !CapOps
29− , capBytes :: !(Vector.Vector Word8)
27+ , cap_bytes :: !CapBytes
30− , sourceString :: !String
28+ , source_string :: !String
31− , paramCount :: !Int
29+ , param_count :: !Word
32− , paramOps :: !ParamOps
30+ , param_ops :: !ParamOps
33− } deriving (Eq)
31+ }
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 ""
43
32
44instance NFData CapExpression where
33instance NFData CapExpression where
45− rnf (CapExpression ops !_bytes !str !c !pOps)
34+ rnf (CapExpression ops !_bytes !_str !_c !_p_ops)
46− = rnf ops `seq` rnf str `seq` rnf c `seq` rnf pOps
35+ = rnf ops
47
36
48type CapParam = Word
37type CapParam = Word
49
38
50type CapOps = [CapOp]
39type CapOps = [CapOp]
51−data CapOp =
40+data CapOp =
52− Bytes !Int !Int -- offset count
41+ Bytes !BytesOffset !BytesLength
53 | DecOut | CharOut
42 | DecOut | CharOut
54− -- This stores a 0-based index to the parameter. However the
43+ -- This stores a 0-based index to the parameter. However the operation that implies this op is
55− -- operation that implies this op is 1-based
44+ -- 1-based
56 | PushParam !Word | PushValue !Word
45 | PushParam !Word | PushValue !Word
57− -- The conditional parts are the sequence of (%t expression, %e
46+ -- The conditional parts are the sequence of (%t expression, %e expression) pairs.
58− -- The expression) pairs. %e expression may be NOP
47+ -- The %e expression may be NOP
59− | Conditional
48+ | Conditional
60− { conditionalExpr :: !CapOps
49+ { conditional_expr :: !CapOps
61− , conditionalParts :: ![(CapOps, CapOps)]
50+ , conditional_parts :: ![(CapOps, CapOps)]
62 }
51 }
63 | BitwiseOr | BitwiseXOr | BitwiseAnd
52 | BitwiseOr | BitwiseXOr | BitwiseAnd
64 | ArithPlus | ArithMinus
53 | ArithPlus | ArithMinus
65 | CompareEq | CompareLt | CompareGt
54 | CompareEq | CompareLt | CompareGt
66− deriving (Show, Eq)
55+ deriving ( Show )
67
56
68instance NFData CapOp where
57instance NFData CapOp where
69− rnf (Bytes offset byteCount ) = rnf offset `seq` rnf byteCount
58+ rnf (Bytes offset c) = rnf offset >| rnf c
70− rnf (PushParam pn) = rnf pn
59+ rnf (PushParam !_pn) = ()
71− rnf (PushValue v) = rnf v
60+ rnf (PushValue !_v) = ()
72− rnf (Conditional cExpr cParts) = rnf cExpr `seq` rnf cParts
61+ rnf (Conditional c_expr c_parts) = rnf c_expr >| rnf c_parts
73− rnf BitwiseOr = ()
62+ rnf _ = ()
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 = ()
83
63
84type ParamOps = [ParamOp]
64type ParamOps = [ParamOp]
85data ParamOp =
65data ParamOp =
86 IncFirstTwo
66 IncFirstTwo
87− deriving (Show, Eq)
67+ deriving ( Show )
88−
68+
89−instance NFData ParamOp where
90− rnf IncFirstTwo = ()
91−
92−parseCapExpression :: String -> Either ParseError CapExpression
69+parse_cap_expression :: String -> Either ParseError CapExpression
93−parseCapExpression capString =
70+parse_cap_expression cap_string =
94− let v = runParser capExpressionParser
71+ let v = runParser cap_expression_parser
95− initialBuildState
72+ initial_build_state
96− "terminfo cap"
73+ "terminfo cap"
97− capString
74+ cap_string
98 in case v of
75 in case v of
99 Left e -> Left e
76 Left e -> Left e
100− Right buildResults -> Right $ constructCapExpression capString buildResults
77+ Right build_results ->
101−
102−constructCapExpression :: String -> BuildResults -> CapExpression
103−constructCapExpression capString buildResults =
104− let expr = CapExpression
78+ Right $! ( CapExpression
105− { capOps = outCapOps buildResults
79+ { cap_ops = out_cap_ops build_results
106− -- The cap bytes are the lower 8 bits of the input
80+ -- The cap bytes are the lower 8 bits of the input string's characters.
107− -- 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)
108− , capBytes = Vector.fromList $ map (toEnum.fromEnum) capString
83+ $ map (toEnum . fromEnum) cap_string
109− , sourceString = capString
84+ , source_string = cap_string
110− , paramCount = outParamCount buildResults
85+ , param_count = out_param_count build_results
111− , paramOps = outParamOps buildResults
86+ , param_ops = out_param_ops build_results
112− }
87+ }
113− in rnf expr `seq` expr
88+ `using` rnf
114−
89+ )
90+
115−type CapParser a = Parsec String BuildState a
91+type CapParser a = GenParser Char BuildState a
116−
92+
117−capExpressionParser :: CapParser BuildResults
93+cap_expression_parser :: CapParser BuildResults
118−capExpressionParser = do
94+cap_expression_parser = do
119− rs <- many $ paramEscapeParser <|> bytesOpParser
95+ rs <- many $ param_escape_parser <|> bytes_op_parser
120 return $ mconcat rs
96 return $ mconcat rs
121
97
122−paramEscapeParser :: CapParser BuildResults
98+param_escape_parser :: CapParser BuildResults
123−paramEscapeParser = do
99+param_escape_parser = do
124− _ <- char '%'
100+ char '%'
125− incOffset 1
101+ inc_offset 1
126− literalPercentParser <|> paramOpParser
102+ literal_percent_parser <|> param_op_parser
127−
103+
128−literalPercentParser :: CapParser BuildResults
104+literal_percent_parser :: CapParser BuildResults
129−literalPercentParser = do
105+literal_percent_parser = do
130− _ <- char '%'
106+ char '%'
131− startOffset <- nextOffset <$> getState
107+ start_offset <- getState >>= return . next_offset
132− incOffset 1
108+ inc_offset 1
133− return $ BuildResults 0 [Bytes startOffset 1] []
109+ return $ BuildResults 0 [Bytes start_offset 1] []
134−
110+
135−paramOpParser :: CapParser BuildResults
111+param_op_parser :: CapParser BuildResults
136−paramOpParser
112+param_op_parser
137− = incrementOpParser
113+ = increment_op_parser
138− <|> pushOpParser
114+ <|> push_op_parser
139− <|> decOutParser
115+ <|> dec_out_parser
140− <|> charOutParser
116+ <|> char_out_parser
141− <|> conditionalOpParser
117+ <|> conditional_op_parser
142− <|> bitwiseOpParser
118+ <|> bitwise_op_parser
143− <|> arithOpParser
119+ <|> arith_op_parser
144− <|> literalIntOpParser
120+ <|> literal_int_op_parser
145− <|> compareOpParser
121+ <|> compare_op_parser
146− <|> charConstParser
122+ <|> char_const_parser
147−
123+
148−incrementOpParser :: CapParser BuildResults
124+increment_op_parser :: CapParser BuildResults
149−incrementOpParser = do
125+increment_op_parser = do
150− _ <- char 'i'
126+ char 'i'
151− incOffset 1
127+ inc_offset 1
152 return $ BuildResults 0 [] [ IncFirstTwo ]
128 return $ BuildResults 0 [] [ IncFirstTwo ]
153
129
154−pushOpParser :: CapParser BuildResults
130+push_op_parser :: CapParser BuildResults
155−pushOpParser = do
131+push_op_parser = do
156− _ <- char 'p'
132+ char 'p'
157− paramN <- read . pure <$> digit
133+ param_n <- digit >>= return . (\d -> read [d])
158− incOffset 2
134+ inc_offset 2
159− return $ BuildResults (fromEnum paramN) [PushParam $ paramN - 1] []
135+ return $ BuildResults param_n [ PushParam $ param_n - 1 ] []
160−
136+
161−decOutParser :: CapParser BuildResults
137+dec_out_parser :: CapParser BuildResults
162−decOutParser = do
138+dec_out_parser = do
163− _ <- char 'd'
139+ char 'd'
164− incOffset 1
140+ inc_offset 1
165 return $ BuildResults 0 [ DecOut ] []
141 return $ BuildResults 0 [ DecOut ] []
166
142
167−charOutParser :: CapParser BuildResults
143+char_out_parser :: CapParser BuildResults
168−charOutParser = do
144+char_out_parser = do
169− _ <- char 'c'
145+ char 'c'
170− incOffset 1
146+ inc_offset 1
171 return $ BuildResults 0 [ CharOut ] []
147 return $ BuildResults 0 [ CharOut ] []
172
148
173−conditionalOpParser :: CapParser BuildResults
149+conditional_op_parser :: CapParser BuildResults
174−conditionalOpParser = do
150+conditional_op_parser = do
175− _ <- char '?'
151+ char '?'
176− incOffset 1
152+ inc_offset 1
177− condPart <- manyExpr conditionalTrueParser
153+ cond_part <- many_expr conditional_true_parser
178− parts <- manyP
154+ parts <- many_p
179− ( do
155+ ( do
180− truePart <- manyExpr $ choice [ try $ lookAhead conditionalEndParser
156+ true_part <- many_expr $ choice [ try $ lookAhead conditional_end_parser
181− , conditionalFalseParser
157+ , conditional_false_parser
182− ]
158+ ]
183− falsePart <- manyExpr $ choice [ try $ lookAhead conditionalEndParser
159+ false_part <- many_expr $ choice [ try $ lookAhead conditional_end_parser
184− , conditionalTrueParser
160+ , conditional_true_parser
185− ]
161+ ]
186− return ( truePart, falsePart )
162+ return ( true_part, false_part )
187− )
163+ )
188− conditionalEndParser
164+ conditional_end_parser
189−
165+
190− let trueParts = map fst parts
166+ let true_parts = map fst parts
191− falseParts = map snd parts
167+ false_parts = map snd parts
192− BuildResults n cond condParamOps = condPart
168+ BuildResults n cond cond_param_ops = cond_part
193−
169+
194− let n' = maximum $ n : map outParamCount trueParts
170+ let n' = maximum $ n : map out_param_count true_parts
195− n'' = maximum $ n' : map outParamCount falseParts
171+ n'' = maximum $ n' : map out_param_count false_parts
196−
172+
197− let trueOps = map outCapOps trueParts
173+ let true_ops = map out_cap_ops true_parts
198− falseOps = map outCapOps falseParts
174+ false_ops = map out_cap_ops false_parts
199− condParts = zip trueOps falseOps
175+ cond_parts = zip true_ops false_ops
200−
176+
201− let trueParamOps = mconcat $ map outParamOps trueParts
177+ let true_param_ops = mconcat $ map out_param_ops true_parts
202− falseParamOps = mconcat $ map outParamOps falseParts
178+ false_param_ops = mconcat $ map out_param_ops false_parts
203− pOps = mconcat [condParamOps, trueParamOps, falseParamOps]
179+ p_ops = mconcat [cond_param_ops, true_param_ops, false_param_ops]
204−
180+
205− return $ BuildResults n'' [ Conditional cond condParts ] pOps
181+ return $ BuildResults n'' [ Conditional cond cond_parts ] p_ops
206−
182+
207− where
183+ where
208− manyP !p !end = choice
184+ many_p !p !end = choice
209 [ try end >> return []
185 [ try end >> return []
210− , do !v <- p
186+ , do !v <- p
211− !vs <- manyP p end
187+ !vs <- many_p p end
212 return $! v : vs
188 return $! v : vs
213 ]
189 ]
214− manyExpr end = liftM mconcat $ manyP ( paramEscapeParser <|> bytesOpParser ) end
190+ many_expr end = liftM mconcat $ many_p ( param_escape_parser <|> bytes_op_parser ) end
215−
191+
216−conditionalTrueParser :: CapParser ()
192+conditional_true_parser :: CapParser ()
217−conditionalTrueParser = do
193+conditional_true_parser = do
218− _ <- string "%t"
194+ string "%t"
219− incOffset 2
195+ inc_offset 2
220−
196+
221−conditionalFalseParser :: CapParser ()
197+conditional_false_parser :: CapParser ()
222−conditionalFalseParser = do
198+conditional_false_parser = do
223− _ <- string "%e"
199+ string "%e"
224− incOffset 2
200+ inc_offset 2
225−
201+
226−conditionalEndParser :: CapParser ()
202+conditional_end_parser :: CapParser ()
227−conditionalEndParser = do
203+conditional_end_parser = do
228− _ <- string "%;"
204+ string "%;"
229− incOffset 2
205+ inc_offset 2
230−
206+
231−bitwiseOpParser :: CapParser BuildResults
207+bitwise_op_parser :: CapParser BuildResults
232−bitwiseOpParser
208+bitwise_op_parser
233− = bitwiseOrParser
209+ = bitwise_or_parser
234− <|> bitwiseAndParser
210+ <|> bitwise_and_parser
235− <|> bitwiseXorParser
211+ <|> bitwise_xor_parser
236−
212+
237−bitwiseOrParser :: CapParser BuildResults
213+bitwise_or_parser :: CapParser BuildResults
238−bitwiseOrParser = do
214+bitwise_or_parser = do
239− _ <- char '|'
215+ char '|'
240− incOffset 1
216+ inc_offset 1
241 return $ BuildResults 0 [ BitwiseOr ] [ ]
217 return $ BuildResults 0 [ BitwiseOr ] [ ]
242
218
243−bitwiseAndParser :: CapParser BuildResults
219+bitwise_and_parser :: CapParser BuildResults
244−bitwiseAndParser = do
220+bitwise_and_parser = do
245− _ <- char '&'
221+ char '&'
246− incOffset 1
222+ inc_offset 1
247 return $ BuildResults 0 [ BitwiseAnd ] [ ]
223 return $ BuildResults 0 [ BitwiseAnd ] [ ]
248
224
249−bitwiseXorParser :: CapParser BuildResults
225+bitwise_xor_parser :: CapParser BuildResults
250−bitwiseXorParser = do
226+bitwise_xor_parser = do
251− _ <- char '^'
227+ char '^'
252− incOffset 1
228+ inc_offset 1
253 return $ BuildResults 0 [ BitwiseXOr ] [ ]
229 return $ BuildResults 0 [ BitwiseXOr ] [ ]
254
230
255−arithOpParser :: CapParser BuildResults
231+arith_op_parser :: CapParser BuildResults
256−arithOpParser
232+arith_op_parser
257− = plusOp
233+ = plus_op
258− <|> minusOp
234+ <|> minus_op
259 where
235 where
260− plusOp = do
236+ plus_op = do
261− _ <- char '+'
237+ char '+'
262− incOffset 1
238+ inc_offset 1
263 return $ BuildResults 0 [ ArithPlus ] [ ]
239 return $ BuildResults 0 [ ArithPlus ] [ ]
264− minusOp = do
240+ minus_op = do
265− _ <- char '-'
241+ char '-'
266− incOffset 1
242+ inc_offset 1
267 return $ BuildResults 0 [ ArithMinus ] [ ]
243 return $ BuildResults 0 [ ArithMinus ] [ ]
268
244
269−literalIntOpParser :: CapParser BuildResults
245+literal_int_op_parser :: CapParser BuildResults
270−literalIntOpParser = do
246+literal_int_op_parser = do
271− _ <- char '{'
247+ char '{'
272− incOffset 1
248+ inc_offset 1
273− nStr <- many1 digit
249+ n_str <- many1 digit
274− incOffset $ toEnum $ length nStr
250+ inc_offset $ toEnum $ length n_str
275− let n :: Word = read nStr
251+ let n :: Word = read n_str
276− _ <- char '}'
252+ char '}'
277− incOffset 1
253+ inc_offset 1
278 return $ BuildResults 0 [ PushValue n ] [ ]
254 return $ BuildResults 0 [ PushValue n ] [ ]
279
255
280−compareOpParser :: CapParser BuildResults
256+compare_op_parser :: CapParser BuildResults
281−compareOpParser
257+compare_op_parser
282− = compareEqOp
258+ = compare_eq_op
283− <|> compareLtOp
259+ <|> compare_lt_op
284− <|> compareGtOp
260+ <|> compare_gt_op
285 where
261 where
286− compareEqOp = do
262+ compare_eq_op = do
287− _ <- char '='
263+ char '='
288− incOffset 1
264+ inc_offset 1
289 return $ BuildResults 0 [ CompareEq ] [ ]
265 return $ BuildResults 0 [ CompareEq ] [ ]
290− compareLtOp = do
266+ compare_lt_op = do
291− _ <- char '<'
267+ char '<'
292− incOffset 1
268+ inc_offset 1
293 return $ BuildResults 0 [ CompareLt ] [ ]
269 return $ BuildResults 0 [ CompareLt ] [ ]
294− compareGtOp = do
270+ compare_gt_op = do
295− _ <- char '>'
271+ char '>'
296− incOffset 1
272+ inc_offset 1
297 return $ BuildResults 0 [ CompareGt ] [ ]
273 return $ BuildResults 0 [ CompareGt ] [ ]
298
274
299−bytesOpParser :: CapParser BuildResults
275+bytes_op_parser :: CapParser BuildResults
300−bytesOpParser = do
276+bytes_op_parser = do
301 bytes <- many1 $ satisfy (/= '%')
277 bytes <- many1 $ satisfy (/= '%')
302− startOffset <- nextOffset <$> getState
278+ start_offset <- getState >>= return . next_offset
303− let !c = length bytes
279+ let !c = toEnum $ length bytes
304 !s <- getState
280 !s <- getState
305− let s' = s { nextOffset = startOffset + c }
281+ let s' = s { next_offset = start_offset + c }
306 setState s'
282 setState s'
307− return $ BuildResults 0 [Bytes startOffset c] []
283+ return $ BuildResults 0 [Bytes start_offset c] []
308−
284+
309−charConstParser :: CapParser BuildResults
285+char_const_parser :: CapParser BuildResults
310−charConstParser = do
286+char_const_parser = do
311− _ <- char '\''
287+ char '\''
312− charValue <- liftM (toEnum . fromEnum) anyChar
288+ char_value <- liftM (toEnum . fromEnum) anyChar
313− _ <- char '\''
289+ char '\''
314− incOffset 3
290+ inc_offset 3
315− return $ BuildResults 0 [ PushValue charValue ] [ ]
291+ return $ BuildResults 0 [ PushValue char_value ] [ ]
316−
292+
317−data BuildState = BuildState
293+data BuildState = BuildState
318− { nextOffset :: Int
294+ { next_offset :: Word8
319− }
295+ }
320−
296+
321−incOffset :: Int -> CapParser ()
297+inc_offset :: Word8 -> CapParser ()
322−incOffset n = do
298+inc_offset n = do
323 s <- getState
299 s <- getState
324− let s' = s { nextOffset = nextOffset s + n }
300+ let s' = s { next_offset = next_offset s + n }
325 setState s'
301 setState s'
326
302
327−initialBuildState :: BuildState
303+initial_build_state :: BuildState
328−initialBuildState = BuildState 0
304+initial_build_state = BuildState 0
329
305
330data BuildResults = BuildResults
306data BuildResults = BuildResults
331− { outParamCount :: !Int
307+ { out_param_count :: !Word
332− , outCapOps :: !CapOps
308+ , out_cap_ops :: !CapOps
333− , outParamOps :: !ParamOps
309+ , out_param_ops :: !ParamOps
334 }
310 }
335
311
336−instance Semigroup BuildResults where
312+instance Monoid BuildResults where
313+ mempty = BuildResults 0 [] []
337− v0 <> v1
314+ v0 `mappend` v1
338 = BuildResults
315 = BuildResults
339− { outParamCount = outParamCount v0 `max` outParamCount v1
316+ { out_param_count = (out_param_count v0) `max` (out_param_count v1)
340− , outCapOps = outCapOps v0 <> outCapOps v1
317+ , out_cap_ops = (out_cap_ops v0) `mappend` (out_cap_ops v1)
341− , outParamOps = outParamOps v0 <> outParamOps v1
318+ , out_param_ops = (out_param_ops v0) `mappend` (out_param_ops v1)
342 }
319 }
343
320
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 #-}
1+-- Copyright 2009 Corey O'Connor
2−{-# LANGUAGE ScopedTypeVariables #-}
2+{-# LANGUAGE ForeignFunctionInterface, BangPatterns, UnboxedTuples #-}
3−
3+{-# CFILES gwinsz.c #-}
4−-- | Vty provides interfaces for both terminal input and terminal
4+
5−-- output.
5+-- Good sources of documentation for terminal programming are:
6−--
6+-- vt100 control sequences: http://vt100.net/docs/vt100-ug/chapter3.html#S3.3.3
7−-- - Input to the terminal is provided to the Vty application as a
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(..)
8−-- sequence of 'Event's.
18+ , Event(..)
9−--
19+ )
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
20+ where
48−
21+
49−import Graphics.Vty.Config
22+
50−import Graphics.Vty.Input
23+import Graphics.Vty.Terminal
51−import Graphics.Vty.Input.Events
52−import Graphics.Vty.Output
53−import Graphics.Vty.Output.Interface
54import Graphics.Vty.Picture
24import Graphics.Vty.Picture
55−import Graphics.Vty.Image
25+import Graphics.Vty.DisplayRegion
56−import Graphics.Vty.Attributes
57−import Graphics.Vty.UnicodeWidthTable.IO
58−import Graphics.Vty.UnicodeWidthTable.Install
26+import Graphics.Vty.LLInput
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
66
27
67import Data.IORef
28import Data.IORef
68−#if !(MIN_VERSION_base(4,11,0))
69−import Data.Semigroup ((<>))
70−#endif
71
29
72−-- | A Vty value represents a handle to the Vty library that the
30+import Data.Maybe ( maybe )
73−-- application must create in order to use Vty.
31+
74−--
32+import qualified System.Console.Terminfo as Terminfo
75−-- The use of Vty typically follows this process:
33+import System.IO
76−--
34+
77−-- 1. Initialize vty with 'mkVty' (this takes control of the terminal).
35+-- | The main object. At most one should be created.
78−--
36+-- An alternative is to use unsafePerformIO to automatically create a singleton Vty instance when
79−-- 2. Use 'update' to display a picture.
37+-- required.
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−--
38+--
88−-- Operations on Vty handles are not thread-safe.
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.
89−data Vty =
44+data Vty = Vty
90− Vty { update :: Picture -> IO ()
91− -- ^ Outputs the given 'Picture'.
45+ { -- | Outputs the given Picture. Equivalent to output_picture applied to a display context
92− , nextEvent :: IO Event
46+ -- implicitly managed by Vty.
93− -- ^ Return the next 'Event' or block until one becomes
47+ update :: Picture -> IO ()
94− -- available.
48+ -- | Get one Event object, blocking if necessary.
95− , nextEventNonblocking :: IO (Maybe Event)
49+ , next_event :: IO Event
96− -- ^ Non-blocking version of 'nextEvent'.
50+ -- | Handle to the terminal interface. See `Terminal`
97− , inputIface :: Input
51+ --
98− -- ^ The input interface. See 'Input'.
52+ -- The use of Vty typically follows this process:
99− , outputIface :: Output
53+ --
100− -- ^ The output interface. See 'Output'.
54+ -- 0. initialize vty
101− , refresh :: IO ()
55+ --
102− -- ^ Refresh the display. If other programs output to the
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
103− -- terminal and mess up the display then the application might
68+ -- other program might output to the terminal and mess the display. In that case the user
104− -- want to force a refresh using this function.
69+ -- might want to force a refresh.
105− , shutdown :: IO ()
70+ , refresh :: IO ()
106− -- ^ Clean up after vty. A call to this function is necessary to
71+ -- | Clean up after vty.
107− -- cleanly restore the terminal state before application exit.
72+ , shutdown :: IO ()
108− -- The above methods will throw an exception if executed after
109− -- this is executed. Idempotent.
110− , isShutdown :: IO Bool
111− }
73+ }
112−
74+
75+-- | Set up the state object for using vty. At most one state object should be
113−-- | Create a Vty handle. At most one handle should be created at a time
76+-- created at a time.
114−-- for a given terminal device.
77+mkVty :: IO Vty
115−--
78+mkVty = mkVtyEscDelay 0
116−-- The specified configuration is added to the the configuration
79+
117−-- loaded by 'userConfig' with the 'userConfig' configuration taking
80+mkVtyEscDelay :: Int -> IO Vty
118−-- precedence. See "Graphics.Vty.Config".
81+mkVtyEscDelay escDelay = do
119−--
82+ term_info <- Terminfo.setupTermFromEnv
120−-- For most applications @mkVty defaultConfig@ is sufficient.
83+ t <- terminal_handle
121−mkVty :: Config -> IO Vty
84+ reserve_display t
122−mkVty appConfig = do
85+ (kvar, endi) <- initTermInput escDelay term_info
123− config <- (<> appConfig) <$> userConfig
86+ intMkVty kvar ( endi >> release_display t >> release_terminal t ) t
124−
87+
125− when (allowCustomUnicodeWidthTables config /= Just False) $
88+intMkVty :: IO Event -> IO () -> TerminalHandle -> IO Vty
126− installCustomWidthTable config
89+intMkVty kvar fend t = do
127−
90+ last_pic_ref <- newIORef undefined
128− input <- inputForConfig config
91+ last_update_ref <- newIORef Nothing
129− out <- outputForConfig config
92+
130− internalMkVty input out
93+ let inner_update in_pic = do
131−
94+ b <- display_bounds t
132−installCustomWidthTable :: Config -> IO ()
95+ mlast_update <- readIORef last_update_ref
133−installCustomWidthTable c = do
96+ update_data <- case mlast_update of
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
188 Nothing -> do
97 Nothing -> do
189− dc <- displayContext out b
98+ d <- display_context t b
190− outputPicture dc inPic
99+ output_picture d in_pic
191− return (b, dc)
100+ return (b, d)
192− Just (lastBounds, lastContext) -> do
101+ Just (last_bounds, last_context) -> do
193− if b /= lastBounds
102+ if b /= last_bounds
194 then do
103 then do
195− dc <- displayContext out b
104+ d <- display_context t b
196− outputPicture dc inPic
105+ output_picture d in_pic
197− return (b, dc)
106+ return (b, d)
198 else do
107 else do
199− outputPicture lastContext inPic
108+ output_picture last_context in_pic
200− return (b, lastContext)
109+ return (b, last_context)
201− writeIORef lastUpdateRef $ Just updateData
110+ writeIORef last_update_ref $ Just update_data
202− writeIORef lastPicRef $ Just inPic
111+ writeIORef last_pic_ref $ Just in_pic
203−
112+
204− let innerRefresh = do
113+ let inner_refresh
205− writeIORef lastUpdateRef Nothing
114+ = writeIORef last_update_ref Nothing
206− bounds <- displayBounds out
207− dc <- displayContext out bounds
208− writeIORef (assumedStateRef $ contextDevice dc) initialAssumedState
209− mPic <- readIORef lastPicRef
115+ >> readIORef last_pic_ref
210− maybe (return ()) innerUpdate mPic
116+ >>= maybe ( return () ) ( \pic -> inner_update pic )
211−
117+
212− let mkResize = uncurry EvResize <$> displayBounds out
213−
214− translateInternalEvent ResumeAfterSignal = mkResize
215− translateInternalEvent (InputEvent e) = return e
216−
217− gkey = do
118+ let gkey = do k <- kvar
218− e <- atomically $ readTChan $ _eventChannel input
219− translateInternalEvent e
220− gkey' = do
221− mEv <- atomically $ tryReadTChan $ _eventChannel input
222− case mEv of
119+ case k of
223− Just e -> Just <$> translateInternalEvent e
120+ (EvResize _ _) -> inner_refresh
121+ >> display_bounds t
122+ >>= return . ( \(DisplayRegion w h)
123+ -> EvResize (fromEnum w) (fromEnum h)
124+ )
224− Nothing -> return Nothing
125+ _ -> return k
225−
126+
226− return $ Vty { update = innerUpdate
127+ return $ Vty { update = inner_update
227− , nextEvent = gkey
128+ , next_event = gkey
228− , nextEventNonblocking = gkey'
129+ , terminal = t
229− , inputIface = input
130+ , refresh = inner_refresh
230− , outputIface = out
231− , refresh = innerRefresh
232− , shutdown = shutdownIo
131+ , shutdown = fend
233− , isShutdown = shutdownStatus
234 }
132 }
235
133
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 #-}
1+-- Copyright 2009 Corey O'Connor
2−{-# LANGUAGE DeriveGeneric #-}
3−{-# LANGUAGE DeriveAnyClass #-}
4−
5−-- | Display attributes
2+-- 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.,
18--
3--
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
4+-- For efficiency this can be, in the future, 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
5+-- first divided into 4 groups of 8 bits where:
86−-- what action should be taken with regards to the other groups.
6+-- The first group codes what action should be taken with regards to the other groups.
87-- XXYYZZ__
7-- XXYYZZ__
88-- XX - style action
8-- XX - style action
89-- 00 => reset to default
9-- 00 => reset to default
@@ -100,53 +20,151 @@ data Attr = Attr
100-- __ - unused
20-- __ - unused
101--
21--
102-- Next is the style flags
22-- Next is the style flags
103−-- SURBDOI_
23+-- SURBDO__
104-- S - standout
24-- S - standout
105-- U - underline
25-- U - underline
106-- R - reverse video
26-- R - reverse video
107-- B - blink
27-- B - blink
108-- D - dim
28-- D - dim
109-- O - bold
29-- O - bold
110−-- I - italic
111−-- _ - unused
30+-- __ - unused
112--
31--
113-- Then the foreground color encoded into 8 bits.
32-- Then the foreground color encoded into 8 bits.
114-- Then the background color encoded into 8 bits.
33-- Then the background color encoded into 8 bits.
34+--
35+module Graphics.Vty.Attributes
36+ where
115
37
116−-- | Specifies the display attributes such that the final style and
38+import Data.Bits
117−-- color values do not depend on the previously applied display
39+import Data.Monoid
118−-- attribute. The display attributes can still depend on the terminal's
40+import Data.Word
119−-- default colors (unfortunately).
41+
120−data FixedAttr = FixedAttr
42+-- | A display attribute defines the Color and Style of all the characters rendered after the
121− { fixedStyle :: !Style
43+-- attribute is applied.
122− , fixedForeColor :: !(Maybe Color)
44+--
123− , fixedBackColor :: !(Maybe Color)
45+-- At most 256 colors, picked from a 240 and 16 color palette, are possible for the background and
124− , fixedURL :: !(Maybe Text)
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)
125 } deriving ( Eq, Show )
52 } deriving ( Eq, Show )
126
53
127−-- | The style and color attributes can either be the terminal defaults.
54+instance Monoid Attr where
128−-- Or be equivalent to the previously applied style. Or be a specific
55+ mempty = Attr mempty mempty mempty
129−-- value.
56+ mappend attr_0 attr_1 =
130−data MaybeDefault v = Default | KeepCurrent | SetTo !v
57+ Attr ( attr_style attr_0 `mappend` attr_style attr_1 )
131− deriving (Eq, Read, Show)
58+ ( attr_fore_color attr_0 `mappend` attr_fore_color attr_1 )
59+ ( attr_back_color attr_0 `mappend` attr_back_color attr_1 )
132
60
133−instance (NFData v) => NFData (MaybeDefault v) where
61+-- | Specifies the display attributes such that the final style and color values do not depend on
134− rnf Default = ()
62+-- the previously applied display attribute. The display attributes can still depend on the
135− rnf KeepCurrent = ()
63+-- terminal's default colors (unfortunately).
136− rnf (SetTo v) = rnf v
64+data FixedAttr = FixedAttr
65+ { fixed_style :: !Style
66+ , fixed_fore_color :: !(Maybe Color)
67+ , fixed_back_color :: !(Maybe Color)
68+ } deriving ( Eq, Show )
137
69
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+
138−-- | Styles are represented as an 8 bit word. Each bit in the word is 1
157+-- | Styles are represented as an 8 bit word. Each bit in the word is 1 if the style attribute
139−-- if the style attribute assigned to that bit should be applied and 0
140−-- if the style attribute should not be applied.
158+-- assigned to that bit should be applied and 0 if the style attribute should not be applied.
141type Style = Word8
159type Style = Word8
142
160
143−-- | Valid style attributes include:
161+-- | The 6 possible style attributes:
144--
162--
145-- * standout
163-- * standout
146--
164--
147-- * underline
165-- * underline
148--
166--
149−-- * reverseVideo
167+-- * reverse_video
150--
168--
151-- * blink
169-- * blink
152--
170--
@@ -154,75 +172,54 @@ type Style = Word8
154--
172--
155-- * bold/bright
173-- * bold/bright
156--
174--
157−-- * italic
158−--
159−-- * strikethrough (via the smxx/rmxx terminfo capabilities)
160−--
161−-- (The invisible, protect, and altcharset display attributes some
175+-- ( The invisible, protect, and altcharset display attributes some terminals support are not
162−-- terminals support are not supported via VTY.)
176+-- supported via VTY.)
163−standout, underline, reverseVideo, blink, dim, bold, italic, strikethrough :: Style
177+standout, underline, reverse_video, blink, dim, bold :: Style
164standout = 0x01
178standout = 0x01
165underline = 0x02
179underline = 0x02
166−reverseVideo = 0x04
180+reverse_video = 0x04
167blink = 0x08
181blink = 0x08
168dim = 0x10
182dim = 0x10
169bold = 0x20
183bold = 0x20
170−italic = 0x40
171−strikethrough = 0x80
172
184
173−defaultStyleMask :: Style
185+default_style_mask :: Style
174−defaultStyleMask = 0x00
186+default_style_mask = 0x00
175
187
176−styleMask :: Attr -> Word8
188+style_mask :: Attr -> Word8
177−styleMask attr
189+style_mask attr
178− = case attrStyle attr of
190+ = case attr_style attr of
179 Default -> 0
191 Default -> 0
180 KeepCurrent -> 0
192 KeepCurrent -> 0
181 SetTo v -> v
193 SetTo v -> v
182
194
183-- | true if the given Style value has the specified Style set.
195-- | true if the given Style value has the specified Style set.
184−hasStyle :: Style -> Style -> Bool
196+has_style :: Style -> Style -> Bool
185−hasStyle s bitMask = ( s .&. bitMask ) /= 0
197+has_style s bit_mask = ( s .&. bit_mask ) /= 0
186
198
187-- | Set the foreground color of an `Attr'.
199-- | Set the foreground color of an `Attr'.
188−withForeColor :: Attr -> Color -> Attr
200+with_fore_color :: Attr -> Color -> Attr
189−withForeColor attr c = attr { attrForeColor = SetTo c }
201+with_fore_color attr c = attr { attr_fore_color = SetTo c }
190
202
191-- | Set the background color of an `Attr'.
203-- | Set the background color of an `Attr'.
192−withBackColor :: Attr -> Color -> Attr
204+with_back_color :: Attr -> Color -> Attr
193−withBackColor attr c = attr { attrBackColor = SetTo c }
205+with_back_color attr c = attr { attr_back_color = SetTo c }
194
206
195-- | Add the given style attribute
207-- | Add the given style attribute
196−withStyle :: Attr -> Style -> Attr
208+with_style :: Attr -> Style -> Attr
197−withStyle attr 0 = attr
209+with_style attr style_flag = attr { attr_style = SetTo $ style_mask attr .|. style_flag }
198−withStyle attr styleFlag = attr { attrStyle = SetTo $ styleMask attr .|. styleFlag }
210+
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
211+-- | Sets the style, background color and foreground color to the default values for the terminal.
215−-- default values for the terminal. There is no easy way to determine
216−-- what the default background and foreground colors are.
212+-- There is no easy way to determine what the default background and foreground colors are.
217−defAttr :: Attr
213+def_attr :: Attr
218−defAttr = Attr Default Default Default Default
214+def_attr = Attr Default Default Default
219−
215+
220−-- | Keeps the style, background color and foreground color that was
216+-- | Keeps the style, background color and foreground color that was previously set. Used to
221−-- previously set. Used to override some part of the previous style.
217+-- override some part of the previous style.
222−--
218+--
223−-- EG: current_style `withForeColor` brightMagenta
219+-- EG: current_style `with_fore_color` bright_magenta
224−--
220+--
225−-- Would be the currently applied style (be it underline, bold, etc) but
221+-- Would be the currently applied style (be it underline, bold, etc) but with the foreground color
226−-- with the foreground color set to brightMagenta.
222+-- set to bright_magenta.
227−currentAttr :: Attr
223+current_attr :: Attr
228−currentAttr = Attr KeepCurrent KeepCurrent KeepCurrent KeepCurrent
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
1+-- Copyright 2009 Corey O'Connor
2+{-# LANGUAGE ScopedTypeVariables #-}
2−module Graphics.Vty.Debug
3+module Graphics.Vty.Debug ( module Graphics.Vty.Debug
3− ( module Graphics.Vty.Debug
4− , module Graphics.Vty.Debug.Image
5− )
4+ )
6where
5where
7
6
8import Graphics.Vty.Attributes
7import Graphics.Vty.Attributes
9−import Graphics.Vty.Image (DisplayRegion)
8+import Graphics.Vty.Image
10−import Graphics.Vty.Debug.Image
9+import Graphics.Vty.Picture
11import Graphics.Vty.Span
10import Graphics.Vty.Span
11+import Graphics.Vty.DisplayRegion
12
12
13−import qualified Data.Vector as Vector
13+import Control.Monad
14+import Data.Array
15+import Data.Word
14
16
15−rowOpsAffectedColumns :: DisplayOps -> [Int]
17+instance Show SpanOpSequence where
16−rowOpsAffectedColumns ops
18+ show (SpanOpSequence _ row_ops)
17− = Vector.toList $ Vector.map spanOpsAffectedColumns ops
19+ = concat $ ["{ "] ++ map (\ops -> show ops ++ "; " ) (elems row_ops) ++ [" }"]
18
20
19−allSpansHaveWidth :: DisplayOps -> Int -> Bool
21+instance Show SpanOp where
20−allSpansHaveWidth ops expected
22+ show (AttributeChange attr) = show attr
21− = all (== expected) $ Vector.toList $ Vector.map spanOpsAffectedColumns ops
23+ show (TextSpan ow cw _) = "TextSpan " ++ show ow ++ " " ++ show cw
22
24
23−spanOpsAffectedRows :: DisplayOps -> Int
25+row_ops_effected_columns :: SpanOpSequence -> [Word]
24−spanOpsAffectedRows ops
26+row_ops_effected_columns spans
25− = toEnum $ length (filter (not . null . Vector.toList) (Vector.toList ops))
27+ = map span_ops_effected_columns (elems $ row_ops spans)
26
28
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+
27type SpanConstructLog = [SpanConstructEvent]
36type SpanConstructLog = [SpanConstructEvent]
28data SpanConstructEvent = SpanSetAttr Attr
37data SpanConstructEvent = SpanSetAttr Attr
29
38
30−isSetAttr :: Attr -> SpanConstructEvent -> Bool
39+is_set_attr :: Attr -> SpanConstructEvent -> Bool
31−isSetAttr expectedAttr (SpanSetAttr inAttr)
40+is_set_attr expected_attr (SpanSetAttr in_attr)
32− | inAttr == expectedAttr = True
41+ | in_attr == expected_attr = True
33−isSetAttr _attr _event = False
42+is_set_attr _attr _event = False
34
43
35−data MockWindow = MockWindow Int Int
44+data DebugWindow = DebugWindow Word Word
36 deriving (Show, Eq)
45 deriving (Show, Eq)
37
46
38−regionForWindow :: MockWindow -> DisplayRegion
47+region_for_window :: DebugWindow -> DisplayRegion
39−regionForWindow (MockWindow w h) = (w,h)
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
2{-# LANGUAGE BangPatterns #-}
1{-# LANGUAGE BangPatterns #-}
3−{-# LANGUAGE CPP #-}
4−
5−module Graphics.Vty.DisplayAttributes where
2+module Graphics.Vty.DisplayAttributes
3+ where
6
4
7import Graphics.Vty.Attributes
5import Graphics.Vty.Attributes
8
6
9−import Data.Bits ((.&.))
7+import Data.Bits ( (.&.) )
10−import Data.ByteString (ByteString)
8+import Data.Monoid ( Monoid(..), mconcat )
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)
16
9
17−-- | Given the previously applied display attributes as a FixedAttr and
10+-- | Given the previously applied display attributes as a FixedAttr and the current display
18−-- the current display attributes as an Attr produces a FixedAttr that
19−-- represents the current display attributes. This is done by using the
11+-- attributes as an Attr produces a FixedAttr that represents the current display attributes. This
20−-- previously applied display attributes to remove the "KeepCurrent"
12+-- is done by using the previously applied display attributes to remove the "KeepCurrent"
21-- abstraction.
13-- abstraction.
22−fixDisplayAttr :: FixedAttr -> Attr -> FixedAttr
14+fix_display_attr :: FixedAttr -> Attr -> FixedAttr
23−fixDisplayAttr fattr attr
15+fix_display_attr fattr attr
24− = FixedAttr (fixStyle (fixedStyle fattr) (attrStyle attr))
16+ = FixedAttr ( fix_style (fixed_style fattr) (attr_style attr) )
25− (fixColor (fixedForeColor fattr) (attrForeColor attr))
17+ ( fix_color (fixed_fore_color fattr) (attr_fore_color attr) )
26− (fixColor (fixedBackColor fattr) (attrBackColor attr))
18+ ( fix_color (fixed_back_color fattr) (attr_back_color attr) )
27− (fixURL (fixedURL fattr) (attrURL attr))
28 where
19 where
29− fixStyle _s Default = defaultStyleMask
20+ fix_style _s Default = default_style_mask
30− fixStyle s KeepCurrent = s
21+ fix_style s KeepCurrent = s
31− fixStyle _s (SetTo newStyle) = newStyle
22+ fix_style _s (SetTo new_style) = new_style
32− fixColor _c Default = Nothing
23+ fix_color _c Default = Nothing
33− fixColor c KeepCurrent = c
24+ fix_color c KeepCurrent = c
34− fixColor _c (SetTo c) = Just c
25+ fix_color _c (SetTo c) = Just c
35− fixURL c KeepCurrent = c
36− fixURL _c (SetTo n) = Just n
37− fixURL _c Default = Nothing
38
26
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.
46data DisplayAttrDiff = DisplayAttrDiff
27data DisplayAttrDiff = DisplayAttrDiff
47− { styleDiffs :: [StyleStateChange]
28+ { style_diffs :: [ StyleStateChange ]
48− , foreColorDiff :: DisplayColorDiff
29+ , fore_color_diff :: DisplayColorDiff
49− , backColorDiff :: DisplayColorDiff
30+ , back_color_diff :: DisplayColorDiff
50− , urlDiff :: URLDiff
51 }
31 }
52− deriving (Show)
32+ 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
61
33
62instance Monoid DisplayAttrDiff where
34instance Monoid DisplayAttrDiff where
63− mempty = DisplayAttrDiff [] NoColorChange NoColorChange NoLinkChange
35+ mempty = DisplayAttrDiff [] NoColorChange NoColorChange
64−#if !(MIN_VERSION_base(4,11,0))
65− mappend = (<>)
36+ mappend d_0 d_1 =
66−#endif
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
67
41
68−-- | Used in the computation of a final style attribute change.
69−simplifyStyleDiffs :: [StyleStateChange] -> [StyleStateChange] -> [StyleStateChange]
42+simplify_style_diffs :: [ StyleStateChange ] -> [ StyleStateChange ] -> [ StyleStateChange ]
70−simplifyStyleDiffs cs0 cs1 = cs0 `mappend` cs1
43+simplify_style_diffs cs_0 cs_1 = cs_0 `mappend` cs_1
71
44
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
45+simplify_color_diffs _cd ColorToDefault = ColorToDefault
76−simplifyColorDiffs cd NoColorChange = cd
46+simplify_color_diffs cd NoColorChange = cd
77−simplifyColorDiffs _cd (SetColor !c) = SetColor c
47+simplify_color_diffs _cd ( SetColor !c ) = SetColor c
78
48
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
87 = ColorToDefault
50 = ColorToDefault
88 | NoColorChange
51 | NoColorChange
89 | SetColor !Color
52 | SetColor !Color
90− deriving (Show, Eq)
53+ deriving ( Show, Eq )
91
54
92−-- | Style attribute changes are transformed into a sequence of
93−-- apply/removes of the individual attributes.
94−data StyleStateChange
55+data StyleStateChange
95 = ApplyStandout
56 = ApplyStandout
96 | RemoveStandout
57 | RemoveStandout
97− | ApplyItalic
98− | RemoveItalic
99− | ApplyStrikethrough
100− | RemoveStrikethrough
101 | ApplyUnderline
58 | ApplyUnderline
102 | RemoveUnderline
59 | RemoveUnderline
103 | ApplyReverseVideo
60 | ApplyReverseVideo
@@ -108,58 +65,42 @@ data StyleStateChange
108 | RemoveDim
65 | RemoveDim
109 | ApplyBold
66 | ApplyBold
110 | RemoveBold
67 | RemoveBold
111− deriving (Show, Eq)
68+ deriving ( Show, Eq )
112−
113−-- Setting and unsetting hyperlinks
114−data URLDiff
115− = LinkTo !ByteString
116− | NoLinkChange
117− | EndLink
118− deriving (Show, Eq)
119
69
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
70+display_attr_diffs :: FixedAttr -> FixedAttr -> DisplayAttrDiff
123−displayAttrDiffs attr attr' = DisplayAttrDiff
71+display_attr_diffs attr attr' = DisplayAttrDiff
124− { styleDiffs = diffStyles (fixedStyle attr) (fixedStyle attr')
72+ { style_diffs = diff_styles ( fixed_style attr ) ( fixed_style attr' )
125− , foreColorDiff = diffColor (fixedForeColor attr) (fixedForeColor attr')
73+ , fore_color_diff = diff_color ( fixed_fore_color attr ) ( fixed_fore_color attr' )
126− , backColorDiff = diffColor (fixedBackColor attr) (fixedBackColor attr')
74+ , back_color_diff = diff_color ( fixed_back_color attr ) ( fixed_back_color attr' )
127− , urlDiff = diffURL (fixedURL attr) (fixedURL attr')
128 }
75 }
129
76
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
77+diff_color :: Maybe Color -> Maybe Color -> DisplayColorDiff
136−diffColor Nothing (Just c') = SetColor c'
78+diff_color Nothing (Just c') = SetColor c'
137−diffColor (Just c) (Just c')
79+diff_color (Just c) (Just c')
138 | c == c' = NoColorChange
80 | c == c' = NoColorChange
139 | otherwise = SetColor c'
81 | otherwise = SetColor c'
140−diffColor Nothing Nothing = NoColorChange
82+diff_color Nothing Nothing = NoColorChange
141−diffColor (Just _) Nothing = ColorToDefault
83+diff_color (Just _) Nothing = ColorToDefault
142−
84+
143−diffStyles :: Style -> Style -> [StyleStateChange]
85+diff_styles :: Style -> Style -> [StyleStateChange]
144−diffStyles prev cur
86+diff_styles prev cur
145− = mconcat
87+ = mconcat
146− [ styleDiff standout ApplyStandout RemoveStandout
88+ [ style_diff standout ApplyStandout RemoveStandout
147− , styleDiff underline ApplyUnderline RemoveUnderline
89+ , style_diff underline ApplyUnderline RemoveUnderline
148− , styleDiff italic ApplyItalic RemoveItalic
149− , styleDiff strikethrough ApplyStrikethrough RemoveStrikethrough
150− , styleDiff reverseVideo ApplyReverseVideo RemoveReverseVideo
90+ , style_diff reverse_video ApplyReverseVideo RemoveReverseVideo
151− , styleDiff blink ApplyBlink RemoveBlink
91+ , style_diff blink ApplyBlink RemoveBlink
152− , styleDiff dim ApplyDim RemoveDim
92+ , style_diff dim ApplyDim RemoveDim
153− , styleDiff bold ApplyBold RemoveBold
93+ , style_diff bold ApplyBold RemoveBold
154 ]
94 ]
155− where
95+ where
156− styleDiff s sm rm
96+ style_diff s sm rm
157− = case (0 == prev .&. s, 0 == cur .&. s) of
97+ = case ( 0 == prev .&. s, 0 == cur .&. s ) of
158 -- not set in either
98 -- not set in either
159− (True, True) -> []
99+ ( True, True ) -> []
160 -- set in both
100 -- set in both
161− (False, False) -> []
101+ ( False, False ) -> []
162 -- now set
102 -- now set
163− (True, False) -> [sm]
103+ ( True, False) -> [ sm ]
164 -- now unset
104 -- 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 #-}
2{-# LANGUAGE NamedFieldPuns #-}
3{-# LANGUAGE NamedFieldPuns #-}
3{-# LANGUAGE DisambiguateRecordFields #-}
4{-# LANGUAGE DisambiguateRecordFields #-}
4−-- | A Vty program makes 'Picture's from 'Image's. This module provides
5+{-# OPTIONS_GHC -funbox-strict-fields #-}
5−-- the core constructors for creating, combining, and modifying
6−-- 'Image's.
7−module Graphics.Vty.Image
6+module Graphics.Vty.Image ( Image(..)
8− (
7+ , image_width
9− -- * Images
8+ , image_height
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− , (<|>)
9+ , (<|>)
28− , vertJoin
29− , (<->)
10+ , (<->)
30− , horizCat
11+ , horiz_cat
31− , vertCat
12+ , vert_cat
32− -- * Image modifications
13+ , background_fill
33− , crop
14+ , char
34− , cropRight
15+ , string
35− , cropLeft
16+ , iso_10646_string
36− , cropBottom
17+ , utf8_string
37− , cropTop
18+ , utf8_bytestring
38− , pad
19+ , char_fill
39− , resize
20+ , empty_image
40− , resizeWidth
41− , resizeHeight
42− , translate
21+ , translate
43− , translateX
22+ -- | The possible display attributes used in constructing an `Image`.
44− , translateY
23+ , module Graphics.Vty.Attributes
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− )
24+ )
60−where
25+ where
61
26
62import Graphics.Vty.Attributes
27import 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
73
28
74−-- | A region of the display (first width, then height)
29+import Codec.Binary.UTF8.Width
75−type DisplayRegion = (Int,Int)
76
30
77−regionWidth :: DisplayRegion -> Int
31+import Codec.Binary.UTF8.String ( decode )
78−regionWidth = fst
79
32
80−regionHeight :: DisplayRegion -> Int
33+import Data.AffineSpace
81−regionHeight = snd
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
82
39
83infixr 5 <|>
40infixr 5 <|>
84infixr 4 <->
41infixr 4 <->
85
42
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+
86−-- | An area of the picture's background (See 'Background').
190+-- | An area of the picture's bacground (See Background) of w columns and h rows.
87−backgroundFill :: Int
191+background_fill :: Word -> Word -> Image
88− -- ^ Fill width in columns
192+background_fill w h
89− -> Int
90− -- ^ Fill height in rows
91− -> Image
92−backgroundFill w h
93 | w == 0 = EmptyImage
193 | w == 0 = EmptyImage
94 | h == 0 = EmptyImage
194 | h == 0 = EmptyImage
95 | otherwise = BGFill w h
195 | otherwise = BGFill w h
96
196
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+
97−-- | Combines two images horizontally. This is an alias for 'horizJoin'.
215+-- | Combines two images side by side.
98--
216--
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.
100(<|>) :: Image -> Image -> Image
220(<|>) :: Image -> Image -> Image
101−(<|>) = horizJoin
102
221
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+
103−-- | Combines two images vertically. This is an alias for 'vertJoin'.
249+-- | Combines two images vertically.
104−--
250+-- The result image will have a height equal to the sum of the heights of both images.
105−-- infixr 4
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.
106(<->) :: Image -> Image -> Image
254(<->) :: Image -> Image -> Image
107−(<->) = vertJoin
255+im_t <-> im_b
108−
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+
109−-- | Compose any number of images together horizontally, with the first
261+-- | Compose any number of images horizontally.
110−-- in the list being leftmost.
111−horizCat :: [Image] -> Image
262+horiz_cat :: [Image] -> Image
112−horizCat = foldr horizJoin EmptyImage
263+horiz_cat = foldr (<|>) EmptyImage
113−
264+
114−-- | Compose any number of images vertically, with the first in the list
265+-- | Compose any number of images vertically.
115−-- being topmost.
116−vertCat :: [Image] -> Image
266+vert_cat :: [Image] -> Image
117−vertCat = foldr vertJoin EmptyImage
267+vert_cat = foldr (<->) EmptyImage
118−
268+
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
269+-- | an image of a single character. This is a standard Haskell 31-bit character assumed to be in
134−-- 31-bit character assumed to be in the ISO-10646 encoding.
270+-- the ISO-10646 encoding.
135char :: Attr -> Char -> Image
271char :: Attr -> Char -> Image
136−char a c =
137− let displayWidth = safeWcwidth c
138− in HorizText a (TL.singleton c) displayWidth 1
272+char !a !c = HorizText a (Seq.singleton c) (safe_wcwidth c) 1
139−
273+
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
274+-- | A string of characters layed out on a single row with the same display attribute. The string is
142−-- sequence of ISO-10646 characters. The input string should be
275+-- assumed to be a sequence of ISO-10646 characters.
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'.
158--
276--
159−-- This is an alias for iso10646String since the usual case is that a
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
160−-- literal string like "foo" is represented internally as a list of ISO
287+-- represented internally as a list of ISO 10646 31 bit characters.
161−-- 10646 31 bit characters.
162--
288--
163−-- Note: Keep in mind that GHC will compile source encoded as UTF-8
289+-- Note: Keep in mind that GHC will compile source encoded as UTF-8 but the literal strings, while
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.
290+-- UTF-8 encoded in the source, will be transcoded to a ISO 10646 31 bit characters runtime
291+-- representation.
166string :: Attr -> String -> Image
292string :: Attr -> String -> Image
167−string = iso10646String
293+string = iso_10646_string
168
294
169−-- | Make an 'Image' from a string of characters layed out on a single
295+-- | A string of characters layed out on a single row. The string is assumed to be a sequence of
170−-- row. The input is assumed to be the bytes for UTF-8 encoded text.
296+-- UTF-8 characters.
171−utf8String :: Attr -> [Word8] -> Image
297+utf8_string :: Attr -> [Word8] -> Image
172−utf8String a bytes = utf8Bytestring a (BL.pack bytes)
298+utf8_string !a !str = string a ( decode str )
173
299
174−-- | Make an 'Image' from a UTF-8 encoded lazy bytestring.
300+safe_wcwidth :: Char -> Word
175−utf8Bytestring :: Attr -> BL.ByteString -> Image
301+safe_wcwidth c = case wcwidth c of
176−utf8Bytestring a bs = text a (TL.decodeUtf8 bs)
302+ i | i < 0 -> 0 -- error "negative wcwidth"
303+ | otherwise -> toEnum i
177
304
178−-- | Make an 'Image' from a UTF-8 encoded strict bytestring.
305+safe_wcswidth :: String -> Word
179−utf8Bytestring' :: Attr -> B.ByteString -> Image
306+safe_wcswidth str = case wcswidth str of
180−utf8Bytestring' a bs = text' a (T.decodeUtf8 bs)
307+ i | i < 0 -> 0 -- error "negative wcswidth"
308+ | otherwise -> toEnum i
181
309
182−-- | Make an image filling a region with the specified character.
310+-- | Renders a UTF-8 encoded bytestring.
183−--
311+utf8_bytestring :: Attr -> BS.ByteString -> Image
184−-- If either the width or height are less than or equal to 0, then
312+utf8_bytestring !a !bs = string a (UTF8.toString $ UTF8.fromRep bs)
185−-- the result is the empty image.
313+
186−charFill :: Integral d
314+-- | creates a fill of the specified character. The dimensions are in number of characters wide and
187− => Attr
315+-- number of rows high.
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.
275--
316--
276−-- This is equivalent to a vertical crop from the bottom followed by
317+-- Unlike the Background fill character this character can have double column display width.
277−-- horizontal crop from the right.
318+char_fill :: Enum d => Attr -> Char -> d -> d -> Image
278−crop :: Int
319+char_fill !a !c w h =
279− -- ^ Cropping width
320+ vert_cat $ replicate (fromEnum h) $ horiz_cat $ replicate (fromEnum w) $ char a c
280− -> Int
321+
281− -- ^ Cropping height
322+-- | The empty image. Useful for fold combinators. These occupy no space nor define any display
282− -> Image
323+-- attributes.
283− -- ^ The image to crop
324+empty_image :: Image
284− -> Image
325+empty_image = EmptyImage
285−crop 0 _ _ = EmptyImage
326+
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
327+translate :: (Int, Int) -> Image -> Image
378−resizeHeight h i = case h `compare` imageHeight i of
328+translate v i = Translation v i
379− LT -> cropBottom h i
329+
380− EQ -> i
381− GT -> i <-> BGFill (imageWidth i) (h - imageHeight i)
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
1+{-# LANGUAGE BangPatterns #-}
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
2+module Graphics.Vty.Inline ( module Graphics.Vty.Inline
31− ( module Graphics.Vty.Inline
32− , withVty
33− )
3+ )
34−where
4+ where
35
5
36−import Graphics.Vty
6+import Graphics.Vty.Attributes
37import Graphics.Vty.DisplayAttributes
7import Graphics.Vty.DisplayAttributes
38−import Graphics.Vty.Inline.Unsafe
8+import Graphics.Vty.Terminal.Generic
39−
40−import Blaze.ByteString.Builder (writeToByteString)
41
9
10+import Control.Applicative
42import Control.Monad.State.Strict
11import Control.Monad.State.Strict
12+import Control.Monad.Trans
43
13
44import Data.Bits ( (.&.), complement )
14import Data.Bits ( (.&.), complement )
45import Data.IORef
15import Data.IORef
16+import Data.Monoid ( mappend )
46
17
47−import System.IO
48−
49−type InlineM v = State InlineState v
18+type InlineM v = State Attr v
50−
51−data InlineState =
52− InlineState { inlineAttr :: Attr
53− , inlineUrlsEnabled :: Bool
54− }
55
19
56−-- | Set the background color to the provided 'Color'.
57−backColor :: Color -> InlineM ()
20+back_color :: Color -> InlineM ()
58−backColor c = modify $ \s ->
21+back_color c = modify $ flip mappend ( current_attr `with_back_color` c )
59− s { inlineAttr = inlineAttr s `withBackColor` c
60− }
61
22
62−-- | Set the foreground color to the provided 'Color'.
63−foreColor :: Color -> InlineM ()
23+fore_color :: Color -> InlineM ()
64−foreColor c = modify $ \s ->
24+fore_color c = modify $ flip mappend ( current_attr `with_fore_color` c )
65− s { inlineAttr = inlineAttr s `withForeColor` c
66− }
67
25
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 ()
26+apply_style :: Style -> InlineM ()
73−applyStyle st = modify $ \s ->
27+apply_style s = modify $ flip mappend ( current_attr `with_style` s )
74− s { inlineAttr = inlineAttr s `withStyle` st
75− }
76
28
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 ()
29+remove_style :: Style -> InlineM ()
83−removeStyle sMask = modify $ \s ->
30+remove_style s_mask = modify $ \attr ->
84− s { inlineAttr =
85− let style' = case attrStyle (inlineAttr s) of
31+ let style' = case attr_style attr of
86− Default -> error "Graphics.Vty.Inline: Cannot removeStyle if applyStyle never used."
32+ Default -> error $ "Graphics.Vty.Inline: Cannot remove_style if apply_style never used."
87− KeepCurrent -> error "Graphics.Vty.Inline: Cannot removeStyle if applyStyle never used."
33+ KeepCurrent -> error $ "Graphics.Vty.Inline: Cannot remove_style if apply_style never used."
88− SetTo st -> st .&. complement sMask
34+ SetTo s -> s .&. complement s_mask
89− in (inlineAttr s) { attrStyle = SetTo style' }
35+ in attr { attr_style = SetTo style' }
90− }
91
36
92−-- | Reset the display attributes.
93−defaultAll :: InlineM ()
37+default_all :: InlineM ()
94−defaultAll = modify $ \s -> s { inlineAttr = defAttr }
38+default_all = put def_attr
95
39
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 ()
40+put_attr_change :: ( Applicative m, MonadIO m ) => TerminalHandle -> InlineM () -> m ()
101−putAttrChange out c = liftIO $ do
41+put_attr_change t c = do
102− bounds <- displayBounds out
42+ bounds <- display_bounds t
103− dc <- displayContext out bounds
43+ d <- display_context t bounds
104− mfattr <- prevFattr <$> readIORef (assumedStateRef out)
44+ mfattr <- liftIO $ known_fattr <$> readIORef ( state_ref t )
105 fattr <- case mfattr of
45 fattr <- case mfattr of
106 Nothing -> do
46 Nothing -> do
107− liftIO $ outputByteBuffer out $ writeToByteString $ writeDefaultAttr dc False
47+ marshall_to_terminal t (default_attr_required_bytes d) (serialize_default_attr d)
108− return $ FixedAttr defaultStyleMask Nothing Nothing Nothing
48+ return $ FixedAttr default_style_mask Nothing Nothing
109 Just v -> return v
49 Just v -> return v
110− let InlineState attr urlsEnabled = execState c (InlineState currentAttr False)
50+ let attr = execState c current_attr
111− attr' = limitAttrForDisplay out attr
51+ attr' = limit_attr_for_display d attr
112− fattr' = fixDisplayAttr fattr attr'
52+ fattr' = fix_display_attr fattr attr'
113− diffs = displayAttrDiffs fattr fattr'
53+ diffs = display_attr_diffs fattr fattr'
114− outputByteBuffer out $ writeToByteString $ writeSetAttr dc urlsEnabled fattr attr' diffs
54+ marshall_to_terminal t ( attr_required_bytes d fattr attr' diffs )
55+ ( serialize_set_attr d fattr attr' diffs )
115− modifyIORef (assumedStateRef out) $ \s -> s { prevFattr = Just fattr' }
56+ liftIO $ modifyIORef ( state_ref t ) $ \s -> s { known_fattr = Just fattr' }
116− inlineHack dc
57+ inline_hack d
117
58
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
1+-- Copyright 2009 Corey O'Connor
2−-- 'Picture' data structure is representative of the final terminal
2+module Graphics.Vty.Picture ( module Graphics.Vty.Picture
3−-- view.
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`.
4−module Graphics.Vty.Picture
20+ , module Graphics.Vty.Attributes
5− ( Picture(..)
21+ )
6− , Cursor(..)
7− , Background(..)
8− , emptyPicture
9− , addToTop
10− , addToBottom
11− , picForImage
12− , picForLayers
13− , picImage
14− )
15−where
22+ where
16
23
17−import Graphics.Vty.Image
18import Graphics.Vty.Attributes
24import Graphics.Vty.Attributes
25+import Graphics.Vty.Image hiding ( attr )
19
26
20−import Control.DeepSeq
27+import Data.Word
21
28
22−-- | A Vty picture.
29+-- |The type of images to be displayed using 'update'.
23−--
24−-- These can be constructed directly or using `picForImage`.
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.
25data Picture = Picture
32data Picture = Picture
26− { picCursor :: Cursor
33+ { pic_cursor :: Cursor
27− -- ^ The picture's cursor.
34+ , pic_image :: Image
28− , picLayers :: [Image]
29− -- ^ The picture's image layers (top-most first).
30− , picBackground :: Background
35+ , pic_background :: 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
61 }
36 }
62
37
63−-- | Create a picture with the given layers, top-most first.
64−--
65−-- The picture will not have a displayed cursor and no background
38+-- | Create a picture for display for the given image. The picture will not have a displayed cursor
66−-- pattern (ClearBackgroun) will be used.
39+-- and the background display attribute will be `current_attr`.
67−picForLayers :: [Image] -> Picture
40+pic_for_image :: Image -> Picture
68−picForLayers is = Picture
41+pic_for_image i = Picture
69− { picCursor = NoCursor
42+ { pic_cursor = NoCursor
70− , picLayers = is
43+ , pic_image = i
71− , picBackground = ClearBackground
44+ , pic_background = Background ' ' current_attr
72 }
45 }
73
46
74−-- | A picture can be configured to hide the cursor or to show the
47+-- | A picture can be configured either to not show the cursor or show the cursor at the specified
75−-- cursor at the specified character position.
48+-- character position.
76--
49--
77−-- There is not a 1:1 map from character positions to a row and column
50+-- There is not a 1 to 1 map from character positions to a row and column on the screen due to
78−-- on the screen due to characters that take more than 1 column.
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.
79−data Cursor =
55+data Cursor =
80− -- | Hide the cursor
81− NoCursor
56+ 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
57+ | Cursor Word Word
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` ()
99
58
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.
101--
63--
102−-- * ClearBackground, which shows the layer below or is blank if the
64+-- (tofix) The current attribute is always set to the default attributes at the start of updating the
103−-- bottom layer
65+-- screen to a picture.
104−-- * A character and a display attribute
105--
66--
106−-- If the display attribute used previously should be used for a
67+-- (tofix) The background character *must* occupy a single column and no more.
107−-- background fill then use `currentAttr` for the background attribute.
108−data Background
68+data Background = Background
109− = Background
69+ { background_char :: Char
110− { backgroundChar :: Char
70+ , background_attr :: Attr
111− , backgroundAttr :: Attr
112 }
71 }
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 = ()
124
72
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>
1+-- Copyright 2009 Corey O'Connor
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
2+module Graphics.Vty.PictureToSpans
3+ where
10
4
11−import Graphics.Vty.Attributes (Attr, currentAttr)
12−import Graphics.Vty.Image
13−import Graphics.Vty.Image.Internal
14import Graphics.Vty.Picture
5import Graphics.Vty.Picture
15import Graphics.Vty.Span
6import Graphics.Vty.Span
16
7
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 #-}
2{-# LANGUAGE GADTs #-}
5{-# LANGUAGE GADTs #-}
3−-- | A picture is translated into a sequences of state changes and
6+{-# LANGUAGE ExistentialQuantification #-}
4−-- character spans. The attribute is applied to all following spans,
7+{-# LANGUAGE ScopedTypeVariables #-}
5−-- including spans of the next row. The nth element of the sequence
8+-- The ops to define the content for an output region.
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
9+module Graphics.Vty.Span
11−
10+ where
12−import Graphics.Vty.Attributes (Attr)
11+
12+import Codec.Binary.UTF8.Width ( wcwidth )
13+
13import Graphics.Vty.Image
14import 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]
15
56
16−import qualified Data.Text.Lazy as TL
57+span_ops_columns :: SpanOpSequence -> Word
17−import Data.Vector (Vector)
58+span_ops_columns ops = region_width $ effected_region ops
18−import qualified Data.Vector as Vector
19
59
20−-- | This represents an operation on the terminal: either an attribute
60+span_ops_rows :: SpanOpSequence -> Word
21−-- change or the output of a text string.
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
22data SpanOp =
75data SpanOp =
76+ AttributeChange !Attr
23− -- | A span of UTF-8 text occupies a specific number of screen space
77+ -- | a span of UTF-8 text occupies a specific number of screen space columns. A single UTF
24− -- columns. A single UTF character does not necessarily represent 1
25− -- colunm. See Codec.Binary.UTF8.Width TextSpan [Attr] [output width
78+ -- character does not necessarially represent 1 colunm. See Codec.Binary.UTF8.Width
26− -- in columns] [number of characters] [data]
79+ -- TextSpan [output width in columns] [number of characters] [data]
27− TextSpan
80+ | TextSpan !Word !Word (UTF8.UTF8 B.ByteString)
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
40 deriving Eq
81 deriving Eq
41
82
42−-- | A vector of span operations executed in succession. This represents
83+span_op_has_width :: SpanOp -> Maybe (Word, Word)
43−-- the operations required to render a row of the terminal. The
84+span_op_has_width (TextSpan ow cw _) = Just (cw, ow)
44−-- operations in one row may affect subsequent rows. For example,
85+span_op_has_width _ = Nothing
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
48
86
49−dropOps :: Int -> SpanOps -> SpanOps
87+columns_to_char_offset :: Word -> SpanOp -> Word
50−dropOps w = snd . splitOpsAt w
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"
51
92
52−splitOpsAt :: Int -> SpanOps -> (SpanOps, SpanOps)
93+-- | Produces the span ops that will render the given picture, possibly cropped or padded, into the
53−splitOpsAt = splitOpsAt'
94+-- specified region.
54− where
95+spans_for_pic :: Picture -> DisplayRegion -> SpanOpSequence
55− splitOpsAt' 0 ops = (Vector.empty, ops)
96+spans_for_pic pic r = SpanOpSequence r $ runSTArray (build_spans pic r)
56− splitOpsAt' remainingColumns ops = case Vector.head ops of
97+
57− t@(TextSpan {}) -> if remainingColumns >= textSpanOutputWidth t
98+build_spans :: Picture -> DisplayRegion -> ST s (STArray s Word [SpanOp])
58− then let (pre,post) = splitOpsAt' (remainingColumns - textSpanOutputWidth t)
99+build_spans pic region = do
59− (Vector.tail ops)
100+ -- m for mutable
60− in (Vector.cons t pre, post)
101+ mrow_ops <- newSTArray (0, region_height region - 1) []
61− else let preTxt = clipText (textSpanText t) 0 remainingColumns
102+ -- I think this can be better optimized if the build produced span operations in display order.
62− preOp = TextSpan { textSpanAttr = textSpanAttr t
103+ -- However, I got stuck trying to implement an algorithm that did this. This will be considered
63− , textSpanOutputWidth = remainingColumns
104+ -- as a possible future optimization.
64− , textSpanCharWidth = fromIntegral $! TL.length preTxt
105+ --
65− , textSpanText = preTxt
106+ -- A depth first traversal of the image is performed.
66− }
107+ -- ordered according to the column range defined by the image from least to greatest.
67− postWidth = textSpanOutputWidth t - remainingColumns
108+ -- The output row ops will at least have the region of the image specified. Iterate over all
68− postTxt = clipText (textSpanText t) remainingColumns postWidth
109+ -- output rows and output background fills for all unspecified columns.
69− postOp = TextSpan { textSpanAttr = textSpanAttr t
110+ if region_height region > 0
70− , textSpanOutputWidth = postWidth
111+ then do
71− , textSpanCharWidth = fromIntegral $! TL.length postTxt
112+ -- The ops builder recursively descends the image and outputs span ops that would
72− , textSpanText = postTxt
113+ -- display that image. The number of columns remaining in this row before exceeding the
73− }
114+ -- bounds is also provided. This is used to clip the span ops produced.
74− in ( Vector.singleton preOp
115+ ops_for_row mrow_ops (pic_background pic) region (pic_image pic) 0 (region_width region)
75− , Vector.cons postOp (Vector.tail ops)
116+ -- Fill in any unspecified columns with the background pattern.
76− )
117+ forM_ [0 .. region_height region - 1] $ \row -> do
77− Skip w -> if remainingColumns >= w
118+ end_x <- readSTArray mrow_ops row >>= return . span_ops_effected_columns
78− then let (pre,post) = splitOpsAt' (remainingColumns - w) (Vector.tail ops)
119+ if end_x < region_width region
79− in (Vector.cons (Skip w) pre, post)
120+ then snoc_bg_fill mrow_ops (pic_background pic) (region_width region - end_x) row
80− else ( Vector.singleton $ Skip remainingColumns
121+ else return ()
81− , Vector.cons (Skip (w - remainingColumns)) (Vector.tail ops)
122+ else return ()
82− )
123+ return mrow_ops
83− RowEnd _ -> error "cannot split ops containing a row end"
124+
84−
125+type MRowOps s = STArray s Word SpanOps
85−-- | A vector of span operation vectors for display, one per row of the
126+
86−-- output region.
127+ops_for_row :: MRowOps s -> Background -> DisplayRegion -> Image -> Word -> Word -> ST s ()
87−type DisplayOps = Vector SpanOps
128+ops_for_row mrow_ops bg region image y remaining_columns
88−
129+ | remaining_columns == 0 = return ()
89−instance Show SpanOp where
130+ | y >= region_height region = return ()
90− show (TextSpan attr ow cw _) = "TextSpan(" ++ show attr ++ ")(" ++ show ow ++ ", " ++ show cw ++ ")"
131+ | otherwise = case image of
91− show (Skip ow) = "Skip(" ++ show ow ++ ")"
132+ EmptyImage -> return ()
92− show (RowEnd ow) = "RowEnd(" ++ show ow ++ ")"
133+ -- The width provided is the number of columns this text span will occupy when displayed.
93−
134+ -- if this is greater than the number of remaining columsn the output has to be produced a
94−-- | The number of columns the DisplayOps are defined for.
135+ -- character at a time.
95−--
136+ HorizText a text_str ow cw -> do
96−-- All spans are verified to define same number of columns.
137+ snoc_text_span a text_str ow cw mrow_ops y remaining_columns
97−displayOpsColumns :: DisplayOps -> Int
138+ VertJoin t b _ _ -> do
98−displayOpsColumns ops
139+ ops_for_row mrow_ops bg region t y remaining_columns
99− | Vector.length ops == 0 = 0
140+ ops_for_row mrow_ops bg region b (y + image_height t) remaining_columns
100− | otherwise = Vector.length $ Vector.head ops
141+ HorizJoin l r _ _ -> do
101−
142+ ops_for_row mrow_ops bg region l y remaining_columns
102−-- | The number of rows the DisplayOps are defined for.
143+ -- Don't output the right part unless there is at least a single column left after
103−displayOpsRows :: DisplayOps -> Int
144+ -- outputting the left part.
104−displayOpsRows = Vector.length
145+ if image_width l < remaining_columns
105−
146+ then ops_for_row mrow_ops bg region r y (remaining_columns - image_width l)
106−affectedRegion :: DisplayOps -> DisplayRegion
147+ else return ()
107−affectedRegion ops = (displayOpsColumns ops, displayOpsRows ops)
148+ BGFill width height -> do
108−
149+ let actual_height = if y + height > region_height region
109−-- | The number of columns a SpanOps affects.
150+ then region_height region - y
110−spanOpsAffectedColumns :: SpanOps -> Int
151+ else height
111−spanOpsAffectedColumns inOps = Vector.foldl' spanOpsAffectedColumns' 0 inOps
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)
112 where
180 where
113− spanOpsAffectedColumns' t (TextSpan _ w _ _ ) = t + w
181+ build_cropped_txt (ow', char_count', b0) c = {-# SCC "build_cropped_txt" #-}
114− spanOpsAffectedColumns' t (Skip w) = t + w
182+ let w = wcwidth c
115− spanOpsAffectedColumns' t (RowEnd w) = t + w
183+ -- Characters with unknown widths occupy 1 column.
116−
184+ --
117−-- | The width of a single SpanOp in columns.
185+ -- todo: Not sure if this is actually correct.
118−spanOpHasWidth :: SpanOp -> Maybe (Int, Int)
186+ -- I presume there is a replacement character that is output by the terminal instead of
119−spanOpHasWidth (TextSpan _ ow cw _) = Just (cw, ow)
187+ -- the character. If so then this replacement process may need to be implemented
120−spanOpHasWidth (Skip ow) = Just (ow,ow)
188+ -- manually for consistent behavior across terminals.
121−spanOpHasWidth (RowEnd ow) = Just (ow,ow)
189+ w' = toEnum $ if w < 0 then 1 else w
122−
190+ in if (w' + ow') > remaining_columns
123−-- | The number of columns to the character at the given position in the
191+ then ( ow', char_count', b0 )
124−-- span op.
192+ else ( ow' + w', char_count' + 1, B.append b0 $ B.pack $ encode [c] )
125−columnsToCharOffset :: Int -> SpanOp -> Int
193+
126−columnsToCharOffset cx (TextSpan _ _ _ utf8Str) =
194+snoc_bg_fill :: MRowOps s -> Background -> Word -> Word -> ST s ()
127− wctlwidth (TL.take (fromIntegral cx) utf8Str)
195+snoc_bg_fill _row_ops _bg 0 _row
128−columnsToCharOffset cx (Skip _) = cx
196+ = return ()
129−columnsToCharOffset cx (RowEnd _) = cx
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
1+{- benchmarks composing images using the renderChar operation.
2−-- what Yi uses in Yi.UI.Vty.drawText. Ideally a sequence of renderChar
2+ - This is what Yi uses in Yi.UI.Vty.drawText. Ideally a sequence of renderChar images horizontally
3−-- images horizontally composed should provide no worse performance than
3+ - composed should provide no worse performance than a fill render op.
4−-- a fill render op.
4+ -}
5−module BenchRenderChar where
6−
7import Graphics.Vty
5import Graphics.Vty
8−import Verify
9
6
10import Control.Monad ( forM_ )
7import Control.Monad ( forM_ )
11
8
12−bench0 = do
9+import System.Time
10+
11+main = do
13− vty <- mkVty defaultConfig
12+ vty <- mkVty
14− (w,h) <- displayBounds $ outputIface vty
13+ (w, h) <- getSize vty
15− let testChars = return $ take 500 $ cycle $ [ c | c <- ['a'..'z']]
14+ let test_chars = take 500 $ cycle $ [ c | c <- ['a'..'z']]
16− bench d = do
15+ start_time_0 <- getClockTime
17− forM_ d $ \testChar -> do
16+ forM_ test_chars $ \test_char -> do
18− let testImage = testImageUsingChar testChar w h
17+ let test_image = test_image_using_renderChar test_char w h
19− outPic = picForImage testImage
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 }
20− update vty outPic
25+ update vty out_pic
26+ end_time_1 <- getClockTime
21− shutdown vty
27+ shutdown vty
22− return $ Bench testChars bench
28+ putStrLn $ timeDiffToString $ diffClockTimes end_time_0 start_time_0
29+ putStrLn $ timeDiffToString $ diffClockTimes end_time_1 start_time_1
23
30
24−testImageUsingChar c w h
31+test_image_using_renderChar c w h = vertcat $ replicate h $ horzcat $ map (renderChar attr) (replicate w c)
25− = vertCat $ replicate (fromIntegral h)
26− $ horizCat $ map (char defAttr) (replicate (fromIntegral w) c)
27
32
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
5import System.Console.Terminfo
5import System.Console.Terminfo
6
6
7main = do
7main = do
8− terminal <- setupTermFromEnv
8+ terminal <- setupTermFromEnv
9− controlTable <- init_controlTable terminal
9+ control_table <- init_control_table terminal
10 putStrLn $ "ANSI terminal show cursor string: " ++ show cvis
10 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)
12 putStrLn $ "ANSI terminal hide cursor string: " ++ show civis
12 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)
14
14
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 @@
1{-# LANGUAGE QuasiQuotes #-}
1{-# LANGUAGE QuasiQuotes #-}
2{-# LANGUAGE TemplateHaskell #-}
2{-# LANGUAGE TemplateHaskell #-}
3−-- From http://www.reddit.com/r/haskell/comments/8ereh/a_here_document_syntax/
3+{- From http://www.reddit.com/r/haskell/comments/8ereh/a_here_document_syntax/
4−-- copyright unknown?
4+ - copyright unknown?
5+ -}
5module HereDoc (heredoc) where
6module HereDoc (heredoc) where
6
7
7import Language.Haskell.TH.Quote
8import Language.Haskell.TH.Quote
@@ -9,4 +10,5 @@ import Language.Haskell.TH.Syntax
9import Language.Haskell.TH.Lib
10import Language.Haskell.TH.Lib
10
11
11heredoc :: QuasiQuoter
12heredoc :: 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 \
4verify_parse_terminfo_caps \
4verify_parse_terminfo_caps \
5verify_eval_terminfo_caps \
5verify_eval_terminfo_caps \
6verify_utf8_width \
6verify_utf8_width \
7−VerifyImageOps \
7+verify_image_ops \
8verify_image_trans \
8verify_image_trans \
9verify_empty_image_props \
9verify_empty_image_props \
10verify_picture_ops \
10verify_picture_ops \
11−VerifySpanOps \
11+verify_span_ops \
12verify_debug_terminal \
12verify_debug_terminal \
13verify_inline \
13verify_inline \
14
14
15
15
16TESTS :=\
16TESTS :=\
17−benchmark \
17+Bench \
18+Bench2 \
19+BenchRenderChar \
18Test \
20Test \
19Test2 \
21Test2 \
20yi_issue_264 \
22yi_issue_264 \
@@ -24,17 +26,7 @@ $(VERIF_TESTS)
24$(shell mkdir -p objects )
26$(shell mkdir -p objects )
25
27
26# TODO: Tests should also be buildable referencing the currently installed vty
28# TODO: Tests should also be buildable referencing the currently installed vty
27−GHC_ARGS=--make -i../src \
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
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
38
30
39GHC_PROF_ARGS=-prof -auto-all $(GHC_ARGS)
31GHC_PROF_ARGS=-prof -auto-all $(GHC_ARGS)
40
32
@@ -45,7 +37,7 @@ all : $(VERIF_TESTS)
45
37
46.PHONY: $(TESTS)
38.PHONY: $(TESTS)
47.SECONDEXPANSION:
39.SECONDEXPANSION:
48−$(TESTS) :
40+$(TESTS) :
49 @echo running test $@
41 @echo running test $@
50 @mkdir -p results/$@
42 @mkdir -p results/$@
51 ( ghc $(GHC_PROF_ARGS) $@ \
43 ( ghc $(GHC_PROF_ARGS) $@ \
@@ -54,10 +46,6 @@ $(TESTS) :
54 )
46 )
55
47
56.PHONY: interactive_terminal_test
48.PHONY: interactive_terminal_test
57−interactive_terminal_test :
49+interactive_terminal_test :
58 ghc $(GHC_ARGS) $@ && ./$@
50 ghc $(GHC_ARGS) $@ && ./$@
59
51
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 @@
1module Main where
1module Main where
2−
3import Graphics.Vty
2import Graphics.Vty
4
3
5main = do
4main = do
test/Verify.hs+55−59
@@ -1,69 +1,68 @@
1−{-# LANGUAGE RankNTypes #-}
2−{-# LANGUAGE GADTs #-}
3−{-# LANGUAGE RecordWildCards #-}
4{-# LANGUAGE TypeSynonymInstances #-}
1{-# LANGUAGE TypeSynonymInstances #-}
5{-# LANGUAGE FlexibleInstances #-}
2{-# LANGUAGE FlexibleInstances #-}
6{-# LANGUAGE StandaloneDeriving #-}
3{-# LANGUAGE StandaloneDeriving #-}
7{-# LANGUAGE DisambiguateRecordFields #-}
4{-# LANGUAGE DisambiguateRecordFields #-}
8{-# LANGUAGE NamedFieldPuns #-}
5{-# LANGUAGE NamedFieldPuns #-}
9−module Verify
6+module Verify ( 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
7+ , module Test.QuickCheck
17− , module Text.Printf
18− , succeeded
8+ , succeeded
19− , failed
9+ , failed
10+ , Result(..)
20− , monadicIO
11+ , monadicIO
21− , liftIO
12+ , liftIO
13+ , liftIOResult
14+ , liftResult
22− , liftBool
15+ , liftBool
23− , Test(..)
24− , Prop.Result(..)
25− )
16+ )
26−where
17+ where
27−
28−import Control.Exception ( bracket, try, SomeException(..) )
29−
30−import Distribution.TestSuite hiding ( Result(..) )
31−import qualified Distribution.TestSuite as TS
32
18
33import Test.QuickCheck hiding ( Result(..) )
19import Test.QuickCheck hiding ( Result(..) )
34import qualified Test.QuickCheck as QC
20import qualified Test.QuickCheck as QC
35−import Test.QuickCheck.Modifiers
21+import Test.QuickCheck.Property
36−import Test.QuickCheck.Property hiding ( Result(..) )
37−import qualified Test.QuickCheck.Property as Prop
38−import Test.QuickCheck.Monadic ( monadicIO )
22+import Test.QuickCheck.Monadic ( monadicIO )
39−
40−import Text.Printf
41
23
42import qualified Codec.Binary.UTF8.String as UTF8
24import qualified Codec.Binary.UTF8.String as UTF8
43
25
44−import Control.Applicative hiding ( (<|>) )
26+import Control.Applicative
45−import Control.DeepSeq
46−import Control.Monad ( forM, mapM, mapM_, forM_ )
47import Control.Monad.State.Strict
27import Control.Monad.State.Strict
28+import Control.Monad.Trans ( liftIO )
29+
30+import Data.IORef
31+import Data.Word
48
32
49import Numeric ( showHex )
33import Numeric ( showHex )
50
34
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+
51−verify :: Testable t => String -> t -> Test
59+verify :: Testable prop => String -> prop -> Test QC.Result
52−verify testName p = Test $ TestInstance
60+verify prop_name prop = do
53− { name = testName
61+ liftIO $ putStrLn $ "verify " ++ prop_name
54− , run = do
62+ get >>= \s -> do
55− qcResult <- quickCheckWithResult (stdArgs {chatty = False}) p
63+ r <- liftIO $ quickCheckResult prop
56− case qcResult of
64+ liftIO $ modifyIORef (results_ref s) (\rs -> r : rs)
57− QC.Success {..} -> return $ Finished TS.Pass
65+ return r
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− }
67
66
68data SingleColumnChar = SingleColumnChar Char
67data SingleColumnChar = SingleColumnChar Char
69 deriving (Show, Eq)
68 deriving (Show, Eq)
@@ -78,12 +77,9 @@ instance Show DoubleColumnChar where
78 show (DoubleColumnChar c) = "(0x" ++ showHex (fromEnum c) "" ++ ") ->" ++ UTF8.encodeString [c]
77 show (DoubleColumnChar c) = "(0x" ++ showHex (fromEnum c) "" ++ ") ->" ++ UTF8.encodeString [c]
79
78
80instance Arbitrary DoubleColumnChar where
79instance Arbitrary DoubleColumnChar where
81− arbitrary = elements $ map DoubleColumnChar $
80+ arbitrary = elements $ map DoubleColumnChar $
82− [ toEnum 0x3040 .. toEnum 0x3098 ]
81+ [ toEnum 0x3040 .. toEnum 0x3098 ]
83− ++ [ toEnum 0x309B .. toEnum 0xA4CF ]
82+ ++ [ toEnum 0x309B .. toEnum 0xA4CF]
84−
85−liftIOResult :: Testable prop => IO prop -> Property
86−liftIOResult = ioProperty
87
83
88−data Bench where
84+instance Arbitrary Word where
89− Bench :: forall v . NFData v => IO v -> (v -> IO ()) -> Bench
85+ arbitrary = choose (0, 1024) >>= return . toEnum
test/Verify/Data/Terminfo/Parse.hs+63−61
@@ -1,41 +1,47 @@
1+{-# LANGUAGE ScopedTypeVariables #-}
1−module Verify.Data.Terminfo.Parse
2+module Verify.Data.Terminfo.Parse ( module Verify.Data.Terminfo.Parse
2− ( module Verify.Data.Terminfo.Parse
3− , module Data.Terminfo.Parse
3+ , module Data.Terminfo.Parse
4− )
4+ )
5−where
5+ where
6
6
7import Data.Terminfo.Parse
7import Data.Terminfo.Parse
8import Data.Terminfo.Eval
8import Data.Terminfo.Eval
9import Verify
9import Verify
10
10
11+import Data.Array.Unboxed
11import Data.Word
12import Data.Word
12−import qualified Data.Vector.Unboxed as Vector
13
13
14import Numeric
14import Numeric
15
15
16−hexDump :: [Word8] -> String
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
17−hexDump bytes = foldr (\b s -> showHex b s) "" bytes
23+hex_dump bytes = foldr (\b s -> showHex b s) "" $ elems bytes
18
24
19data NonParamCapString = NonParamCapString String
25data NonParamCapString = NonParamCapString String
20 deriving Show
26 deriving Show
21
27
22instance Arbitrary NonParamCapString where
28instance Arbitrary NonParamCapString where
23− arbitrary
29+ arbitrary
24 = ( do
30 = ( do
25 s <- listOf1 $ (choose (0, 255) >>= return . toEnum) `suchThat` (/= '%')
31 s <- listOf1 $ (choose (0, 255) >>= return . toEnum) `suchThat` (/= '%')
26 return $ NonParamCapString s
32 return $ NonParamCapString s
27− ) `suchThat` ( \(NonParamCapString str) -> length str < 255 )
33+ ) `suchThat` ( \(NonParamCapString str) -> length str < 255 )
28
34
29data LiteralPercentCap = LiteralPercentCap String [Word8]
35data LiteralPercentCap = LiteralPercentCap String [Word8]
30 deriving ( Show )
36 deriving ( Show )
31
37
32instance Arbitrary LiteralPercentCap where
38instance Arbitrary LiteralPercentCap where
33− arbitrary
39+ arbitrary
34 = ( do
40 = ( do
35 NonParamCapString s <- arbitrary
41 NonParamCapString s <- arbitrary
36− (s', bytes) <- insertEscapeOp "%" [toEnum $ fromEnum '%'] s
42+ (s', bytes) <- insert_escape_op "%" [toEnum $ fromEnum '%'] s
37 return $ LiteralPercentCap s' bytes
43 return $ LiteralPercentCap s' bytes
38− ) `suchThat` ( \(LiteralPercentCap str _) -> length str < 255 )
44+ ) `suchThat` ( \(LiteralPercentCap str _) -> length str < 255 )
39
45
40data IncFirstTwoCap = IncFirstTwoCap String [Word8]
46data IncFirstTwoCap = IncFirstTwoCap String [Word8]
41 deriving ( Show )
47 deriving ( Show )
@@ -44,69 +50,65 @@ instance Arbitrary IncFirstTwoCap where
44 arbitrary
50 arbitrary
45 = ( do
51 = ( do
46 NonParamCapString s <- arbitrary
52 NonParamCapString s <- arbitrary
47− (s', bytes) <- insertEscapeOp "i" [] s
53+ (s', bytes) <- insert_escape_op "i" [] s
48 return $ IncFirstTwoCap s' bytes
54 return $ IncFirstTwoCap s' bytes
49− ) `suchThat` ( \(IncFirstTwoCap str _) -> length str < 255 )
55+ ) `suchThat` ( \(IncFirstTwoCap str _) -> length str < 255 )
50
56
51−data PushParamCap = PushParamCap String Int [Word8]
57+data PushParamCap = PushParamCap String Word [Word8]
52 deriving ( Show )
58 deriving ( Show )
53
59
54instance Arbitrary PushParamCap where
60instance Arbitrary PushParamCap where
55 arbitrary
61 arbitrary
56 = ( do
62 = ( do
57 NonParamCapString s <- arbitrary
63 NonParamCapString s <- arbitrary
58− n <- choose (1,9)
64+ n :: Word <- choose (1,9) >>= return . toEnum
59− (s', bytes) <- insertEscapeOp ("p" ++ show n) [] s
65+ (s', bytes) <- insert_escape_op ("p" ++ show n) [] s
60 return $ PushParamCap s' n bytes
66 return $ PushParamCap s' n bytes
61− ) `suchThat` ( \(PushParamCap str _ _) -> length str < 255 )
67+ ) `suchThat` ( \(PushParamCap str _ _) -> length str < 255 )
62
68
63−data DecPrintCap = DecPrintCap String Int [Word8]
69+data DecPrintCap = DecPrintCap String Word [Word8]
64 deriving ( Show )
70 deriving ( Show )
65
71
66instance Arbitrary DecPrintCap where
72instance Arbitrary DecPrintCap where
67 arbitrary
73 arbitrary
68 = ( do
74 = ( do
69 NonParamCapString s <- arbitrary
75 NonParamCapString s <- arbitrary
70− n <- choose (1,9)
76+ n :: Word <- choose (1,9) >>= return . toEnum
71− (s', bytes) <- insertEscapeOp ("p" ++ show n ++ "%d") [] s
77+ (s', bytes) <- insert_escape_op ("p" ++ show n ++ "%d") [] s
72 return $ DecPrintCap s' n bytes
78 return $ DecPrintCap s' n bytes
73− ) `suchThat` ( \(DecPrintCap str _ _) -> length str < 255 )
79+ ) `suchThat` ( \(DecPrintCap str _ _) -> length str < 255 )
74−
80+
75−insertEscapeOp opStr replBytes s = do
81+insert_escape_op op_str repl_bytes s = do
76− insertPoints <- listOf1 $ elements [0 .. length s - 1]
82+ insert_points <- listOf1 $ elements [0 .. length s - 1]
77− let s' = f s ('%' : opStr)
83+ let s' = f s ('%' : op_str)
78− remainingBytes = f (map (toEnum . fromEnum) s) replBytes
84+ remaining_bytes = f (map (toEnum . fromEnum) s) repl_bytes
79− f inVs out_v = concat [ vs
85+ f in_vs out_v = concat [ vs
80− | vi <- zip inVs [0 .. length s - 1]
86+ | vi <- zip in_vs [0 .. length s - 1]
81− , let vs = fst vi : ( if snd vi `elem` insertPoints
87+ , let vs = fst vi : ( if snd vi `elem` insert_points
82− then out_v
88+ then out_v
83− else []
89+ else []
84− )
90+ )
85− ]
91+ ]
86− return (s', remainingBytes)
92+ return (s', remaining_bytes)
87−
93+
88−isBytesOp :: CapOp -> Bool
94+is_bytes_op :: CapOp -> Bool
89−isBytesOp (Bytes {}) = True
95+is_bytes_op (Bytes {}) = True
90−-- isBytesOp _ = False
96+-- is_bytes_op _ = False
91−
97+
92−bytesForRange cap offset count
93− = Vector.toList $ Vector.take count $ Vector.drop offset $ capBytes cap
94−
95−collectBytes :: CapExpression -> [Word8]
98+collect_bytes :: CapExpression -> [Word8]
96−collectBytes e = concat [ bytes
99+collect_bytes e = concat [ bytes
97− | Bytes offset count <- capOps e
100+ | Bytes offset c <- cap_ops e
98− , let bytes = bytesForRange e offset count
101+ , let bytes = bytes_for_range e offset c
99− ]
102+ ]
100−
103+
101−
104+verify_bytes_equal out_bytes expected_bytes
102−verifyBytesEqual :: [Word8] -> [Word8] -> Result
105+ = if out_bytes == expected_bytes
103−verifyBytesEqual outBytes expectedBytes
104− = if outBytes == expectedBytes
105− then succeeded
106+ then liftResult succeeded
106− else failed
107+ else liftResult $ failed
107− { reason = "outBytes ["
108+ { reason = "out_bytes ["
108− ++ hexDump outBytes
109+ ++ hex_dump ( listArray (0, toEnum $ length out_bytes - 1) out_bytes )
109− ++ "] /= expectedBytes ["
110+ ++ "] /= expected_bytes ["
110− ++ hexDump expectedBytes
111+ ++ hex_dump ( listArray (0, toEnum $ length expected_bytes - 1) expected_bytes )
111− ++ "]"
112+ ++ "]"
112 }
113 }
114+
test/Verify/Graphics/Vty/Attributes.hs+19−52
@@ -1,73 +1,40 @@
1−module Verify.Graphics.Vty.Attributes
1+module Verify.Graphics.Vty.Attributes ( module Verify.Graphics.Vty.Attributes
2− ( module Verify.Graphics.Vty.Attributes
3− , module Graphics.Vty.Attributes
2+ , module Graphics.Vty.Attributes
4− )
3+ )
5−where
4+ where
6
5
7import Graphics.Vty.Attributes
6import Graphics.Vty.Attributes
8import Verify
7import Verify
9
8
10import Data.List ( delete )
9import Data.List ( delete )
11
10
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−
42-- Limit the possible attributes to just a few for now.
11-- Limit the possible attributes to just a few for now.
43−possibleAttrMods :: [ AttrOp ]
12+possible_attr_mods :: [ AttrOp ]
44−possibleAttrMods =
13+possible_attr_mods =
45− [ idOp
14+ [ set_bold_op
46− ] ++ map setForeColorOp allColors
15+ , id_op
47− ++ map setBackColorOp allColors
16+ ]
48− ++ map setStyleOp allStyles
49
17
50instance Arbitrary Attr where
18instance Arbitrary Attr where
51− arbitrary = elements possibleAttrMods >>= return . flip applyOp defAttr
19+ arbitrary = elements possible_attr_mods >>= return . flip apply_op def_attr
52
20
53data DiffAttr = DiffAttr Attr Attr
21data DiffAttr = DiffAttr Attr Attr
54
22
55instance Arbitrary DiffAttr where
23instance Arbitrary DiffAttr where
56 arbitrary = do
24 arbitrary = do
57− op0 <- elements possibleAttrMods
25+ op0 <- elements possible_attr_mods
58− let possibleAttrMods' = delete op0 possibleAttrMods
26+ let possible_attr_mods' = delete op0 possible_attr_mods
59− op1 <- elements possibleAttrMods'
27+ op1 <- elements possible_attr_mods'
60− return $ DiffAttr (applyOp op0 defAttr) (applyOp op1 defAttr)
28+ return $ DiffAttr (apply_op op0 def_attr) (apply_op op1 def_attr)
61
29
62data AttrOp = AttrOp String (Attr -> Attr)
30data AttrOp = AttrOp String (Attr -> Attr)
63
31
64instance Eq AttrOp where
32instance Eq AttrOp where
65 AttrOp n0 _ == AttrOp n1 _ = n0 == n1
33 AttrOp n0 _ == AttrOp n1 _ = n0 == n1
66
34
67−setStyleOp s = AttrOp "set_style" (flip withStyle s)
35+set_bold_op = AttrOp "set_bold" (flip with_style bold)
68−setForeColorOp c = AttrOp "set_fore_color" (flip withForeColor c)
36+id_op = AttrOp "id" id
69−setBackColorOp c = AttrOp "set_back_color" (flip withBackColor c)
37+
70−idOp = AttrOp "id" id
38+apply_op :: AttrOp -> Attr -> Attr
39+apply_op (AttrOp _ f) a = f a
71
40
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
1+module Verify.Graphics.Vty.DisplayAttributes ( module Verify.Graphics.Vty.DisplayAttributes
2− ( module Verify.Graphics.Vty.DisplayAttributes
3− , module Graphics.Vty.DisplayAttributes
2+ , module Graphics.Vty.DisplayAttributes
4− )
3+ )
5−where
4+ where
6
5
7import Graphics.Vty.DisplayAttributes
6import Graphics.Vty.DisplayAttributes
8import Verify
7import 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 #-}
2{-# LANGUAGE NamedFieldPuns #-}
1{-# LANGUAGE NamedFieldPuns #-}
3{-# LANGUAGE DisambiguateRecordFields #-}
2{-# LANGUAGE DisambiguateRecordFields #-}
4−module Verify.Graphics.Vty.Image
3+module Verify.Graphics.Vty.Image ( module Verify.Graphics.Vty.Image
5− ( module Verify.Graphics.Vty.Image
6− , module Graphics.Vty.Image
4+ , module Graphics.Vty.Image
7− )
5+ )
8−where
6+ where
9
7
10−import Verify.Graphics.Vty.Attributes
8+import Graphics.Vty.Debug
11import Graphics.Vty.Image
9import Graphics.Vty.Image
12−import Graphics.Vty.Image.Internal
10+import Graphics.Vty.DisplayRegion
13
11
14import Verify
12import Verify
13+import Verify.Graphics.Vty.Attributes
14+
15+import Data.Word
15
16
16data UnitImage = UnitImage Char Image
17data UnitImage = UnitImage Char Image
17
18
18instance Arbitrary UnitImage where
19instance Arbitrary UnitImage where
19 arbitrary = do
20 arbitrary = do
20 SingleColumnChar c <- arbitrary
21 SingleColumnChar c <- arbitrary
21− a <- arbitrary
22− return $ UnitImage c (char a c)
22+ return $ UnitImage c (char def_attr c)
23
23
24instance Show UnitImage where
24instance Show UnitImage where
25 show (UnitImage c _) = "UnitImage " ++ show c
25 show (UnitImage c _) = "UnitImage " ++ show c
26
26
27−data DefaultImage = DefaultImage Image
27+data DefaultImage = DefaultImage Image ImageConstructLog
28
28
29instance Show DefaultImage where
29instance Show DefaultImage where
30− show (DefaultImage i)
30+ show (DefaultImage i image_log)
31− = "DefaultImage (" ++ show i ++ ") " ++ show (imageWidth i, imageHeight i)
31+ = "DefaultImage (" ++ show i ++ ") " ++ show (image_width i, image_height i) ++ " " ++ show image_log
32
32
33instance Arbitrary DefaultImage where
33instance Arbitrary DefaultImage where
34 arbitrary = do
34 arbitrary = do
35− i <- return $ char defAttr 'X'
35+ i <- return $ char def_attr 'X' -- elements forward_image_ops >>= return . (\op -> op empty_image)
36− return $ DefaultImage i
36+ return $ DefaultImage i []
37−
37+
38−data SingleRowSingleAttrImage
38+data SingleRowSingleAttrImage
39− = SingleRowSingleAttrImage
39+ = SingleRowSingleAttrImage
40− { expectedAttr :: Attr
40+ { expected_attr :: Attr
41− , expectedColumns :: Int
41+ , expected_columns :: Word
42− , rowImage :: Image
42+ , row_image :: Image
43 }
43 }
44
44
45instance Show SingleRowSingleAttrImage where
45instance Show SingleRowSingleAttrImage where
46− show (SingleRowSingleAttrImage attr columns image)
46+ show (SingleRowSingleAttrImage attr columns image)
47 = "SingleRowSingleAttrImage (" ++ show attr ++ ") " ++ show columns ++ " ( " ++ show image ++ " )"
47 = "SingleRowSingleAttrImage (" ++ show attr ++ ") " ++ show columns ++ " ( " ++ show image ++ " )"
48
48
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−
102instance Arbitrary SingleRowSingleAttrImage where
49instance Arbitrary SingleRowSingleAttrImage where
103 arbitrary = do
50 arbitrary = do
104− -- The text must contain at least one character. Otherwise the
51+ -- The text must contain at least one character. Otherwise the image simplifies to 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
52+ -- IdImage which has a height of 0. If this is to represent a single row then the height
107− singleColumnRowText <- Verify.resize 16 (listOf1 arbitrary)
53+ -- must be 1
54+ single_column_row_text <- listOf1 arbitrary
108− a <- arbitrary
55+ attr <- arbitrary
109− let outImage = horizCat $ [char a c | SingleColumnChar c <- singleColumnRowText]
56+ return $ SingleRowSingleAttrImage
110− outWidth = length singleColumnRowText
57+ attr
111− return $ SingleRowSingleAttrImage a outWidth outImage
58+ ( fromIntegral $ length single_column_row_text )
112−
59+ ( horiz_cat $ [ char attr c | SingleColumnChar c <- single_column_row_text ] )
60+
113−data SingleRowTwoAttrImage
61+data SingleRowTwoAttrImage
114− = SingleRowTwoAttrImage
62+ = SingleRowTwoAttrImage
115− { part0 :: SingleRowSingleAttrImage
63+ { part_0 :: SingleRowSingleAttrImage
116− , part1 :: SingleRowSingleAttrImage
64+ , part_1 :: SingleRowSingleAttrImage
117− , joinImage :: Image
65+ , join_image :: Image
118 } deriving Show
66 } deriving Show
119
67
120instance Arbitrary SingleRowTwoAttrImage where
68instance Arbitrary SingleRowTwoAttrImage where
121 arbitrary = do
69 arbitrary = do
122 p0 <- arbitrary
70 p0 <- arbitrary
123 p1 <- arbitrary
71 p1 <- arbitrary
124− return $ SingleRowTwoAttrImage p0 p1 (rowImage p0 <|> rowImage p1)
72+ return $ SingleRowTwoAttrImage p0 p1 (row_image p0 <|> row_image p1)
125
73
126−data SingleAttrSingleSpanStack = SingleAttrSingleSpanStack
74+data SingleAttrSingleSpanStack = SingleAttrSingleSpanStack
127− { stackImage :: Image
75+ { stack_image :: Image
128− , stackSourceImages :: [SingleRowSingleAttrImage]
76+ , stack_source_images :: [SingleRowSingleAttrImage]
129− , stackWidth :: Int
77+ , stack_width :: Word
130− , stackHeight :: Int
78+ , stack_height :: Word
131 }
79 }
132 deriving Show
80 deriving Show
133
81
134instance Arbitrary SingleAttrSingleSpanStack where
82instance Arbitrary SingleAttrSingleSpanStack where
135 arbitrary = do
83 arbitrary = do
136− imageList <- Verify.resize 16 (listOf1 arbitrary)
84+ NonEmpty image_list <- 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 ]
85+ let image = vert_cat [ i | SingleRowSingleAttrImage { row_image = i } <- image_list ]
146− in SingleAttrSingleSpanStack image imageList (maximum $ map expectedColumns imageList)
86+ return $ SingleAttrSingleSpanStack
87+ image
88+ image_list
89+ ( maximum $ map expected_columns image_list )
147− (toEnum $ length imageList)
90+ ( toEnum $ length image_list )
148−
91+
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'
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 @@
1{-# LANGUAGE DisambiguateRecordFields #-}
1{-# LANGUAGE DisambiguateRecordFields #-}
2−module Verify.Graphics.Vty.Picture
2+module Verify.Graphics.Vty.Picture ( module Verify.Graphics.Vty.Picture
3− ( module Verify.Graphics.Vty.Picture
4− , module Graphics.Vty.Picture
3+ , module Graphics.Vty.Picture
5− )
4+ )
6−where
5+ where
7−
8−import Verify.Graphics.Vty.Prelude
9
6
10import Graphics.Vty.Picture
7import Graphics.Vty.Picture
11import Graphics.Vty.Debug
8import Graphics.Vty.Debug
12
9
13import Verify.Graphics.Vty.Attributes
10import Verify.Graphics.Vty.Attributes
14import Verify.Graphics.Vty.Image
11import Verify.Graphics.Vty.Image
12+import Verify.Graphics.Vty.DisplayRegion
15
13
16import Verify
14import Verify
17
15
18−data DefaultPic = DefaultPic
16+data DefaultPic = DefaultPic
19− { defaultPic :: Picture
17+ { default_pic :: Picture
20− , defaultWin :: MockWindow
18+ , default_win :: DebugWindow
19+ , default_construct_log :: ImageConstructLog
21 }
20 }
22
21
23instance Show DefaultPic where
22instance Show DefaultPic where
24− show (DefaultPic pic win)
23+ show (DefaultPic pic win image_log)
25− = "DefaultPic\n\t( " ++ show pic ++ ")\n\t" ++ show win ++ "\n"
24+ = "DefaultPic\n\t( " ++ show pic ++ ")\n\t" ++ show win ++ "\n\t" ++ show image_log ++ "\n"
26
25
27instance Arbitrary DefaultPic where
26instance Arbitrary DefaultPic where
28 arbitrary = do
27 arbitrary = do
29− DefaultImage image <- arbitrary
28+ DefaultImage image image_construct_events <- arbitrary
30− let win = MockWindow (imageWidth image) (imageHeight image)
29+ let win = DebugWindow (image_width image) (image_height image)
31− return $ DefaultPic (picForImage image)
30+ return $ DefaultPic (pic_for_image image)
32− win
31+ win
33−
32+ image_construct_events
33+
34−data PicWithBGAttr = PicWithBGAttr
34+data PicWithBGAttr = PicWithBGAttr
35− { withAttrPic :: Picture
35+ { with_attr_pic :: Picture
36− , withAttrWin :: MockWindow
36+ , with_attr_win :: DebugWindow
37− , withAttrSpecifiedAttr :: Attr
37+ , with_attr_construct_log :: ImageConstructLog
38+ , with_attr_specified_attr :: Attr
38 } deriving ( Show )
39 } deriving ( Show )
39
40
40instance Arbitrary PicWithBGAttr where
41instance Arbitrary PicWithBGAttr where
41 arbitrary = do
42 arbitrary = do
42− DefaultImage image <- arbitrary
43+ DefaultImage image image_construct_events <- arbitrary
43− let win = MockWindow (imageWidth image) (imageHeight image)
44+ let win = DebugWindow (image_width image) (image_height image)
44 attr <- arbitrary
45 attr <- arbitrary
45− return $ PicWithBGAttr (picForImage image)
46+ return $ PicWithBGAttr (pic_for_image image)
46− win
47+ win
48+ image_construct_events
47 attr
49 attr
48−
50+
49−instance Arbitrary Picture where
50− arbitrary = do
51− layers <- Verify.resize 20 (listOf1 arbitrary)
52− return $ picForLayers layers
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 @@
1{-# LANGUAGE FlexibleInstances #-}
1{-# LANGUAGE FlexibleInstances #-}
2−module Verify.Graphics.Vty.Span
2+module Verify.Graphics.Vty.Span ( module Verify.Graphics.Vty.Span
3− ( module Verify.Graphics.Vty.Span
4− , module Graphics.Vty.Span
3+ , module Graphics.Vty.Span
5− )
4+ )
6−where
5+ where
7
6
8import Graphics.Vty.Debug
7import Graphics.Vty.Debug
9import Graphics.Vty.Span
8import Graphics.Vty.Span
10
9
11import Verify.Graphics.Vty.Picture
10import Verify.Graphics.Vty.Picture
12
11
13−import qualified Data.Vector as Vector
14import Data.Word
12import Data.Word
15
13
16import Verify
14import Verify
17
15
18−isAttrSpanOp :: SpanOp -> Bool
16+is_attr_span_op :: SpanOp -> Bool
19−isAttrSpanOp TextSpan {} = True
17+is_attr_span_op AttributeChange {} = True
20−isAttrSpanOp _ = False
18+is_attr_span_op _ = False
21
19
22−verifyAllSpansHaveWidth i spans w
20+verify_all_spans_have_width i spans w
23− = let v = map (\s -> (spanOpsAffectedColumns s /= w, s)) (Vector.toList spans)
21+ = case all_spans_have_width spans w of
24− in case any ((== True) . fst) v of
22+ True -> succeeded
25− False -> succeeded
26− True -> failed { reason = "Not all spans contained operations defining exactly "
23+ False -> failed { reason = "Not all spans contained operations defining exactly "
27− ++ show w
24+ ++ show w
28− ++ " columns of output - \n"
25+ ++ " columns of output -\n"
29− ++ (concatMap ((++ "\n") . show)) v
26+ ++ show i
30− }
27+ ++ "\n->\n"
31−
28+ ++ show spans
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− }
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 @@
2{-# LANGUAGE ScopedTypeVariables #-}
2{-# LANGUAGE ScopedTypeVariables #-}
3module Main where
3module Main where
4
4
5−import Graphics.Vty
6import Graphics.Vty.Attributes
5import Graphics.Vty.Attributes
6+import Graphics.Vty.Image
7import Graphics.Vty.Inline
7import Graphics.Vty.Inline
8import Graphics.Vty.Picture
8import Graphics.Vty.Picture
9−import Graphics.Vty.Output
9+import Graphics.Vty.Terminal
10+import Graphics.Vty.DisplayRegion
10
11
11−import Control.Concurrent (threadDelay)
12−import Control.Exception
13import Control.Monad
12import Control.Monad
14
13
15import Data.List ( lookup )
14import Data.List ( lookup )
16import Data.Maybe ( isJust, fromJust )
15import Data.Maybe ( isJust, fromJust )
17import Data.Monoid
16import Data.Monoid
18−import Data.String.QQ
19import Data.Word
17import Data.Word
20
18
21−import Foreign.Marshal.Array
19+import Foreign.Marshal.Array
20+import HereDoc
22
21
23import qualified System.Environment as Env
22import qualified System.Environment as Env
24
23
25import System.IO ( hFlush, hPutStr, hPutBuf, stdout )
24import System.IO ( hFlush, hPutStr, hPutBuf, stdout )
26
25
27main = do
26main = do
28− printIntro
27+ print_intro
29
28
30−outputFilePath = "test_results.list"
29+output_file_path = "test_results.list"
31
30
32−printIntro = do
31+print_intro = do
33− putStr $ [s|
32+ putStr $ [$heredoc|
34This is an interactive verification program for the terminal input and output
33This is an interactive verification program for the terminal input and output
35support of the VTY library. This will ask a series of questions about what you
34support of the VTY library. This will ask a series of questions about what you
36see on screen. The goal is to verify that VTY's output and input support
35see on screen. The goal is to verify that VTY's output and input support
37performs as expected with your terminal.
36performs as expected with your terminal.
38
37
39This program produces a file named
38This program produces a file named
40− |] ++ outputFilePath ++ [s|
39+ |] ++ output_file_path ++ [$heredoc|
41in the current directory that contains the results for each test assertion. This
40in the current directory that contains the results for each test assertion. This
42can be emailed to coreyoconnor@gmail.com and used by the VTY authors to improve
41can be emailed to coreyoconnor@gmail.com and used by the VTY authors to improve
43support for your terminal. No personal information is contained in the report.
42support 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:
63 a good range of the unicode characters. Each test involving unicode display
62 a good range of the unicode characters. Each test involving unicode display
64 describes the expected appearance of each glyph.
63 describes the expected appearance of each glyph.
65
64
66−Thanks for the help! :-D
65+Thanks for the help! :-D
67−To exit the test early enter "q" anytime at the following menu screen.
66+To exit the test early enter "q" anytime at the following menu screen. Even if
68−
67+you exit the test early please email the test_results.list file to
69−If any test fails then please post an issue to
68+coreyoconnor@gmail.com. The results file will still contain information useful
70− https://github.com/coreyoconnor/vty/issues
69+to debug terminal support.
71−with the test_results.list file pasted into the issue. A suitable summary is:
70+
72−"interactive terminal test failure".
73−|]
71+|]
74− waitForReturn
72+ wait_for_return
75− results <- doTestMenu 1
73+ results <- do_test_menu 1
76− envAttributes <- mapM ( \envName -> Control.Exception.catch ( Env.getEnv envName >>= return . (,) envName )
74+ env_attributes <- mapM ( \env_name -> catch ( Env.getEnv env_name >>= return . (,) env_name )
77− ( \ (_ :: SomeException) -> return (envName, "") )
75+ ( const $ return (env_name, "") )
78− )
76+ )
79− [ "TERM", "COLORTERM", "LANG", "TERM_PROGRAM", "XTERM_VERSION" ]
77+ [ "TERM", "COLORTERM", "LANG", "TERM_PROGRAM", "XTERM_VERSION" ]
80− t <- standardIOConfig >>= outputForConfig
78+ t <- terminal_handle
81− let resultsTxt = show envAttributes ++ "\n"
79+ let results_txt = show env_attributes ++ "\n"
82− ++ terminalID t ++ "\n"
80+ ++ terminal_ID t ++ "\n"
83− ++ show results ++ "\n"
81+ ++ show results ++ "\n"
84− releaseTerminal t
82+ release_terminal t
85− writeFile outputFilePath resultsTxt
83+ writeFile output_file_path results_txt
86−
84+
87−waitForReturn = do
85+wait_for_return = do
88 putStr "\n(press return to continue)"
86 putStr "\n(press return to continue)"
89 hFlush stdout
87 hFlush stdout
90 getLine
88 getLine
91
89
92−testMenu :: [(String, Test)]
90+test_menu :: [(String, Test)]
93−testMenu = zip (map show [1..]) allTests
91+test_menu = zip (map show [1..]) all_tests
94
92
95−doTestMenu :: Int -> IO [(String, Bool)]
93+do_test_menu :: Int -> IO [(String, Bool)]
96−doTestMenu nextID
94+do_test_menu next_ID
97− | nextID > length allTests = do
95+ | next_ID > length all_tests = do
98− putStrLn $ "Done! Please email the " ++ outputFilePath ++ " file to coreyoconnor@gmail.com"
96+ putStrLn $ "Done! Please email the " ++ output_file_path ++ " file to coreyoconnor@gmail.com"
99 return []
97 return []
100 | otherwise = do
98 | otherwise = do
101− displayTestMenu
99+ display_test_menu
102− putStrLn $ "Press return to start with #" ++ show nextID ++ "."
100+ putStrLn $ "Press return to start with #" ++ show next_ID ++ "."
103 putStrLn "Enter a test number to perform only that test."
101 putStrLn "Enter a test number to perform only that test."
104 putStrLn "q (or control-C) to quit."
102 putStrLn "q (or control-C) to quit."
105 putStr "> "
103 putStr "> "
@@ -107,102 +105,90 @@ doTestMenu nextID
107 s <- getLine >>= return . filter (/= '\n')
105 s <- getLine >>= return . filter (/= '\n')
108 case s of
106 case s of
109 "q" -> return mempty
107 "q" -> return mempty
110− "" -> do
108+ "" -> do
111− r <- runTest $ show nextID
109+ r <- run_test $ show next_ID
112− rs <- doTestMenu ( nextID + 1 )
110+ rs <- do_test_menu ( next_ID + 1 )
113 return $ r : rs
111 return $ r : rs
114− i | isJust ( lookup i testMenu ) -> do
112+ i | isJust ( lookup i test_menu ) -> do
115− r <- runTest i
113+ r <- run_test i
116− rs <- doTestMenu ( read i + 1 )
114+ rs <- do_test_menu ( read i + 1 )
117 return $ r : rs
115 return $ r : rs
118 where
116 where
119− displayTestMenu
117+ display_test_menu
120− = mapM_ displayTestMenu' testMenu
118+ = mapM_ display_test_menu' test_menu
121− displayTestMenu' ( i, t )
119+ display_test_menu' ( i, t )
122− = putStrLn $ ( if i == show nextID
120+ = putStrLn $ ( if i == show next_ID
123− then "> "
121+ then "> "
124 else " "
122 else " "
125− ) ++ i ++ ". " ++ testName t
123+ ) ++ i ++ ". " ++ test_name t
126−
124+
127−runTest :: String -> IO (String, Bool)
125+run_test :: String -> IO (String, Bool)
128−runTest i = do
126+run_test i = do
129− let t = fromJust $ lookup i testMenu
127+ let t = fromJust $ lookup i test_menu
130− printSummary t
128+ print_summary t
131− waitForReturn
129+ wait_for_return
132− testAction t
130+ test_action t
133− r <- confirmResults t
131+ r <- confirm_results t
134− return (testID t, r)
132+ return (test_ID t, r)
135−
133+
136−defaultSuccessConfirmResults = do
134+default_success_confirm_results = do
137 putStr "\n"
135 putStr "\n"
138 putStr "[Y/n] "
136 putStr "[Y/n] "
139 hFlush stdout
137 hFlush stdout
140 r <- getLine
138 r <- getLine
141 case r of
139 case r of
142− "" -> return True
140+ "" -> return True
143 "y" -> return True
141 "y" -> return True
144 "Y" -> return True
142 "Y" -> return True
145 "n" -> return False
143 "n" -> return False
146
144
147data Test = Test
145data Test = Test
148− { testName :: String
146+ { test_name :: String
149− , testID :: String
147+ , test_ID :: String
150− , testAction :: IO ()
148+ , test_action :: IO ()
151− , printSummary :: IO ()
149+ , print_summary :: IO ()
152− , confirmResults :: IO Bool
150+ , confirm_results :: IO Bool
153 }
151 }
154
152
155−allTests
153+all_tests
156− = [ reserveOutputTest
154+ = [ reserve_output_test
157− , displayBoundsTest0
155+ , display_bounds_test_0
158− , displayBoundsTest1
156+ , display_bounds_test_1
159− , displayBoundsTest2
157+ , display_bounds_test_2
160− , displayBoundsTest3
158+ , display_bounds_test_3
161− , unicodeSingleWidth0
159+ , unicode_single_width_0
162− , unicodeSingleWidth1
160+ , unicode_single_width_1
163− , unicodeDoubleWidth0
161+ , unicode_double_width_0
164− , unicodeDoubleWidth1
162+ , unicode_double_width_1
165− , attributesTest0
163+ , attributes_test_0
166− , attributesTest1
164+ , attributes_test_1
167− , attributesTest2
165+ , attributes_test_2
168− , attributesTest3
166+ , attributes_test_3
169− , attributesTest4
167+ , attributes_test_4
170− , attributesTest5
168+ , attributes_test_5
171− , inlineTest0
169+ , inline_test_0
172− , inlineTest1
170+ , inline_test_1
173− , inlineTest2
174− , cursorHideTest0
175− , vertCropTest0
176− , vertCropTest1
177− , vertCropTest2
178− , vertCropTest3
179− , horizCropTest0
180− , horizCropTest1
181− , horizCropTest2
182− , horizCropTest3
183− , layer0
184− , layer1
185 ]
171 ]
186
172
187−reserveOutputTest = Test
173+reserve_output_test = Test
188− { testName = "Initialize and reserve terminal output then restore previous state."
174+ { test_name = "Initialize and reserve terminal output then restore previous state."
189− , testID = "reserveOutputTest"
175+ , test_ID = "reserve_output_test"
190− , testAction = do
176+ , test_action = do
191− t <- standardIOConfig >>= outputForConfig
177+ t <- terminal_handle
192− reserveDisplay t
178+ reserve_display t
193 putStrLn "Line 1"
179 putStrLn "Line 1"
194 putStrLn "Line 2"
180 putStrLn "Line 2"
195 putStrLn "Line 3"
181 putStrLn "Line 3"
196 putStrLn "Line 4 (press return)"
182 putStrLn "Line 4 (press return)"
197 hFlush stdout
183 hFlush stdout
198 getLine
184 getLine
199− releaseDisplay t
185+ release_display t
200− releaseTerminal t
186+ release_terminal t
201 return ()
187 return ()
202− , printSummary = do
188+ , print_summary = do
203− putStr $ [s|
189+ putStr $ [$heredoc|
204Once return is pressed:
190Once return is pressed:
205− 0. The screen will be cleared.
191+ 0. The screen will be cleared.
206 1. Four lines of text should be visible.
192 1. Four lines of text should be visible.
207 1. The cursor should be visible and at the start of the fifth line.
193 1. The cursor should be visible and at the start of the fifth line.
208
194
@@ -210,143 +196,135 @@ After return is pressed for the second time this test then:
210 * The screen containing the test summary should be restored;
196 * The screen containing the test summary should be restored;
211 * The cursor is visible.
197 * The cursor is visible.
212|]
198|]
213− , confirmResults = do
199+ , confirm_results = do
214− putStr $ [s|
200+ putStr $ [$heredoc|
215Did the test output match the description?
201Did the test output match the description?
216|]
202|]
217− defaultSuccessConfirmResults
203+ default_success_confirm_results
218 }
204 }
219
205
220−displayBoundsTest0 = Test
206+display_bounds_test_0 = Test
221− { testName = "Verify display bounds are correct test 0: Using spaces."
207+ { test_name = "Verify display bounds are correct test 0: Using spaces."
222− , testID = "displayBoundsTest0"
208+ , test_ID = "display_bounds_test_0"
223− , testAction = do
209+ , test_action = do
224− t <- standardIOConfig >>= outputForConfig
210+ t <- terminal_handle
225− reserveDisplay t
211+ reserve_display t
226− (w,h) <- displayBounds t
212+ DisplayRegion w h <- display_bounds t
227− let row0 = replicate (fromEnum w) 'X' ++ "\n"
213+ let row_0 = replicate (fromEnum w) 'X' ++ "\n"
228− rowH = replicate (fromEnum w - 1) 'X'
214+ row_h = replicate (fromEnum w - 1) 'X'
229− rowN = "X" ++ replicate (fromEnum w - 2) ' ' ++ "X\n"
215+ row_n = "X" ++ replicate (fromEnum w - 2) ' ' ++ "X\n"
230− image = row0 ++ (concat $ replicate (fromEnum h - 2) rowN) ++ rowH
216+ image = row_0 ++ (concat $ replicate (fromEnum h - 2) row_n) ++ row_h
231 putStr image
217 putStr image
232 hFlush stdout
218 hFlush stdout
233 getLine
219 getLine
234− releaseDisplay t
220+ release_display t
235− releaseTerminal t
221+ release_terminal t
236 return ()
222 return ()
237− , printSummary = displayBoundsTestSummary True
223+ , print_summary = display_bounds_test_summary True
238− , confirmResults = genericOutputMatchConfirm
224+ , confirm_results = generic_output_match_confirm
239 }
225 }
240
226
241−displayBoundsTest1 = Test
227+display_bounds_test_1 = Test
242− { testName = "Verify display bounds are correct test 0: Using cursor movement."
228+ { test_name = "Verify display bounds are correct test 0: Using cursor movement."
243− , testID = "displayBoundsTest1"
229+ , test_ID = "display_bounds_test_1"
244− , testAction = do
230+ , test_action = do
245− t <- standardIOConfig >>= outputForConfig
231+ t <- terminal_handle
246− reserveDisplay t
232+ reserve_display t
247− (w,h) <- displayBounds t
233+ DisplayRegion w h <- display_bounds t
248− setCursorPos t 0 0
234+ set_cursor_pos t 0 0
249− let row0 = replicate (fromEnum w) 'X' ++ "\n"
235+ let row_0 = replicate (fromEnum w) 'X' ++ "\n"
250− putStr row0
236+ putStr row_0
251 forM_ [1 .. h - 2] $ \y -> do
237 forM_ [1 .. h - 2] $ \y -> do
252− setCursorPos t 0 y
238+ set_cursor_pos t 0 y
253 putStr "X"
239 putStr "X"
254− hFlush stdout
240+ set_cursor_pos t (w - 1) y
255− setCursorPos t (w - 1) y
256 putStr "X"
241 putStr "X"
257− hFlush stdout
242+ set_cursor_pos t 0 (h - 1)
258− setCursorPos t 0 (h - 1)
259− let rowH = replicate (fromEnum w - 1) 'X'
243+ let row_h = replicate (fromEnum w - 1) 'X'
260− putStr rowH
244+ putStr row_h
261 hFlush stdout
245 hFlush stdout
262 getLine
246 getLine
263− releaseDisplay t
247+ release_display t
264− releaseTerminal t
248+ release_terminal t
265 return ()
249 return ()
266− , printSummary = displayBoundsTestSummary True
250+ , print_summary = display_bounds_test_summary True
267− , confirmResults = genericOutputMatchConfirm
251+ , confirm_results = generic_output_match_confirm
268 }
252 }
269
253
270−displayBoundsTest2 = Test
254+display_bounds_test_2 = Test
271− { testName = "Verify display bounds are correct test 0: Using Image ops."
255+ { test_name = "Verify display bounds are correct test 0: Using Image ops."
272− , testID = "displayBoundsTest2"
256+ , test_ID = "display_bounds_test_2"
273− , testAction = do
257+ , test_action = do
274− t <- standardIOConfig >>= outputForConfig
258+ t <- terminal_handle
275− reserveDisplay t
259+ reserve_display t
276− bounds@(w,h) <- displayBounds t
260+ bounds@(DisplayRegion w h) <- display_bounds t
277− let firstRow = horizCat $ replicate (fromEnum w) (char defAttr 'X')
261+ let first_row = horiz_cat $ replicate (fromEnum w) (char def_attr 'X')
278− middleRows = vertCat $ replicate (fromEnum h - 2) middleRow
262+ middle_rows = vert_cat $ replicate (fromEnum h - 2) middle_row
279− middleRow = (char defAttr 'X') <|> backgroundFill (w - 2) 1 <|> (char defAttr 'X')
263+ middle_row = (char def_attr 'X') <|> background_fill (w - 2) 1 <|> (char def_attr 'X')
280− endRow = firstRow
264+ end_row = first_row
281− image = firstRow <-> middleRows <-> endRow
265+ image = first_row <-> middle_rows <-> end_row
282− pic = (picForImage image) { picCursor = Cursor (w - 1) (h - 1) }
266+ pic = (pic_for_image image) { pic_cursor = Cursor (w - 1) (h - 1) }
283− d <- displayContext t bounds
267+ d <- display_context t bounds
284− outputPicture d pic
268+ output_picture d pic
285 getLine
269 getLine
286− releaseDisplay t
270+ release_display t
287− releaseTerminal t
271+ release_terminal t
288 return ()
272 return ()
289− , printSummary = displayBoundsTestSummary True
273+ , print_summary = display_bounds_test_summary True
290− , confirmResults = genericOutputMatchConfirm
274+ , confirm_results = generic_output_match_confirm
291 }
275 }
292
276
293−displayBoundsTest3 = Test
277+display_bounds_test_3 = Test
294− { testName = "Verify display bounds are correct test 0: Hide cursor; Set cursor pos."
278+ { test_name = "Verify display bounds are correct test 0: Hide cursor; Set cursor pos."
295− , testID = "displayBoundsTest3"
279+ , test_ID = "display_bounds_test_3"
296− , testAction = do
280+ , test_action = do
297− t <- standardIOConfig >>= outputForConfig
281+ t <- terminal_handle
298− reserveDisplay t
282+ reserve_display t
299− (w,h) <- displayBounds t
283+ DisplayRegion w h <- display_bounds t
300− hideCursor t
284+ hide_cursor t
301− setCursorPos t 0 0
285+ set_cursor_pos t 0 0
302− let row0 = replicate (fromEnum w) 'X'
286+ let row_0 = replicate (fromEnum w) 'X'
303− putStrLn row0
287+ putStrLn row_0
304 forM_ [1 .. h - 2] $ \y -> do
288 forM_ [1 .. h - 2] $ \y -> do
305− setCursorPos t 0 y
289+ set_cursor_pos t 0 y
306 putStr "X"
290 putStr "X"
307− hFlush stdout
291+ set_cursor_pos t (w - 1) y
308− setCursorPos t (w - 1) y
309 putStr "X"
292 putStr "X"
310− hFlush stdout
293+ set_cursor_pos t 0 (h - 1)
311− setCursorPos t 0 (h - 1)
294+ let row_h = row_0
312− let rowH = row0
313− putStr rowH
295+ putStr row_h
314 hFlush stdout
296 hFlush stdout
315 getLine
297 getLine
316− showCursor t
298+ show_cursor t
317− releaseDisplay t
299+ release_display t
318− releaseTerminal t
300+ release_terminal t
319 return ()
301 return ()
320− , printSummary = displayBoundsTestSummary False
302+ , print_summary = display_bounds_test_summary False
321− , confirmResults = genericOutputMatchConfirm
303+ , confirm_results = generic_output_match_confirm
322 }
304 }
323
305
324−displayBoundsTestSummary hasCursor = do
306+display_bounds_test_summary has_cursor = do
325− putStr $ [s|
307+ putStr $ [$heredoc|
326Once return is pressed:
308Once return is pressed:
327 0. The screen will be cleared.
309 0. The screen will be cleared.
328|]
310|]
329− if hasCursor
311+ if has_cursor
330 then putStr " 1. The cursor will be visible."
312 then putStr " 1. The cursor will be visible."
331 else putStr " 1. The cursor will NOT be visible."
313 else putStr " 1. The cursor will NOT be visible."
332− putStr [s|
314+ putStr [$heredoc|
333−
334 2. The border of the display will be outlined in Xs.
315 2. The border of the display will be outlined in Xs.
335 So if - and | represented the edge of the terminal window:
316 So if - and | represented the edge of the terminal window:
336 |-------------|
317 |-------------|
337 |XXXXXXXXXXXXX|
318 |XXXXXXXXXXXXX|
338 |X X||]
319 |X X||]
339
320
340− if hasCursor
321+ if has_cursor
341− then putStr $ [s|
322+ then putStr $ [$heredoc|
342−
343 |XXXXXXXXXXXXC| |]
323 |XXXXXXXXXXXXC| |]
344− else putStr $ [s|
324+ else putStr $ [$heredoc|
345−
346 |XXXXXXXXXXXXX| |]
325 |XXXXXXXXXXXXX| |]
347
326
348− putStr $ [s|
327+ putStr $ [$heredoc|
349−
350 |-------------|
328 |-------------|
351
329
352 ( Where C is the final position of the cursor. There may be an X drawn
330 ( 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:
358 1. The cursor should be visible.
336 1. The cursor should be visible.
359|]
337|]
360
338
361−genericOutputMatchConfirm = do
339+generic_output_match_confirm = do
362− putStr $ [s|
340+ putStr $ [$heredoc|
363Did the test output match the description?
341Did the test output match the description?
364|]
342|]
365− defaultSuccessConfirmResults
343+ default_success_confirm_results
366
344
367−-- Explicitly define the bytes that encode each example text.
345+-- Explicitely definethe bytes that encode each example text.
368-- This avoids any issues with how the compiler represents string literals.
346-- This avoids any issues with how the compiler represents string literals.
369--
347--
370-- This document is UTF-8 encoded so the UTF-8 string is still included for
348-- 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?
373-- It's assumed the compiler will at least not barf on UTF-8 encoded text in
351-- It's assumed the compiler will at least not barf on UTF-8 encoded text in
374-- comments ;-)
352-- comments ;-)
375--
353--
376−-- txt0 = ↑↑↓↓←→←→BA
354+-- txt_0 = ↑↑↓↓←→←→BA
377−
355+
378−utf8Txt0 :: [[Word8]]
356+utf8_txt_0 :: [[Word8]]
379−utf8Txt0 = [ [ 0xe2 , 0x86 , 0x91 ]
357+utf8_txt_0 = [ [ 0xe2 , 0x86 , 0x91 ]
380− , [ 0xe2 , 0x86 , 0x91 ]
358+ , [ 0xe2 , 0x86 , 0x91 ]
381− , [ 0xe2 , 0x86 , 0x93 ]
359+ , [ 0xe2 , 0x86 , 0x93 ]
382− , [ 0xe2 , 0x86 , 0x93 ]
360+ , [ 0xe2 , 0x86 , 0x93 ]
383− , [ 0xe2 , 0x86 , 0x90 ]
361+ , [ 0xe2 , 0x86 , 0x90 ]
384− , [ 0xe2 , 0x86 , 0x92 ]
362+ , [ 0xe2 , 0x86 , 0x92 ]
385− , [ 0xe2 , 0x86 , 0x90 ]
363+ , [ 0xe2 , 0x86 , 0x90 ]
386− , [ 0xe2 , 0x86 , 0x92 ]
364+ , [ 0xe2 , 0x86 , 0x92 ]
387− , [ 0x42 ]
365+ , [ 0x42 ]
388− , [ 0x41 ]
366+ , [ 0x41 ]
389− ]
367+ ]
390−
368+
391−iso10646Txt0 :: String
369+iso_10646_txt_0 :: String
392−iso10646Txt0 = map toEnum
370+iso_10646_txt_0 = map toEnum
393− [ 8593
371+ [ 8593
394 , 8593
372 , 8593
395 , 8595
373 , 8595
396 , 8595
374 , 8595
@@ -402,46 +380,46 @@ iso10646Txt0 = map toEnum
402 , 65
380 , 65
403 ]
381 ]
404
382
405−unicodeSingleWidth0 = Test
383+unicode_single_width_0 = Test
406− { testName = "Verify terminal can display unicode single-width characters. (Direct UTF-8)"
384+ { test_name = "Verify terminal can display unicode single-width characters. (Direct UTF-8)"
407− , testID = "unicodeSingleWidth0"
385+ , test_ID = "unicode_single_width_0"
408− , testAction = do
386+ , test_action = do
409− t <- standardIOConfig >>= outputForConfig
387+ t <- terminal_handle
410− reserveDisplay t
388+ reserve_display t
411− hideCursor t
389+ hide_cursor t
412− withArrayLen (concat utf8Txt0) (flip $ hPutBuf stdout)
390+ withArrayLen (concat utf8_txt_0) (flip $ hPutBuf stdout)
413 hPutStr stdout "\n"
391 hPutStr stdout "\n"
414 hPutStr stdout "0123456789\n"
392 hPutStr stdout "0123456789\n"
415 hFlush stdout
393 hFlush stdout
416 getLine
394 getLine
417− releaseDisplay t
395+ release_display t
418− releaseTerminal t
396+ release_terminal t
419 return ()
397 return ()
420− , printSummary = unicodeSingleWidthSummary
398+ , print_summary = unicode_single_width_summary
421− , confirmResults = genericOutputMatchConfirm
399+ , confirm_results = generic_output_match_confirm
422 }
400 }
423
401
424−unicodeSingleWidth1 = Test
402+unicode_single_width_1 = Test
425− { testName = "Verify terminal can display unicode single-width characters. (Image ops)"
403+ { test_name = "Verify terminal can display unicode single-width characters. (Image ops)"
426− , testID = "unicodeSingleWidth1"
404+ , test_ID = "unicode_single_width_1"
427− , testAction = do
405+ , test_action = do
428− t <- standardIOConfig >>= outputForConfig
406+ t <- terminal_handle
429− reserveDisplay t
407+ reserve_display t
430− let pic = picForImage image
408+ let pic = pic_for_image image
431− image = line0 <-> line1
409+ image = line_0 <-> line_1
432− line0 = iso10646String defAttr iso10646Txt0
410+ line_0 = iso_10646_string def_attr iso_10646_txt_0
433− line1 = string defAttr "0123456789"
411+ line_1 = string def_attr "0123456789"
434− d <- displayBounds t >>= displayContext t
412+ d <- display_bounds t >>= display_context t
435− outputPicture d pic
413+ output_picture d pic
436 getLine
414 getLine
437− releaseDisplay t
415+ release_display t
438− releaseTerminal t
416+ release_terminal t
439 return ()
417 return ()
440− , printSummary = unicodeSingleWidthSummary
418+ , print_summary = unicode_single_width_summary
441− , confirmResults = genericOutputMatchConfirm
419+ , confirm_results = generic_output_match_confirm
442 }
420 }
443
421
444−unicodeSingleWidthSummary = putStr [s|
422+unicode_single_width_summary = putStr [$heredoc|
445Once return is pressed:
423Once return is pressed:
446 0. The screen will be cleared.
424 0. The screen will be cleared.
447 1. The cursor will be hidden.
425 1. The cursor will be hidden.
@@ -475,55 +453,55 @@ After return is pressed for the second time:
475
453
476-- The second example is a unicode string containing double-width glyphs
454-- The second example is a unicode string containing double-width glyphs
477-- 你好吗
455-- 你好吗
478−utf8Txt1 :: [[Word8]]
456+utf8_txt_1 :: [[Word8]]
479−utf8Txt1 = [ [0xe4,0xbd,0xa0]
457+utf8_txt_1 = [ [0xe4,0xbd,0xa0]
480− , [0xe5,0xa5,0xbd]
458+ , [0xe5,0xa5,0xbd]
481− , [0xe5,0x90,0x97]
459+ , [0xe5,0x90,0x97]
482− ]
460+ ]
483−
461+
484−iso10646Txt1 :: String
462+iso_10646_txt_1 :: String
485−iso10646Txt1 = map toEnum [20320,22909,21527]
463+iso_10646_txt_1 = map toEnum [20320,22909,21527]
486−
464+
487−unicodeDoubleWidth0 = Test
465+unicode_double_width_0 = Test
488− { testName = "Verify terminal can display unicode double-width characters. (Direct UTF-8)"
466+ { test_name = "Verify terminal can display unicode double-width characters. (Direct UTF-8)"
489− , testID = "unicodeDoubleWidth0"
467+ , test_ID = "unicode_double_width_0"
490− , testAction = do
468+ , test_action = do
491− t <- standardIOConfig >>= outputForConfig
469+ t <- terminal_handle
492− reserveDisplay t
470+ reserve_display t
493− hideCursor t
471+ hide_cursor t
494− withArrayLen (concat utf8Txt1) (flip $ hPutBuf stdout)
472+ withArrayLen (concat utf8_txt_1) (flip $ hPutBuf stdout)
495 hPutStr stdout "\n"
473 hPutStr stdout "\n"
496 hPutStr stdout "012345\n"
474 hPutStr stdout "012345\n"
497 hFlush stdout
475 hFlush stdout
498 getLine
476 getLine
499− releaseDisplay t
477+ release_display t
500− releaseTerminal t
478+ release_terminal t
501 return ()
479 return ()
502− , printSummary = unicodeDoubleWidthSummary
480+ , print_summary = unicode_double_width_summary
503− , confirmResults = genericOutputMatchConfirm
481+ , confirm_results = generic_output_match_confirm
504 }
482 }
505
483
506−unicodeDoubleWidth1 = Test
484+unicode_double_width_1 = Test
507− { testName = "Verify terminal can display unicode double-width characters. (Image ops)"
485+ { test_name = "Verify terminal can display unicode double-width characters. (Image ops)"
508− , testID = "unicodeDoubleWidth1"
486+ , test_ID = "unicode_double_width_1"
509− , testAction = do
487+ , test_action = do
510− t <- standardIOConfig >>= outputForConfig
488+ t <- terminal_handle
511− reserveDisplay t
489+ reserve_display t
512− let pic = picForImage image
490+ let pic = pic_for_image image
513− image = line0 <-> line1
491+ image = line_0 <-> line_1
514− line0 = iso10646String defAttr iso10646Txt1
492+ line_0 = iso_10646_string def_attr iso_10646_txt_1
515− line1 = string defAttr "012345"
493+ line_1 = string def_attr "012345"
516− d <- displayBounds t >>= displayContext t
494+ d <- display_bounds t >>= display_context t
517− outputPicture d pic
495+ output_picture d pic
518 getLine
496 getLine
519− releaseDisplay t
497+ release_display t
520− releaseTerminal t
498+ release_terminal t
521 return ()
499 return ()
522− , printSummary = unicodeDoubleWidthSummary
500+ , print_summary = unicode_double_width_summary
523− , confirmResults = genericOutputMatchConfirm
501+ , confirm_results = generic_output_match_confirm
524 }
502 }
525
503
526−unicodeDoubleWidthSummary = putStr [s|
504+unicode_double_width_summary = putStr [$heredoc|
527Once return is pressed:
505Once return is pressed:
528 0. The screen will be cleared.
506 0. The screen will be cleared.
529 1. The cursor will be hidden.
507 1. The cursor will be hidden.
@@ -550,33 +528,33 @@ After return is pressed for the second time:
550 1. The cursor should be visible.
528 1. The cursor should be visible.
551|]
529|]
552
530
553−allColors = zip [ black, red, green, yellow, blue, magenta, cyan, white ]
531+all_colors = zip [ black, red, green, yellow, blue, magenta, cyan, white ]
554− [ "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white" ]
532+ [ "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white" ]
555
533
556−allBrightColors
534+all_bright_colors
557− = zip [ brightBlack, brightRed, brightGreen, brightYellow, brightBlue, brightMagenta, brightCyan, brightWhite ]
535+ = zip [ bright_black, bright_red, bright_green, bright_yellow, bright_blue, bright_magenta, bright_cyan, bright_white ]
558 [ "bright black", "bright red", "bright green", "bright yellow", "bright blue", "bright magenta", "bright cyan", "bright white" ]
536 [ "bright black", "bright red", "bright green", "bright yellow", "bright blue", "bright magenta", "bright cyan", "bright white" ]
559
537
560−attributesTest0 = Test
538+attributes_test_0 = Test
561− { testName = "Character attributes: foreground colors."
539+ { test_name = "Character attributes: foreground colors."
562− , testID = "attributesTest0"
540+ , test_ID = "attributes_test_0"
563− , testAction = do
541+ , test_action = do
564− t <- standardIOConfig >>= outputForConfig
542+ t <- terminal_handle
565− reserveDisplay t
543+ reserve_display t
566− let pic = picForImage image
544+ let pic = pic_for_image image
567− image = border <|> column0 <|> border <|> column1 <|> border
545+ image = border <|> column_0 <|> border <|> column_1 <|> border
568− column0 = vertCat $ map lineWithColor allColors
546+ column_0 = vert_cat $ map line_with_color all_colors
569− border = vertCat $ replicate (length allColors) $ string defAttr " | "
547+ border = vert_cat $ replicate (length all_colors) $ string def_attr " | "
570− column1 = vertCat $ map (string defAttr . snd) allColors
548+ column_1 = vert_cat $ map (string def_attr . snd) all_colors
571− lineWithColor (c, cName) = string (defAttr `withForeColor` c) cName
549+ line_with_color (c, c_name) = string (def_attr `with_fore_color` c) c_name
572− d <- displayBounds t >>= displayContext t
550+ d <- display_bounds t >>= display_context t
573− outputPicture d pic
551+ output_picture d pic
574 getLine
552 getLine
575− releaseDisplay t
553+ release_display t
576− releaseTerminal t
554+ release_terminal t
577 return ()
555 return ()
578− , printSummary = do
556+ , print_summary = do
579− putStr $ [s|
557+ putStr $ [$heredoc|
580Once return is pressed:
558Once return is pressed:
581 0. The screen will be cleared.
559 0. The screen will be cleared.
582 1. The cursor will be hidden.
560 1. The cursor will be hidden.
@@ -596,33 +574,33 @@ After return is pressed for the second time:
596 0. The screen containing the test summary should be restored.
574 0. The screen containing the test summary should be restored.
597 1. The cursor should be visible.
575 1. The cursor should be visible.
598|]
576|]
599− , confirmResults = do
577+ , confirm_results = do
600− putStr $ [s|
578+ putStr $ [$heredoc|
601Did the test output match the description?
579Did the test output match the description?
602|]
580|]
603− defaultSuccessConfirmResults
581+ default_success_confirm_results
604 }
582 }
605
583
606−attributesTest1 = Test
584+attributes_test_1 = Test
607− { testName = "Character attributes: background colors."
585+ { test_name = "Character attributes: background colors."
608− , testID = "attributesTest1"
586+ , test_ID = "attributes_test_1"
609− , testAction = do
587+ , test_action = do
610− t <- standardIOConfig >>= outputForConfig
588+ t <- terminal_handle
611− reserveDisplay t
589+ reserve_display t
612− let pic = picForImage image
590+ let pic = pic_for_image image
613− image = border <|> column0 <|> border <|> column1 <|> border
591+ image = border <|> column_0 <|> border <|> column_1 <|> border
614− column0 = vertCat $ map lineWithColor allColors
592+ column_0 = vert_cat $ map line_with_color all_colors
615− border = vertCat $ replicate (length allColors) $ string defAttr " | "
593+ border = vert_cat $ replicate (length all_colors) $ string def_attr " | "
616− column1 = vertCat $ map (string defAttr . snd) allColors
594+ column_1 = vert_cat $ map (string def_attr . snd) all_colors
617− lineWithColor (c, cName) = string (defAttr `withBackColor` c) cName
595+ line_with_color (c, c_name) = string (def_attr `with_back_color` c) c_name
618− d <- displayBounds t >>= displayContext t
596+ d <- display_bounds t >>= display_context t
619− outputPicture d pic
597+ output_picture d pic
620 getLine
598 getLine
621− releaseDisplay t
599+ release_display t
622− releaseTerminal t
600+ release_terminal t
623 return ()
601 return ()
624− , printSummary = do
602+ , print_summary = do
625− putStr $ [s|
603+ putStr $ [$heredoc|
626Once return is pressed:
604Once return is pressed:
627 0. The screen will be cleared.
605 0. The screen will be cleared.
628 1. The cursor will be hidden.
606 1. The cursor will be hidden.
@@ -651,57 +629,57 @@ After return is pressed for the second time:
651 0. The screen containing the test summary should be restored.
629 0. The screen containing the test summary should be restored.
652 1. The cursor should be visible.
630 1. The cursor should be visible.
653|]
631|]
654− , confirmResults = do
632+ , confirm_results = do
655− putStr $ [s|
633+ putStr $ [$heredoc|
656Did the test output match the description?
634Did the test output match the description?
657|]
635|]
658− defaultSuccessConfirmResults
636+ default_success_confirm_results
659 }
637 }
660
638
661−attributesTest2 = Test
639+attributes_test_2 = Test
662− { testName = "Character attributes: Vivid foreground colors."
640+ { test_name = "Character attributes: Vivid foreground colors."
663− , testID = "attributesTest2"
641+ , test_ID = "attributes_test_2"
664− , testAction = do
642+ , test_action = do
665− t <- standardIOConfig >>= outputForConfig
643+ t <- terminal_handle
666− reserveDisplay t
644+ reserve_display t
667− let pic = picForImage image
645+ let pic = pic_for_image image
668− image = horizCat [border, column0, border, column1, border, column2, border]
646+ image = horiz_cat [border, column_0, border, column_1, border, column_2, border]
669− border = vertCat $ replicate (length allColors) $ string defAttr " | "
647+ border = vert_cat $ replicate (length all_colors) $ string def_attr " | "
670− column0 = vertCat $ map lineWithColor0 allColors
648+ column_0 = vert_cat $ map line_with_color_0 all_colors
671− column1 = vertCat $ map lineWithColor1 allBrightColors
649+ column_1 = vert_cat $ map line_with_color_1 all_bright_colors
672− column2 = vertCat $ map (string defAttr . snd) allColors
650+ column_2 = vert_cat $ map (string def_attr . snd) all_colors
673− lineWithColor0 (c, cName) = string (defAttr `withForeColor` c) cName
651+ line_with_color_0 (c, c_name) = string (def_attr `with_fore_color` c) c_name
674− lineWithColor1 (c, cName) = string (defAttr `withForeColor` c) cName
652+ line_with_color_1 (c, c_name) = string (def_attr `with_fore_color` c) c_name
675− d <- displayBounds t >>= displayContext t
653+ d <- display_bounds t >>= display_context t
676− outputPicture d pic
654+ output_picture d pic
677 getLine
655 getLine
678− releaseDisplay t
656+ release_display t
679− releaseTerminal t
657+ release_terminal t
680 return ()
658 return ()
681− , printSummary = do
659+ , print_summary = do
682− putStr $ [s|
660+ putStr $ [$heredoc|
683Once return is pressed:
661Once return is pressed:
684 0. The screen will be cleared.
662 0. The screen will be cleared.
685 1. The cursor will be hidden.
663 1. The cursor will be hidden.
686 2. 9 lines of text in three columns will be drawn:
664 2. 9 lines of text in three columns will be drawn:
687 a. The first column will be a name of a standard color (for an 8 color
665 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.
689 b. The next column will be also be the name of a standard color rendered
667 b. The next column will be also be the name of a standard color rendered
690 with that color as the foreground color but the shade used should be
668 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.
692 c. The final column will be the name of a color rendered with the
670 c. The final column will be the name of a color rendered with the
693 default attributes.
671 default attributes.
694
672
695For instance, one line will be the word "magenta" and that word should be
673For 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.
697
675
698I'm not actually sure exactly what "vivid" means in this context. For xterm the
676I'm not actually sure exactly what "vivid" means in this context. For xterm the
699−vivid colors are brighter.
677+vivid colors are brighter.
700
678
701−Verify:
679+Verify:
702 * The first column: The foreground color matches the named color.
680 * The first column: The foreground color matches the named color.
703 * The second column: The foreground color matches the named color but is
681 * 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.
705 * The third column: All text is rendered with the default attributes.
683 * The third column: All text is rendered with the default attributes.
706 * The vertical bars used in each line to mark the border of a column are
684 * The vertical bars used in each line to mark the border of a column are
707 lined up.
685 lined up.
@@ -710,58 +688,58 @@ After return is pressed for the second time:
710 0. The screen containing the test summary should be restored.
688 0. The screen containing the test summary should be restored.
711 1. The cursor should be visible.
689 1. The cursor should be visible.
712|]
690|]
713− , confirmResults = do
691+ , confirm_results = do
714− putStr $ [s|
692+ putStr $ [$heredoc|
715Did the test output match the description?
693Did the test output match the description?
716|]
694|]
717− defaultSuccessConfirmResults
695+ default_success_confirm_results
718 }
696 }
719
697
720−attributesTest3 = Test
698+attributes_test_3 = Test
721− { testName = "Character attributes: Vivid background colors."
699+ { test_name = "Character attributes: Vivid background colors."
722− , testID = "attributesTest3"
700+ , test_ID = "attributes_test_3"
723− , testAction = do
701+ , test_action = do
724− t <- standardIOConfig >>= outputForConfig
702+ t <- terminal_handle
725− reserveDisplay t
703+ reserve_display t
726− let pic = picForImage image
704+ let pic = pic_for_image image
727− image = horizCat [border, column0, border, column1, border, column2, border]
705+ image = horiz_cat [border, column_0, border, column_1, border, column_2, border]
728− border = vertCat $ replicate (length allColors) $ string defAttr " | "
706+ border = vert_cat $ replicate (length all_colors) $ string def_attr " | "
729− column0 = vertCat $ map lineWithColor0 allColors
707+ column_0 = vert_cat $ map line_with_color_0 all_colors
730− column1 = vertCat $ map lineWithColor1 allBrightColors
708+ column_1 = vert_cat $ map line_with_color_1 all_bright_colors
731− column2 = vertCat $ map (string defAttr . snd) allColors
709+ column_2 = vert_cat $ map (string def_attr . snd) all_colors
732− lineWithColor0 (c, cName) = string (defAttr `withBackColor` c) cName
710+ line_with_color_0 (c, c_name) = string (def_attr `with_back_color` c) c_name
733− lineWithColor1 (c, cName) = string (defAttr `withBackColor` c) cName
711+ line_with_color_1 (c, c_name) = string (def_attr `with_back_color` c) c_name
734− d <- displayBounds t >>= displayContext t
712+ d <- display_bounds t >>= display_context t
735− outputPicture d pic
713+ output_picture d pic
736 getLine
714 getLine
737− releaseDisplay t
715+ release_display t
738− releaseTerminal t
716+ release_terminal t
739 return ()
717 return ()
740− , printSummary = do
718+ , print_summary = do
741− putStr $ [s|
719+ putStr $ [$heredoc|
742Once return is pressed:
720Once return is pressed:
743 0. The screen will be cleared.
721 0. The screen will be cleared.
744 1. The cursor will be hidden.
722 1. The cursor will be hidden.
745 2. 9 lines of text in three columns will be drawn:
723 2. 9 lines of text in three columns will be drawn:
746 a. The first column will contain be a name of a standard color for an 8
724 a. The first column will contain be a name of a standard color for an 8
747 color terminal rendered with the default foreground color with a
725 color terminal rendered with the default foreground color with a
748− background the named color.
726+ background the named color.
749 b. The first column will contain be a name of a standard color for an 8
727 b. The first column will contain be a name of a standard color for an 8
750 color terminal rendered with the default foreground color with the
728 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.
752 c. The third column will be the name of a standard color rendered with
730 c. The third column will be the name of a standard color rendered with
753 the default attributes.
731 the default attributes.
754−
732+
755For instance, one line will contain be the word "magenta" and the word should
733For 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.
757
735
758I'm not actually sure exactly what "vivid" means in this context. For xterm the
736I'm not actually sure exactly what "vivid" means in this context. For xterm the
759vivid colors are brighter.
737vivid colors are brighter.
760
738
761−Verify:
739+Verify:
762 * The first column: The background color matches the named color.
740 * The first column: The background color matches the named color.
763 * The second column: The background color matches the named color and is
741 * 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.
765 * The third column column: All text is rendered with the default attributes.
743 * The third column column: All text is rendered with the default attributes.
766 * The vertical bars used in each line to mark the border of a column are
744 * The vertical bars used in each line to mark the border of a column are
767 lined up.
745 lined up.
@@ -777,51 +755,51 @@ After return is pressed for the second time:
777 0. The screen containing the test summary should be restored.
755 0. The screen containing the test summary should be restored.
778 1. The cursor should be visible.
756 1. The cursor should be visible.
779|]
757|]
780− , confirmResults = do
758+ , confirm_results = do
781− putStr $ [s|
759+ putStr $ [$heredoc|
782Did the test output match the description?
760Did the test output match the description?
783|]
761|]
784− defaultSuccessConfirmResults
762+ default_success_confirm_results
785 }
763 }
786
764
787−attrCombos =
765+attr_combos =
788 [ ( "default", id )
766 [ ( "default", id )
789− , ( "bold", flip withStyle bold )
767+ , ( "bold", flip with_style bold )
790− , ( "blink", flip withStyle blink )
768+ , ( "blink", flip with_style blink )
791− , ( "underline", flip withStyle underline )
769+ , ( "underline", flip with_style underline )
792− , ( "bold + blink", flip withStyle (bold + blink) )
770+ , ( "bold + blink", flip with_style (bold + blink) )
793− , ( "bold + underline", flip withStyle (bold + underline) )
771+ , ( "bold + underline", flip with_style (bold + underline) )
794− , ( "underline + blink", flip withStyle (underline + blink) )
772+ , ( "underline + blink", flip with_style (underline + blink) )
795− , ( "bold + blink + underline", flip withStyle (bold + blink + underline) )
773+ , ( "bold + blink + underline", flip with_style (bold + blink + underline) )
796 ]
774 ]
797
775
798−attributesTest4 = Test
776+attributes_test_4 = Test
799− { testName = "Character attributes: Bold; Blink; Underline."
777+ { test_name = "Character attributes: Bold; Blink; Underline."
800− , testID = "attributesTest4"
778+ , test_ID = "attributes_test_4"
801− , testAction = do
779+ , test_action = do
802− t <- standardIOConfig >>= outputForConfig
780+ t <- terminal_handle
803− reserveDisplay t
781+ reserve_display t
804− let pic = picForImage image
782+ let pic = pic_for_image image
805− image = horizCat [border, column0, border, column1, border]
783+ image = horiz_cat [border, column_0, border, column_1, border]
806− border = vertCat $ replicate (length attrCombos) $ string defAttr " | "
784+ border = vert_cat $ replicate (length attr_combos) $ string def_attr " | "
807− column0 = vertCat $ map lineWithAttrs attrCombos
785+ column_0 = vert_cat $ map line_with_attrs attr_combos
808− column1 = vertCat $ map (string defAttr . fst) attrCombos
786+ column_1 = vert_cat $ map (string def_attr . fst) attr_combos
809− lineWithAttrs (desc, attrF) = string (attrF defAttr) desc
787+ line_with_attrs (desc, attr_f) = string (attr_f def_attr) desc
810− d <- displayBounds t >>= displayContext t
788+ d <- display_bounds t >>= display_context t
811− outputPicture d pic
789+ output_picture d pic
812 getLine
790 getLine
813− releaseDisplay t
791+ release_display t
814− releaseTerminal t
792+ release_terminal t
815 return ()
793 return ()
816− , printSummary = do
794+ , print_summary = do
817− putStr $ [s|
795+ putStr $ [$heredoc|
818Once return is pressed:
796Once return is pressed:
819 0. The screen will be cleared.
797 0. The screen will be cleared.
820 1. The cursor will be hidden.
798 1. The cursor will be hidden.
821− 2. 8 rows of text in two columns.
799+ 2. 8 rows of text in two columns.
822 The rows will contain the following text:
800 The rows will contain the following text:
823 default
801 default
824− bold
802+ bold
825 blink
803 blink
826 underline
804 underline
827 bold + blink
805 bold + blink
@@ -830,8 +808,8 @@ Once return is pressed:
830 bold + blink + underline
808 bold + blink + underline
831 The first column will be rendered with the described attributes. The second
809 The first column will be rendered with the described attributes. The second
832 column will be rendered with the default attributes.
810 column will be rendered with the default attributes.
833−
811+
834−Verify:
812+Verify:
835 * The vertical bars used in each line to mark the border of a column are
813 * The vertical bars used in each line to mark the border of a column are
836 lined up.
814 lined up.
837 * The text in the first column is rendered as described.
815 * The text in the first column is rendered as described.
@@ -840,353 +818,86 @@ After return is pressed for the second time:
840 0. The screen containing the test summary should be restored.
818 0. The screen containing the test summary should be restored.
841 1. The cursor should be visible.
819 1. The cursor should be visible.
842|]
820|]
843− , confirmResults = do
821+ , confirm_results = do
844− putStr $ [s|
822+ putStr $ [$heredoc|
845Did the test output match the description?
823Did the test output match the description?
846|]
824|]
847− defaultSuccessConfirmResults
825+ default_success_confirm_results
848 }
826 }
849
827
850−attributesTest5 = Test
828+attributes_test_5 = Test
851− { testName = "Character attributes: 240 color palette"
829+ { test_name = "Character attributes: 240 color palette"
852− , testID = "attributesTest5"
830+ , test_ID = "attributes_test_5"
853− , testAction = do
831+ , test_action = do
854− t <- standardIOConfig >>= outputForConfig
832+ t <- terminal_handle
855− reserveDisplay t
833+ reserve_display t
856− let pic = picForImage image
834+ let pic = pic_for_image image
857− image = vertCat $ map horizCat $ splitColorImages colorImages
835+ image = vert_cat $ map horiz_cat $ split_color_images color_images
858− colorImages = map (\i -> string (currentAttr `withBackColor` Color240 i) " ") [0..239]
836+ color_images = map (\i -> string (current_attr `with_back_color` Color240 i) " ") [0..239]
859− splitColorImages [] = []
837+ split_color_images [] = []
860− splitColorImages is = (take 20 is ++ [string defAttr " "]) : (splitColorImages (drop 20 is))
838+ split_color_images is = (take 20 is ++ [string def_attr " "]) : (split_color_images (drop 20 is))
861− d <- displayBounds t >>= displayContext t
839+ d <- display_bounds t >>= display_context t
862− outputPicture d pic
840+ output_picture d pic
863 getLine
841 getLine
864− releaseDisplay t
842+ release_display t
865− releaseTerminal t
843+ release_terminal t
866 return ()
844 return ()
867− , printSummary = do
845+ , print_summary = do
868− putStr $ [s|
846+ putStr $ [$heredoc|
869Once return is pressed:
847Once return is pressed:
870 0. The screen will be cleared.
848 0. The screen will be cleared.
871 1. The cursor will be hidden.
849 1. The cursor will be hidden.
872 2. A 20 character wide and 12 row high block of color squares. This should look like a palette
850 2. A 20 character wide and 12 row high block of color squares. This should look like a palette
873 of some sort. I'm not exactly sure if all color terminals use the same palette. I doubt it...
851 of some sort. I'm not exactly sure if all color terminals use the same palette. I doubt it...
874
852
875−Verify:
853+Verify:
876
854
877After return is pressed for the second time:
855After return is pressed for the second time:
878 0. The screen containing the test summary should be restored.
856 0. The screen containing the test summary should be restored.
879 1. The cursor should be visible.
857 1. The cursor should be visible.
880|]
858|]
881− , confirmResults = do
859+ , confirm_results = do
882− putStr $ [s|
860+ putStr $ [$heredoc|
883Did the test output match the description?
861Did the test output match the description?
884|]
862|]
885− defaultSuccessConfirmResults
863+ default_success_confirm_results
886 }
864 }
887
865
888−inlineTest0 = Test
866+inline_test_0 = Test
889− { testName = "Verify styled output can be performed without clearing the screen."
867+ { test_name = "Verify styled output can be performed without clearing the screen."
890− , testID = "inlineTest0"
868+ , test_ID = "inline_test_0"
891− , testAction = do
869+ , test_action = do
870+ t <- terminal_handle
892 putStrLn "line 1."
871 putStrLn "line 1."
893− putAttrChange_ $ backColor red >> applyStyle underline
872+ put_attr_change t $ back_color red >> apply_style underline
894 putStrLn "line 2."
873 putStrLn "line 2."
895− putAttrChange_ $ defaultAll
874+ put_attr_change t $ default_all
896 putStrLn "line 3."
875 putStrLn "line 3."
897− , printSummary = putStr $ [s|
876+ release_terminal t
877+ return ()
878+ , print_summary = putStr $ [$heredoc|
898lines are in order.
879lines are in order.
899The second line "line 2" should have a red background and the text underline.
880The second line "line 2" should have a red background and the text underline.
900The third line "line 3" should be drawn in the same style as the first line.
881The third line "line 3" should be drawn in the same style as the first line.
901|]
882|]
902
883
903− , confirmResults = genericOutputMatchConfirm
884+ , confirm_results = generic_output_match_confirm
904 }
885 }
905
886
906−inlineTest1 = Test
887+inline_test_1 = Test
907− { testName = "Verify styled output can be performed without clearing the screen."
888+ { test_name = "Verify styled output can be performed without clearing the screen."
908− , testID = "inlineTest1"
889+ , test_ID = "inline_test_1"
909− , testAction = do
890+ , test_action = do
891+ t <- terminal_handle
910 putStr "Not styled. "
892 putStr "Not styled. "
911− putAttrChange_ $ backColor red >> applyStyle underline
893+ put_attr_change t $ back_color red >> apply_style underline
912 putStr " Styled! "
894 putStr " Styled! "
913− putAttrChange_ $ defaultAll
895+ put_attr_change t $ default_all
914 putStrLn "Not styled."
896 putStrLn "Not styled."
915− , printSummary = putStr $ [s|
897+ release_terminal t
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
947 return ()
898 return ()
948− , printSummary = putStr $ [s|
899+ , print_summary = putStr $ [$heredoc|
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.
954|]
900|]
955− , confirmResults = genericOutputMatchConfirm
956− }
957
901
958−outputImageAndWait :: Image -> IO ()
902+ , confirm_results = generic_output_match_confirm
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
1006 }
903 }
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
2
2
3import Verify.Graphics.Vty.DisplayAttributes
3import Verify.Graphics.Vty.DisplayAttributes
4import Verify.Graphics.Vty.Attributes
4import Verify.Graphics.Vty.Attributes
5
5
6import Verify
6import Verify
7
7
8−tests :: IO [Test]
8+main = run_test $ do
9−tests = return []
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
2import Graphics.Vty.Inline
2import Graphics.Vty.Inline
3
3
4main = do
4main = do
5+ t <- terminal_handle
5 putStr "Not styled. "
6 putStr "Not styled. "
6− putAttrChange_ t $ backColor red >> applyStyle underline
7+ put_attr_change t $ back_color red >> apply_style underline
7 putStr " Styled! "
8 putStr " Styled! "
8− putAttrChange_ t $ defaultAll
9+ put_attr_change t $ default_all
9 putStrLn "Not styled."
10 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
2
3
3import Graphics.Vty
4import Graphics.Vty
4import Graphics.Vty.Debug
5import Graphics.Vty.Debug
@@ -12,8 +13,8 @@ main = do
12 play vty sx sy
13 play vty sx sy
13
14
14play :: Vty -> Int -> Int -> IO ()
15play :: Vty -> Int -> Int -> IO ()
15−play vty sx sy =
16+play vty sx sy =
16− let
17+ let
17 testScreen = pic {
18 testScreen = pic {
18 pCursor = NoCursor
19 pCursor = NoCursor
19 , pImage = box 10 10 }
20 , pImage = box 10 10 }
@@ -30,5 +31,6 @@ box w h =
30 vertLine = renderFill attr '|' 1 (h - 2)
31 vertLine = renderFill attr '|' 1 (h - 2)
31 horizLine = corner <|> renderHFill attr '-' (w - 2) <|> corner
32 horizLine = corner <|> renderHFill attr '-' (w - 2) <|> corner
32 centerArea = vertLine <|> renderFill attr 'X' (w - 2) (h - 2) <|> vertLine
33 centerArea = vertLine <|> renderFill attr 'X' (w - 2) (h - 2) <|> vertLine
33− in
34+ in
34 horizLine <-> centerArea <-> horizLine
35 horizLine <-> centerArea <-> horizLine
36+
test/YiIssue264.hs → test/yi_issue_264.hsrenamed−1
@@ -1,5 +1,4 @@
1module Main where
1module Main where
2−
3import Graphics.Vty
2import Graphics.Vty
4import Control.Exception
3import Control.Exception
5
4
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
1+Name: vty
2−version: 5.35.1
2+Version: 4.2.1.0
3−license: BSD3
3+License: BSD3
4−license-file: LICENSE
4+License-file: LICENSE
5−author: AUTHORS
5+Author: Stefan O'Rear, Corey O'Connor
6−maintainer: Jonathan Daugherty (cygnus@foobox.com)
6+Maintainer: Corey O'Connor (coreyoconnor@gmail.com)
7−homepage: https://github.com/jtdaugherty/vty
7+Homepage: http://trac.haskell.org/vty/
8−category: User Interfaces
8+Category: User Interfaces
9−synopsis: A simple terminal UI library
9+Synopsis: A simple terminal access library
10−description:
10+Description:
11− vty is terminal GUI library in the niche of ncurses. It is intended to
11+ vty is terminal GUI library in the niche of ncurses. It is intended to be easy to use, have no
12− be easy to use, have no confusing corner cases, and good support for
12+ confusing corner cases, and good support for common terminal types.
13− common terminal types.
14 .
13 .
15− See the @vty-examples@ package as well as the program
16− @test/interactive_terminal_test.hs@ included in the @vty@ package for
14+ Included in the source distribution is a program test/interactive_terminal_test.hs that
17− examples on how to use the library.
15+ demonstrates the various features.
18 .
16 .
19− Import the "Graphics.Vty" convenience module to get access to the core
17+ If your terminal is not behaving as expected the results of the test/interactive_terminal_test.hs
20− parts of the library.
18+ program should be sent to the Vty maintainter to aid in debugging the issue.
21 .
19 .
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;
23 .
22 .
24− &#169; Corey O'Connor; BSD3 license.
23+ You can 'darcs get' it from <http://code.haskell.org/vty/>
25 .
24 .
26− &#169; Jonathan Daugherty; BSD3 license.
25+ &#169; 2006-2007 Stefan O'Rear; BSD3 license.
27−cabal-version: 1.18
26+ .
28−build-type: Simple
27+ &#169; 2008-2009 Corey O'Connor; BSD3 license.
29−extra-doc-files: README.md,
28+
30− AUTHORS,
29+Build-Depends: base >= 4 && < 5, bytestring, containers, unix
31− CHANGELOG.md,
30+Build-Depends: terminfo >= 0.3 && < 0.4
32− LICENSE
31+Build-Depends: utf8-string >= 0.3 && < 0.4
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,
32+Build-Depends: mtl >= 1.1.0.0 && < 1.2
53− parallel >= 2.2 && < 3.3,
33+Build-Depends: ghc-prim, parallel < 2
34+Build-Depends: array
54− parsec >= 2 && < 4,
35+Build-Depends: parsec >= 2 && < 4
55− stm,
36+Build-Depends: vector-space >= 0.5 && < 0.6
56− terminfo >= 0.3 && < 0.5,
37+Build-Type: Simple
57− transformers >= 0.3.0.0,
38+Data-Files: README, TODO
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
39+Exposed-Modules: Graphics.Vty
70− Graphics.Vty.Attributes
40+ Graphics.Vty.Terminal
71− Graphics.Vty.Attributes.Color
41+ Graphics.Vty.LLInput
72− Graphics.Vty.Attributes.Color240
42+ Graphics.Vty.Attributes
73− Graphics.Vty.Config
74− Graphics.Vty.Error
75− Graphics.Vty.Image
76− Graphics.Vty.Inline
43+ Graphics.Vty.Inline
77− Graphics.Vty.Inline.Unsafe
44+ Graphics.Vty.Picture
78− Graphics.Vty.Input
79− Graphics.Vty.Input.Events
45+ Graphics.Vty.DisplayRegion
80− Graphics.Vty.Picture
46+
81− Graphics.Vty.Output
47+other-modules: Codec.Binary.UTF8.Width
82− Graphics.Text.Width
48+ Data.Marshalling
83− Codec.Binary.UTF8.Debug
84− Data.Terminfo.Parse
49+ Data.Terminfo.Parse
85− Data.Terminfo.Eval
50+ Data.Terminfo.Eval
86− Graphics.Vty.Debug
87− Graphics.Vty.DisplayAttributes
51+ Graphics.Vty.DisplayAttributes
88− Graphics.Vty.Image.Internal
52+ Graphics.Vty.Image
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
53+ Graphics.Vty.Span
95− Graphics.Vty.Input.Paste
96− Graphics.Vty.Input.Terminfo
97− Graphics.Vty.PictureToSpans
98− Graphics.Vty.Span
99− Graphics.Vty.Output.Mock
54+ Graphics.Vty.Terminal.Generic
100− Graphics.Vty.Output.Interface
55+ Graphics.Vty.Terminal.MacOSX
101− Graphics.Vty.Output.XTermColor
56+ Graphics.Vty.Terminal.XTermColor
102− Graphics.Vty.Output.TerminfoBased
57+ Graphics.Vty.Terminal.TerminfoBased
103− Graphics.Vty.UnicodeWidthTable.Types
58+
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
59+C-Sources: cbits/gwinsz.c
112− cbits/set_term_timing.c
60+ cbits/set_term_timing.c
113− cbits/get_tty_erase.c
114− cbits/mk_wcwidth.c
61+ cbits/mk_wcwidth.c
115−
62+Include-Dirs: cbits
116− include-dirs: cbits
117−
118− hs-source-dirs: src
63+hs-source-dirs: src
119−
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+
120− ghc-options: -O2 -funbox-strict-fields -Wall -fspec-constr -fspec-constr-count=10
100+ghc-options: -funbox-strict-fields -Wall -fno-full-laziness -fspec-constr
121−
122− ghc-prof-options: -O2 -funbox-strict-fields -caf-all -Wall -fspec-constr -fspec-constr-count=10
101+ghc-prof-options: -funbox-strict-fields -caf-all -Wall -fno-full-laziness -fspec-constr
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
632
102
633− ghc-options: -threaded -Wall