crossterm rewrite

This commit is contained in:
MeexReay 2025-02-10 00:39:28 +03:00
parent 2812e51a9b
commit 85c379ecfc
6 changed files with 291 additions and 102 deletions

215
Cargo.lock generated
View File

@ -11,15 +11,21 @@ dependencies = [
"memchr",
]
[[package]]
name = "autocfg"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "bRAC"
version = "1.99.2"
dependencies = [
"colored",
"crossterm",
"lazy_static",
"rand",
"regex",
"termion",
]
[[package]]
@ -46,7 +52,42 @@ version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e"
dependencies = [
"windows-sys",
"windows-sys 0.59.0",
]
[[package]]
name = "crossterm"
version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
dependencies = [
"bitflags",
"crossterm_winapi",
"mio",
"parking_lot",
"rustix",
"signal-hook",
"signal-hook-mio",
"winapi",
]
[[package]]
name = "crossterm_winapi"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
dependencies = [
"winapi",
]
[[package]]
name = "errno"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
dependencies = [
"libc",
"windows-sys 0.59.0",
]
[[package]]
@ -57,7 +98,7 @@ checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
dependencies = [
"cfg-if",
"libc",
"wasi",
"wasi 0.13.3+wasi-0.2.2",
"windows-targets",
]
@ -74,16 +115,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
[[package]]
name = "libredox"
version = "0.1.3"
name = "linux-raw-sys"
version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
[[package]]
name = "lock_api"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
dependencies = [
"bitflags",
"libc",
"redox_syscall",
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
[[package]]
name = "memchr"
version = "2.7.4"
@ -91,10 +143,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "numtoa"
version = "0.2.4"
name = "mio"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6aa2c4e539b869820a2b82e1aef6ff40aa85e65decdd5185e83fb4b1249cd00f"
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
dependencies = [
"libc",
"log",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys 0.52.0",
]
[[package]]
name = "parking_lot"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-targets",
]
[[package]]
name = "ppv-lite86"
@ -163,12 +244,6 @@ dependencies = [
"bitflags",
]
[[package]]
name = "redox_termios"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb"
[[package]]
name = "regex"
version = "1.11.1"
@ -198,6 +273,61 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "rustix"
version = "0.38.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.59.0",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "signal-hook"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-mio"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
dependencies = [
"libc",
"mio",
"signal-hook",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
dependencies = [
"libc",
]
[[package]]
name = "smallvec"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "syn"
version = "2.0.98"
@ -209,24 +339,18 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "termion"
version = "4.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7eaa98560e51a2cf4f0bb884d8b2098a9ea11ecf3b7078e9c68242c74cc923a7"
dependencies = [
"libc",
"libredox",
"numtoa",
"redox_termios",
]
[[package]]
name = "unicode-ident"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasi"
version = "0.13.3+wasi-0.2.2"
@ -236,6 +360,37 @@ dependencies = [
"wit-bindgen-rt",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.59.0"

View File

@ -5,7 +5,7 @@ edition = "2021"
[dependencies]
rand = "0.9.0"
termion = "4.0.3"
regex = "1.11.1"
colored = "3.0.0"
lazy_static = "1.5.0"
crossterm = "0.28.1"

16
build-all.sh Normal file
View File

@ -0,0 +1,16 @@
#!/usr/bin/env bash
set -e
TARGETS=(
x86_64-unknown-linux-gnu
i686-unknown-linux-gnu
x86_64-pc-windows-gnu
i686-pc-windows-gnu
x86_64-apple-darwin
aarch64-apple-darwin
)
for TARGET in "${TARGETS[@]}"; do
cargo build --release --target "$TARGET"
done

View File

@ -1,4 +1,5 @@
host: meex.lol:11234
name: null
magic_key: "\uB9AC\u3E70"
ad: "\r\x1B[1A use bRAC client! https://github.com/MeexReay/bRAC \x1B[1B"
host: meex.lol:11234 # reverse proxy through tor
name: null # username (null - ask every time)
magic_key: "\uB9AC\u3E70" # default bRAC marker
ad_enabled: false # enable sending ad of bRAC above your message
update_time: 50 # update messages interval

13
shell.nix Normal file
View File

@ -0,0 +1,13 @@
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
buildInputs = with pkgs; [
rustup
gcc_multi
pkg-config
zlib
openssl
pkgsCross.gnu32.buildPackages.gcc
pkgsCross.mingw32.buildPackages.gcc
pkgsCross.mingwW64.buildPackages.gcc
];
}

View File

@ -1,18 +1,29 @@
use std::{
collections::HashMap, error::Error, io::{stdin, stdout, BufRead, Read, Stdout, Write}, net::TcpStream, sync::{Arc, Mutex, RwLock}, thread, time::Duration
error::Error,
io::{stdin, stdout, BufRead, Read, Write},
net::TcpStream,
sync::{Arc, RwLock},
thread,
time::Duration
};
use colored::{Color, Colorize};
use crossterm::{
cursor::MoveLeft,
event::{self, Event, KeyCode},
terminal::{disable_raw_mode, enable_raw_mode},
ExecutableCommand
};
use rand::random;
use regex::Regex;
use termion::{async_stdin, clear, cursor, event::Key, input::TermRead, raw::{IntoRawMode, RawTerminal}};
use lazy_static::lazy_static;
const MAX_MESSAGES: usize = 100;
const DEFAULT_HOST: &str = "meex.lol:11234";
const MAGIC_KEY: &str = "\u{B9AC}\u{3E70}";
const ADVERTISEMENT: &str = "\r\x1B[1A use bRAC client! https://github.com/MeexReay/bRAC \x1B[1B";
const MAX_MESSAGES: usize = 100;
const MAGIC_KEY: &str = "\u{B9AC}\u{3E70}";
const ADVERTISEMENT_ENABLED: bool = false;
const UPDATE_TIME: u64 = 50;
@ -93,14 +104,14 @@ fn read_messages(host: &str) -> Result<String, Box<dyn Error>> {
Ok(packet_data)
}
fn recv_loop(terminal: Arc<Mutex<RawTerminal<Stdout>>>, host: &str, cache: Arc<RwLock<String>>, input: Arc<RwLock<String>>) -> Result<(), Box<dyn Error>> {
fn recv_loop(host: &str, cache: Arc<RwLock<String>>, input: Arc<RwLock<String>>) -> Result<(), Box<dyn Error>> {
while let Ok(data) = read_messages(host) {
if data == cache.read().unwrap().clone() {
continue
}
*cache.write().unwrap() = data;
print_console(terminal.clone(), &cache.read().unwrap(), &input.read().unwrap())?;
print_console(&cache.read().unwrap(), &input.read().unwrap())?;
thread::sleep(Duration::from_millis(UPDATE_TIME));
}
Ok(())
@ -179,7 +190,7 @@ fn format_message(message: String) -> Option<String> {
})
}
fn on_command(host: &str, command: &str, input: Arc<RwLock<String>>) -> Result<(), Box<dyn Error>> {
fn on_command(host: &str, command: &str) -> Result<(), Box<dyn Error>> {
let command = command.trim_start_matches("/");
let (command, args) = command.split_once(" ").unwrap_or((&command, ""));
let args = args.split(" ").collect::<Vec<&str>>();
@ -195,7 +206,7 @@ fn on_command(host: &str, command: &str, input: Arc<RwLock<String>>) -> Result<(
Ok(())
}
fn print_console(terminal: Arc<Mutex<RawTerminal<Stdout>>>, messages: &str, input: &str) -> Result<(), Box<dyn Error>> {
fn print_console(messages: &str, input: &str) -> Result<(), Box<dyn Error>> {
let mut messages = messages.split("\n")
.map(|o| o.to_string())
.collect::<Vec<String>>();
@ -211,8 +222,8 @@ fn print_console(terminal: Arc<Mutex<RawTerminal<Stdout>>>, messages: &str, inpu
input
);
for line in text.lines() {
write!(terminal.lock().unwrap(), "\r\n{}", line)?;
terminal.lock().unwrap().flush()?;
write!(stdout().lock(), "\r\n{}", line)?;
stdout().lock().flush()?;
}
Ok(())
}
@ -225,19 +236,15 @@ fn main() {
let messages = Arc::new(RwLock::new(String::new()));
let input = Arc::new(RwLock::new(String::new()));
let terminal = stdout().into_raw_mode().unwrap();
terminal.activate_raw_mode().unwrap();
let terminal = Arc::new(Mutex::new(terminal));
enable_raw_mode().unwrap();
thread::spawn({
let host = host.clone();
let messages = messages.clone();
let input = input.clone();
let terminal = terminal.clone();
move || {
let _ = recv_loop(terminal, &host, messages, input);
let _ = recv_loop(&host, messages, input);
println!("Connection closed");
}
});
@ -245,11 +252,9 @@ fn main() {
thread::spawn({
let messages = messages.clone();
let input = input.clone();
let terminal = terminal.clone();
move || {
print_console(
terminal.clone(),
&messages.read().unwrap(),
&input.read().unwrap()
).expect("Error printing console");
@ -257,59 +262,58 @@ fn main() {
}
});
let stdin = stdin();
for key in stdin.keys() {
match key.unwrap() {
Key::Char('\n') => {
let message = input.read().unwrap().clone();
loop {
if !event::poll(Duration::from_millis(50)).unwrap_or(false) { continue }
{
let input_len = input.read().unwrap().len();
write!(terminal.lock().unwrap(),
"{}{}{}",
cursor::Left(1).to_string().repeat(input_len),
" ".repeat(input_len),
cursor::Left(1).to_string().repeat(input_len)
).unwrap();
terminal.lock().unwrap().flush().unwrap();
input.write().unwrap().clear();
}
let event = match event::read() {
Ok(i) => i,
Err(_) => { continue },
};
if !message.is_empty() {
if message.starts_with("/") {
on_command(&host, &message, input.clone()).expect("Error on command");
} else {
send_message(&host, &format!("{}<{}> {}", MAGIC_KEY, name, message)).expect("Error sending message");
match event {
Event::Key(event) => {
match event.code {
KeyCode::Enter => {
let message = input.read().unwrap().clone();
let input_len = input.read().unwrap().len();
stdout().lock().execute(MoveLeft(input_len as u16)).unwrap();
write!(stdout(), "{}{}", " ".repeat(input_len), MoveLeft(input_len as u16).to_string()).unwrap();
stdout().lock().flush().unwrap();
input.write().unwrap().clear();
if !message.is_empty() {
if message.starts_with("/") {
on_command(&host, &message).expect("Error on command");
} else {
send_message(&host, &format!("{}<{}> {}", MAGIC_KEY, name, message)).expect("Error sending message");
}
}
}
KeyCode::Backspace => {
if input.write().unwrap().pop().is_some() {
stdout().lock().execute(MoveLeft(1)).unwrap();
write!(stdout(), " {}", MoveLeft(1).to_string()).unwrap();
stdout().lock().flush().unwrap();
}
}
KeyCode::Char(c) => {
input.write().unwrap().push(c);
write!(stdout(), "{}", c).unwrap();
stdout().lock().flush().unwrap();
}
KeyCode::Esc => {
disable_raw_mode().unwrap();
break;
},
_ => {}
}
},
Event::Paste(data) => {
input.write().unwrap().push_str(&data);
write!(stdout(), "{}", &data).unwrap();
stdout().lock().flush().unwrap();
}
Key::Backspace => {
if input.write().unwrap().pop().is_some() {
write!(terminal.lock().unwrap(), "{} {}", cursor::Left(1), cursor::Left(1)).unwrap();
terminal.lock().unwrap().flush().unwrap();
}
}
Key::Char(c) => {
input.write().unwrap().push(c);
write!(terminal.lock().unwrap(), "{}", c).unwrap();
terminal.lock().unwrap().flush().unwrap();
}
Key::Esc => {
terminal.lock().unwrap().suspend_raw_mode().unwrap();
break;
},
Key::Ctrl('c') => {
terminal.lock().unwrap().suspend_raw_mode().unwrap();
break;
},
Key::Ctrl('z') => {
terminal.lock().unwrap().suspend_raw_mode().unwrap();
break;
},
Key::Ctrl('x') => {
terminal.lock().unwrap().suspend_raw_mode().unwrap();
break;
},
_ => {}
}
}