diff --git a/Cargo.lock b/Cargo.lock index df78cf2..2f70491 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index b25381b..f4d3cc0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" \ No newline at end of file +lazy_static = "1.5.0" +crossterm = "0.28.1" \ No newline at end of file diff --git a/build-all.sh b/build-all.sh new file mode 100644 index 0000000..f6b31a0 --- /dev/null +++ b/build-all.sh @@ -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 \ No newline at end of file diff --git a/config.yml b/config.yml index d79686b..23586ae 100644 --- a/config.yml +++ b/config.yml @@ -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" \ No newline at end of file +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 \ No newline at end of file diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..e405ebc --- /dev/null +++ b/shell.nix @@ -0,0 +1,13 @@ +{ pkgs ? import {} }: +pkgs.mkShell { + buildInputs = with pkgs; [ + rustup + gcc_multi + pkg-config + zlib + openssl + pkgsCross.gnu32.buildPackages.gcc + pkgsCross.mingw32.buildPackages.gcc + pkgsCross.mingwW64.buildPackages.gcc + ]; +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 8ccb164..f9d5c13 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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> { Ok(packet_data) } -fn recv_loop(terminal: Arc>>, host: &str, cache: Arc>, input: Arc>) -> Result<(), Box> { +fn recv_loop(host: &str, cache: Arc>, input: Arc>) -> Result<(), Box> { 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 { }) } -fn on_command(host: &str, command: &str, input: Arc>) -> Result<(), Box> { +fn on_command(host: &str, command: &str) -> Result<(), Box> { let command = command.trim_start_matches("/"); let (command, args) = command.split_once(" ").unwrap_or((&command, "")); let args = args.split(" ").collect::>(); @@ -195,7 +206,7 @@ fn on_command(host: &str, command: &str, input: Arc>) -> Result<( Ok(()) } -fn print_console(terminal: Arc>>, messages: &str, input: &str) -> Result<(), Box> { +fn print_console(messages: &str, input: &str) -> Result<(), Box> { let mut messages = messages.split("\n") .map(|o| o.to_string()) .collect::>(); @@ -211,8 +222,8 @@ fn print_console(terminal: Arc>>, 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; - }, _ => {} } }