ratatui-termion-0.1.0/.cargo_vcs_info.json0000644000000001550000000000100141570ustar { "git": { "sha1": "0a2a7c0363a4806b0cf05c1915bf7cdd438f756c" }, "path_in_vcs": "ratatui-termion" }ratatui-termion-0.1.0/Cargo.lock0000644000000376770000000000100121550ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "allocator-api2" version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "bitflags" version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "castaway" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" dependencies = [ "rustversion", ] [[package]] name = "cfg-if" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "compact_str" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb1325a1cece981e8a296ab8f0f9b63ae357bd0784a9faaf548cc7b480707a" dependencies = [ "castaway", "cfg-if", "itoa", "rustversion", "ryu", "static_assertions", ] [[package]] name = "darling" version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ "darling_core", "darling_macro", ] [[package]] name = "darling_core" version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", "syn", ] [[package]] name = "darling_macro" version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", "syn", ] [[package]] name = "document-features" version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" dependencies = [ "litrs", ] [[package]] name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" [[package]] name = "futures-core" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-macro" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "futures-task" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-timer" version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" [[package]] name = "futures-util" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", "futures-macro", "futures-task", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "glob" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "hashbrown" version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" [[package]] name = "hashbrown" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ "allocator-api2", "equivalent", "foldhash", ] [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "ident_case" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "indexmap" version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", "hashbrown 0.15.5", ] [[package]] name = "indoc" version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" dependencies = [ "rustversion", ] [[package]] name = "instability" version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6778b0196eefee7df739db78758e5cf9b37412268bfa5650bfeed028aed20d9c" dependencies = [ "darling", "indoc", "proc-macro2", "quote", "syn", ] [[package]] name = "itertools" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] [[package]] name = "itertools" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" dependencies = [ "either", ] [[package]] name = "itoa" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "kasuari" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fe90c1150662e858c7d5f945089b7517b0a80d8bf7ba4b1b5ffc984e7230a5b" dependencies = [ "hashbrown 0.16.1", "thiserror", ] [[package]] name = "libc" version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "litrs" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" [[package]] name = "lru" version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96051b46fc183dc9cd4a223960ef37b9af631b55191852a8274bfef064cda20f" dependencies = [ "hashbrown 0.16.1", ] [[package]] name = "memchr" version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "numtoa" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6aa2c4e539b869820a2b82e1aef6ff40aa85e65decdd5185e83fb4b1249cd00f" [[package]] name = "pin-project-lite" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "proc-macro-crate" version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" dependencies = [ "toml_edit", ] [[package]] name = "proc-macro2" version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] name = "ratatui-core" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ef8dea09a92caaf73bff7adb70b76162e5937524058a7e5bff37869cbbec293" dependencies = [ "bitflags", "compact_str", "hashbrown 0.16.1", "indoc", "itertools 0.14.0", "kasuari", "lru", "strum", "thiserror", "unicode-segmentation", "unicode-truncate", "unicode-width", ] [[package]] name = "ratatui-termion" version = "0.1.0" dependencies = [ "document-features", "instability", "ratatui-core", "rstest", "termion", ] [[package]] name = "regex" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "relative-path" version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" [[package]] name = "rstest" version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5a3193c063baaa2a95a33f03035c8a72b83d97a54916055ba22d35ed3839d49" dependencies = [ "futures-timer", "futures-util", "rstest_macros", ] [[package]] name = "rstest_macros" version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0" dependencies = [ "cfg-if", "glob", "proc-macro-crate", "proc-macro2", "quote", "regex", "relative-path", "rustc_version", "syn", "unicode-ident", ] [[package]] name = "rustc_version" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] [[package]] name = "rustversion" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "semver" version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "serde" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", "serde_derive", ] [[package]] name = "serde_core" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "slab" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ "heck", "proc-macro2", "quote", "syn", ] [[package]] name = "syn" version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "termion" version = "4.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f44138a9ae08f0f502f24104d82517ef4da7330c35acd638f1f29d3cd5475ecb" dependencies = [ "libc", "numtoa", "serde", ] [[package]] name = "thiserror" version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "toml_datetime" version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" [[package]] name = "toml_edit" version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap", "toml_datetime", "winnow", ] [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-segmentation" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-truncate" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fbf03860ff438702f3910ca5f28f8dac63c1c11e7efb5012b8b175493606330" dependencies = [ "itertools 0.13.0", "unicode-segmentation", "unicode-width", ] [[package]] name = "unicode-width" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "winnow" version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" dependencies = [ "memchr", ] ratatui-termion-0.1.0/Cargo.toml0000644000000054270000000000100121640ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2024" rust-version = "1.86.0" name = "ratatui-termion" version = "0.1.0" authors = [ "Florian Dehau ", "The Ratatui Developers", ] build = false exclude = [ "assets/*", ".github", "Makefile.toml", "CONTRIBUTING.md", "*.log", "tags", ] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Termion backend for the Ratatui Terminal UI library." homepage = "https://ratatui.rs" documentation = "https://docs.rs/ratatui-termion" readme = "README.md" keywords = [ "tui", "terminal", "dashboard", ] categories = ["command-line-interface"] license = "MIT" repository = "https://github.com/ratatui/ratatui" resolver = "2" [package.metadata.docs.rs] all-features = true cargo-args = [ "-Zunstable-options", "-Zrustdoc-scrape-examples", ] rustdoc-args = [ "--cfg", "docsrs", ] [features] default = [] scrolling-regions = ["ratatui-core/scrolling-regions"] serde = ["termion/serde"] unstable = ["unstable-backend-writer"] unstable-backend-writer = [] [lib] name = "ratatui_termion" path = "src/lib.rs" [dependencies.document-features] version = "0.2" optional = true [dependencies.instability] version = "0.3" [dependencies.ratatui-core] version = "0.1.0" [dependencies.termion] version = "4" [dev-dependencies.rstest] version = "0.26" [lints.clippy] as_underscore = "warn" cast_possible_truncation = "allow" cast_possible_wrap = "allow" cast_precision_loss = "allow" cast_sign_loss = "allow" deref_by_slicing = "warn" else_if_without_else = "warn" empty_line_after_doc_comments = "warn" equatable_if_let = "warn" fn_to_numeric_cast_any = "warn" format_push_string = "warn" implicit_clone = "warn" map_err_ignore = "warn" missing_const_for_fn = "warn" missing_errors_doc = "allow" missing_panics_doc = "allow" mixed_read_write_in_expression = "warn" mod_module_files = "warn" module_inception = "allow" module_name_repetitions = "allow" must_use_candidate = "allow" needless_pass_by_ref_mut = "warn" needless_raw_strings = "warn" or_fun_call = "warn" redundant_type_annotations = "warn" rest_pat_in_fully_bound_structs = "warn" string_lit_chars_any = "warn" string_slice = "warn" unnecessary_self_imports = "warn" use_self = "warn" [lints.clippy.pedantic] level = "warn" priority = -1 [lints.rust] unsafe_code = "forbid" ratatui-termion-0.1.0/Cargo.toml.orig000064400000000000000000000022601046102023000156350ustar 00000000000000[package] name = "ratatui-termion" version = "0.1.0" description = "Termion backend for the Ratatui Terminal UI library." documentation = "https://docs.rs/ratatui-termion" readme = "README.md" authors.workspace = true repository.workspace = true homepage.workspace = true keywords.workspace = true categories.workspace = true license.workspace = true exclude.workspace = true edition.workspace = true rust-version.workspace = true [package.metadata.docs.rs] all-features = true cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] rustdoc-args = ["--cfg", "docsrs"] [features] default = [] ## Enables serde for termion dependency serde = ["termion/serde"] ## Use terminal scrolling regions to make Terminal::insert_before less prone to flickering. scrolling-regions = ["ratatui-core/scrolling-regions"] ## Enables all unstable features. unstable = ["unstable-backend-writer"] ## Enables getting access to backends' writer. unstable-backend-writer = [] [dependencies] document-features = { workspace = true, optional = true } instability.workspace = true ratatui-core = { workspace = true } termion.workspace = true [dev-dependencies] rstest.workspace = true [lints] workspace = true ratatui-termion-0.1.0/README.md000064400000000000000000000021541046102023000142270ustar 00000000000000# Ratatui-termion This module provides the [`TermionBackend`] implementation for the [`Backend`] trait. It uses the [Termion] crate to interact with the terminal. [`Backend`]: ratatui_core::backend::Backend [Termion]: https://docs.rs/termion ## Crate Organization `ratatui-termion` is part of the Ratatui workspace that was modularized in version 0.30.0. This crate provides the [Termion] backend implementation for Ratatui. **When to use `ratatui-termion`:** - You need fine-grained control over dependencies - Building a widget library that needs backend functionality - You want to use only the Termion backend without other backends - You prefer Termion's Unix-focused approach **When to use the main [`ratatui`] crate:** - Building applications (recommended - includes termion backend when enabled) - You want the convenience of having everything available For detailed information about the workspace organization, see [ARCHITECTURE.md]. [`ratatui`]: https://crates.io/crates/ratatui [ARCHITECTURE.md]: https://github.com/ratatui/ratatui/blob/main/ARCHITECTURE.md ratatui-termion-0.1.0/src/lib.rs000064400000000000000000000644001046102023000146550ustar 00000000000000// show the feature flags in the generated documentation #![cfg_attr(docsrs, feature(doc_cfg))] #![doc( html_logo_url = "https://raw.githubusercontent.com/ratatui/ratatui/main/assets/logo.png", html_favicon_url = "https://raw.githubusercontent.com/ratatui/ratatui/main/assets/favicon.ico" )] #![warn(missing_docs)] //! This module provides the [`TermionBackend`] implementation for the [`Backend`] trait. It uses //! the [Termion] crate to interact with the terminal. //! //! [`Backend`]: ratatui_core::backend::Backend //! [Termion]: https://docs.rs/termion //! //! # Crate Organization //! //! `ratatui-termion` is part of the Ratatui workspace that was modularized in version 0.30.0. //! This crate provides the [Termion] backend implementation for Ratatui. //! //! **When to use `ratatui-termion`:** //! //! - You need fine-grained control over dependencies //! - Building a widget library that needs backend functionality //! - You want to use only the Termion backend without other backends //! - You prefer Termion's Unix-focused approach //! //! **When to use the main [`ratatui`] crate:** //! //! - Building applications (recommended - includes termion backend when enabled) //! - You want the convenience of having everything available //! //! For detailed information about the workspace organization, see [ARCHITECTURE.md]. //! //! [`ratatui`]: https://crates.io/crates/ratatui //! [ARCHITECTURE.md]: https://github.com/ratatui/ratatui/blob/main/ARCHITECTURE.md #![cfg_attr(feature = "document-features", doc = "\n## Features")] #![cfg_attr(feature = "document-features", doc = document_features::document_features!())] use std::fmt; use std::io::{self, Write}; use ratatui_core::backend::{Backend, ClearType, WindowSize}; use ratatui_core::buffer::Cell; use ratatui_core::layout::{Position, Size}; use ratatui_core::style::{Color, Modifier, Style}; pub use termion; use termion::color::Color as _; use termion::{color as tcolor, style as tstyle}; /// A [`Backend`] implementation that uses [Termion] to render to the terminal. /// /// The `TermionBackend` struct is a wrapper around a writer implementing [`Write`], which is used /// to send commands to the terminal. It provides methods for drawing content, manipulating the /// cursor, and clearing the terminal screen. /// /// Most applications should not call the methods on `TermionBackend` directly, but will instead /// use the [`Terminal`] struct, which provides a more ergonomic interface. /// /// Usually applications will enable raw mode and switch to alternate screen mode when starting. /// This is done by calling [`IntoRawMode::into_raw_mode()`] and /// [`IntoAlternateScreen::into_alternate_screen()`] on the writer before creating the backend. /// This is not done automatically by the backend because it is possible that the application may /// want to use the terminal for other purposes (like showing help text) before entering alternate /// screen mode. This backend automatically disable raw mode and switches back to the primary /// screen when the writer is dropped. /// /// # Example /// /// ```rust,ignore /// use std::io::{stderr, stdout}; /// /// use ratatui::Terminal; /// use ratatui::backend::TermionBackend; /// use ratatui::termion::raw::IntoRawMode; /// use ratatui::termion::screen::IntoAlternateScreen; /// /// let writer = stdout().into_raw_mode()?.into_alternate_screen()?; /// let mut backend = TermionBackend::new(writer); /// // or /// let writer = stderr().into_raw_mode()?.into_alternate_screen()?; /// let backend = TermionBackend::new(stderr()); /// let mut terminal = Terminal::new(backend)?; /// /// terminal.clear()?; /// terminal.draw(|frame| { /// // -- snip -- /// })?; /// # std::io::Result::Ok(()) /// ``` /// /// [`IntoRawMode::into_raw_mode()`]: termion::raw::IntoRawMode /// [`IntoAlternateScreen::into_alternate_screen()`]: termion::screen::IntoAlternateScreen /// [`Terminal`]: ratatui_core::terminal::Terminal /// [Termion]: https://docs.rs/termion #[derive(Debug, Default, Clone, Eq, PartialEq, Hash)] pub struct TermionBackend where W: Write, { writer: W, } impl TermionBackend where W: Write, { /// Creates a new Termion backend with the given writer. /// /// Most applications will use either [`stdout`](std::io::stdout) or /// [`stderr`](std::io::stderr) as writer. See the [FAQ] to determine which one to use. /// /// [FAQ]: https://ratatui.rs/faq/#should-i-use-stdout-or-stderr /// /// # Example /// /// ```rust,ignore /// use std::io::stdout; /// /// use ratatui::backend::TermionBackend; /// /// let backend = TermionBackend::new(stdout()); /// ``` pub const fn new(writer: W) -> Self { Self { writer } } /// Gets the writer. #[instability::unstable( feature = "backend-writer", issue = "https://github.com/ratatui/ratatui/pull/991" )] pub const fn writer(&self) -> &W { &self.writer } /// Gets the writer as a mutable reference. /// Note: writing to the writer may cause incorrect output after the write. This is due to the /// way that the Terminal implements diffing Buffers. #[instability::unstable( feature = "backend-writer", issue = "https://github.com/ratatui/ratatui/pull/991" )] pub const fn writer_mut(&mut self) -> &mut W { &mut self.writer } } impl Write for TermionBackend where W: Write, { fn write(&mut self, buf: &[u8]) -> io::Result { self.writer.write(buf) } fn flush(&mut self) -> io::Result<()> { self.writer.flush() } } impl Backend for TermionBackend where W: Write, { type Error = io::Error; fn clear(&mut self) -> io::Result<()> { self.clear_region(ClearType::All) } fn clear_region(&mut self, clear_type: ClearType) -> io::Result<()> { match clear_type { ClearType::All => write!(self.writer, "{}", termion::clear::All)?, ClearType::AfterCursor => write!(self.writer, "{}", termion::clear::AfterCursor)?, ClearType::BeforeCursor => write!(self.writer, "{}", termion::clear::BeforeCursor)?, ClearType::CurrentLine => write!(self.writer, "{}", termion::clear::CurrentLine)?, ClearType::UntilNewLine => write!(self.writer, "{}", termion::clear::UntilNewline)?, } self.writer.flush() } fn append_lines(&mut self, n: u16) -> io::Result<()> { for _ in 0..n { writeln!(self.writer)?; } self.writer.flush() } fn hide_cursor(&mut self) -> io::Result<()> { write!(self.writer, "{}", termion::cursor::Hide)?; self.writer.flush() } fn show_cursor(&mut self) -> io::Result<()> { write!(self.writer, "{}", termion::cursor::Show)?; self.writer.flush() } fn get_cursor_position(&mut self) -> io::Result { termion::cursor::DetectCursorPos::cursor_pos(&mut self.writer) .map(|(x, y)| Position { x: x - 1, y: y - 1 }) } fn set_cursor_position>(&mut self, position: P) -> io::Result<()> { let Position { x, y } = position.into(); write!(self.writer, "{}", termion::cursor::Goto(x + 1, y + 1))?; self.writer.flush() } fn draw<'a, I>(&mut self, content: I) -> io::Result<()> where I: Iterator, { use std::fmt::Write; let mut string = String::with_capacity(content.size_hint().0 * 3); let mut fg = Color::Reset; let mut bg = Color::Reset; let mut modifier = Modifier::empty(); let mut last_pos: Option = None; for (x, y, cell) in content { // Move the cursor if the previous location was not (x - 1, y) if !matches!(last_pos, Some(p) if x == p.x + 1 && y == p.y) { write!(string, "{}", termion::cursor::Goto(x + 1, y + 1)).unwrap(); } last_pos = Some(Position { x, y }); if cell.modifier != modifier { write!( string, "{}", ModifierDiff { from: modifier, to: cell.modifier } ) .unwrap(); modifier = cell.modifier; } if cell.fg != fg { write!(string, "{}", Fg(cell.fg)).unwrap(); fg = cell.fg; } if cell.bg != bg { write!(string, "{}", Bg(cell.bg)).unwrap(); bg = cell.bg; } string.push_str(cell.symbol()); } write!( self.writer, "{string}{}{}{}", Fg(Color::Reset), Bg(Color::Reset), termion::style::Reset, ) } fn size(&self) -> io::Result { let terminal = termion::terminal_size()?; Ok(Size::new(terminal.0, terminal.1)) } fn window_size(&mut self) -> io::Result { Ok(WindowSize { columns_rows: termion::terminal_size()?.into(), pixels: termion::terminal_size_pixels()?.into(), }) } fn flush(&mut self) -> io::Result<()> { self.writer.flush() } #[cfg(feature = "scrolling-regions")] fn scroll_region_up(&mut self, region: std::ops::Range, amount: u16) -> io::Result<()> { write!( self.writer, "{}{}{}", SetRegion(region.start.saturating_add(1), region.end), termion::scroll::Up(amount), ResetRegion, )?; self.writer.flush() } #[cfg(feature = "scrolling-regions")] fn scroll_region_down(&mut self, region: std::ops::Range, amount: u16) -> io::Result<()> { write!( self.writer, "{}{}{}", SetRegion(region.start.saturating_add(1), region.end), termion::scroll::Down(amount), ResetRegion, )?; self.writer.flush() } } struct Fg(Color); struct Bg(Color); /// The `ModifierDiff` struct is used to calculate the difference between two `Modifier` /// values. This is useful when updating the terminal display, as it allows for more /// efficient updates by only sending the necessary changes. struct ModifierDiff { from: Modifier, to: Modifier, } impl fmt::Display for Fg { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.0 { Color::Reset => termion::color::Reset.write_fg(f), Color::Black => termion::color::Black.write_fg(f), Color::Red => termion::color::Red.write_fg(f), Color::Green => termion::color::Green.write_fg(f), Color::Yellow => termion::color::Yellow.write_fg(f), Color::Blue => termion::color::Blue.write_fg(f), Color::Magenta => termion::color::Magenta.write_fg(f), Color::Cyan => termion::color::Cyan.write_fg(f), Color::Gray => termion::color::White.write_fg(f), Color::DarkGray => termion::color::LightBlack.write_fg(f), Color::LightRed => termion::color::LightRed.write_fg(f), Color::LightGreen => termion::color::LightGreen.write_fg(f), Color::LightBlue => termion::color::LightBlue.write_fg(f), Color::LightYellow => termion::color::LightYellow.write_fg(f), Color::LightMagenta => termion::color::LightMagenta.write_fg(f), Color::LightCyan => termion::color::LightCyan.write_fg(f), Color::White => termion::color::LightWhite.write_fg(f), Color::Indexed(i) => termion::color::AnsiValue(i).write_fg(f), Color::Rgb(r, g, b) => termion::color::Rgb(r, g, b).write_fg(f), } } } impl fmt::Display for Bg { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.0 { Color::Reset => termion::color::Reset.write_bg(f), Color::Black => termion::color::Black.write_bg(f), Color::Red => termion::color::Red.write_bg(f), Color::Green => termion::color::Green.write_bg(f), Color::Yellow => termion::color::Yellow.write_bg(f), Color::Blue => termion::color::Blue.write_bg(f), Color::Magenta => termion::color::Magenta.write_bg(f), Color::Cyan => termion::color::Cyan.write_bg(f), Color::Gray => termion::color::White.write_bg(f), Color::DarkGray => termion::color::LightBlack.write_bg(f), Color::LightRed => termion::color::LightRed.write_bg(f), Color::LightGreen => termion::color::LightGreen.write_bg(f), Color::LightBlue => termion::color::LightBlue.write_bg(f), Color::LightYellow => termion::color::LightYellow.write_bg(f), Color::LightMagenta => termion::color::LightMagenta.write_bg(f), Color::LightCyan => termion::color::LightCyan.write_bg(f), Color::White => termion::color::LightWhite.write_bg(f), Color::Indexed(i) => termion::color::AnsiValue(i).write_bg(f), Color::Rgb(r, g, b) => termion::color::Rgb(r, g, b).write_bg(f), } } } /// A trait for converting a Termion type to a Ratatui type. /// /// This trait is necessary to avoid the orphan rule, as we cannot implement a trait for a type /// defined in another crate. pub trait FromTermion { /// Convert the Termion type to the Ratatui type. fn from_termion(termion: T) -> Self; } /// A trait for converting a Ratatui type to a Termion type. /// /// This trait is necessary to avoid the orphan rule, as we cannot implement a trait for a type /// defined in another crate. pub trait IntoTermion { /// Convert the Ratatui type to the Termion type. fn into_termion(self) -> T; } macro_rules! from_termion_for_color { ($termion_color:ident, $color:ident) => { impl FromTermion for Color { fn from_termion(_: tcolor::$termion_color) -> Self { Color::$color } } impl FromTermion> for Style { fn from_termion(_: tcolor::Bg) -> Self { Style::default().bg(Color::$color) } } impl FromTermion> for Style { fn from_termion(_: tcolor::Fg) -> Self { Style::default().fg(Color::$color) } } }; } from_termion_for_color!(Reset, Reset); from_termion_for_color!(Black, Black); from_termion_for_color!(Red, Red); from_termion_for_color!(Green, Green); from_termion_for_color!(Yellow, Yellow); from_termion_for_color!(Blue, Blue); from_termion_for_color!(Magenta, Magenta); from_termion_for_color!(Cyan, Cyan); from_termion_for_color!(White, Gray); from_termion_for_color!(LightBlack, DarkGray); from_termion_for_color!(LightRed, LightRed); from_termion_for_color!(LightGreen, LightGreen); from_termion_for_color!(LightBlue, LightBlue); from_termion_for_color!(LightYellow, LightYellow); from_termion_for_color!(LightMagenta, LightMagenta); from_termion_for_color!(LightCyan, LightCyan); from_termion_for_color!(LightWhite, White); impl FromTermion for Color { fn from_termion(value: tcolor::AnsiValue) -> Self { Self::Indexed(value.0) } } impl FromTermion> for Style { fn from_termion(value: tcolor::Bg) -> Self { Self::default().bg(Color::Indexed(value.0.0)) } } impl FromTermion> for Style { fn from_termion(value: tcolor::Fg) -> Self { Self::default().fg(Color::Indexed(value.0.0)) } } impl FromTermion for Color { fn from_termion(value: tcolor::Rgb) -> Self { Self::Rgb(value.0, value.1, value.2) } } impl FromTermion> for Style { fn from_termion(value: tcolor::Bg) -> Self { Self::default().bg(Color::Rgb(value.0.0, value.0.1, value.0.2)) } } impl FromTermion> for Style { fn from_termion(value: tcolor::Fg) -> Self { Self::default().fg(Color::Rgb(value.0.0, value.0.1, value.0.2)) } } impl fmt::Display for ModifierDiff { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let remove = self.from - self.to; if remove.contains(Modifier::REVERSED) { write!(f, "{}", termion::style::NoInvert)?; } if remove.contains(Modifier::BOLD) { // XXX: the termion NoBold flag actually enables double-underline on ECMA-48 compliant // terminals, and NoFaint additionally disables bold... so we use this trick to get // the right semantics. write!(f, "{}", termion::style::NoFaint)?; if self.to.contains(Modifier::DIM) { write!(f, "{}", termion::style::Faint)?; } } if remove.contains(Modifier::ITALIC) { write!(f, "{}", termion::style::NoItalic)?; } if remove.contains(Modifier::UNDERLINED) { write!(f, "{}", termion::style::NoUnderline)?; } if remove.contains(Modifier::DIM) { write!(f, "{}", termion::style::NoFaint)?; // XXX: the NoFaint flag additionally disables bold as well, so we need to re-enable it // here if we want it. if self.to.contains(Modifier::BOLD) { write!(f, "{}", termion::style::Bold)?; } } if remove.contains(Modifier::CROSSED_OUT) { write!(f, "{}", termion::style::NoCrossedOut)?; } if remove.contains(Modifier::SLOW_BLINK) || remove.contains(Modifier::RAPID_BLINK) { write!(f, "{}", termion::style::NoBlink)?; } let add = self.to - self.from; if add.contains(Modifier::REVERSED) { write!(f, "{}", termion::style::Invert)?; } if add.contains(Modifier::BOLD) { write!(f, "{}", termion::style::Bold)?; } if add.contains(Modifier::ITALIC) { write!(f, "{}", termion::style::Italic)?; } if add.contains(Modifier::UNDERLINED) { write!(f, "{}", termion::style::Underline)?; } if add.contains(Modifier::DIM) { write!(f, "{}", termion::style::Faint)?; } if add.contains(Modifier::CROSSED_OUT) { write!(f, "{}", termion::style::CrossedOut)?; } if add.contains(Modifier::SLOW_BLINK) || add.contains(Modifier::RAPID_BLINK) { write!(f, "{}", termion::style::Blink)?; } Ok(()) } } macro_rules! from_termion_for_modifier { ($termion_modifier:ident, $modifier:ident) => { impl FromTermion for Modifier { fn from_termion(_: tstyle::$termion_modifier) -> Self { Modifier::$modifier } } }; } from_termion_for_modifier!(Invert, REVERSED); from_termion_for_modifier!(Bold, BOLD); from_termion_for_modifier!(Italic, ITALIC); from_termion_for_modifier!(Underline, UNDERLINED); from_termion_for_modifier!(Faint, DIM); from_termion_for_modifier!(CrossedOut, CROSSED_OUT); from_termion_for_modifier!(Blink, SLOW_BLINK); impl FromTermion for Modifier { fn from_termion(_: termion::style::Reset) -> Self { Self::empty() } } /// Set scrolling region. #[derive(Copy, Clone, PartialEq, Eq)] pub struct SetRegion(pub u16, pub u16); impl fmt::Display for SetRegion { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "\x1B[{};{}r", self.0, self.1) } } /// Reset scrolling region. #[derive(Copy, Clone, PartialEq, Eq)] pub struct ResetRegion; impl fmt::Display for ResetRegion { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "\x1B[r") } } #[cfg(test)] mod tests { use super::*; #[test] fn from_termion_color() { assert_eq!(Color::from_termion(tcolor::Reset), Color::Reset); assert_eq!(Color::from_termion(tcolor::Black), Color::Black); assert_eq!(Color::from_termion(tcolor::Red), Color::Red); assert_eq!(Color::from_termion(tcolor::Green), Color::Green); assert_eq!(Color::from_termion(tcolor::Yellow), Color::Yellow); assert_eq!(Color::from_termion(tcolor::Blue), Color::Blue); assert_eq!(Color::from_termion(tcolor::Magenta), Color::Magenta); assert_eq!(Color::from_termion(tcolor::Cyan), Color::Cyan); assert_eq!(Color::from_termion(tcolor::White), Color::Gray); assert_eq!(Color::from_termion(tcolor::LightBlack), Color::DarkGray); assert_eq!(Color::from_termion(tcolor::LightRed), Color::LightRed); assert_eq!(Color::from_termion(tcolor::LightGreen), Color::LightGreen); assert_eq!(Color::from_termion(tcolor::LightBlue), Color::LightBlue); assert_eq!(Color::from_termion(tcolor::LightYellow), Color::LightYellow); assert_eq!( Color::from_termion(tcolor::LightMagenta), Color::LightMagenta ); assert_eq!(Color::from_termion(tcolor::LightCyan), Color::LightCyan); assert_eq!(Color::from_termion(tcolor::LightWhite), Color::White); assert_eq!( Color::from_termion(tcolor::AnsiValue(31)), Color::Indexed(31) ); assert_eq!( Color::from_termion(tcolor::Rgb(1, 2, 3)), Color::Rgb(1, 2, 3) ); } #[test] fn from_termion_bg() { use tc::Bg; use tcolor as tc; assert_eq!( Style::from_termion(Bg(tc::Reset)), Style::new().bg(Color::Reset) ); assert_eq!(Style::from_termion(Bg(tc::Black)), Style::new().on_black()); assert_eq!(Style::from_termion(Bg(tc::Red)), Style::new().on_red()); assert_eq!(Style::from_termion(Bg(tc::Green)), Style::new().on_green()); assert_eq!( Style::from_termion(Bg(tc::Yellow)), Style::new().on_yellow() ); assert_eq!(Style::from_termion(Bg(tc::Blue)), Style::new().on_blue()); assert_eq!( Style::from_termion(Bg(tc::Magenta)), Style::new().on_magenta() ); assert_eq!(Style::from_termion(Bg(tc::Cyan)), Style::new().on_cyan()); assert_eq!(Style::from_termion(Bg(tc::White)), Style::new().on_gray()); assert_eq!( Style::from_termion(Bg(tc::LightBlack)), Style::new().on_dark_gray() ); assert_eq!( Style::from_termion(Bg(tc::LightRed)), Style::new().on_light_red() ); assert_eq!( Style::from_termion(Bg(tc::LightGreen)), Style::new().on_light_green() ); assert_eq!( Style::from_termion(Bg(tc::LightBlue)), Style::new().on_light_blue() ); assert_eq!( Style::from_termion(Bg(tc::LightYellow)), Style::new().on_light_yellow() ); assert_eq!( Style::from_termion(Bg(tc::LightMagenta)), Style::new().on_light_magenta() ); assert_eq!( Style::from_termion(Bg(tc::LightCyan)), Style::new().on_light_cyan() ); assert_eq!( Style::from_termion(Bg(tc::LightWhite)), Style::new().on_white() ); assert_eq!( Style::from_termion(Bg(tc::AnsiValue(31))), Style::new().bg(Color::Indexed(31)) ); assert_eq!( Style::from_termion(Bg(tc::Rgb(1, 2, 3))), Style::new().bg(Color::Rgb(1, 2, 3)) ); } #[test] fn from_termion_fg() { use tc::Fg; use tcolor as tc; assert_eq!( Style::from_termion(Fg(tc::Reset)), Style::new().fg(Color::Reset) ); assert_eq!(Style::from_termion(Fg(tc::Black)), Style::new().black()); assert_eq!(Style::from_termion(Fg(tc::Red)), Style::new().red()); assert_eq!(Style::from_termion(Fg(tc::Green)), Style::new().green()); assert_eq!(Style::from_termion(Fg(tc::Yellow)), Style::new().yellow()); assert_eq!(Style::from_termion(Fg(tc::Blue)), Style::default().blue()); assert_eq!( Style::from_termion(Fg(tc::Magenta)), Style::default().magenta() ); assert_eq!(Style::from_termion(Fg(tc::Cyan)), Style::default().cyan()); assert_eq!(Style::from_termion(Fg(tc::White)), Style::default().gray()); assert_eq!( Style::from_termion(Fg(tc::LightBlack)), Style::new().dark_gray() ); assert_eq!( Style::from_termion(Fg(tc::LightRed)), Style::new().light_red() ); assert_eq!( Style::from_termion(Fg(tc::LightGreen)), Style::new().light_green() ); assert_eq!( Style::from_termion(Fg(tc::LightBlue)), Style::new().light_blue() ); assert_eq!( Style::from_termion(Fg(tc::LightYellow)), Style::new().light_yellow() ); assert_eq!( Style::from_termion(Fg(tc::LightMagenta)), Style::new().light_magenta() ); assert_eq!( Style::from_termion(Fg(tc::LightCyan)), Style::new().light_cyan() ); assert_eq!( Style::from_termion(Fg(tc::LightWhite)), Style::new().white() ); assert_eq!( Style::from_termion(Fg(tc::AnsiValue(31))), Style::default().fg(Color::Indexed(31)) ); assert_eq!( Style::from_termion(Fg(tc::Rgb(1, 2, 3))), Style::default().fg(Color::Rgb(1, 2, 3)) ); } #[test] fn from_termion_style() { assert_eq!(Modifier::from_termion(tstyle::Invert), Modifier::REVERSED); assert_eq!(Modifier::from_termion(tstyle::Bold), Modifier::BOLD); assert_eq!(Modifier::from_termion(tstyle::Italic), Modifier::ITALIC); assert_eq!( Modifier::from_termion(tstyle::Underline), Modifier::UNDERLINED ); assert_eq!(Modifier::from_termion(tstyle::Faint), Modifier::DIM); assert_eq!( Modifier::from_termion(tstyle::CrossedOut), Modifier::CROSSED_OUT ); assert_eq!(Modifier::from_termion(tstyle::Blink), Modifier::SLOW_BLINK); assert_eq!(Modifier::from_termion(tstyle::Reset), Modifier::empty()); } }