Merge branch 'main' of https://github.com/zellij-org/zellij into layout-path-506

This commit is contained in:
a-kenji 2021-05-18 10:05:15 +02:00
commit bcbde9fbb5
106 changed files with 1508 additions and 1107 deletions

View File

@ -16,10 +16,18 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: |
~/.cargo/bin/
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Add WASM target
run: rustup target add wasm32-wasi
- name: Install cargo-make
run: cargo install --debug cargo-make
run: test -x "${HOME}/.cargo/bin/cargo-make" || cargo install --debug cargo-make
- name: Build
run: cargo make build
- name: Test
@ -31,8 +39,16 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: |
~/.cargo/bin/
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Install cargo-make
run: cargo install --debug cargo-make
run: test -x "${HOME}/.cargo/bin/cargo-make" || cargo install --debug cargo-make
- name: Check Format
run: cargo make check-format
@ -42,7 +58,15 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: |
~/.cargo/bin/
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Install cargo-make
run: cargo install --debug cargo-make
run: test -x "${HOME}/.cargo/bin/cargo-make" || cargo install --debug cargo-make
- name: Check Lints
run: cargo make clippy -D clippy::all

View File

@ -7,6 +7,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
## [Unreleased]
* Remove unused imports (https://github.com/zellij-org/zellij/pull/504)
* More Infrastructure changes for the upcoming session detach feature: run server and client in separate processes (https://github.com/zellij-org/zellij/pull/499)
* Restructuring cargo workspace: Separate client, server and utils into separate crates (https://github.com/zellij-org/zellij/pull/515)
* Terminal compatibility: handle most OSC sequences (https://github.com/zellij-org/zellij/pull/517)
## [0.11.0] - 2021-05-15

262
Cargo.lock generated
View File

@ -132,7 +132,7 @@ dependencies = [
"event-listener",
"futures-lite",
"once_cell",
"signal-hook",
"signal-hook 0.3.8",
"winapi",
]
@ -222,18 +222,6 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "bitvec"
version = "0.19.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8942c8d352ae1838c9dda0b0ca2ab657696ef2232a20147cf1b30ae1a9cb4321"
dependencies = [
"funty",
"radium",
"tap",
"wyz",
]
[[package]]
name = "blocking"
version = "1.0.2"
@ -463,6 +451,31 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "crossterm"
version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e86d73f2a0b407b5768d10a8c720cf5d2df49a9efc10ca09176d201ead4b7fb"
dependencies = [
"bitflags",
"crossterm_winapi",
"lazy_static",
"libc",
"mio",
"parking_lot",
"signal-hook 0.1.17",
"winapi",
]
[[package]]
name = "crossterm_winapi"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2265c3f8e080075d9b6417aa72293fc71662f34b4af2612d8d1b074d29510db"
dependencies = [
"winapi",
]
[[package]]
name = "ctor"
version = "0.1.20"
@ -620,12 +633,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
[[package]]
name = "funty"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7"
[[package]]
name = "futures"
version = "0.3.14"
@ -950,19 +957,6 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3576a87f2ba00f6f106fdfcd16db1d698d648a26ad8e0573cad8537c3c362d2a"
[[package]]
name = "lexical-core"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21f866863575d0e1d654fbeeabdc927292fdf862873dc3c96c6f753357e13374"
dependencies = [
"arrayvec",
"bitflags",
"cfg-if 1.0.0",
"ryu",
"static_assertions",
]
[[package]]
name = "libc"
version = "0.2.93"
@ -987,9 +981,9 @@ checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
[[package]]
name = "lock_api"
version = "0.4.3"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3c91c24eae6777794bb1997ad98bbb87daf92890acab859f7eaa4320333176"
checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb"
dependencies = [
"scopeguard",
]
@ -1047,6 +1041,28 @@ dependencies = [
"autocfg",
]
[[package]]
name = "mio"
version = "0.7.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf80d3e903b34e0bd7282b218398aec54e082c840d9baf8339e0080a0c542956"
dependencies = [
"libc",
"log",
"miow",
"ntapi",
"winapi",
]
[[package]]
name = "miow"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
dependencies = [
"winapi",
]
[[package]]
name = "more-asserts"
version = "0.2.1"
@ -1075,16 +1091,12 @@ dependencies = [
]
[[package]]
name = "nom"
version = "6.1.2"
name = "ntapi"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2"
checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
dependencies = [
"bitvec",
"funty",
"lexical-core",
"memchr",
"version_check",
"winapi",
]
[[package]]
@ -1131,6 +1143,31 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72"
[[package]]
name = "parking_lot"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
dependencies = [
"instant",
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018"
dependencies = [
"cfg-if 1.0.0",
"instant",
"libc",
"redox_syscall",
"smallvec",
"winapi",
]
[[package]]
name = "pin-project-lite"
version = "0.2.6"
@ -1226,12 +1263,6 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "radium"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8"
[[package]]
name = "rand"
version = "0.3.23"
@ -1480,6 +1511,17 @@ dependencies = [
"yaml-rust",
]
[[package]]
name = "signal-hook"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e31d442c16f047a671b5a71e2161d6e68814012b7f5379d269ebd915fac2729"
dependencies = [
"libc",
"mio",
"signal-hook-registry",
]
[[package]]
name = "signal-hook"
version = "0.3.8"
@ -1542,12 +1584,6 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "status-bar"
version = "0.1.0"
@ -1651,12 +1687,6 @@ dependencies = [
"zellij-tile-utils",
]
[[package]]
name = "tap"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "target-lexicon"
version = "0.11.2"
@ -1677,6 +1707,17 @@ dependencies = [
"winapi",
]
[[package]]
name = "termbg"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "133c09f40b7a6a10616bb46d2b7d78d50cb8c6c475c84f3c02fe261957d9e0e0"
dependencies = [
"crossterm",
"thiserror",
"winapi",
]
[[package]]
name = "terminal_size"
version = "0.1.16"
@ -1699,15 +1740,6 @@ dependencies = [
"redox_termios",
]
[[package]]
name = "termios"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "411c5bf740737c7918b8b1fe232dca4dc9f8e754b8ad5e20966814001ed0ac6b"
dependencies = [
"libc",
]
[[package]]
name = "textwrap"
version = "0.11.0"
@ -1810,15 +1842,6 @@ version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796"
[[package]]
name = "unicode-truncate"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a04be5ca5f7a4a7270ffea82bc41c59b87c611ed04f20e77c338e8d3c2348e42"
dependencies = [
"unicode-width",
]
[[package]]
name = "unicode-width"
version = "0.1.8"
@ -2253,12 +2276,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "wyz"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"
[[package]]
name = "yaml-rust"
version = "0.4.5"
@ -2272,37 +2289,53 @@ dependencies = [
name = "zellij"
version = "0.12.0"
dependencies = [
"ansi_term 0.12.1",
"async-std",
"backtrace",
"bincode",
"colors-transform",
"daemonize",
"directories-next",
"futures",
"insta",
"interprocess",
"lazy_static",
"libc",
"names",
"nix",
"nom",
"structopt",
"vte 0.10.1",
"zellij-client",
"zellij-server",
"zellij-tile",
"zellij-utils",
]
[[package]]
name = "zellij-client"
version = "0.12.0"
dependencies = [
"interprocess",
"libc",
"nix",
"signal-hook 0.3.8",
"termbg",
"termion",
"zellij-tile",
"zellij-utils",
]
[[package]]
name = "zellij-server"
version = "0.12.0"
dependencies = [
"ansi_term 0.12.1",
"async-std",
"daemonize",
"insta",
"interprocess",
"libc",
"nix",
"serde",
"serde_json",
"serde_yaml",
"signal-hook",
"strip-ansi-escapes",
"structopt",
"strum",
"tempfile",
"signal-hook 0.3.8",
"termion",
"termios",
"unicode-truncate",
"unicode-width",
"vte 0.10.1",
"wasmer",
"wasmer-wasi",
"zellij-tile",
"zellij-utils",
]
[[package]]
@ -2321,3 +2354,26 @@ version = "0.12.0"
dependencies = [
"ansi_term 0.12.1",
]
[[package]]
name = "zellij-utils"
version = "0.12.0"
dependencies = [
"async-std",
"backtrace",
"bincode",
"colors-transform",
"directories-next",
"interprocess",
"lazy_static",
"names",
"nix",
"serde",
"serde_yaml",
"strip-ansi-escapes",
"structopt",
"strum",
"tempfile",
"termion",
"zellij-tile",
]

View File

@ -8,52 +8,34 @@ license = "MIT"
repository = "https://github.com/zellij-org/zellij"
homepage = "https://zellij.dev"
include = ["src/**/*", "assets/plugins/*", "assets/layouts/*", "assets/config/*", "LICENSE.md", "README.md", "!**/*_test.*", "!**/tests/**/*"]
resolver = "2"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
ansi_term = "0.12.1"
backtrace = "0.3.55"
bincode = "1.3.1"
daemonize = "0.4.1"
directories-next = "2.0"
futures = "0.3.5"
libc = "0.2"
nix = "0.19.1"
nom = "6.0.1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_yaml = "0.8"
signal-hook = "0.3"
strip-ansi-escapes = "0.1.0"
structopt = "0.3"
termion = "1.5.0"
termios = "0.3"
unicode-truncate = "0.2.0"
unicode-width = "0.1.8"
vte = "0.10.1"
strum = "0.20.0"
lazy_static = "1.4.0"
wasmer = "1.0.0"
wasmer-wasi = "1.0.0"
interprocess = "1.1.1"
names = "0.11.0"
colors-transform = "0.2.5"
zellij-utils = { path = "zellij-utils/", version = "0.12.0" }
zellij-client = { path = "zellij-client/", version = "0.12.0" }
zellij-server = { path = "zellij-server/", version = "0.12.0" }
zellij-tile = { path = "zellij-tile/", version = "0.12.0" }
[dependencies.async-std]
version = "1.3.0"
features = ["unstable"]
structopt = "0.3"
interprocess = "1.1.1"
vte = "0.10.1"
nix = "0.19.1"
[dev-dependencies]
insta = "1.6.0"
tempfile = "3.2.0"
zellij-utils = { path = "zellij-utils/", version = "*", features = ["test"] }
zellij-client = { path = "zellij-client/", version = "*", features = ["test"] }
zellij-server = { path = "zellij-server/", version = "*", features = ["test"] }
[build-dependencies]
structopt = "0.3"
[workspace]
members = [
"zellij-client",
"zellij-server",
"zellij-utils",
"zellij-tile",
"zellij-tile-utils",
"default-plugins/status-bar",

View File

@ -24,6 +24,7 @@ env = { "SKIP_TEST" = true }
[tasks.test]
condition = { env_false = ["SKIP_TEST"] }
dependencies = ["pre-test"]
args = ["test"]
[tasks.post-test]
env = { "SKIP_TEST" = false }
@ -37,6 +38,12 @@ run_task = "launch"
[tasks.build-workspace]
run_task = { name = "build", fork = true }
[tasks.build]
args = ["build"]
[tasks.build-release]
args = ["build", "--release"]
[tasks.build-dev-data-dir]
script_runner = "@duckscript"
script = '''

View File

@ -36,15 +36,14 @@ cargo install zellij
Or you can download a prebuilt binary from our [Releases](https://github.com/zellij-org/zellij/releases).
As the default plugins make use of characters that are mostly only found in [nerdfonts](https://www.nerdfonts.com/),
you get the best experience either with installing nerdfonts, or telling the plugins that you request a ui, that
does not rely on such characters with `zellij options --simplified-ui`, or putting `simplified_ui: true` in the
config file.
The default plugins make use of characters that are mostly found in [nerdfonts](https://www.nerdfonts.com/).
To get the best experience either install nerdfonts, or use the simplified ui by starting Zellij with `zellij options --simplified-ui`, or putting `simplified_ui: true` in the config file.
## How do I hack on it? (Contributing)
* Clone the project
* Install cargo-make with `cargo install --force cargo-make`
* In the project folder, for debug builds run: `cargo make run`
* To run all tests: `cargo make test`
For more build commands, see [`Contributing.md`](CONTRIBUTING.md).

View File

@ -72,55 +72,61 @@ pub struct ColoredElements {
// that can be defined in the config perhaps
fn color_elements(palette: Palette) -> ColoredElements {
match palette.source {
// "cyan" here is used as a background as a dirty hack
// this is because the Palette struct doesn't have a "gray" section
// and we can't use its "bg" because that is now dynamically taken from the terminal
// and might often not actually fit the rest of the colorscheme
//
// to fix this, we need to restructure the Palette struct
PaletteSource::Default => ColoredElements {
selected_prefix_separator: style!(palette.bg, palette.green),
selected_prefix_separator: style!(palette.cyan, palette.green),
selected_char_left_separator: style!(palette.black, palette.green).bold(),
selected_char_shortcut: style!(palette.red, palette.green).bold(),
selected_char_right_separator: style!(palette.black, palette.green).bold(),
selected_styled_text: style!(palette.black, palette.green).bold(),
selected_suffix_separator: style!(palette.green, palette.bg).bold(),
unselected_prefix_separator: style!(palette.bg, palette.fg),
selected_suffix_separator: style!(palette.green, palette.cyan).bold(),
unselected_prefix_separator: style!(palette.cyan, palette.fg),
unselected_char_left_separator: style!(palette.black, palette.fg).bold(),
unselected_char_shortcut: style!(palette.red, palette.fg).bold(),
unselected_char_right_separator: style!(palette.black, palette.fg).bold(),
unselected_styled_text: style!(palette.black, palette.fg).bold(),
unselected_suffix_separator: style!(palette.fg, palette.bg),
disabled_prefix_separator: style!(palette.bg, palette.fg),
disabled_styled_text: style!(palette.bg, palette.fg).dimmed(),
disabled_suffix_separator: style!(palette.fg, palette.bg),
selected_single_letter_prefix_separator: style!(palette.bg, palette.green),
unselected_suffix_separator: style!(palette.fg, palette.cyan),
disabled_prefix_separator: style!(palette.cyan, palette.fg),
disabled_styled_text: style!(palette.cyan, palette.fg).dimmed(),
disabled_suffix_separator: style!(palette.fg, palette.cyan),
selected_single_letter_prefix_separator: style!(palette.cyan, palette.green),
selected_single_letter_char_shortcut: style!(palette.red, palette.green).bold(),
selected_single_letter_suffix_separator: style!(palette.green, palette.bg),
unselected_single_letter_prefix_separator: style!(palette.bg, palette.fg),
selected_single_letter_suffix_separator: style!(palette.green, palette.cyan),
unselected_single_letter_prefix_separator: style!(palette.cyan, palette.fg),
unselected_single_letter_char_shortcut: style!(palette.red, palette.fg).bold(),
unselected_single_letter_suffix_separator: style!(palette.fg, palette.bg),
superkey_prefix: style!(palette.white, palette.bg).bold(),
superkey_suffix_separator: style!(palette.bg, palette.bg),
unselected_single_letter_suffix_separator: style!(palette.fg, palette.cyan),
superkey_prefix: style!(palette.white, palette.cyan).bold(),
superkey_suffix_separator: style!(palette.cyan, palette.cyan),
},
PaletteSource::Xresources => ColoredElements {
selected_prefix_separator: style!(palette.bg, palette.green),
selected_prefix_separator: style!(palette.cyan, palette.green),
selected_char_left_separator: style!(palette.fg, palette.green).bold(),
selected_char_shortcut: style!(palette.red, palette.green).bold(),
selected_char_right_separator: style!(palette.fg, palette.green).bold(),
selected_styled_text: style!(palette.bg, palette.green).bold(),
selected_suffix_separator: style!(palette.green, palette.bg).bold(),
unselected_prefix_separator: style!(palette.bg, palette.fg),
unselected_char_left_separator: style!(palette.bg, palette.fg).bold(),
selected_styled_text: style!(palette.cyan, palette.green).bold(),
selected_suffix_separator: style!(palette.green, palette.cyan).bold(),
unselected_prefix_separator: style!(palette.cyan, palette.fg),
unselected_char_left_separator: style!(palette.cyan, palette.fg).bold(),
unselected_char_shortcut: style!(palette.red, palette.fg).bold(),
unselected_char_right_separator: style!(palette.bg, palette.fg).bold(),
unselected_styled_text: style!(palette.bg, palette.fg).bold(),
unselected_suffix_separator: style!(palette.fg, palette.bg),
disabled_prefix_separator: style!(palette.bg, palette.fg),
disabled_styled_text: style!(palette.bg, palette.fg).dimmed(),
disabled_suffix_separator: style!(palette.fg, palette.bg),
unselected_char_right_separator: style!(palette.cyan, palette.fg).bold(),
unselected_styled_text: style!(palette.cyan, palette.fg).bold(),
unselected_suffix_separator: style!(palette.fg, palette.cyan),
disabled_prefix_separator: style!(palette.cyan, palette.fg),
disabled_styled_text: style!(palette.cyan, palette.fg).dimmed(),
disabled_suffix_separator: style!(palette.fg, palette.cyan),
selected_single_letter_prefix_separator: style!(palette.fg, palette.green),
selected_single_letter_char_shortcut: style!(palette.red, palette.green).bold(),
selected_single_letter_suffix_separator: style!(palette.green, palette.fg),
unselected_single_letter_prefix_separator: style!(palette.fg, palette.bg),
unselected_single_letter_prefix_separator: style!(palette.fg, palette.cyan),
unselected_single_letter_char_shortcut: style!(palette.red, palette.fg).bold(),
unselected_single_letter_suffix_separator: style!(palette.fg, palette.bg),
superkey_prefix: style!(palette.bg, palette.fg).bold(),
superkey_suffix_separator: style!(palette.fg, palette.bg),
unselected_single_letter_suffix_separator: style!(palette.fg, palette.cyan),
superkey_prefix: style!(palette.cyan, palette.fg).bold(),
superkey_suffix_separator: style!(palette.fg, palette.cyan),
},
}
}
@ -155,7 +161,7 @@ impl ZellijPlugin for State {
// [48;5;238m is gray background, [0K is so that it fills the rest of the line
// [m is background reset, [0K is so that it clears the rest of the line
match self.mode_info.palette.bg {
match self.mode_info.palette.cyan {
PaletteColor::Rgb((r, g, b)) => {
println!("{}\u{1b}[48;2;{};{};{}m\u{1b}[0K", first_line, r, g, b);
}

View File

@ -62,11 +62,11 @@ fn left_more_message(tab_count_to_the_left: usize, palette: Palette, separator:
};
// 238
let more_text_len = more_text.chars().count() + 2; // 2 for the arrows
let left_separator = style!(palette.bg, palette.orange).paint(separator);
let left_separator = style!(palette.cyan, palette.orange).paint(separator);
let more_styled_text = style!(palette.black, palette.orange)
.bold()
.paint(more_text);
let right_separator = style!(palette.orange, palette.bg).paint(separator);
let right_separator = style!(palette.orange, palette.cyan).paint(separator);
let more_styled_text = format!(
"{}",
ANSIStrings(&[left_separator, more_styled_text, right_separator,])
@ -94,11 +94,11 @@ fn right_more_message(
" +many → ".to_string()
};
let more_text_len = more_text.chars().count() + 1; // 2 for the arrow
let left_separator = style!(palette.bg, palette.orange).paint(separator);
let left_separator = style!(palette.cyan, palette.orange).paint(separator);
let more_styled_text = style!(palette.black, palette.orange)
.bold()
.paint(more_text);
let right_separator = style!(palette.orange, palette.bg).paint(separator);
let right_separator = style!(palette.orange, palette.cyan).paint(separator);
let more_styled_text = format!(
"{}",
ANSIStrings(&[left_separator, more_styled_text, right_separator,])
@ -147,7 +147,9 @@ fn add_next_tabs_msg(
fn tab_line_prefix(palette: Palette) -> LinePart {
let prefix_text = " Zellij ".to_string();
let prefix_text_len = prefix_text.chars().count();
let prefix_styled_text = style!(palette.white, palette.bg).bold().paint(prefix_text);
let prefix_styled_text = style!(palette.white, palette.cyan)
.bold()
.paint(prefix_text);
LinePart {
part: format!("{}", prefix_styled_text),
len: prefix_text_len,

View File

@ -75,7 +75,7 @@ impl ZellijPlugin for State {
for bar_part in tab_line {
s = format!("{}{}", s, bar_part.part);
}
match self.mode_info.palette.bg {
match self.mode_info.palette.cyan {
PaletteColor::Rgb((r, g, b)) => {
println!("{}\u{1b}[48;2;{};{};{}m\u{1b}[0K", s, r, g, b);
}

View File

@ -4,12 +4,12 @@ use zellij_tile::prelude::*;
use zellij_tile_utils::style;
pub fn active_tab(text: String, palette: Palette, separator: &str) -> LinePart {
let left_separator = style!(palette.bg, palette.green).paint(separator);
let left_separator = style!(palette.cyan, palette.green).paint(separator);
let tab_text_len = text.chars().count() + 4; // 2 for left and right separators, 2 for the text padding
let tab_styled_text = style!(palette.black, palette.green)
.bold()
.paint(format!(" {} ", text));
let right_separator = style!(palette.green, palette.bg).paint(separator);
let right_separator = style!(palette.green, palette.cyan).paint(separator);
let tab_styled_text = format!(
"{}",
ANSIStrings(&[left_separator, tab_styled_text, right_separator,])
@ -21,12 +21,12 @@ pub fn active_tab(text: String, palette: Palette, separator: &str) -> LinePart {
}
pub fn non_active_tab(text: String, palette: Palette, separator: &str) -> LinePart {
let left_separator = style!(palette.bg, palette.fg).paint(separator);
let left_separator = style!(palette.cyan, palette.fg).paint(separator);
let tab_text_len = text.chars().count() + 4; // 2 for left and right separators, 2 for the padding
let tab_styled_text = style!(palette.black, palette.fg)
.bold()
.paint(format!(" {} ", text));
let right_separator = style!(palette.fg, palette.bg).paint(separator);
let right_separator = style!(palette.fg, palette.cyan).paint(separator);
let tab_styled_text = format!(
"{}",
ANSIStrings(&[left_separator, tab_styled_text, right_separator,])

View File

@ -1,7 +0,0 @@
//! The way terminal input is handled.
pub mod actions;
pub mod config;
pub mod handler;
pub mod keybinds;
pub mod options;

View File

@ -1,14 +0,0 @@
pub mod command_is_executing;
pub mod errors;
pub mod input;
pub mod ipc;
pub mod os_input_output;
pub mod pty;
pub mod screen;
pub mod setup;
pub mod thread_bus;
pub mod utils;
pub mod wasm_vm;
use crate::panes::PaneId;
use crate::server::ServerInstruction;

View File

@ -1,142 +0,0 @@
//! Definitions and helpers for sending and receiving messages between threads.
use async_std::task_local;
use std::cell::RefCell;
use std::sync::mpsc;
use crate::common::pty::PtyInstruction;
use crate::common::ServerInstruction;
use crate::errors::{get_current_ctx, ErrorContext};
use crate::os_input_output::ServerOsApi;
use crate::screen::ScreenInstruction;
use crate::wasm_vm::PluginInstruction;
/// An [MPSC](mpsc) asynchronous channel with added error context.
pub type ChannelWithContext<T> = (
mpsc::Sender<(T, ErrorContext)>,
mpsc::Receiver<(T, ErrorContext)>,
);
/// An [MPSC](mpsc) synchronous channel with added error context.
pub type SyncChannelWithContext<T> = (
mpsc::SyncSender<(T, ErrorContext)>,
mpsc::Receiver<(T, ErrorContext)>,
);
/// Wrappers around the two standard [MPSC](mpsc) sender types, [`mpsc::Sender`] and [`mpsc::SyncSender`], with an additional [`ErrorContext`].
#[derive(Clone)]
pub enum SenderType<T: Clone> {
/// A wrapper around an [`mpsc::Sender`], adding an [`ErrorContext`].
Sender(mpsc::Sender<(T, ErrorContext)>),
/// A wrapper around an [`mpsc::SyncSender`], adding an [`ErrorContext`].
SyncSender(mpsc::SyncSender<(T, ErrorContext)>),
}
/// Sends messages on an [MPSC](std::sync::mpsc) channel, along with an [`ErrorContext`],
/// synchronously or asynchronously depending on the underlying [`SenderType`].
#[derive(Clone)]
pub struct SenderWithContext<T: Clone> {
sender: SenderType<T>,
}
impl<T: Clone> SenderWithContext<T> {
pub fn new(sender: SenderType<T>) -> Self {
Self { sender }
}
/// Sends an event, along with the current [`ErrorContext`], on this
/// [`SenderWithContext`]'s channel.
pub fn send(&self, event: T) -> Result<(), mpsc::SendError<(T, ErrorContext)>> {
let err_ctx = get_current_ctx();
match self.sender {
SenderType::Sender(ref s) => s.send((event, err_ctx)),
SenderType::SyncSender(ref s) => s.send((event, err_ctx)),
}
}
}
unsafe impl<T: Clone> Send for SenderWithContext<T> {}
unsafe impl<T: Clone> Sync for SenderWithContext<T> {}
thread_local!(
/// A key to some thread local storage (TLS) that holds a representation of the thread's call
/// stack in the form of an [`ErrorContext`].
pub static OPENCALLS: RefCell<ErrorContext> = RefCell::default()
);
task_local! {
/// A key to some task local storage that holds a representation of the task's call
/// stack in the form of an [`ErrorContext`].
pub static ASYNCOPENCALLS: RefCell<ErrorContext> = RefCell::default()
}
/// A container for senders to the different threads in zellij on the server side
#[derive(Clone)]
pub struct ThreadSenders {
pub to_screen: Option<SenderWithContext<ScreenInstruction>>,
pub to_pty: Option<SenderWithContext<PtyInstruction>>,
pub to_plugin: Option<SenderWithContext<PluginInstruction>>,
pub to_server: Option<SenderWithContext<ServerInstruction>>,
}
impl ThreadSenders {
pub fn send_to_screen(
&self,
instruction: ScreenInstruction,
) -> Result<(), mpsc::SendError<(ScreenInstruction, ErrorContext)>> {
self.to_screen.as_ref().unwrap().send(instruction)
}
pub fn send_to_pty(
&self,
instruction: PtyInstruction,
) -> Result<(), mpsc::SendError<(PtyInstruction, ErrorContext)>> {
self.to_pty.as_ref().unwrap().send(instruction)
}
pub fn send_to_plugin(
&self,
instruction: PluginInstruction,
) -> Result<(), mpsc::SendError<(PluginInstruction, ErrorContext)>> {
self.to_plugin.as_ref().unwrap().send(instruction)
}
pub fn send_to_server(
&self,
instruction: ServerInstruction,
) -> Result<(), mpsc::SendError<(ServerInstruction, ErrorContext)>> {
self.to_server.as_ref().unwrap().send(instruction)
}
}
/// A container for a receiver, OS input and the senders to a given thread
pub struct Bus<T> {
pub receiver: mpsc::Receiver<(T, ErrorContext)>,
pub senders: ThreadSenders,
pub os_input: Option<Box<dyn ServerOsApi>>,
}
impl<T> Bus<T> {
pub fn new(
receiver: mpsc::Receiver<(T, ErrorContext)>,
to_screen: Option<&SenderWithContext<ScreenInstruction>>,
to_pty: Option<&SenderWithContext<PtyInstruction>>,
to_plugin: Option<&SenderWithContext<PluginInstruction>>,
to_server: Option<&SenderWithContext<ServerInstruction>>,
os_input: Option<Box<dyn ServerOsApi>>,
) -> Self {
Bus {
receiver,
senders: ThreadSenders {
to_screen: to_screen.cloned(),
to_pty: to_pty.cloned(),
to_plugin: to_plugin.cloned(),
to_server: to_server.cloned(),
},
os_input: os_input.clone(),
}
}
pub fn recv(&self) -> Result<(T, ErrorContext), mpsc::RecvError> {
self.receiver.recv()
}
}

View File

@ -1,5 +0,0 @@
//! Zellij utilities.
pub mod consts;
pub mod logging;
pub mod shared;

View File

@ -1,31 +1,22 @@
mod cli;
mod client;
mod common;
mod server;
#[cfg(test)]
mod tests;
use client::{boundaries, layout, panes, start_client, tab};
use common::{
command_is_executing, errors, os_input_output, pty, screen, setup::Setup, utils, wasm_vm,
};
use server::start_server;
use structopt::StructOpt;
use crate::cli::CliArgs;
use crate::command_is_executing::CommandIsExecuting;
use crate::common::input::config::Config;
use crate::os_input_output::{get_client_os_input, get_server_os_input};
use crate::utils::{
consts::{ZELLIJ_TMP_DIR, ZELLIJ_TMP_LOG_DIR},
logging::*,
};
use std::convert::TryFrom;
use structopt::StructOpt;
use zellij_client::{os_input_output::get_client_os_input, start_client};
use zellij_server::{os_input_output::get_server_os_input, start_server};
use zellij_utils::{
cli::{CliArgs, ConfigCli},
consts::{ZELLIJ_TMP_DIR, ZELLIJ_TMP_LOG_DIR},
input::config::Config,
logging::*,
setup::Setup,
};
pub fn main() {
let opts = CliArgs::from_args();
if let Some(crate::cli::ConfigCli::Setup(setup)) = opts.option.clone() {
if let Some(ConfigCli::Setup(setup)) = opts.option.clone() {
Setup::from_cli(&setup, opts).expect("Failed to print to stdout");
std::process::exit(0);
} else {
@ -39,10 +30,22 @@ pub fn main() {
atomic_create_dir(&*ZELLIJ_TMP_DIR).unwrap();
atomic_create_dir(&*ZELLIJ_TMP_LOG_DIR).unwrap();
if let Some(path) = opts.server {
let os_input = get_server_os_input();
let os_input = match get_server_os_input() {
Ok(server_os_input) => server_os_input,
Err(e) => {
eprintln!("failed to open terminal:\n{}", e);
std::process::exit(1);
}
};
start_server(Box::new(os_input), path);
} else {
let os_input = get_client_os_input();
let os_input = match get_client_os_input() {
Ok(os_input) => os_input,
Err(e) => {
eprintln!("failed to open terminal:\n{}", e);
std::process::exit(1);
}
};
start_client(Box::new(os_input), opts, config);
}
}

View File

@ -1,4 +1,5 @@
use crate::panes::PositionAndSize;
use crate::tests::possible_tty_inputs::{get_possible_tty_inputs, Bytes};
use crate::tests::utils::commands::{QUIT, SLEEP};
use interprocess::local_socket::LocalSocketStream;
use std::collections::{HashMap, VecDeque};
use std::io::Write;
@ -6,15 +7,16 @@ use std::os::unix::io::RawFd;
use std::path::PathBuf;
use std::sync::{mpsc, Arc, Condvar, Mutex};
use std::time::{Duration, Instant};
use crate::common::ipc::{ClientToServerMsg, ServerToClientMsg};
use crate::common::thread_bus::{ChannelWithContext, SenderType, SenderWithContext};
use crate::errors::ErrorContext;
use crate::os_input_output::{ClientOsApi, ServerOsApi};
use crate::tests::possible_tty_inputs::{get_possible_tty_inputs, Bytes};
use crate::tests::utils::commands::{QUIT, SLEEP};
use crate::utils::shared::default_palette;
use zellij_client::os_input_output::ClientOsApi;
use zellij_server::os_input_output::ServerOsApi;
use zellij_tile::data::Palette;
use zellij_utils::{
channels::{ChannelWithContext, SenderType, SenderWithContext},
errors::ErrorContext,
ipc::{ClientToServerMsg, ServerToClientMsg},
pane_size::PositionAndSize,
shared::default_palette,
};
const MIN_TIME_BETWEEN_SNAPSHOTS: Duration = Duration::from_millis(150);
@ -217,6 +219,9 @@ impl ClientOsApi for FakeInputOutput {
}
}
fn connect_to_server(&self, _path: &std::path::Path) {}
fn load_palette(&self) -> Palette {
default_palette()
}
}
impl ServerOsApi for FakeInputOutput {

View File

@ -1,7 +1,6 @@
use crate::panes::PositionAndSize;
use ::insta::assert_snapshot;
use zellij_utils::pane_size::PositionAndSize;
use crate::common::input::config::Config;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::start;
use crate::tests::utils::commands::{
@ -12,6 +11,7 @@ use crate::tests::utils::commands::{
};
use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
use crate::CliArgs;
use zellij_utils::input::config::Config;
fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput {
FakeInputOutput::new(fake_win_size.clone())

View File

@ -1,17 +1,17 @@
use crate::panes::PositionAndSize;
use ::insta::assert_snapshot;
use zellij_utils::pane_size::PositionAndSize;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::start;
use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
use crate::CliArgs;
use crate::common::input::config::Config;
use crate::tests::utils::commands::{
CLOSE_PANE_IN_PANE_MODE, ESC, MOVE_FOCUS_IN_PANE_MODE, PANE_MODE, QUIT,
RESIZE_DOWN_IN_RESIZE_MODE, RESIZE_LEFT_IN_RESIZE_MODE, RESIZE_MODE, RESIZE_UP_IN_RESIZE_MODE,
SPLIT_DOWN_IN_PANE_MODE, SPLIT_RIGHT_IN_PANE_MODE,
};
use zellij_utils::input::config::Config;
fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput {
FakeInputOutput::new(fake_win_size.clone())

View File

@ -1,15 +1,15 @@
use ::insta::assert_snapshot;
use ::std::collections::HashMap;
use crate::panes::PositionAndSize;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::possible_tty_inputs::Bytes;
use crate::tests::start;
use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
use crate::CliArgs;
use zellij_utils::pane_size::PositionAndSize;
use crate::common::input::config::Config;
use crate::tests::utils::commands::QUIT;
use zellij_utils::input::config::Config;
/*
* These tests are general compatibility tests for non-trivial scenarios running in the terminal.

View File

@ -1,13 +1,13 @@
use insta::assert_snapshot;
use std::path::PathBuf;
use crate::common::input::config::Config;
use crate::panes::PositionAndSize;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::start;
use crate::tests::utils::commands::QUIT;
use crate::tests::utils::get_output_frame_snapshots;
use crate::CliArgs;
use zellij_utils::input::config::Config;
use zellij_utils::pane_size::PositionAndSize;
fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput {
FakeInputOutput::new(fake_win_size.clone())

View File

@ -1,16 +1,16 @@
use ::insta::assert_snapshot;
use crate::panes::PositionAndSize;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::start;
use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
use crate::CliArgs;
use zellij_utils::pane_size::PositionAndSize;
use crate::common::input::config::Config;
use crate::tests::utils::commands::{
MOVE_FOCUS_DOWN_IN_PANE_MODE, MOVE_FOCUS_UP_IN_PANE_MODE, PANE_MODE, QUIT,
SPLIT_DOWN_IN_PANE_MODE, SPLIT_RIGHT_IN_PANE_MODE,
};
use zellij_utils::input::config::Config;
fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput {
FakeInputOutput::new(*fake_win_size)

View File

@ -1,17 +1,17 @@
use ::insta::assert_snapshot;
use crate::panes::PositionAndSize;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::start;
use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
use crate::CliArgs;
use zellij_utils::pane_size::PositionAndSize;
use crate::common::input::config::Config;
use crate::tests::utils::commands::{
ENTER, MOVE_FOCUS_LEFT_IN_NORMAL_MODE, MOVE_FOCUS_LEFT_IN_PANE_MODE,
MOVE_FOCUS_RIGHT_IN_PANE_MODE, NEW_TAB_IN_TAB_MODE, PANE_MODE, QUIT, SPLIT_DOWN_IN_PANE_MODE,
SPLIT_RIGHT_IN_PANE_MODE, TAB_MODE,
};
use zellij_utils::input::config::Config;
fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput {
FakeInputOutput::new(*fake_win_size)

View File

@ -1,17 +1,17 @@
use ::insta::assert_snapshot;
use crate::panes::PositionAndSize;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::start;
use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
use crate::CliArgs;
use zellij_utils::pane_size::PositionAndSize;
use crate::common::input::config::Config;
use crate::tests::utils::commands::{
ENTER, MOVE_FOCUS_LEFT_IN_PANE_MODE, MOVE_FOCUS_RIGHT_IN_NORMAL_MODE,
MOVE_FOCUS_RIGHT_IN_PANE_MODE, NEW_TAB_IN_TAB_MODE, PANE_MODE, QUIT, SPLIT_DOWN_IN_PANE_MODE,
SPLIT_RIGHT_IN_PANE_MODE, TAB_MODE,
};
use zellij_utils::input::config::Config;
fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput {
FakeInputOutput::new(*fake_win_size)

View File

@ -1,16 +1,16 @@
use ::insta::assert_snapshot;
use crate::panes::PositionAndSize;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::start;
use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
use crate::CliArgs;
use zellij_utils::pane_size::PositionAndSize;
use crate::common::input::config::Config;
use crate::tests::utils::commands::{
MOVE_FOCUS_DOWN_IN_PANE_MODE, MOVE_FOCUS_UP_IN_PANE_MODE, PANE_MODE, QUIT,
SPLIT_DOWN_IN_PANE_MODE, SPLIT_RIGHT_IN_PANE_MODE,
};
use zellij_utils::input::config::Config;
fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput {
FakeInputOutput::new(*fake_win_size)

View File

@ -1,17 +1,17 @@
use insta::assert_snapshot;
use crate::panes::PositionAndSize;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::start;
use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
use crate::CliArgs;
use zellij_utils::pane_size::PositionAndSize;
use crate::common::input::config::Config;
use crate::tests::utils::commands::{
MOVE_FOCUS_IN_PANE_MODE, PANE_MODE, QUIT, RESIZE_DOWN_IN_RESIZE_MODE,
RESIZE_LEFT_IN_RESIZE_MODE, RESIZE_MODE, SLEEP, SPLIT_DOWN_IN_PANE_MODE,
SPLIT_RIGHT_IN_PANE_MODE,
};
use zellij_utils::input::config::Config;
fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput {
FakeInputOutput::new(*fake_win_size)

View File

@ -1,16 +1,16 @@
use ::insta::assert_snapshot;
use crate::panes::PositionAndSize;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::start;
use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
use crate::CliArgs;
use zellij_utils::pane_size::PositionAndSize;
use crate::common::input::config::Config;
use crate::tests::utils::commands::{
MOVE_FOCUS_IN_PANE_MODE, PANE_MODE, QUIT, RESIZE_LEFT_IN_RESIZE_MODE, RESIZE_MODE,
RESIZE_UP_IN_RESIZE_MODE, SLEEP, SPLIT_DOWN_IN_PANE_MODE, SPLIT_RIGHT_IN_PANE_MODE,
};
use zellij_utils::input::config::Config;
fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput {
FakeInputOutput::new(*fake_win_size)

View File

@ -1,16 +1,16 @@
use ::insta::assert_snapshot;
use crate::panes::PositionAndSize;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::start;
use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
use crate::CliArgs;
use zellij_utils::pane_size::PositionAndSize;
use crate::common::input::config::Config;
use crate::tests::utils::commands::{
MOVE_FOCUS_IN_PANE_MODE, PANE_MODE, QUIT, RESIZE_MODE, RESIZE_RIGHT_IN_RESIZE_MODE,
RESIZE_UP_IN_RESIZE_MODE, SLEEP, SPLIT_DOWN_IN_PANE_MODE, SPLIT_RIGHT_IN_PANE_MODE,
};
use zellij_utils::input::config::Config;
fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput {
FakeInputOutput::new(*fake_win_size)

View File

@ -1,16 +1,16 @@
use ::insta::assert_snapshot;
use crate::panes::PositionAndSize;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::start;
use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
use crate::CliArgs;
use zellij_utils::pane_size::PositionAndSize;
use crate::common::input::config::Config;
use crate::tests::utils::commands::{
MOVE_FOCUS_IN_PANE_MODE, PANE_MODE, QUIT, RESIZE_LEFT_IN_RESIZE_MODE, RESIZE_MODE,
RESIZE_UP_IN_RESIZE_MODE, SLEEP, SPLIT_DOWN_IN_PANE_MODE, SPLIT_RIGHT_IN_PANE_MODE,
};
use zellij_utils::input::config::Config;
fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput {
FakeInputOutput::new(*fake_win_size)

View File

@ -2,16 +2,17 @@ use insta::assert_snapshot;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::start;
use crate::tests::utils::commands::CLOSE_PANE_IN_PANE_MODE;
use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
use crate::CliArgs;
use crate::{panes::PositionAndSize, tests::utils::commands::CLOSE_PANE_IN_PANE_MODE};
use crate::common::input::config::Config;
use crate::tests::utils::commands::{
CLOSE_TAB_IN_TAB_MODE, NEW_TAB_IN_TAB_MODE, PANE_MODE, QUIT, SPLIT_DOWN_IN_PANE_MODE,
SWITCH_NEXT_TAB_IN_TAB_MODE, SWITCH_PREV_TAB_IN_TAB_MODE, TAB_MODE,
TOGGLE_ACTIVE_TERMINAL_FULLSCREEN_IN_PANE_MODE,
};
use zellij_utils::input::config::Config;
use zellij_utils::pane_size::PositionAndSize;
fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput {
FakeInputOutput::new(*fake_win_size)

View File

@ -1,12 +1,12 @@
use crate::panes::PositionAndSize;
use ::insta::assert_snapshot;
use zellij_utils::pane_size::PositionAndSize;
use crate::common::input::config::Config;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::start;
use crate::tests::utils::commands::QUIT;
use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
use crate::CliArgs;
use zellij_utils::input::config::Config;
fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput {
FakeInputOutput::new(fake_win_size.clone())

View File

@ -1,16 +1,16 @@
use insta::assert_snapshot;
use crate::panes::PositionAndSize;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::start;
use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
use crate::CliArgs;
use zellij_utils::pane_size::PositionAndSize;
use crate::common::input::config::Config;
use crate::tests::utils::commands::{
MOVE_FOCUS_IN_PANE_MODE, PANE_MODE, QUIT, SPLIT_DOWN_IN_PANE_MODE, SPLIT_RIGHT_IN_PANE_MODE,
TOGGLE_ACTIVE_TERMINAL_FULLSCREEN_IN_PANE_MODE,
};
use zellij_utils::input::config::Config;
fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput {
FakeInputOutput::new(*fake_win_size)

View File

@ -4,12 +4,10 @@ pub mod possible_tty_inputs;
pub mod tty_inputs;
pub mod utils;
use crate::cli::CliArgs;
use crate::client::start_client;
use crate::common::input::config::Config;
use crate::os_input_output::{ClientOsApi, ServerOsApi};
use crate::server::start_server;
use std::path::PathBuf;
use zellij_client::{os_input_output::ClientOsApi, start_client};
use zellij_server::{os_input_output::ServerOsApi, start_server};
use zellij_utils::{cli::CliArgs, input::config::Config};
pub fn start(
client_os_input: Box<dyn ClientOsApi>,

View File

@ -1,5 +1,6 @@
use crate::panes::PositionAndSize;
use crate::panes::TerminalPane;
use zellij_server::{panes::TerminalPane, tab::Pane};
use zellij_tile::data::Palette;
use zellij_utils::pane_size::PositionAndSize;
pub fn get_output_frame_snapshots(
output_frames: &[Vec<u8>],
@ -7,7 +8,7 @@ pub fn get_output_frame_snapshots(
) -> Vec<String> {
let mut vte_parser = vte::Parser::new();
let main_pid = 0;
let mut terminal_output = TerminalPane::new(main_pid, *win_size);
let mut terminal_output = TerminalPane::new(main_pid, *win_size, Palette::default());
let mut snapshots = vec![];
for frame in output_frames.iter() {

22
zellij-client/Cargo.toml Normal file
View File

@ -0,0 +1,22 @@
[package]
name = "zellij-client"
version = "0.12.0"
authors = ["Kunal Mohan <kunalmohan99@gmail.com>"]
edition = "2018"
description = "The client-side library for Zellij"
license = "MIT"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
zellij-utils = { path = "../zellij-utils/", version = "0.12.0" }
zellij-tile = { path = "../zellij-tile/", version = "0.12.0" }
termion = "1.5.0"
signal-hook = "0.3"
nix = "0.19.1"
interprocess = "1.1.1"
libc = "0.2"
termbg = "0.2.0"
[features]
test = ["zellij-utils/test"]

View File

@ -2,7 +2,7 @@
use std::sync::{Arc, Condvar, Mutex};
#[derive(Clone)]
pub struct CommandIsExecuting {
pub(crate) struct CommandIsExecuting {
input_thread: Arc<(Mutex<bool>, Condvar)>,
}

View File

@ -1,17 +1,15 @@
//! Main input logic.
use super::actions::Action;
use super::keybinds::Keybinds;
use crate::client::ClientInstruction;
use crate::common::input::config::Config;
use crate::common::ipc::ClientToServerMsg;
use crate::common::thread_bus::{SenderWithContext, OPENCALLS};
use crate::errors::ContextType;
use crate::os_input_output::ClientOsApi;
use crate::CommandIsExecuting;
use crate::{os_input_output::ClientOsApi, ClientInstruction, CommandIsExecuting};
use zellij_utils::{
channels::{SenderWithContext, OPENCALLS},
errors::ContextType,
input::{actions::Action, cast_termion_key, config::Config, keybinds::Keybinds},
ipc::ClientToServerMsg,
};
use termion::input::{TermRead, TermReadEventsAndRaw};
use zellij_tile::data::{InputMode, Key, ModeInfo, Palette, PluginCapabilities};
use termion::input::TermReadEventsAndRaw;
use zellij_tile::data::{InputMode, Key};
/// Handles the dispatching of [`Action`]s according to the current
/// [`InputMode`], and keep tracks of the current [`InputMode`].
@ -172,55 +170,9 @@ impl InputHandler {
}
}
/// Creates a [`Help`] struct indicating the current [`InputMode`] and its keybinds
/// (as pairs of [`String`]s).
// TODO this should probably be automatically generated in some way
pub fn get_mode_info(
mode: InputMode,
palette: Palette,
capabilities: PluginCapabilities,
) -> ModeInfo {
let mut keybinds: Vec<(String, String)> = vec![];
match mode {
InputMode::Normal | InputMode::Locked => {}
InputMode::Resize => {
keybinds.push(("←↓↑→".to_string(), "Resize".to_string()));
}
InputMode::Pane => {
keybinds.push(("←↓↑→".to_string(), "Move focus".to_string()));
keybinds.push(("p".to_string(), "Next".to_string()));
keybinds.push(("n".to_string(), "New".to_string()));
keybinds.push(("d".to_string(), "Down split".to_string()));
keybinds.push(("r".to_string(), "Right split".to_string()));
keybinds.push(("x".to_string(), "Close".to_string()));
keybinds.push(("f".to_string(), "Fullscreen".to_string()));
}
InputMode::Tab => {
keybinds.push(("←↓↑→".to_string(), "Move focus".to_string()));
keybinds.push(("n".to_string(), "New".to_string()));
keybinds.push(("x".to_string(), "Close".to_string()));
keybinds.push(("r".to_string(), "Rename".to_string()));
keybinds.push(("s".to_string(), "Sync".to_string()));
}
InputMode::Scroll => {
keybinds.push(("↓↑".to_string(), "Scroll".to_string()));
keybinds.push(("PgUp/PgDn".to_string(), "Scroll Page".to_string()));
}
InputMode::RenameTab => {
keybinds.push(("Enter".to_string(), "when done".to_string()));
}
}
ModeInfo {
mode,
keybinds,
palette,
capabilities,
}
}
/// Entry point to the module. Instantiates an [`InputHandler`] and starts
/// its [`InputHandler::handle_input()`] loop.
pub fn input_loop(
pub(crate) fn input_loop(
os_input: Box<dyn ClientOsApi>,
config: Config,
command_is_executing: CommandIsExecuting,
@ -234,35 +186,3 @@ pub fn input_loop(
)
.handle_input();
}
pub fn parse_keys(input_bytes: &[u8]) -> Vec<Key> {
input_bytes.keys().flatten().map(cast_termion_key).collect()
}
// FIXME: This is an absolutely cursed function that should be destroyed as soon
// as an alternative that doesn't touch zellij-tile can be developed...
fn cast_termion_key(event: termion::event::Key) -> Key {
match event {
termion::event::Key::Backspace => Key::Backspace,
termion::event::Key::Left => Key::Left,
termion::event::Key::Right => Key::Right,
termion::event::Key::Up => Key::Up,
termion::event::Key::Down => Key::Down,
termion::event::Key::Home => Key::Home,
termion::event::Key::End => Key::End,
termion::event::Key::PageUp => Key::PageUp,
termion::event::Key::PageDown => Key::PageDown,
termion::event::Key::BackTab => Key::BackTab,
termion::event::Key::Delete => Key::Delete,
termion::event::Key::Insert => Key::Insert,
termion::event::Key::F(n) => Key::F(n),
termion::event::Key::Char(c) => Key::Char(c),
termion::event::Key::Alt(c) => Key::Alt(c),
termion::event::Key::Ctrl(c) => Key::Ctrl(c),
termion::event::Key::Null => Key::Null,
termion::event::Key::Esc => Key::Esc,
_ => {
unimplemented!("Encountered an unknown key!")
}
}
}

View File

@ -1,8 +1,7 @@
pub mod boundaries;
pub mod layout;
pub mod pane_resizer;
pub mod panes;
pub mod tab;
pub mod os_input_output;
mod command_is_executing;
mod input_handler;
use std::env::current_exe;
use std::io::{self, Write};
@ -11,22 +10,23 @@ use std::process::Command;
use std::sync::mpsc;
use std::thread;
use crate::cli::CliArgs;
use crate::common::{
command_is_executing::CommandIsExecuting,
errors::ContextType,
input::config::Config,
input::handler::input_loop,
input::options::Options,
ipc::{ClientToServerMsg, ServerToClientMsg},
use crate::{
command_is_executing::CommandIsExecuting, input_handler::input_loop,
os_input_output::ClientOsApi,
thread_bus::{SenderType, SenderWithContext, SyncChannelWithContext},
utils::consts::ZELLIJ_IPC_PIPE,
};
use zellij_utils::cli::CliArgs;
use zellij_utils::{
channels::{SenderType, SenderWithContext, SyncChannelWithContext},
consts::ZELLIJ_IPC_PIPE,
errors::{ClientContext, ContextType, ErrorInstruction},
input::config::Config,
input::options::Options,
ipc::{ClientAttributes, ClientToServerMsg, ServerToClientMsg},
};
/// Instructions related to the client-side application and sent from server to client
/// Instructions related to the client-side application
#[derive(Debug, Clone)]
pub enum ClientInstruction {
pub(crate) enum ClientInstruction {
Error(String),
Render(Option<String>),
UnblockInputThread,
@ -45,6 +45,24 @@ impl From<ServerToClientMsg> for ClientInstruction {
}
}
impl From<&ClientInstruction> for ClientContext {
fn from(client_instruction: &ClientInstruction) -> Self {
match *client_instruction {
ClientInstruction::Exit => ClientContext::Exit,
ClientInstruction::Error(_) => ClientContext::Error,
ClientInstruction::ServerError(_) => ClientContext::ServerError,
ClientInstruction::Render(_) => ClientContext::Render,
ClientInstruction::UnblockInputThread => ClientContext::UnblockInputThread,
}
}
}
impl ErrorInstruction for ClientInstruction {
fn error(err: String) -> Self {
ClientInstruction::Error(err)
}
}
fn spawn_server(socket_path: &Path) -> io::Result<()> {
let status = Command::new(current_exe()?)
.arg("--server")
@ -67,6 +85,7 @@ pub fn start_client(mut os_input: Box<dyn ClientOsApi>, opts: CliArgs, config: C
let take_snapshot = "\u{1b}[?1049h";
let bracketed_paste = "\u{1b}[?2004h";
os_input.unset_raw_mode(0);
let palette = os_input.load_palette();
let _ = os_input
.get_stdout_writer()
.write(take_snapshot.as_bytes())
@ -77,7 +96,7 @@ pub fn start_client(mut os_input: Box<dyn ClientOsApi>, opts: CliArgs, config: C
.unwrap();
std::env::set_var(&"ZELLIJ", "0");
#[cfg(not(test))]
#[cfg(not(any(feature = "test", test)))]
spawn_server(&*ZELLIJ_IPC_PIPE).unwrap();
let mut command_is_executing = CommandIsExecuting::new();
@ -85,9 +104,13 @@ pub fn start_client(mut os_input: Box<dyn ClientOsApi>, opts: CliArgs, config: C
let config_options = Options::from_cli(&config.options, opts.option.clone());
let full_screen_ws = os_input.get_terminal_size_using_fd(0);
let client_attributes = ClientAttributes {
position_and_size: full_screen_ws,
palette,
};
os_input.connect_to_server(&*ZELLIJ_IPC_PIPE);
os_input.send_to_server(ClientToServerMsg::NewClient(
full_screen_ws,
client_attributes,
Box::new(opts),
Box::new(config_options),
));
@ -103,9 +126,9 @@ pub fn start_client(mut os_input: Box<dyn ClientOsApi>, opts: CliArgs, config: C
let send_client_instructions =
SenderWithContext::new(SenderType::SyncSender(send_client_instructions));
#[cfg(not(test))]
#[cfg(not(any(feature = "test", test)))]
std::panic::set_hook({
use crate::errors::handle_panic;
use zellij_utils::errors::handle_panic;
let send_client_instructions = send_client_instructions.clone();
Box::new(move |info| {
handle_panic(info, &send_client_instructions);

View File

@ -0,0 +1,188 @@
use interprocess::local_socket::LocalSocketStream;
use nix::pty::Winsize;
use nix::sys::termios;
use signal_hook::{consts::signal::*, iterator::Signals};
use std::io;
use std::io::prelude::*;
use std::os::unix::io::RawFd;
use std::path::Path;
use std::sync::{Arc, Mutex};
use zellij_tile::data::{Palette, PaletteColor};
use zellij_utils::errors::ErrorContext;
use zellij_utils::ipc::{
ClientToServerMsg, IpcReceiverWithContext, IpcSenderWithContext, ServerToClientMsg,
};
use zellij_utils::pane_size::PositionAndSize;
use zellij_utils::shared::default_palette;
fn into_raw_mode(pid: RawFd) {
let mut tio = termios::tcgetattr(pid).expect("could not get terminal attribute");
termios::cfmakeraw(&mut tio);
match termios::tcsetattr(pid, termios::SetArg::TCSANOW, &tio) {
Ok(_) => {}
Err(e) => panic!("error {:?}", e),
};
}
fn unset_raw_mode(pid: RawFd, orig_termios: termios::Termios) {
match termios::tcsetattr(pid, termios::SetArg::TCSANOW, &orig_termios) {
Ok(_) => {}
Err(e) => panic!("error {:?}", e),
};
}
pub(crate) fn get_terminal_size_using_fd(fd: RawFd) -> PositionAndSize {
// TODO: do this with the nix ioctl
use libc::ioctl;
use libc::TIOCGWINSZ;
let mut winsize = Winsize {
ws_row: 0,
ws_col: 0,
ws_xpixel: 0,
ws_ypixel: 0,
};
unsafe { ioctl(fd, TIOCGWINSZ, &mut winsize) };
PositionAndSize::from(winsize)
}
#[derive(Clone)]
pub struct ClientOsInputOutput {
orig_termios: Arc<Mutex<termios::Termios>>,
send_instructions_to_server: Arc<Mutex<Option<IpcSenderWithContext<ClientToServerMsg>>>>,
receive_instructions_from_server: Arc<Mutex<Option<IpcReceiverWithContext<ServerToClientMsg>>>>,
}
/// The `ClientOsApi` trait represents an abstract interface to the features of an operating system that
/// Zellij client requires.
pub trait ClientOsApi: Send + Sync {
/// Returns the size of the terminal associated to file descriptor `fd`.
fn get_terminal_size_using_fd(&self, fd: RawFd) -> PositionAndSize;
/// Set the terminal associated to file descriptor `fd` to
/// [raw mode](https://en.wikipedia.org/wiki/Terminal_mode).
fn set_raw_mode(&mut self, fd: RawFd);
/// Set the terminal associated to file descriptor `fd` to
/// [cooked mode](https://en.wikipedia.org/wiki/Terminal_mode).
fn unset_raw_mode(&self, fd: RawFd);
/// Returns the writer that allows writing to standard output.
fn get_stdout_writer(&self) -> Box<dyn io::Write>;
/// Returns the raw contents of standard input.
fn read_from_stdin(&self) -> Vec<u8>;
/// Returns a [`Box`] pointer to this [`ClientOsApi`] struct.
fn box_clone(&self) -> Box<dyn ClientOsApi>;
/// Sends a message to the server.
fn send_to_server(&self, msg: ClientToServerMsg);
/// Receives a message on client-side IPC channel
// This should be called from the client-side router thread only.
fn recv_from_server(&self) -> (ServerToClientMsg, ErrorContext);
fn handle_signals(&self, sigwinch_cb: Box<dyn Fn()>, quit_cb: Box<dyn Fn()>);
/// Establish a connection with the server socket.
fn connect_to_server(&self, path: &Path);
fn load_palette(&self) -> Palette;
}
impl ClientOsApi for ClientOsInputOutput {
fn get_terminal_size_using_fd(&self, fd: RawFd) -> PositionAndSize {
get_terminal_size_using_fd(fd)
}
fn set_raw_mode(&mut self, fd: RawFd) {
into_raw_mode(fd);
}
fn unset_raw_mode(&self, fd: RawFd) {
let orig_termios = self.orig_termios.lock().unwrap();
unset_raw_mode(fd, orig_termios.clone());
}
fn box_clone(&self) -> Box<dyn ClientOsApi> {
Box::new((*self).clone())
}
fn read_from_stdin(&self) -> Vec<u8> {
let stdin = std::io::stdin();
let mut stdin = stdin.lock();
let buffer = stdin.fill_buf().unwrap();
let length = buffer.len();
let read_bytes = Vec::from(buffer);
stdin.consume(length);
read_bytes
}
fn get_stdout_writer(&self) -> Box<dyn io::Write> {
let stdout = ::std::io::stdout();
Box::new(stdout)
}
fn send_to_server(&self, msg: ClientToServerMsg) {
self.send_instructions_to_server
.lock()
.unwrap()
.as_mut()
.unwrap()
.send(msg);
}
fn recv_from_server(&self) -> (ServerToClientMsg, ErrorContext) {
self.receive_instructions_from_server
.lock()
.unwrap()
.as_mut()
.unwrap()
.recv()
}
fn handle_signals(&self, sigwinch_cb: Box<dyn Fn()>, quit_cb: Box<dyn Fn()>) {
let mut signals = Signals::new(&[SIGWINCH, SIGTERM, SIGINT, SIGQUIT, SIGHUP]).unwrap();
for signal in signals.forever() {
match signal {
SIGWINCH => {
sigwinch_cb();
}
SIGTERM | SIGINT | SIGQUIT | SIGHUP => {
quit_cb();
break;
}
_ => unreachable!(),
}
}
}
fn connect_to_server(&self, path: &Path) {
let socket;
loop {
match LocalSocketStream::connect(path) {
Ok(sock) => {
socket = sock;
break;
}
Err(_) => {
std::thread::sleep(std::time::Duration::from_millis(50));
}
}
}
let sender = IpcSenderWithContext::new(socket);
let receiver = sender.get_receiver();
*self.send_instructions_to_server.lock().unwrap() = Some(sender);
*self.receive_instructions_from_server.lock().unwrap() = Some(receiver);
}
fn load_palette(&self) -> Palette {
let timeout = std::time::Duration::from_millis(100);
let mut palette = default_palette();
if let Ok(rgb) = termbg::rgb(timeout) {
palette.bg = PaletteColor::Rgb((rgb.r as u8, rgb.g as u8, rgb.b as u8));
// TODO: also dynamically get all other colors from the user's terminal
// this should be done in the same method (OSC ]11), but there might be other
// considerations here, hence using the library
};
palette
}
}
impl Clone for Box<dyn ClientOsApi> {
fn clone(&self) -> Box<dyn ClientOsApi> {
self.box_clone()
}
}
pub fn get_client_os_input() -> Result<ClientOsInputOutput, nix::Error> {
let current_termios = termios::tcgetattr(0)?;
let orig_termios = Arc::new(Mutex::new(current_termios));
Ok(ClientOsInputOutput {
orig_termios,
send_instructions_to_server: Arc::new(Mutex::new(None)),
receive_instructions_from_server: Arc::new(Mutex::new(None)),
})
}

37
zellij-server/Cargo.toml Normal file
View File

@ -0,0 +1,37 @@
[package]
name = "zellij-server"
version = "0.12.0"
authors = ["Kunal Mohan <kunalmohan99@gmail.com>"]
edition = "2018"
description = "The server-side library for Zellij"
license = "MIT"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
serde = { version = "1.0", features = ["derive"] }
wasmer = "1.0.0"
wasmer-wasi = "1.0.0"
zellij-tile = { path = "../zellij-tile/", version = "0.12.0" }
zellij-utils = { path = "../zellij-utils/", version = "0.12.0" }
vte = "0.10.1"
unicode-width = "0.1.8"
ansi_term = "0.12.1"
serde_yaml = "0.8"
nix = "0.19.1"
termion = "1.5.0"
signal-hook = "0.3"
libc = "0.2"
serde_json = "1.0"
daemonize = "0.4.1"
interprocess = "1.1.1"
[dependencies.async-std]
version = "1.3.0"
features = ["unstable"]
[dev-dependencies]
insta = "1.6.0"
[features]
test = ["zellij-utils/test"]

View File

@ -1,4 +1,13 @@
pub mod route;
pub mod os_input_output;
pub mod panes;
pub mod tab;
mod pty;
mod route;
mod screen;
mod thread_bus;
mod ui;
mod wasm_vm;
use std::sync::{Arc, RwLock};
use std::thread;
@ -6,28 +15,28 @@ use std::{path::PathBuf, sync::mpsc};
use wasmer::Store;
use zellij_tile::data::PluginCapabilities;
use crate::cli::CliArgs;
use crate::common::thread_bus::{Bus, ThreadSenders};
use crate::common::{
errors::ContextType,
input::options::Options,
ipc::{ClientToServerMsg, ServerToClientMsg},
use crate::{
os_input_output::ServerOsApi,
pty::{pty_thread_main, Pty, PtyInstruction},
screen::{screen_thread_main, ScreenInstruction},
setup::{get_default_data_dir, install::populate_data_dir},
thread_bus::{ChannelWithContext, SenderType, SenderWithContext, SyncChannelWithContext},
thread_bus::{Bus, ThreadSenders},
ui::layout::Layout,
wasm_vm::{wasm_thread_main, PluginInstruction},
};
use crate::layout::Layout;
use crate::panes::PositionAndSize;
use route::route_thread_main;
use zellij_utils::{
channels::{ChannelWithContext, SenderType, SenderWithContext, SyncChannelWithContext},
cli::CliArgs,
errors::{ContextType, ErrorInstruction, ServerContext},
input::options::Options,
ipc::{ClientAttributes, ClientToServerMsg, ServerToClientMsg},
setup::{get_default_data_dir, install::populate_data_dir},
};
/// Instructions related to server-side application including the
/// ones sent by client to server
/// Instructions related to server-side application
#[derive(Debug, Clone)]
pub enum ServerInstruction {
NewClient(PositionAndSize, Box<CliArgs>, Box<Options>),
pub(crate) enum ServerInstruction {
NewClient(ClientAttributes, Box<CliArgs>, Box<Options>),
Render(Option<String>),
UnblockInputThread,
ClientExit,
@ -46,7 +55,25 @@ impl From<ClientToServerMsg> for ServerInstruction {
}
}
pub struct SessionMetaData {
impl From<&ServerInstruction> for ServerContext {
fn from(server_instruction: &ServerInstruction) -> Self {
match *server_instruction {
ServerInstruction::NewClient(..) => ServerContext::NewClient,
ServerInstruction::Render(_) => ServerContext::Render,
ServerInstruction::UnblockInputThread => ServerContext::UnblockInputThread,
ServerInstruction::ClientExit => ServerContext::ClientExit,
ServerInstruction::Error(_) => ServerContext::Error,
}
}
}
impl ErrorInstruction for ServerInstruction {
fn error(err: String) -> Self {
ServerInstruction::Error(err)
}
}
pub(crate) struct SessionMetaData {
pub senders: ThreadSenders,
pub capabilities: PluginCapabilities,
screen_thread: Option<thread::JoinHandle<()>>,
@ -66,9 +93,9 @@ impl Drop for SessionMetaData {
}
pub fn start_server(os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
#[cfg(not(test))]
#[cfg(not(any(feature = "test", test)))]
daemonize::Daemonize::new()
.working_directory(std::env::var("HOME").unwrap())
.working_directory(std::env::current_dir().unwrap())
.umask(0o077)
.start()
.expect("could not daemonize the server process");
@ -80,16 +107,16 @@ pub fn start_server(os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
let to_server = SenderWithContext::new(SenderType::SyncSender(to_server));
let sessions: Arc<RwLock<Option<SessionMetaData>>> = Arc::new(RwLock::new(None));
#[cfg(not(test))]
#[cfg(not(any(feature = "test", test)))]
std::panic::set_hook({
use crate::errors::handle_panic;
use zellij_utils::errors::handle_panic;
let to_server = to_server.clone();
Box::new(move |info| {
handle_panic(info, &to_server);
})
});
#[cfg(test)]
#[cfg(any(feature = "test", test))]
thread::Builder::new()
.name("server_router".to_string())
.spawn({
@ -100,12 +127,12 @@ pub fn start_server(os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
move || route_thread_main(sessions, os_input, to_server)
})
.unwrap();
#[cfg(not(test))]
#[cfg(not(any(feature = "test", test)))]
let _ = thread::Builder::new()
.name("server_listener".to_string())
.spawn({
use crate::common::os_input_output::set_permissions;
use interprocess::local_socket::LocalSocketListener;
use zellij_utils::shared::set_permissions;
let os_input = os_input.clone();
let sessions = sessions.clone();
@ -145,13 +172,13 @@ pub fn start_server(os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
let (instruction, mut err_ctx) = server_receiver.recv().unwrap();
err_ctx.add_call(ContextType::IPCServer((&instruction).into()));
match instruction {
ServerInstruction::NewClient(full_screen_ws, opts, config_options) => {
ServerInstruction::NewClient(client_attributes, opts, config_options) => {
let session_data = init_session(
os_input.clone(),
opts,
config_options,
to_server.clone(),
full_screen_ws,
client_attributes,
);
*sessions.write().unwrap() = Some(session_data);
sessions
@ -180,7 +207,7 @@ pub fn start_server(os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
}
}
}
#[cfg(not(test))]
#[cfg(not(any(feature = "test", test)))]
drop(std::fs::remove_file(&socket_path));
}
@ -189,7 +216,7 @@ fn init_session(
opts: Box<CliArgs>,
config_options: Box<Options>,
to_server: SenderWithContext<ServerInstruction>,
full_screen_ws: PositionAndSize,
client_attributes: ClientAttributes,
) -> SessionMetaData {
let (to_screen, screen_receiver): ChannelWithContext<ScreenInstruction> = mpsc::channel();
let to_screen = SenderWithContext::new(SenderType::Sender(to_screen));
@ -210,9 +237,9 @@ fn init_session(
};
// Don't use default layouts in tests, but do everywhere else
#[cfg(not(test))]
#[cfg(not(any(feature = "test", test)))]
let default_layout = Some(PathBuf::from("default"));
#[cfg(test)]
#[cfg(any(feature = "test", test))]
let default_layout = None;
let layout_path = opts.layout_path;
let maybe_layout = opts
@ -255,7 +282,7 @@ fn init_session(
let max_panes = opts.max_panes;
move || {
screen_thread_main(screen_bus, max_panes, full_screen_ws, config_options);
screen_thread_main(screen_bus, max_panes, client_attributes, config_options);
}
})
.unwrap();

View File

@ -1,9 +1,3 @@
use crate::common::ipc::{
ClientToServerMsg, IpcReceiverWithContext, IpcSenderWithContext, ServerToClientMsg,
};
use crate::errors::ErrorContext;
use crate::panes::PositionAndSize;
use crate::utils::shared::default_palette;
use interprocess::local_socket::LocalSocketStream;
use nix::fcntl::{fcntl, FcntlArg, OFlag};
use nix::pty::{forkpty, Winsize};
@ -11,56 +5,20 @@ use nix::sys::signal::{kill, Signal};
use nix::sys::termios;
use nix::sys::wait::waitpid;
use nix::unistd::{self, ForkResult, Pid};
use signal_hook::{consts::signal::*, iterator::Signals};
use std::io::prelude::*;
use std::os::unix::{fs::PermissionsExt, io::RawFd};
use std::path::{Path, PathBuf};
use signal_hook::consts::*;
use std::env;
use std::os::unix::io::RawFd;
use std::path::PathBuf;
use std::process::{Child, Command};
use std::sync::{Arc, Mutex};
use std::{env, fs, io};
use zellij_tile::data::Palette;
use zellij_utils::errors::ErrorContext;
use zellij_utils::ipc::{
ClientToServerMsg, IpcReceiverWithContext, IpcSenderWithContext, ServerToClientMsg,
};
use zellij_utils::shared::default_palette;
const UNIX_PERMISSIONS: u32 = 0o700;
pub fn set_permissions(path: &Path) -> io::Result<()> {
let mut permissions = fs::metadata(path)?.permissions();
permissions.set_mode(UNIX_PERMISSIONS);
fs::set_permissions(path, permissions)
}
fn into_raw_mode(pid: RawFd) {
let mut tio = termios::tcgetattr(pid).expect("could not get terminal attribute");
termios::cfmakeraw(&mut tio);
match termios::tcsetattr(pid, termios::SetArg::TCSANOW, &tio) {
Ok(_) => {}
Err(e) => panic!("error {:?}", e),
};
}
fn unset_raw_mode(pid: RawFd, orig_termios: termios::Termios) {
match termios::tcsetattr(pid, termios::SetArg::TCSANOW, &orig_termios) {
Ok(_) => {}
Err(e) => panic!("error {:?}", e),
};
}
pub fn get_terminal_size_using_fd(fd: RawFd) -> PositionAndSize {
// TODO: do this with the nix ioctl
use libc::ioctl;
use libc::TIOCGWINSZ;
let mut winsize = Winsize {
ws_row: 0,
ws_col: 0,
ws_xpixel: 0,
ws_ypixel: 0,
};
unsafe { ioctl(fd, TIOCGWINSZ, &mut winsize) };
PositionAndSize::from(winsize)
}
pub fn set_terminal_size_using_fd(fd: RawFd, columns: u16, rows: u16) {
pub(crate) fn set_terminal_size_using_fd(fd: RawFd, columns: u16, rows: u16) {
// TODO: do this with the nix ioctl
use libc::ioctl;
use libc::TIOCSWINSZ;
@ -275,140 +233,12 @@ impl Clone for Box<dyn ServerOsApi> {
}
}
pub fn get_server_os_input() -> ServerOsInputOutput {
let current_termios = termios::tcgetattr(0).unwrap();
pub fn get_server_os_input() -> Result<ServerOsInputOutput, nix::Error> {
let current_termios = termios::tcgetattr(0)?;
let orig_termios = Arc::new(Mutex::new(current_termios));
ServerOsInputOutput {
Ok(ServerOsInputOutput {
orig_termios,
receive_instructions_from_client: None,
send_instructions_to_client: Arc::new(Mutex::new(None)),
}
}
#[derive(Clone)]
pub struct ClientOsInputOutput {
orig_termios: Arc<Mutex<termios::Termios>>,
send_instructions_to_server: Arc<Mutex<Option<IpcSenderWithContext<ClientToServerMsg>>>>,
receive_instructions_from_server: Arc<Mutex<Option<IpcReceiverWithContext<ServerToClientMsg>>>>,
}
/// The `ClientOsApi` trait represents an abstract interface to the features of an operating system that
/// Zellij client requires.
pub trait ClientOsApi: Send + Sync {
/// Returns the size of the terminal associated to file descriptor `fd`.
fn get_terminal_size_using_fd(&self, fd: RawFd) -> PositionAndSize;
/// Set the terminal associated to file descriptor `fd` to
/// [raw mode](https://en.wikipedia.org/wiki/Terminal_mode).
fn set_raw_mode(&mut self, fd: RawFd);
/// Set the terminal associated to file descriptor `fd` to
/// [cooked mode](https://en.wikipedia.org/wiki/Terminal_mode).
fn unset_raw_mode(&self, fd: RawFd);
/// Returns the writer that allows writing to standard output.
fn get_stdout_writer(&self) -> Box<dyn io::Write>;
/// Returns the raw contents of standard input.
fn read_from_stdin(&self) -> Vec<u8>;
/// Returns a [`Box`] pointer to this [`ClientOsApi`] struct.
fn box_clone(&self) -> Box<dyn ClientOsApi>;
/// Sends a message to the server.
fn send_to_server(&self, msg: ClientToServerMsg);
/// Receives a message on client-side IPC channel
// This should be called from the client-side router thread only.
fn recv_from_server(&self) -> (ServerToClientMsg, ErrorContext);
fn handle_signals(&self, sigwinch_cb: Box<dyn Fn()>, quit_cb: Box<dyn Fn()>);
/// Establish a connection with the server socket.
fn connect_to_server(&self, path: &Path);
}
impl ClientOsApi for ClientOsInputOutput {
fn get_terminal_size_using_fd(&self, fd: RawFd) -> PositionAndSize {
get_terminal_size_using_fd(fd)
}
fn set_raw_mode(&mut self, fd: RawFd) {
into_raw_mode(fd);
}
fn unset_raw_mode(&self, fd: RawFd) {
let orig_termios = self.orig_termios.lock().unwrap();
unset_raw_mode(fd, orig_termios.clone());
}
fn box_clone(&self) -> Box<dyn ClientOsApi> {
Box::new((*self).clone())
}
fn read_from_stdin(&self) -> Vec<u8> {
let stdin = std::io::stdin();
let mut stdin = stdin.lock();
let buffer = stdin.fill_buf().unwrap();
let length = buffer.len();
let read_bytes = Vec::from(buffer);
stdin.consume(length);
read_bytes
}
fn get_stdout_writer(&self) -> Box<dyn io::Write> {
let stdout = ::std::io::stdout();
Box::new(stdout)
}
fn send_to_server(&self, msg: ClientToServerMsg) {
self.send_instructions_to_server
.lock()
.unwrap()
.as_mut()
.unwrap()
.send(msg);
}
fn recv_from_server(&self) -> (ServerToClientMsg, ErrorContext) {
self.receive_instructions_from_server
.lock()
.unwrap()
.as_mut()
.unwrap()
.recv()
}
fn handle_signals(&self, sigwinch_cb: Box<dyn Fn()>, quit_cb: Box<dyn Fn()>) {
let mut signals = Signals::new(&[SIGWINCH, SIGTERM, SIGINT, SIGQUIT, SIGHUP]).unwrap();
for signal in signals.forever() {
match signal {
SIGWINCH => {
sigwinch_cb();
}
SIGTERM | SIGINT | SIGQUIT | SIGHUP => {
quit_cb();
break;
}
_ => unreachable!(),
}
}
}
fn connect_to_server(&self, path: &Path) {
let socket;
loop {
match LocalSocketStream::connect(path) {
Ok(sock) => {
socket = sock;
break;
}
Err(_) => {
std::thread::sleep(std::time::Duration::from_millis(50));
}
}
}
let sender = IpcSenderWithContext::new(socket);
let receiver = sender.get_receiver();
*self.send_instructions_to_server.lock().unwrap() = Some(sender);
*self.receive_instructions_from_server.lock().unwrap() = Some(receiver);
}
}
impl Clone for Box<dyn ClientOsApi> {
fn clone(&self) -> Box<dyn ClientOsApi> {
self.box_clone()
}
}
pub fn get_client_os_input() -> ClientOsInputOutput {
let current_termios = termios::tcgetattr(0).unwrap();
let orig_termios = Arc::new(Mutex::new(current_termios));
ClientOsInputOutput {
orig_termios,
send_instructions_to_server: Arc::new(Mutex::new(None)),
receive_instructions_from_server: Arc::new(Mutex::new(None)),
}
})
}

View File

@ -2,6 +2,7 @@ use std::{
cmp::Ordering,
collections::{BTreeSet, VecDeque},
fmt::{self, Debug, Formatter},
str,
};
use vte::{Params, Perform};
@ -9,15 +10,34 @@ use vte::{Params, Perform};
const TABSTOP_WIDTH: usize = 8; // TODO: is this always right?
const SCROLL_BACK: usize = 10_000;
use crate::utils::consts::VERSION;
use crate::utils::logging::debug_log_to_file;
use crate::utils::shared::version_number;
use zellij_tile::data::{Palette, PaletteColor};
use zellij_utils::{consts::VERSION, logging::debug_log_to_file, shared::version_number};
use crate::panes::terminal_character::{
CharacterStyles, CharsetIndex, Cursor, CursorShape, StandardCharset, TerminalCharacter,
EMPTY_TERMINAL_CHARACTER,
};
// this was copied verbatim from alacritty
fn parse_number(input: &[u8]) -> Option<u8> {
if input.is_empty() {
return None;
}
let mut num: u8 = 0;
for c in input {
let c = *c as char;
if let Some(digit) = c.to_digit(10) {
num = match num.checked_mul(10).and_then(|v| v.checked_add(digit as u8)) {
Some(v) => v,
None => return None,
}
} else {
return None;
}
}
Some(num)
}
fn get_top_non_canonical_rows(rows: &mut Vec<Row>) -> Vec<Row> {
let mut index_of_last_non_canonical_row = None;
for (i, row) in rows.iter().enumerate() {
@ -180,6 +200,7 @@ pub struct Grid {
scroll_region: Option<(usize, usize)>,
active_charset: CharsetIndex,
preceding_char: Option<TerminalCharacter>,
colors: Palette,
pub should_render: bool,
pub cursor_key_mode: bool, // DECCKM - when set, cursor keys should send ANSI direction codes (eg. "OD") instead of the arrow keys (eg. "")
pub erasure_mode: bool, // ERM
@ -205,7 +226,7 @@ impl Debug for Grid {
}
impl Grid {
pub fn new(rows: usize, columns: usize) -> Self {
pub fn new(rows: usize, columns: usize, colors: Palette) -> Self {
Grid {
lines_above: VecDeque::with_capacity(SCROLL_BACK),
viewport: vec![Row::new().canonical()],
@ -226,6 +247,7 @@ impl Grid {
clear_viewport_before_rendering: false,
active_charset: Default::default(),
pending_messages_to_pty: vec![],
colors,
}
}
pub fn contains_widechar(&self) -> bool {
@ -1013,8 +1035,139 @@ impl Perform for Grid {
// TBD
}
fn osc_dispatch(&mut self, _params: &[&[u8]], _bell_terminated: bool) {
// TBD
fn osc_dispatch(&mut self, params: &[&[u8]], bell_terminated: bool) {
let terminator = if bell_terminated { "\x07" } else { "\x1b\\" };
if params.is_empty() || params[0].is_empty() {
return;
}
match params[0] {
// Set window title.
b"0" | b"2" => {
if params.len() >= 2 {
let _title = params[1..]
.iter()
.flat_map(|x| str::from_utf8(x))
.collect::<Vec<&str>>()
.join(";")
.trim()
.to_owned();
// TBD: do something with title?
}
}
// Set color index.
b"4" => {
// TBD: set color index - currently unsupported
//
// this changes a terminal color index to something else
// meaning anything set to that index will be changed
// during rendering
}
// Get/set Foreground, Background, Cursor colors.
b"10" | b"11" | b"12" => {
if params.len() >= 2 {
if let Some(mut dynamic_code) = parse_number(params[0]) {
for param in &params[1..] {
// currently only getting the color sequence is supported,
// setting still isn't
if param == b"?" {
let color_response_message = match self.colors.bg {
PaletteColor::Rgb((r, g, b)) => {
format!(
"\u{1b}]{};rgb:{1:02x}{1:02x}/{2:02x}{2:02x}/{3:02x}{3:02x}{4}",
// dynamic_code, color.r, color.g, color.b, terminator
dynamic_code, r, g, b, terminator
)
}
_ => {
format!(
"\u{1b}]{};rgb:{1:02x}{1:02x}/{2:02x}{2:02x}/{3:02x}{3:02x}{4}",
// dynamic_code, color.r, color.g, color.b, terminator
dynamic_code, 0, 0, 0, terminator
)
}
};
self.pending_messages_to_pty
.push(color_response_message.as_bytes().to_vec());
}
dynamic_code += 1;
}
return;
}
}
}
// Set cursor style.
b"50" => {
if params.len() >= 2
&& params[1].len() >= 13
&& params[1][0..12] == *b"CursorShape="
{
let shape = match params[1][12] as char {
'0' => Some(CursorShape::Block),
'1' => Some(CursorShape::Beam),
'2' => Some(CursorShape::Underline),
_ => None,
};
if let Some(cursor_shape) = shape {
self.cursor.change_shape(cursor_shape);
}
}
}
// Set clipboard.
b"52" => {
if params.len() < 3 {
return;
}
let _clipboard = params[1].get(0).unwrap_or(&b'c');
match params[2] {
b"?" => {
// TBD: paste from own clipboard - currently unsupported
}
_base64 => {
// TBD: copy to own clipboard - currently unsupported
}
}
}
// Reset color index.
b"104" => {
// Reset all color indexes when no parameters are given.
if params.len() == 1 {
// TBD - reset all color changes - currently unsupported
return;
}
// Reset color indexes given as parameters.
for param in &params[1..] {
if let Some(_index) = parse_number(param) {
// TBD - reset color index - currently unimplemented
}
}
}
// Reset foreground color.
b"110" => {
// TBD - reset foreground color - currently unimplemented
}
// Reset background color.
b"111" => {
// TBD - reset background color - currently unimplemented
}
// Reset text cursor color.
b"112" => {
// TBD - reset text cursor color - currently unimplemented
}
_ => {}
}
}
fn csi_dispatch(&mut self, params: &Params, intermediates: &[u8], _ignore: bool, c: char) {
@ -1347,7 +1500,7 @@ impl Perform for Grid {
}
} else {
let result = debug_log_to_file(format!("Unhandled csi: {}->{:?}", c, params));
#[cfg(not(test))]
#[cfg(not(any(feature = "test", test)))]
result.unwrap();
}
}
@ -1436,13 +1589,19 @@ impl Debug for Row {
}
}
impl Row {
pub fn new() -> Self {
impl Default for Row {
fn default() -> Self {
Row {
columns: vec![],
is_canonical: false,
}
}
}
impl Row {
pub fn new() -> Self {
Self::default()
}
pub fn from_columns(columns: Vec<TerminalCharacter>) -> Self {
Row {
columns,
@ -1528,6 +1687,9 @@ impl Row {
pub fn len(&self) -> usize {
self.columns.len()
}
pub fn is_empty(&self) -> bool {
self.columns.is_empty()
}
pub fn delete_character(&mut self, x: usize) {
if x < self.columns.len() {
self.columns.remove(x);

View File

@ -4,6 +4,6 @@ mod terminal_character;
mod terminal_pane;
pub use grid::*;
pub use plugin_pane::*;
pub(crate) use plugin_pane::*;
pub use terminal_character::*;
pub use terminal_pane::*;

View File

@ -2,13 +2,13 @@ use std::sync::mpsc::channel;
use std::time::Instant;
use std::unimplemented;
use crate::common::thread_bus::SenderWithContext;
use crate::panes::{PaneId, PositionAndSize};
use crate::panes::PaneId;
use crate::pty::VteBytes;
use crate::tab::Pane;
use crate::wasm_vm::PluginInstruction;
use zellij_utils::{channels::SenderWithContext, pane_size::PositionAndSize};
pub struct PluginPane {
pub(crate) struct PluginPane {
pub pid: u32,
pub should_render: bool,
pub selectable: bool,

View File

@ -1,10 +1,10 @@
use unicode_width::UnicodeWidthChar;
use crate::utils::logging::debug_log_to_file;
use std::convert::TryFrom;
use std::fmt::{self, Debug, Display, Formatter};
use std::ops::{Index, IndexMut};
use vte::ParamsIter;
use zellij_utils::logging::debug_log_to_file;
pub const EMPTY_TERMINAL_CHARACTER: TerminalCharacter = TerminalCharacter {
character: ' ',
@ -110,9 +110,9 @@ pub struct CharacterStyles {
pub italic: Option<AnsiCode>,
}
impl CharacterStyles {
pub fn new() -> Self {
CharacterStyles {
impl Default for CharacterStyles {
fn default() -> Self {
Self {
foreground: None,
background: None,
strike: None,
@ -126,6 +126,12 @@ impl CharacterStyles {
italic: None,
}
}
}
impl CharacterStyles {
pub fn new() -> Self {
Self::default()
}
pub fn foreground(mut self, foreground_code: Option<AnsiCode>) -> Self {
self.foreground = foreground_code;
self

View File

@ -1,45 +1,24 @@
use std::fmt::Debug;
use std::os::unix::io::RawFd;
use std::time::Instant;
use zellij_tile::data::Palette;
use zellij_utils::pane_size::PositionAndSize;
use nix::pty::Winsize;
use serde::{Deserialize, Serialize};
use crate::panes::grid::Grid;
use crate::panes::terminal_character::{
CharacterStyles, CursorShape, TerminalCharacter, EMPTY_TERMINAL_CHARACTER,
use crate::panes::{
grid::Grid,
terminal_character::{
CharacterStyles, CursorShape, TerminalCharacter, EMPTY_TERMINAL_CHARACTER,
},
};
use crate::pty::VteBytes;
use crate::tab::Pane;
#[derive(PartialEq, Eq, Ord, PartialOrd, Hash, Clone, Copy, Debug, Serialize, Deserialize)]
#[derive(PartialEq, Eq, Ord, PartialOrd, Hash, Clone, Copy, Debug)]
pub enum PaneId {
Terminal(RawFd),
Plugin(u32), // FIXME: Drop the trait object, make this a wrapper for the struct?
}
/// Contains the position and size of a [`Pane`], or more generally of any terminal, measured
/// in character rows and columns.
#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)]
pub struct PositionAndSize {
pub x: usize,
pub y: usize,
pub rows: usize,
pub columns: usize,
pub max_rows: Option<usize>,
pub max_columns: Option<usize>,
}
impl From<Winsize> for PositionAndSize {
fn from(winsize: Winsize) -> PositionAndSize {
PositionAndSize {
columns: winsize.ws_col as usize,
rows: winsize.ws_row as usize,
..Default::default()
}
}
}
pub struct TerminalPane {
pub grid: Grid,
pub pid: RawFd,
@ -49,6 +28,7 @@ pub struct TerminalPane {
pub max_height: Option<usize>,
pub max_width: Option<usize>,
pub active_at: Instant,
pub colors: Palette,
vte_parser: vte::Parser,
}
@ -307,8 +287,8 @@ impl Pane for TerminalPane {
}
impl TerminalPane {
pub fn new(pid: RawFd, position_and_size: PositionAndSize) -> TerminalPane {
let grid = Grid::new(position_and_size.rows, position_and_size.columns);
pub fn new(pid: RawFd, position_and_size: PositionAndSize, palette: Palette) -> TerminalPane {
let grid = Grid::new(position_and_size.rows, position_and_size.columns, palette);
TerminalPane {
pid,
grid,
@ -319,6 +299,7 @@ impl TerminalPane {
max_width: None,
vte_parser: vte::Parser::new(),
active_at: Instant::now(),
colors: palette,
}
}
pub fn get_x(&self) -> usize {
@ -354,7 +335,7 @@ impl TerminalPane {
pub fn read_buffer_as_lines(&self) -> Vec<Vec<TerminalCharacter>> {
self.grid.as_character_lines()
}
#[cfg(test)]
#[cfg(any(feature = "test", test))]
pub fn cursor_coordinates(&self) -> Option<(usize, usize)> {
// (x, y)
self.grid.cursor_coordinates()

View File

@ -1,9 +1,10 @@
use super::super::Grid;
use ::insta::assert_snapshot;
use zellij_tile::data::Palette;
fn read_fixture(fixture_name: &str) -> Vec<u8> {
let mut path_to_file = std::path::PathBuf::new();
path_to_file.push("src");
path_to_file.push("../src");
path_to_file.push("tests");
path_to_file.push("fixtures");
path_to_file.push(fixture_name);
@ -15,7 +16,7 @@ fn read_fixture(fixture_name: &str) -> Vec<u8> {
#[test]
fn vttest1_0() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(41, 110);
let mut grid = Grid::new(41, 110, Palette::default());
let fixture_name = "vttest1-0";
let content = read_fixture(fixture_name);
for byte in content {
@ -27,7 +28,7 @@ fn vttest1_0() {
#[test]
fn vttest1_1() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(41, 110);
let mut grid = Grid::new(41, 110, Palette::default());
let fixture_name = "vttest1-1";
let content = read_fixture(fixture_name);
for byte in content {
@ -39,7 +40,7 @@ fn vttest1_1() {
#[test]
fn vttest1_2() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(41, 110);
let mut grid = Grid::new(41, 110, Palette::default());
let fixture_name = "vttest1-2";
let content = read_fixture(fixture_name);
for byte in content {
@ -51,7 +52,7 @@ fn vttest1_2() {
#[test]
fn vttest1_3() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(41, 110);
let mut grid = Grid::new(41, 110, Palette::default());
let fixture_name = "vttest1-3";
let content = read_fixture(fixture_name);
for byte in content {
@ -63,7 +64,7 @@ fn vttest1_3() {
#[test]
fn vttest1_4() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(41, 110);
let mut grid = Grid::new(41, 110, Palette::default());
let fixture_name = "vttest1-4";
let content = read_fixture(fixture_name);
for byte in content {
@ -75,7 +76,7 @@ fn vttest1_4() {
#[test]
fn vttest1_5() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(41, 110);
let mut grid = Grid::new(41, 110, Palette::default());
let fixture_name = "vttest1-5";
let content = read_fixture(fixture_name);
for byte in content {
@ -87,7 +88,7 @@ fn vttest1_5() {
#[test]
fn vttest2_0() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(41, 110);
let mut grid = Grid::new(41, 110, Palette::default());
let fixture_name = "vttest2-0";
let content = read_fixture(fixture_name);
for byte in content {
@ -99,7 +100,7 @@ fn vttest2_0() {
#[test]
fn vttest2_1() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(41, 110);
let mut grid = Grid::new(41, 110, Palette::default());
let fixture_name = "vttest2-1";
let content = read_fixture(fixture_name);
for byte in content {
@ -111,7 +112,7 @@ fn vttest2_1() {
#[test]
fn vttest2_2() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(41, 110);
let mut grid = Grid::new(41, 110, Palette::default());
let fixture_name = "vttest2-2";
let content = read_fixture(fixture_name);
for byte in content {
@ -123,7 +124,7 @@ fn vttest2_2() {
#[test]
fn vttest2_3() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(41, 110);
let mut grid = Grid::new(41, 110, Palette::default());
let fixture_name = "vttest2-3";
let content = read_fixture(fixture_name);
for byte in content {
@ -135,7 +136,7 @@ fn vttest2_3() {
#[test]
fn vttest2_4() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(41, 110);
let mut grid = Grid::new(41, 110, Palette::default());
let fixture_name = "vttest2-4";
let content = read_fixture(fixture_name);
for byte in content {
@ -147,7 +148,7 @@ fn vttest2_4() {
#[test]
fn vttest2_5() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(41, 110);
let mut grid = Grid::new(41, 110, Palette::default());
let fixture_name = "vttest2-5";
let content = read_fixture(fixture_name);
for byte in content {
@ -159,7 +160,7 @@ fn vttest2_5() {
#[test]
fn vttest2_6() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(41, 110);
let mut grid = Grid::new(41, 110, Palette::default());
let fixture_name = "vttest2-6";
let content = read_fixture(fixture_name);
for byte in content {
@ -171,7 +172,7 @@ fn vttest2_6() {
#[test]
fn vttest2_7() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(41, 110);
let mut grid = Grid::new(41, 110, Palette::default());
let fixture_name = "vttest2-7";
let content = read_fixture(fixture_name);
for byte in content {
@ -183,7 +184,7 @@ fn vttest2_7() {
#[test]
fn vttest2_8() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(41, 110);
let mut grid = Grid::new(41, 110, Palette::default());
let fixture_name = "vttest2-8";
let content = read_fixture(fixture_name);
for byte in content {
@ -195,7 +196,7 @@ fn vttest2_8() {
#[test]
fn vttest2_9() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(41, 110);
let mut grid = Grid::new(41, 110, Palette::default());
let fixture_name = "vttest2-9";
let content = read_fixture(fixture_name);
for byte in content {
@ -207,7 +208,7 @@ fn vttest2_9() {
#[test]
fn vttest2_10() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(41, 110);
let mut grid = Grid::new(41, 110, Palette::default());
let fixture_name = "vttest2-10";
let content = read_fixture(fixture_name);
for byte in content {
@ -219,7 +220,7 @@ fn vttest2_10() {
#[test]
fn vttest2_11() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(41, 110);
let mut grid = Grid::new(41, 110, Palette::default());
let fixture_name = "vttest2-11";
let content = read_fixture(fixture_name);
for byte in content {
@ -231,7 +232,7 @@ fn vttest2_11() {
#[test]
fn vttest2_12() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(41, 110);
let mut grid = Grid::new(41, 110, Palette::default());
let fixture_name = "vttest2-12";
let content = read_fixture(fixture_name);
for byte in content {
@ -243,7 +244,7 @@ fn vttest2_12() {
#[test]
fn vttest2_13() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(41, 110);
let mut grid = Grid::new(41, 110, Palette::default());
let fixture_name = "vttest2-13";
let content = read_fixture(fixture_name);
for byte in content {
@ -255,7 +256,7 @@ fn vttest2_13() {
#[test]
fn vttest2_14() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(41, 110);
let mut grid = Grid::new(41, 110, Palette::default());
let fixture_name = "vttest2-14";
let content = read_fixture(fixture_name);
for byte in content {
@ -267,7 +268,7 @@ fn vttest2_14() {
#[test]
fn vttest3_0() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(41, 110);
let mut grid = Grid::new(41, 110, Palette::default());
let fixture_name = "vttest3-0";
let content = read_fixture(fixture_name);
for byte in content {
@ -279,7 +280,7 @@ fn vttest3_0() {
#[test]
fn vttest8_0() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(51, 97);
let mut grid = Grid::new(51, 97, Palette::default());
let fixture_name = "vttest8-0";
let content = read_fixture(fixture_name);
for byte in content {
@ -291,7 +292,7 @@ fn vttest8_0() {
#[test]
fn vttest8_1() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(51, 97);
let mut grid = Grid::new(51, 97, Palette::default());
let fixture_name = "vttest8-1";
let content = read_fixture(fixture_name);
for byte in content {
@ -303,7 +304,7 @@ fn vttest8_1() {
#[test]
fn vttest8_2() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(51, 97);
let mut grid = Grid::new(51, 97, Palette::default());
let fixture_name = "vttest8-2";
let content = read_fixture(fixture_name);
for byte in content {
@ -315,7 +316,7 @@ fn vttest8_2() {
#[test]
fn vttest8_3() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(51, 97);
let mut grid = Grid::new(51, 97, Palette::default());
let fixture_name = "vttest8-3";
let content = read_fixture(fixture_name);
for byte in content {
@ -327,7 +328,7 @@ fn vttest8_3() {
#[test]
fn vttest8_4() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(51, 97);
let mut grid = Grid::new(51, 97, Palette::default());
let fixture_name = "vttest8-4";
let content = read_fixture(fixture_name);
for byte in content {
@ -339,7 +340,7 @@ fn vttest8_4() {
#[test]
fn vttest8_5() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(51, 97);
let mut grid = Grid::new(51, 97, Palette::default());
let fixture_name = "vttest8-5";
let content = read_fixture(fixture_name);
for byte in content {
@ -351,7 +352,7 @@ fn vttest8_5() {
#[test]
fn csi_b() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(51, 97);
let mut grid = Grid::new(51, 97, Palette::default());
let fixture_name = "csi-b";
let content = read_fixture(fixture_name);
for byte in content {
@ -363,7 +364,7 @@ fn csi_b() {
#[test]
fn csi_capital_i() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(51, 97);
let mut grid = Grid::new(51, 97, Palette::default());
let fixture_name = "csi-capital-i";
let content = read_fixture(fixture_name);
for byte in content {
@ -375,7 +376,7 @@ fn csi_capital_i() {
#[test]
fn csi_capital_z() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(51, 97);
let mut grid = Grid::new(51, 97, Palette::default());
let fixture_name = "csi-capital-z";
let content = read_fixture(fixture_name);
for byte in content {
@ -387,7 +388,7 @@ fn csi_capital_z() {
#[test]
fn terminal_reports() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(51, 97);
let mut grid = Grid::new(51, 97, Palette::default());
let fixture_name = "terminal_reports";
let content = read_fixture(fixture_name);
for byte in content {

View File

@ -1,5 +1,5 @@
---
source: src/client/panes/./unit/grid_tests.rs
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---

View File

@ -1,5 +1,5 @@
---
source: src/client/panes/./unit/grid_tests.rs
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---

View File

@ -1,5 +1,5 @@
---
source: src/client/panes/./unit/grid_tests.rs
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---

View File

@ -1,5 +1,5 @@
---
source: src/client/panes/./unit/grid_tests.rs
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid . pending_messages_to_pty)"
---

View File

@ -1,5 +1,5 @@
---
source: src/client/panes/grid.rs
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---

View File

@ -1,5 +1,5 @@
---
source: src/client/panes/grid.rs
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---

View File

@ -1,5 +1,5 @@
---
source: src/client/panes/grid.rs
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---

View File

@ -1,5 +1,5 @@
---
source: src/client/panes/grid.rs
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---

View File

@ -1,5 +1,5 @@
---
source: src/client/panes/grid.rs
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---

View File

@ -1,5 +1,5 @@
---
source: src/client/panes/grid.rs
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---

View File

@ -1,5 +1,5 @@
---
source: src/client/panes/./unit/grid_tests.rs
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---

View File

@ -1,5 +1,5 @@
---
source: src/client/panes/./unit/grid_tests.rs
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---

View File

@ -1,5 +1,5 @@
---
source: src/client/panes/./unit/grid_tests.rs
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---

View File

@ -1,5 +1,5 @@
---
source: src/client/panes/./unit/grid_tests.rs
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---

View File

@ -1,5 +1,5 @@
---
source: src/client/panes/./unit/grid_tests.rs
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---

View File

@ -1,5 +1,5 @@
---
source: src/client/panes/./unit/grid_tests.rs
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---

View File

@ -1,5 +1,5 @@
---
source: src/client/panes/./unit/grid_tests.rs
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---

View File

@ -1,5 +1,5 @@
---
source: src/client/panes/./unit/grid_tests.rs
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---

View File

@ -1,5 +1,5 @@
---
source: src/client/panes/./unit/grid_tests.rs
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---

View File

@ -1,5 +1,5 @@
---
source: src/client/panes/./unit/grid_tests.rs
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---

View File

@ -1,5 +1,5 @@
---
source: src/client/panes/./unit/grid_tests.rs
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---

View File

@ -1,5 +1,5 @@
---
source: src/client/panes/./unit/grid_tests.rs
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---

View File

@ -1,5 +1,5 @@
---
source: src/client/panes/./unit/grid_tests.rs
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---

View File

@ -1,5 +1,5 @@
---
source: src/client/panes/./unit/grid_tests.rs
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---

View File

@ -1,5 +1,5 @@
---
source: src/client/panes/./unit/grid_tests.rs
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---

View File

@ -1,5 +1,5 @@
---
source: src/client/panes/./unit/grid_tests.rs
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---

View File

@ -1,5 +1,5 @@
---
source: src/client/panes/./unit/grid_tests.rs
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---

View File

@ -1,5 +1,5 @@
---
source: src/client/panes/./unit/grid_tests.rs
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---

View File

@ -1,5 +1,5 @@
---
source: src/client/panes/./unit/grid_tests.rs
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---

View File

@ -1,5 +1,5 @@
---
source: src/client/panes/./unit/grid_tests.rs
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---

View File

@ -1,5 +1,5 @@
---
source: src/client/panes/./unit/grid_tests.rs
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---

View File

@ -1,5 +1,5 @@
---
source: src/client/panes/./unit/grid_tests.rs
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---

View File

@ -7,15 +7,19 @@ use std::path::PathBuf;
use std::pin::*;
use std::time::{Duration, Instant};
use crate::client::panes::PaneId;
use crate::common::errors::{get_current_ctx, ContextType};
use crate::common::screen::ScreenInstruction;
use crate::common::thread_bus::{Bus, ThreadSenders};
use crate::layout::Layout;
use crate::os_input_output::ServerOsApi;
use crate::server::ServerInstruction;
use crate::utils::logging::debug_to_file;
use crate::wasm_vm::PluginInstruction;
use crate::{
os_input_output::ServerOsApi,
panes::PaneId,
screen::ScreenInstruction,
thread_bus::{Bus, ThreadSenders},
ui::layout::Layout,
wasm_vm::PluginInstruction,
ServerInstruction,
};
use zellij_utils::{
errors::{get_current_ctx, ContextType, PtyContext},
logging::debug_to_file,
};
pub struct ReadFromPid {
pid: RawFd,
@ -67,7 +71,7 @@ pub type VteBytes = Vec<u8>;
/// Instructions related to PTYs (pseudoterminals).
#[derive(Clone, Debug)]
pub enum PtyInstruction {
pub(crate) enum PtyInstruction {
SpawnTerminal(Option<PathBuf>),
SpawnTerminalVertically(Option<PathBuf>),
SpawnTerminalHorizontally(Option<PathBuf>),
@ -77,14 +81,28 @@ pub enum PtyInstruction {
Exit,
}
pub struct Pty {
impl From<&PtyInstruction> for PtyContext {
fn from(pty_instruction: &PtyInstruction) -> Self {
match *pty_instruction {
PtyInstruction::SpawnTerminal(_) => PtyContext::SpawnTerminal,
PtyInstruction::SpawnTerminalVertically(_) => PtyContext::SpawnTerminalVertically,
PtyInstruction::SpawnTerminalHorizontally(_) => PtyContext::SpawnTerminalHorizontally,
PtyInstruction::ClosePane(_) => PtyContext::ClosePane,
PtyInstruction::CloseTab(_) => PtyContext::CloseTab,
PtyInstruction::NewTab => PtyContext::NewTab,
PtyInstruction::Exit => PtyContext::Exit,
}
}
}
pub(crate) struct Pty {
pub bus: Bus<PtyInstruction>,
pub id_to_child_pid: HashMap<RawFd, RawFd>,
debug_to_file: bool,
task_handles: HashMap<RawFd, JoinHandle<()>>,
}
pub fn pty_thread_main(mut pty: Pty, maybe_layout: Option<Layout>) {
pub(crate) fn pty_thread_main(mut pty: Pty, maybe_layout: Option<Layout>) {
loop {
let (event, mut err_ctx) = pty.bus.recv().expect("failed to receive event on channel");
err_ctx.add_call(ContextType::Pty((&event).into()));
@ -196,7 +214,7 @@ fn stream_terminal_bytes(
}
}
senders.send_to_screen(ScreenInstruction::Render).unwrap();
#[cfg(not(test))]
#[cfg(not(any(feature = "test", test)))]
// this is a little hacky, and is because the tests end the file as soon as
// we read everything, rather than hanging until there is new data
// a better solution would be to fix the test fakes, but this will do for now

View File

@ -2,15 +2,18 @@ use std::sync::{Arc, RwLock};
use zellij_tile::data::Event;
use crate::common::input::actions::{Action, Direction};
use crate::common::input::handler::get_mode_info;
use crate::common::ipc::ClientToServerMsg;
use crate::common::os_input_output::ServerOsApi;
use crate::common::pty::PtyInstruction;
use crate::common::screen::ScreenInstruction;
use crate::common::thread_bus::SenderWithContext;
use crate::common::wasm_vm::PluginInstruction;
use crate::server::{ServerInstruction, SessionMetaData};
use crate::{
os_input_output::ServerOsApi, pty::PtyInstruction, screen::ScreenInstruction,
wasm_vm::PluginInstruction, ServerInstruction, SessionMetaData,
};
use zellij_utils::{
channels::SenderWithContext,
input::{
actions::{Action, Direction},
get_mode_info,
},
ipc::ClientToServerMsg,
};
fn route_action(action: Action, session: &SessionMetaData, os_input: &dyn ServerOsApi) {
match action {
@ -26,6 +29,9 @@ fn route_action(action: Action, session: &SessionMetaData, os_input: &dyn Server
}
Action::SwitchToMode(mode) => {
let palette = os_input.load_palette();
// TODO: use the palette from the client and remove it from the server os api
// this is left here as a stop gap measure until we shift some code around
// to allow for this
session
.senders
.send_to_plugin(PluginInstruction::Update(
@ -181,7 +187,7 @@ fn route_action(action: Action, session: &SessionMetaData, os_input: &dyn Server
}
}
pub fn route_thread_main(
pub(crate) fn route_thread_main(
sessions: Arc<RwLock<Option<SessionMetaData>>>,
mut os_input: Box<dyn ServerOsApi>,
to_server: SenderWithContext<ServerInstruction>,
@ -196,7 +202,9 @@ pub fn route_thread_main(
break;
}
ClientToServerMsg::Action(action) => {
route_action(action, rlocked_sessions.as_ref().unwrap(), &*os_input);
if let Some(rlocked_sessions) = rlocked_sessions.as_ref() {
route_action(action, rlocked_sessions, &*os_input);
}
}
ClientToServerMsg::TerminalResize(new_size) => {
rlocked_sessions

View File

@ -4,22 +4,26 @@ use std::collections::BTreeMap;
use std::os::unix::io::RawFd;
use std::str;
use crate::common::input::options::Options;
use crate::common::pty::{PtyInstruction, VteBytes};
use crate::common::thread_bus::Bus;
use crate::errors::ContextType;
use crate::layout::Layout;
use crate::panes::PaneId;
use crate::panes::PositionAndSize;
use crate::server::ServerInstruction;
use crate::tab::Tab;
use crate::wasm_vm::PluginInstruction;
use crate::{
panes::PaneId,
pty::{PtyInstruction, VteBytes},
tab::Tab,
thread_bus::Bus,
ui::layout::Layout,
wasm_vm::PluginInstruction,
ServerInstruction,
};
use zellij_tile::data::{Event, InputMode, ModeInfo, Palette, PluginCapabilities, TabInfo};
use zellij_utils::{
errors::{ContextType, ScreenContext},
input::options::Options,
ipc::ClientAttributes,
pane_size::PositionAndSize,
};
/// Instructions that can be sent to the [`Screen`].
#[derive(Debug, Clone)]
pub enum ScreenInstruction {
pub(crate) enum ScreenInstruction {
PtyBytes(RawFd, VteBytes),
Render,
NewPane(PaneId),
@ -63,9 +67,61 @@ pub enum ScreenInstruction {
ChangeMode(ModeInfo),
}
impl From<&ScreenInstruction> for ScreenContext {
fn from(screen_instruction: &ScreenInstruction) -> Self {
match *screen_instruction {
ScreenInstruction::PtyBytes(..) => ScreenContext::HandlePtyBytes,
ScreenInstruction::Render => ScreenContext::Render,
ScreenInstruction::NewPane(_) => ScreenContext::NewPane,
ScreenInstruction::HorizontalSplit(_) => ScreenContext::HorizontalSplit,
ScreenInstruction::VerticalSplit(_) => ScreenContext::VerticalSplit,
ScreenInstruction::WriteCharacter(_) => ScreenContext::WriteCharacter,
ScreenInstruction::ResizeLeft => ScreenContext::ResizeLeft,
ScreenInstruction::ResizeRight => ScreenContext::ResizeRight,
ScreenInstruction::ResizeDown => ScreenContext::ResizeDown,
ScreenInstruction::ResizeUp => ScreenContext::ResizeUp,
ScreenInstruction::SwitchFocus => ScreenContext::SwitchFocus,
ScreenInstruction::FocusNextPane => ScreenContext::FocusNextPane,
ScreenInstruction::FocusPreviousPane => ScreenContext::FocusPreviousPane,
ScreenInstruction::MoveFocusLeft => ScreenContext::MoveFocusLeft,
ScreenInstruction::MoveFocusLeftOrPreviousTab => {
ScreenContext::MoveFocusLeftOrPreviousTab
}
ScreenInstruction::MoveFocusDown => ScreenContext::MoveFocusDown,
ScreenInstruction::MoveFocusUp => ScreenContext::MoveFocusUp,
ScreenInstruction::MoveFocusRight => ScreenContext::MoveFocusRight,
ScreenInstruction::MoveFocusRightOrNextTab => ScreenContext::MoveFocusRightOrNextTab,
ScreenInstruction::Exit => ScreenContext::Exit,
ScreenInstruction::ScrollUp => ScreenContext::ScrollUp,
ScreenInstruction::ScrollDown => ScreenContext::ScrollDown,
ScreenInstruction::PageScrollUp => ScreenContext::PageScrollUp,
ScreenInstruction::PageScrollDown => ScreenContext::PageScrollDown,
ScreenInstruction::ClearScroll => ScreenContext::ClearScroll,
ScreenInstruction::CloseFocusedPane => ScreenContext::CloseFocusedPane,
ScreenInstruction::ToggleActiveTerminalFullscreen => {
ScreenContext::ToggleActiveTerminalFullscreen
}
ScreenInstruction::SetSelectable(..) => ScreenContext::SetSelectable,
ScreenInstruction::SetInvisibleBorders(..) => ScreenContext::SetInvisibleBorders,
ScreenInstruction::SetMaxHeight(..) => ScreenContext::SetMaxHeight,
ScreenInstruction::ClosePane(_) => ScreenContext::ClosePane,
ScreenInstruction::ApplyLayout(..) => ScreenContext::ApplyLayout,
ScreenInstruction::NewTab(_) => ScreenContext::NewTab,
ScreenInstruction::SwitchTabNext => ScreenContext::SwitchTabNext,
ScreenInstruction::SwitchTabPrev => ScreenContext::SwitchTabPrev,
ScreenInstruction::CloseTab => ScreenContext::CloseTab,
ScreenInstruction::GoToTab(_) => ScreenContext::GoToTab,
ScreenInstruction::UpdateTabName(_) => ScreenContext::UpdateTabName,
ScreenInstruction::TerminalResize(_) => ScreenContext::TerminalResize,
ScreenInstruction::ChangeMode(_) => ScreenContext::ChangeMode,
ScreenInstruction::ToggleActiveSyncTab => ScreenContext::ToggleActiveSyncTab,
}
}
}
/// A [`Screen`] holds multiple [`Tab`]s, each one holding multiple [`panes`](crate::client::panes).
/// It only directly controls which tab is active, delegating the rest to the individual `Tab`.
pub struct Screen {
pub(crate) struct Screen {
/// A Bus for sending and receiving messages with the other threads.
pub bus: Bus<ScreenInstruction>,
/// An optional maximal amount of panes allowed per [`Tab`] in this [`Screen`] instance.
@ -73,7 +129,7 @@ pub struct Screen {
/// A map between this [`Screen`]'s tabs and their ID/key.
tabs: BTreeMap<usize, Tab>,
/// The full size of this [`Screen`].
full_screen_ws: PositionAndSize,
position_and_size: PositionAndSize,
/// The index of this [`Screen`]'s active [`Tab`].
active_tab_index: Option<usize>,
mode_info: ModeInfo,
@ -85,21 +141,20 @@ impl Screen {
/// Creates and returns a new [`Screen`].
pub fn new(
bus: Bus<ScreenInstruction>,
full_screen_ws: &PositionAndSize,
client_attributes: &ClientAttributes,
max_panes: Option<usize>,
mode_info: ModeInfo,
input_mode: InputMode,
colors: Palette,
) -> Self {
Screen {
bus,
max_panes,
full_screen_ws: *full_screen_ws,
position_and_size: client_attributes.position_and_size,
colors: client_attributes.palette,
active_tab_index: None,
tabs: BTreeMap::new(),
mode_info,
input_mode,
colors,
}
}
@ -112,7 +167,7 @@ impl Screen {
tab_index,
position,
String::new(),
&self.full_screen_ws,
&self.position_and_size,
self.bus.os_input.as_ref().unwrap().clone(),
self.bus.senders.clone(),
self.max_panes,
@ -219,7 +274,7 @@ impl Screen {
}
pub fn resize_to_screen(&mut self, new_screen_size: PositionAndSize) {
self.full_screen_ws = new_screen_size;
self.position_and_size = new_screen_size;
for (_, tab) in self.tabs.iter_mut() {
tab.resize_whole_tab(new_screen_size);
}
@ -268,7 +323,7 @@ impl Screen {
tab_index,
position,
String::new(),
&self.full_screen_ws,
&self.position_and_size,
self.bus.os_input.as_ref().unwrap().clone(),
self.bus.senders.clone(),
self.max_panes,
@ -328,28 +383,26 @@ impl Screen {
// The box is here in order to make the
// NewClient enum smaller
#[allow(clippy::boxed_local)]
pub fn screen_thread_main(
pub(crate) fn screen_thread_main(
bus: Bus<ScreenInstruction>,
max_panes: Option<usize>,
full_screen_ws: PositionAndSize,
client_attributes: ClientAttributes,
config_options: Box<Options>,
) {
let colors = bus.os_input.as_ref().unwrap().load_palette();
let capabilities = config_options.simplified_ui;
let mut screen = Screen::new(
bus,
&full_screen_ws,
&client_attributes,
max_panes,
ModeInfo {
palette: colors,
palette: client_attributes.palette,
capabilities: PluginCapabilities {
arrow_fonts: capabilities,
},
..ModeInfo::default()
},
InputMode::Normal,
colors,
);
loop {
let (event, mut err_ctx) = screen

View File

@ -1,17 +1,15 @@
//! `Tab`s holds multiple panes. It tracks their coordinates (x/y) and size,
//! as well as how they should be resized
use crate::client::pane_resizer::PaneResizer;
use crate::common::input::handler::parse_keys;
use crate::common::thread_bus::ThreadSenders;
use crate::layout::Layout;
use crate::os_input_output::ServerOsApi;
use crate::panes::{PaneId, PositionAndSize, TerminalPane};
use crate::pty::{PtyInstruction, VteBytes};
use crate::server::ServerInstruction;
use crate::utils::shared::adjust_to_size;
use crate::wasm_vm::PluginInstruction;
use crate::{boundaries::Boundaries, panes::PluginPane};
use crate::{
os_input_output::ServerOsApi,
panes::{PaneId, PluginPane, TerminalPane},
pty::{PtyInstruction, VteBytes},
thread_bus::ThreadSenders,
ui::{boundaries::Boundaries, layout::Layout, pane_resizer::PaneResizer},
wasm_vm::PluginInstruction,
ServerInstruction,
};
use serde::{Deserialize, Serialize};
use std::os::unix::io::RawFd;
use std::sync::mpsc::channel;
@ -21,6 +19,7 @@ use std::{
collections::{BTreeMap, HashSet},
};
use zellij_tile::data::{Event, InputMode, ModeInfo, Palette};
use zellij_utils::{input::parse_keys, pane_size::PositionAndSize, shared::adjust_to_size};
const CURSOR_HEIGHT_WIDTH_RATIO: usize = 4; // this is not accurate and kind of a magic number, TODO: look into this
@ -59,7 +58,7 @@ fn split_horizontally_with_gap(rect: &PositionAndSize) -> (PositionAndSize, Posi
(first_rect, second_rect)
}
pub struct Tab {
pub(crate) struct Tab {
pub index: usize,
pub position: usize,
pub name: String,
@ -79,7 +78,7 @@ pub struct Tab {
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct TabData {
pub(crate) struct TabData {
/* subset of fields to publish to plugins */
pub position: usize,
pub name: String,
@ -242,7 +241,7 @@ impl Tab {
colors: Palette,
) -> Self {
let panes = if let Some(PaneId::Terminal(pid)) = pane_id {
let new_terminal = TerminalPane::new(pid, *full_screen_ws);
let new_terminal = TerminalPane::new(pid, *full_screen_ws, colors);
os_api.set_terminal_size_using_fd(
new_terminal.pid,
new_terminal.columns() as u16,
@ -344,7 +343,7 @@ impl Tab {
} else {
// there are still panes left to fill, use the pids we received in this method
let pid = new_pids.next().unwrap(); // if this crashes it means we got less pids than there are panes in this layout
let new_terminal = TerminalPane::new(*pid, *position_and_size);
let new_terminal = TerminalPane::new(*pid, *position_and_size, self.colors);
self.os_api.set_terminal_size_using_fd(
new_terminal.pid,
new_terminal.columns() as u16,
@ -372,7 +371,7 @@ impl Tab {
}
if !self.has_panes() {
if let PaneId::Terminal(term_pid) = pid {
let new_terminal = TerminalPane::new(term_pid, self.full_screen_ws);
let new_terminal = TerminalPane::new(term_pid, self.full_screen_ws, self.colors);
self.os_api.set_terminal_size_using_fd(
new_terminal.pid,
new_terminal.columns() as u16,
@ -422,7 +421,7 @@ impl Tab {
{
if let PaneId::Terminal(term_pid) = pid {
let (top_winsize, bottom_winsize) = split_horizontally_with_gap(&terminal_ws);
let new_terminal = TerminalPane::new(term_pid, bottom_winsize);
let new_terminal = TerminalPane::new(term_pid, bottom_winsize, self.colors);
self.os_api.set_terminal_size_using_fd(
new_terminal.pid,
bottom_winsize.columns as u16,
@ -442,7 +441,7 @@ impl Tab {
} else if terminal_to_split.columns() > terminal_to_split.min_width() * 2 {
if let PaneId::Terminal(term_pid) = pid {
let (left_winsize, right_winsize) = split_vertically_with_gap(&terminal_ws);
let new_terminal = TerminalPane::new(term_pid, right_winsize);
let new_terminal = TerminalPane::new(term_pid, right_winsize, self.colors);
self.os_api.set_terminal_size_using_fd(
new_terminal.pid,
right_winsize.columns as u16,
@ -470,7 +469,7 @@ impl Tab {
}
if !self.has_panes() {
if let PaneId::Terminal(term_pid) = pid {
let new_terminal = TerminalPane::new(term_pid, self.full_screen_ws);
let new_terminal = TerminalPane::new(term_pid, self.full_screen_ws, self.colors);
self.os_api.set_terminal_size_using_fd(
new_terminal.pid,
new_terminal.columns() as u16,
@ -500,7 +499,7 @@ impl Tab {
active_pane.change_pos_and_size(&top_winsize);
let new_terminal = TerminalPane::new(term_pid, bottom_winsize);
let new_terminal = TerminalPane::new(term_pid, bottom_winsize, self.colors);
self.os_api.set_terminal_size_using_fd(
new_terminal.pid,
bottom_winsize.columns as u16,
@ -527,7 +526,7 @@ impl Tab {
}
if !self.has_panes() {
if let PaneId::Terminal(term_pid) = pid {
let new_terminal = TerminalPane::new(term_pid, self.full_screen_ws);
let new_terminal = TerminalPane::new(term_pid, self.full_screen_ws, self.colors);
self.os_api.set_terminal_size_using_fd(
new_terminal.pid,
new_terminal.columns() as u16,
@ -557,7 +556,7 @@ impl Tab {
active_pane.change_pos_and_size(&left_winsize);
let new_terminal = TerminalPane::new(term_pid, right_winsize);
let new_terminal = TerminalPane::new(term_pid, right_winsize, self.colors);
self.os_api.set_terminal_size_using_fd(
new_terminal.pid,
right_winsize.columns as u16,

View File

@ -0,0 +1,80 @@
//! Definitions and helpers for sending and receiving messages between threads.
use crate::{
os_input_output::ServerOsApi, pty::PtyInstruction, screen::ScreenInstruction,
wasm_vm::PluginInstruction, ServerInstruction,
};
use std::sync::mpsc;
use zellij_utils::{channels::SenderWithContext, errors::ErrorContext};
/// A container for senders to the different threads in zellij on the server side
#[derive(Clone)]
pub(crate) struct ThreadSenders {
pub to_screen: Option<SenderWithContext<ScreenInstruction>>,
pub to_pty: Option<SenderWithContext<PtyInstruction>>,
pub to_plugin: Option<SenderWithContext<PluginInstruction>>,
pub to_server: Option<SenderWithContext<ServerInstruction>>,
}
impl ThreadSenders {
pub fn send_to_screen(
&self,
instruction: ScreenInstruction,
) -> Result<(), mpsc::SendError<(ScreenInstruction, ErrorContext)>> {
self.to_screen.as_ref().unwrap().send(instruction)
}
pub fn send_to_pty(
&self,
instruction: PtyInstruction,
) -> Result<(), mpsc::SendError<(PtyInstruction, ErrorContext)>> {
self.to_pty.as_ref().unwrap().send(instruction)
}
pub fn send_to_plugin(
&self,
instruction: PluginInstruction,
) -> Result<(), mpsc::SendError<(PluginInstruction, ErrorContext)>> {
self.to_plugin.as_ref().unwrap().send(instruction)
}
pub fn send_to_server(
&self,
instruction: ServerInstruction,
) -> Result<(), mpsc::SendError<(ServerInstruction, ErrorContext)>> {
self.to_server.as_ref().unwrap().send(instruction)
}
}
/// A container for a receiver, OS input and the senders to a given thread
pub(crate) struct Bus<T> {
pub receiver: mpsc::Receiver<(T, ErrorContext)>,
pub senders: ThreadSenders,
pub os_input: Option<Box<dyn ServerOsApi>>,
}
impl<T> Bus<T> {
pub fn new(
receiver: mpsc::Receiver<(T, ErrorContext)>,
to_screen: Option<&SenderWithContext<ScreenInstruction>>,
to_pty: Option<&SenderWithContext<PtyInstruction>>,
to_plugin: Option<&SenderWithContext<PluginInstruction>>,
to_server: Option<&SenderWithContext<ServerInstruction>>,
os_input: Option<Box<dyn ServerOsApi>>,
) -> Self {
Bus {
receiver,
senders: ThreadSenders {
to_screen: to_screen.cloned(),
to_pty: to_pty.cloned(),
to_plugin: to_plugin.cloned(),
to_server: to_server.cloned(),
},
os_input: os_input.clone(),
}
}
pub fn recv(&self) -> Result<(T, ErrorContext), mpsc::RecvError> {
self.receiver.recv()
}
}

View File

@ -1,8 +1,8 @@
use crate::tab::Pane;
use crate::utils::shared::colors;
use ansi_term::Colour::{Fixed, RGB};
use std::collections::HashMap;
use zellij_tile::data::{InputMode, Palette, PaletteColor};
use zellij_utils::shared::colors;
use std::fmt::{Display, Error, Formatter};
pub mod boundary_type {
@ -19,10 +19,10 @@ pub mod boundary_type {
pub const CROSS: &str = "";
}
pub type BoundaryType = &'static str; // easy way to refer to boundary_type above
pub(crate) type BoundaryType = &'static str; // easy way to refer to boundary_type above
#[derive(Clone, Copy, Debug)]
pub struct BoundarySymbol {
pub(crate) struct BoundarySymbol {
boundary_type: BoundaryType,
invisible: bool,
color: Option<PaletteColor>,
@ -392,7 +392,7 @@ fn combine_symbols(
}
#[derive(PartialEq, Eq, Hash, Debug)]
pub struct Coordinates {
pub(crate) struct Coordinates {
x: usize,
y: usize,
}
@ -403,7 +403,7 @@ impl Coordinates {
}
}
pub trait Rect {
pub(crate) trait Rect {
fn x(&self) -> usize;
fn y(&self) -> usize;
fn rows(&self) -> usize;

View File

@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use std::{fs::File, io::prelude::*};
use crate::panes::PositionAndSize;
use zellij_utils::pane_size::PositionAndSize;
fn split_space_to_parts_vertically(
space_to_split: &PositionAndSize,
@ -167,19 +167,19 @@ fn split_space(
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum Direction {
pub(crate) enum Direction {
Horizontal,
Vertical,
}
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
pub enum SplitSize {
pub(crate) enum SplitSize {
Percent(u8), // 1 to 100
Fixed(u16), // An absolute number of columns or rows
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Layout {
pub(crate) struct Layout {
pub direction: Direction,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub parts: Vec<Layout>,

View File

@ -0,0 +1,3 @@
pub mod boundaries;
pub mod layout;
pub mod pane_resizer;

View File

@ -1,12 +1,11 @@
use crate::os_input_output::ServerOsApi;
use crate::panes::{PaneId, PositionAndSize};
use crate::tab::Pane;
use crate::{os_input_output::ServerOsApi, panes::PaneId, tab::Pane};
use std::{
cmp::Ordering,
collections::{BTreeMap, HashSet},
};
use zellij_utils::pane_size::PositionAndSize;
pub struct PaneResizer<'a> {
pub(crate) struct PaneResizer<'a> {
panes: &'a mut BTreeMap<PaneId, Box<dyn Pane>>,
os_api: &'a mut Box<dyn ServerOsApi>,
}

View File

@ -15,14 +15,16 @@ use wasmer::{
use wasmer_wasi::{Pipe, WasiEnv, WasiState};
use zellij_tile::data::{Event, EventType, PluginIds};
use crate::common::errors::ContextType;
use crate::common::pty::PtyInstruction;
use crate::common::screen::ScreenInstruction;
use crate::common::thread_bus::{Bus, ThreadSenders};
use crate::common::PaneId;
use crate::{
panes::PaneId,
pty::PtyInstruction,
screen::ScreenInstruction,
thread_bus::{Bus, ThreadSenders},
};
use zellij_utils::errors::{ContextType, PluginContext};
#[derive(Clone, Debug)]
pub enum PluginInstruction {
pub(crate) enum PluginInstruction {
Load(Sender<u32>, PathBuf),
Update(Option<u32>, Event), // Focused plugin / broadcast, event data
Render(Sender<String>, u32, usize, usize), // String buffer, plugin id, rows, cols
@ -30,8 +32,20 @@ pub enum PluginInstruction {
Exit,
}
impl From<&PluginInstruction> for PluginContext {
fn from(plugin_instruction: &PluginInstruction) -> Self {
match *plugin_instruction {
PluginInstruction::Load(..) => PluginContext::Load,
PluginInstruction::Update(..) => PluginContext::Update,
PluginInstruction::Render(..) => PluginContext::Render,
PluginInstruction::Unload(_) => PluginContext::Unload,
PluginInstruction::Exit => PluginContext::Exit,
}
}
}
#[derive(WasmerEnv, Clone)]
pub struct PluginEnv {
pub(crate) struct PluginEnv {
pub plugin_id: u32,
pub senders: ThreadSenders,
pub wasi_env: WasiEnv,
@ -39,7 +53,7 @@ pub struct PluginEnv {
}
// Thread main --------------------------------------------------------------------------------------------------------
pub fn wasm_thread_main(bus: Bus<PluginInstruction>, store: Store, data_dir: PathBuf) {
pub(crate) fn wasm_thread_main(bus: Bus<PluginInstruction>, store: Store, data_dir: PathBuf) {
let mut plugin_id = 0;
let mut plugin_map = HashMap::new();
loop {
@ -126,7 +140,7 @@ pub fn wasm_thread_main(bus: Bus<PluginInstruction>, store: Store, data_dir: Pat
// Plugin API ---------------------------------------------------------------------------------------------------------
pub fn zellij_exports(store: &Store, plugin_env: &PluginEnv) -> ImportObject {
pub(crate) fn zellij_exports(store: &Store, plugin_env: &PluginEnv) -> ImportObject {
macro_rules! zellij_export {
($($host_function:ident),+ $(,)?) => {
imports! {

36
zellij-utils/Cargo.toml Normal file
View File

@ -0,0 +1,36 @@
[package]
name = "zellij-utils"
version = "0.12.0"
authors = ["Kunal Mohan <kunalmohan99@gmail.com>"]
edition = "2018"
description = "A utility library for Zellij client and server"
license = "MIT"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
backtrace = "0.3.55"
bincode = "1.3.1"
interprocess = "1.1.1"
structopt = "0.3"
serde = { version = "1.0", features = ["derive"] }
zellij-tile = { path = "../zellij-tile/", version = "0.12.0" }
names = "0.11.0"
colors-transform = "0.2.5"
strip-ansi-escapes = "0.1.0"
strum = "0.20.0"
serde_yaml = "0.8"
nix = "0.19.1"
lazy_static = "1.4.0"
directories-next = "2.0"
termion = "1.5.0"
[dependencies.async-std]
version = "1.3.0"
features = ["unstable"]
[dev-dependencies]
tempfile = "3.2.0"
[features]
test = []

View File

@ -0,0 +1,65 @@
//! Definitions and helpers for sending and receiving messages between threads.
use async_std::task_local;
use std::cell::RefCell;
use std::sync::mpsc;
use crate::errors::{get_current_ctx, ErrorContext};
/// An [MPSC](mpsc) asynchronous channel with added error context.
pub type ChannelWithContext<T> = (
mpsc::Sender<(T, ErrorContext)>,
mpsc::Receiver<(T, ErrorContext)>,
);
/// An [MPSC](mpsc) synchronous channel with added error context.
pub type SyncChannelWithContext<T> = (
mpsc::SyncSender<(T, ErrorContext)>,
mpsc::Receiver<(T, ErrorContext)>,
);
/// Wrappers around the two standard [MPSC](mpsc) sender types, [`mpsc::Sender`] and [`mpsc::SyncSender`], with an additional [`ErrorContext`].
#[derive(Clone)]
pub enum SenderType<T: Clone> {
/// A wrapper around an [`mpsc::Sender`], adding an [`ErrorContext`].
Sender(mpsc::Sender<(T, ErrorContext)>),
/// A wrapper around an [`mpsc::SyncSender`], adding an [`ErrorContext`].
SyncSender(mpsc::SyncSender<(T, ErrorContext)>),
}
/// Sends messages on an [MPSC](std::sync::mpsc) channel, along with an [`ErrorContext`],
/// synchronously or asynchronously depending on the underlying [`SenderType`].
#[derive(Clone)]
pub struct SenderWithContext<T: Clone> {
sender: SenderType<T>,
}
impl<T: Clone> SenderWithContext<T> {
pub fn new(sender: SenderType<T>) -> Self {
Self { sender }
}
/// Sends an event, along with the current [`ErrorContext`], on this
/// [`SenderWithContext`]'s channel.
pub fn send(&self, event: T) -> Result<(), mpsc::SendError<(T, ErrorContext)>> {
let err_ctx = get_current_ctx();
match self.sender {
SenderType::Sender(ref s) => s.send((event, err_ctx)),
SenderType::SyncSender(ref s) => s.send((event, err_ctx)),
}
}
}
unsafe impl<T: Clone> Send for SenderWithContext<T> {}
unsafe impl<T: Clone> Sync for SenderWithContext<T> {}
thread_local!(
/// A key to some thread local storage (TLS) that holds a representation of the thread's call
/// stack in the form of an [`ErrorContext`].
pub static OPENCALLS: RefCell<ErrorContext> = RefCell::default()
);
task_local! {
/// A key to some task local storage that holds a representation of the task's call
/// stack in the form of an [`ErrorContext`].
pub static ASYNCOPENCALLS: RefCell<ErrorContext> = RefCell::default()
}

View File

@ -1,6 +1,6 @@
use super::common::utils::consts::{ZELLIJ_CONFIG_DIR_ENV, ZELLIJ_CONFIG_FILE_ENV};
use crate::common::input::options::Options;
use crate::common::setup::Setup;
use crate::consts::{ZELLIJ_CONFIG_DIR_ENV, ZELLIJ_CONFIG_FILE_ENV};
use crate::input::options::Options;
use crate::setup::Setup;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use structopt::StructOpt;

View File

@ -1,6 +1,6 @@
//! Zellij program-wide constants.
use crate::os_input_output::set_permissions;
use crate::shared::set_permissions;
use directories_next::ProjectDirs;
use lazy_static::lazy_static;
use nix::unistd::Uid;

View File

@ -1,43 +1,20 @@
//! Error context system based on a thread-local representation of the call stack, itself based on
//! the instructions that are sent between threads.
use crate::channels::{SenderWithContext, ASYNCOPENCALLS, OPENCALLS};
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Error, Formatter};
use crate::client::ClientInstruction;
use crate::common::thread_bus::{ASYNCOPENCALLS, OPENCALLS};
use crate::pty::PtyInstruction;
use crate::screen::ScreenInstruction;
use crate::server::ServerInstruction;
use std::panic::PanicInfo;
/// The maximum amount of calls an [`ErrorContext`] will keep track
/// of in its stack representation. This is a per-thread maximum.
const MAX_THREAD_CALL_STACK: usize = 6;
#[cfg(not(test))]
use super::thread_bus::SenderWithContext;
#[cfg(not(test))]
use std::panic::PanicInfo;
pub trait ErrorInstruction {
fn error(err: String) -> Self;
}
impl ErrorInstruction for ClientInstruction {
fn error(err: String) -> Self {
ClientInstruction::Error(err)
}
}
impl ErrorInstruction for ServerInstruction {
fn error(err: String) -> Self {
ServerInstruction::Error(err)
}
}
/// Custom panic handler/hook. Prints the [`ErrorContext`].
#[cfg(not(test))]
pub fn handle_panic<T>(info: &PanicInfo<'_>, sender: &SenderWithContext<T>)
where
T: ErrorInstruction + Clone,
@ -243,59 +220,6 @@ pub enum ScreenContext {
ChangeMode,
}
// FIXME: Just deriving EnumDiscriminants from strum will remove the need for any of this!!!
impl From<&ScreenInstruction> for ScreenContext {
fn from(screen_instruction: &ScreenInstruction) -> Self {
match *screen_instruction {
ScreenInstruction::PtyBytes(..) => ScreenContext::HandlePtyBytes,
ScreenInstruction::Render => ScreenContext::Render,
ScreenInstruction::NewPane(_) => ScreenContext::NewPane,
ScreenInstruction::HorizontalSplit(_) => ScreenContext::HorizontalSplit,
ScreenInstruction::VerticalSplit(_) => ScreenContext::VerticalSplit,
ScreenInstruction::WriteCharacter(_) => ScreenContext::WriteCharacter,
ScreenInstruction::ResizeLeft => ScreenContext::ResizeLeft,
ScreenInstruction::ResizeRight => ScreenContext::ResizeRight,
ScreenInstruction::ResizeDown => ScreenContext::ResizeDown,
ScreenInstruction::ResizeUp => ScreenContext::ResizeUp,
ScreenInstruction::SwitchFocus => ScreenContext::SwitchFocus,
ScreenInstruction::FocusNextPane => ScreenContext::FocusNextPane,
ScreenInstruction::FocusPreviousPane => ScreenContext::FocusPreviousPane,
ScreenInstruction::MoveFocusLeft => ScreenContext::MoveFocusLeft,
ScreenInstruction::MoveFocusLeftOrPreviousTab => {
ScreenContext::MoveFocusLeftOrPreviousTab
}
ScreenInstruction::MoveFocusDown => ScreenContext::MoveFocusDown,
ScreenInstruction::MoveFocusUp => ScreenContext::MoveFocusUp,
ScreenInstruction::MoveFocusRight => ScreenContext::MoveFocusRight,
ScreenInstruction::MoveFocusRightOrNextTab => ScreenContext::MoveFocusRightOrNextTab,
ScreenInstruction::Exit => ScreenContext::Exit,
ScreenInstruction::ScrollUp => ScreenContext::ScrollUp,
ScreenInstruction::ScrollDown => ScreenContext::ScrollDown,
ScreenInstruction::PageScrollUp => ScreenContext::PageScrollUp,
ScreenInstruction::PageScrollDown => ScreenContext::PageScrollDown,
ScreenInstruction::ClearScroll => ScreenContext::ClearScroll,
ScreenInstruction::CloseFocusedPane => ScreenContext::CloseFocusedPane,
ScreenInstruction::ToggleActiveTerminalFullscreen => {
ScreenContext::ToggleActiveTerminalFullscreen
}
ScreenInstruction::SetSelectable(..) => ScreenContext::SetSelectable,
ScreenInstruction::SetInvisibleBorders(..) => ScreenContext::SetInvisibleBorders,
ScreenInstruction::SetMaxHeight(..) => ScreenContext::SetMaxHeight,
ScreenInstruction::ClosePane(_) => ScreenContext::ClosePane,
ScreenInstruction::ApplyLayout(..) => ScreenContext::ApplyLayout,
ScreenInstruction::NewTab(_) => ScreenContext::NewTab,
ScreenInstruction::SwitchTabNext => ScreenContext::SwitchTabNext,
ScreenInstruction::SwitchTabPrev => ScreenContext::SwitchTabPrev,
ScreenInstruction::CloseTab => ScreenContext::CloseTab,
ScreenInstruction::GoToTab(_) => ScreenContext::GoToTab,
ScreenInstruction::UpdateTabName(_) => ScreenContext::UpdateTabName,
ScreenInstruction::TerminalResize(_) => ScreenContext::TerminalResize,
ScreenInstruction::ChangeMode(_) => ScreenContext::ChangeMode,
ScreenInstruction::ToggleActiveSyncTab => ScreenContext::ToggleActiveSyncTab,
}
}
}
/// Stack call representations corresponding to the different types of [`PtyInstruction`]s.
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum PtyContext {
@ -308,24 +232,6 @@ pub enum PtyContext {
Exit,
}
impl From<&PtyInstruction> for PtyContext {
fn from(pty_instruction: &PtyInstruction) -> Self {
match *pty_instruction {
PtyInstruction::SpawnTerminal(_) => PtyContext::SpawnTerminal,
PtyInstruction::SpawnTerminalVertically(_) => PtyContext::SpawnTerminalVertically,
PtyInstruction::SpawnTerminalHorizontally(_) => PtyContext::SpawnTerminalHorizontally,
PtyInstruction::ClosePane(_) => PtyContext::ClosePane,
PtyInstruction::CloseTab(_) => PtyContext::CloseTab,
PtyInstruction::NewTab => PtyContext::NewTab,
PtyInstruction::Exit => PtyContext::Exit,
}
}
}
// FIXME: This whole pattern *needs* a macro eventually, it's soul-crushing to write
use crate::wasm_vm::PluginInstruction;
/// Stack call representations corresponding to the different types of [`PluginInstruction`]s.
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum PluginContext {
@ -336,18 +242,6 @@ pub enum PluginContext {
Exit,
}
impl From<&PluginInstruction> for PluginContext {
fn from(plugin_instruction: &PluginInstruction) -> Self {
match *plugin_instruction {
PluginInstruction::Load(..) => PluginContext::Load,
PluginInstruction::Update(..) => PluginContext::Update,
PluginInstruction::Render(..) => PluginContext::Render,
PluginInstruction::Unload(_) => PluginContext::Unload,
PluginInstruction::Exit => PluginContext::Exit,
}
}
}
/// Stack call representations corresponding to the different types of [`ClientInstruction`]s.
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum ClientContext {
@ -358,18 +252,6 @@ pub enum ClientContext {
ServerError,
}
impl From<&ClientInstruction> for ClientContext {
fn from(client_instruction: &ClientInstruction) -> Self {
match *client_instruction {
ClientInstruction::Exit => ClientContext::Exit,
ClientInstruction::Error(_) => ClientContext::Error,
ClientInstruction::ServerError(_) => ClientContext::ServerError,
ClientInstruction::Render(_) => ClientContext::Render,
ClientInstruction::UnblockInputThread => ClientContext::UnblockInputThread,
}
}
}
/// Stack call representations corresponding to the different types of [`ServerInstruction`]s.
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum ServerContext {
@ -379,15 +261,3 @@ pub enum ServerContext {
ClientExit,
Error,
}
impl From<&ServerInstruction> for ServerContext {
fn from(server_instruction: &ServerInstruction) -> Self {
match *server_instruction {
ServerInstruction::NewClient(..) => ServerContext::NewClient,
ServerInstruction::Render(_) => ServerContext::Render,
ServerInstruction::UnblockInputThread => ServerContext::UnblockInputThread,
ServerInstruction::ClientExit => ServerContext::ClientExit,
ServerInstruction::Error(_) => ServerContext::Error,
}
}
}

View File

@ -8,7 +8,7 @@ use std::path::{Path, PathBuf};
use super::keybinds::{Keybinds, KeybindsFromYaml};
use super::options::Options;
use crate::cli::{CliArgs, ConfigCli};
use crate::common::setup;
use crate::setup;
use serde::{Deserialize, Serialize};
use std::convert::TryFrom;
@ -177,7 +177,7 @@ mod config_test {
#[test]
fn try_from_cli_args_with_option_clean() {
use crate::common::setup::Setup;
use crate::setup::Setup;
let mut opts = CliArgs::default();
opts.option = Some(ConfigCli::Setup(Setup {
clean: true,

View File

@ -0,0 +1,87 @@
//! The way terminal input is handled.
pub mod actions;
pub mod config;
pub mod keybinds;
pub mod options;
use termion::input::TermRead;
use zellij_tile::data::{InputMode, Key, ModeInfo, Palette, PluginCapabilities};
/// Creates a [`Help`] struct indicating the current [`InputMode`] and its keybinds
/// (as pairs of [`String`]s).
// TODO this should probably be automatically generated in some way
pub fn get_mode_info(
mode: InputMode,
palette: Palette,
capabilities: PluginCapabilities,
) -> ModeInfo {
let mut keybinds: Vec<(String, String)> = vec![];
match mode {
InputMode::Normal | InputMode::Locked => {}
InputMode::Resize => {
keybinds.push(("←↓↑→".to_string(), "Resize".to_string()));
}
InputMode::Pane => {
keybinds.push(("←↓↑→".to_string(), "Move focus".to_string()));
keybinds.push(("p".to_string(), "Next".to_string()));
keybinds.push(("n".to_string(), "New".to_string()));
keybinds.push(("d".to_string(), "Down split".to_string()));
keybinds.push(("r".to_string(), "Right split".to_string()));
keybinds.push(("x".to_string(), "Close".to_string()));
keybinds.push(("f".to_string(), "Fullscreen".to_string()));
}
InputMode::Tab => {
keybinds.push(("←↓↑→".to_string(), "Move focus".to_string()));
keybinds.push(("n".to_string(), "New".to_string()));
keybinds.push(("x".to_string(), "Close".to_string()));
keybinds.push(("r".to_string(), "Rename".to_string()));
keybinds.push(("s".to_string(), "Sync".to_string()));
}
InputMode::Scroll => {
keybinds.push(("↓↑".to_string(), "Scroll".to_string()));
keybinds.push(("PgUp/PgDn".to_string(), "Scroll Page".to_string()));
}
InputMode::RenameTab => {
keybinds.push(("Enter".to_string(), "when done".to_string()));
}
}
ModeInfo {
mode,
keybinds,
palette,
capabilities,
}
}
pub fn parse_keys(input_bytes: &[u8]) -> Vec<Key> {
input_bytes.keys().flatten().map(cast_termion_key).collect()
}
// FIXME: This is an absolutely cursed function that should be destroyed as soon
// as an alternative that doesn't touch zellij-tile can be developed...
pub fn cast_termion_key(event: termion::event::Key) -> Key {
match event {
termion::event::Key::Backspace => Key::Backspace,
termion::event::Key::Left => Key::Left,
termion::event::Key::Right => Key::Right,
termion::event::Key::Up => Key::Up,
termion::event::Key::Down => Key::Down,
termion::event::Key::Home => Key::Home,
termion::event::Key::End => Key::End,
termion::event::Key::PageUp => Key::PageUp,
termion::event::Key::PageDown => Key::PageDown,
termion::event::Key::BackTab => Key::BackTab,
termion::event::Key::Delete => Key::Delete,
termion::event::Key::Insert => Key::Insert,
termion::event::Key::F(n) => Key::F(n),
termion::event::Key::Char(c) => Key::Char(c),
termion::event::Key::Alt(c) => Key::Alt(c),
termion::event::Key::Ctrl(c) => Key::Ctrl(c),
termion::event::Key::Null => Key::Null,
termion::event::Key::Esc => Key::Esc,
_ => {
unimplemented!("Encountered an unknown key!")
}
}
}

Some files were not shown because too many files have changed in this diff Show More