diff --git a/.github/workflows/test-beta.yml b/.github/workflows/test-beta.yml deleted file mode 100644 index 8ea91d3611..0000000000 --- a/.github/workflows/test-beta.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Test Beta - -on: [push, pull_request] - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -env: - CARGO_TERM_COLOR: always - RUST_BACKTRACE: full - -jobs: - test-native-beta: - strategy: - matrix: - os: [macos-latest] - - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v3 - - uses: Swatinem/rust-cache@v2 - - run: rustup toolchain install stable --profile minimal - - run: rustup component add rustfmt clippy - - run: cargo fetch - - run: cargo clippy -p rioterm-beta -- -D warnings - - run: cargo test -p rioterm-beta diff --git a/Cargo.lock b/Cargo.lock index 9316df22fc..04368c423f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -265,29 +265,13 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" -[[package]] -name = "block-sys" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "078aa3a3535876bebff823280f99839535c406a34438b2a877106db45ae53575" - [[package]] name = "block-sys" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dd7cf50912cddc06dc5ea7c08c5e81c1b2c842a70d19def1848d54c586fed92" dependencies = [ - "objc-sys 0.3.2", -] - -[[package]] -name = "block2" -version = "0.2.0-alpha.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "602e30baab86355a0ec4c99e65fa350cc4b92d8d3d404f8ecf4683e59e462325" -dependencies = [ - "block-sys 0.0.4", - "objc2-encode 2.0.0-pre.0", + "objc-sys", ] [[package]] @@ -296,8 +280,8 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15b55663a85f33501257357e6421bb33e769d5c9ffb5ba0921c975a123e35e68" dependencies = [ - "block-sys 0.2.0", - "objc2 0.4.1", + "block-sys", + "objc2", ] [[package]] @@ -1408,9 +1392,9 @@ version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d3aaff8a54577104bafdf686ff18565c3b6903ca5782a2026ef06e2c7aa319" dependencies = [ - "block2 0.3.0", + "block2", "dispatch", - "objc2 0.4.1", + "objc2", ] [[package]] @@ -1964,60 +1948,28 @@ dependencies = [ "objc_id", ] -[[package]] -name = "objc-sys" -version = "0.2.0-beta.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7cb23a031fed7d6cb8f2d03b04b981d1b7cfe56dc3d0ad5fb1d8daab67d0e26" - [[package]] name = "objc-sys" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7c71324e4180d0899963fc83d9d241ac39e699609fc1025a850aadac8257459" -[[package]] -name = "objc2" -version = "0.3.0-beta.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f325aba6a304899c08c8db71e40d86e58b54d70febfe22212c23ab8a732e6267" -dependencies = [ - "objc-sys 0.2.0-beta.0", - "objc2-encode 2.0.0-pre.0", -] - [[package]] name = "objc2" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "559c5a40fdd30eb5e344fbceacf7595a81e242529fb4e21cf5f43fb4f11ff98d" dependencies = [ - "objc-sys 0.3.2", - "objc2-encode 3.0.0", + "objc-sys", + "objc2-encode", ] -[[package]] -name = "objc2-encode" -version = "2.0.0-pre.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f345f5981fb5b9f68c5f3ec79efd8a9fb01385d199fa0eff9ed739d854e321ea" - [[package]] name = "objc2-encode" version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666" -[[package]] -name = "objc2-foundation" -version = "0.2.0-alpha.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeca4b9f5cb162b14fd3da3f8efe8424b7550dc8bb255515ec8441d65a1738d0" -dependencies = [ - "block2 0.2.0-alpha.4", - "objc2 0.3.0-beta.0", -] - [[package]] name = "objc_exception" version = "0.1.2" @@ -2472,39 +2424,6 @@ dependencies = [ [[package]] name = "rioterm" version = "0.0.35" -dependencies = [ - "ahash", - "bitflags 2.4.1", - "clap", - "cocoa", - "copa", - "corcovado", - "dirs", - "futures", - "image", - "libc", - "log 0.4.20", - "notify", - "objc", - "objc2 0.3.0-beta.0", - "objc2-foundation", - "parking_lot", - "raw-window-handle", - "regex", - "rio-backend", - "serde", - "teletypewriter", - "tinyvec", - "tokio", - "unicode-width", - "wgpu", - "windows-sys 0.48.0", - "winit", -] - -[[package]] -name = "rioterm-beta" -version = "0.1.0" dependencies = [ "ahash", "bitflags 2.4.1", @@ -2531,6 +2450,7 @@ dependencies = [ "wa", "wgpu", "windows-sys 0.48.0", + "winit", ] [[package]] @@ -3811,7 +3731,7 @@ dependencies = [ "memmap2", "ndk", "ndk-sys", - "objc2 0.4.1", + "objc2", "once_cell", "orbclient", "percent-encoding", diff --git a/Cargo.toml b/Cargo.toml index 75024d892e..86586bffad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,8 +7,7 @@ members = [ "rio-proc-macros", "rio-backend", "wa", - "frontends/rioterm", - "frontends/rioterm-beta" + "frontends/rioterm" ] resolver = "2" diff --git a/Makefile b/Makefile index ac6918a9b3..ad1678114b 100644 --- a/Makefile +++ b/Makefile @@ -29,9 +29,6 @@ run: dev: cargo run -p rioterm -dev-beta: - cargo run -p rioterm-beta - run-wasm: cargo build -p rioterm --target wasm32-unknown-unknown --lib cd rio-wasm && make run @@ -167,7 +164,7 @@ release-windows: # flag has been conflicting in the checks lint: cargo fmt -- --check --color always - cargo clippy --workspace --exclude rioterm-beta --all-targets --all-features -- -D warnings + cargo clippy --all-targets --all-features -- -D warnings # There is errors regarding null pointers in corcovado that needs to be fixed for Windows test-win: @@ -177,7 +174,7 @@ test-win: test: make lint - RUST_BACKTRACE=full cargo test --workspace --release --exclude rioterm-beta + RUST_BACKTRACE=full cargo test --release publish-crates: cargo build --release diff --git a/frontends/rioterm-beta/Cargo.toml b/frontends/rioterm-beta/Cargo.toml deleted file mode 100644 index 021e06d4ef..0000000000 --- a/frontends/rioterm-beta/Cargo.toml +++ /dev/null @@ -1,69 +0,0 @@ -[package] -name = "rioterm-beta" -description = "Rio terminal is a hardware-accelerated GPU terminal emulator, focusing to run in desktops and browsers." -version = "0.1.0" -authors.workspace = true -edition.workspace = true -license.workspace = true -keywords.workspace = true -rust-version.workspace = true -repository.workspace = true -homepage.workspace = true -documentation.workspace = true - -[[bin]] -name = "rio-beta" -path = "src/main.rs" - -[dependencies] -wa = { workspace = true } -log = { workspace = true } -rio-backend = { workspace = true } - -[target.'cfg(not(target_arch = "wasm32"))'.dependencies] -bitflags = "2.4.1" -futures = { workspace = true } -corcovado = { workspace = true } -regex = { workspace = true } -raw-window-handle = { workspace = true } -clap = { version = "4.4.7", features = ["derive"] } -dirs = "5.0.1" -notify = "6.1.1" -image = { workspace = true } -libc = { workspace = true } -parking_lot = { workspace = true } -serde = { workspace = true } -teletypewriter = { workspace = true } -tokio = { workspace = true } -unicode-width = { workspace = true } -copa = { workspace = true } -wgpu = { workspace = true } -smol_str = "0.2.0" - -[features] -default = ["wayland", "x11"] -x11 = [ - "rio-backend/x11", -] -wayland = [ - "rio-backend/wayland", -] - -[target.'cfg(target_os = "macos")'.dependencies] -objc = "0.2.7" - -[target.'cfg(windows)'.dependencies] -ahash = { version = "0.8.2", default-features = false, features = ["std"] } -tinyvec = { version = "1.6.0", features = ["alloc"] } -windows-sys = { version = "0.48", features = [ - "Win32_System_Console", - "Win32_Foundation", - "Win32_Security", - "Win32_System_LibraryLoader", - "Win32_System_Threading", - "Win32_System_WindowsProgramming", - "Win32_System_IO", - "Win32_Graphics_Gdi", - "Win32_UI_Shell", - "Win32_UI_WindowsAndMessaging", -]} diff --git a/frontends/rioterm-beta/src/cli.rs b/frontends/rioterm-beta/src/cli.rs deleted file mode 100644 index 9199540b6c..0000000000 --- a/frontends/rioterm-beta/src/cli.rs +++ /dev/null @@ -1,63 +0,0 @@ -// cli.rs was retired originally from https://github.com/alacritty/alacritty/blob/e35e5ad14fce8456afdd89f2b392b9924bb27471/alacritty/src/cli.rs -// which is licensed under Apache 2.0 license. - -use clap::{Args, Parser, ValueHint}; -use rio_backend::config::Shell; -use serde::{Deserialize, Serialize}; - -#[derive(Parser, Default, Debug)] -#[clap(author, about, version)] -pub struct Cli { - /// Options which can be passed via IPC. - #[clap(flatten)] - pub window_options: WindowOptions, -} - -#[derive(Serialize, Deserialize, Args, Default, Clone, Debug, PartialEq, Eq)] -pub struct WindowOptions { - /// Terminal options which can be passed via IPC. - #[clap(flatten)] - pub terminal_options: TerminalOptions, -} - -#[derive(Serialize, Deserialize, Args, Default, Debug, Clone, PartialEq, Eq)] -pub struct TerminalOptions { - /// Command and args to execute (must be last argument). - #[clap(short = 'e', long, allow_hyphen_values = true, num_args = 1..)] - pub command: Vec, - - /// Start the shell in the specified working directory. - #[clap(long, value_hint = ValueHint::FilePath)] - pub working_dir: Option, -} - -impl TerminalOptions { - /// Shell override passed through the CLI. - pub fn command(&self) -> Option { - let (program, args) = self.command.split_first()?; - if program.is_empty() { - return None; - } - - Some(Shell { - program: program.clone(), - args: args.to_vec(), - }) - } - - // pub fn override_pty_config(&self, pty_config: &mut PtyConfig) { - // if let Some(working_directory) = &self.working_directory { - // if working_directory.is_dir() { - // pty_config.working_directory = Some(working_directory.to_owned()); - // } else { - // error!("Invalid working directory: {:?}", working_directory); - // } - // } - - // if let Some(command) = self.command() { - // pty_config.shell = Some(command); - // } - - // pty_config.hold |= self.hold; - // } -} diff --git a/frontends/rioterm-beta/src/ime.rs b/frontends/rioterm-beta/src/ime.rs deleted file mode 100644 index 9dd58b882c..0000000000 --- a/frontends/rioterm-beta/src/ime.rs +++ /dev/null @@ -1,76 +0,0 @@ -use unicode_width::UnicodeWidthChar; -#[derive(Debug, Default)] -pub struct Ime { - /// Whether the IME is enabled. - enabled: bool, - - /// Current IME preedit. - preedit: Option, -} - -impl Ime { - pub fn new() -> Self { - Default::default() - } - - #[inline] - pub fn set_enabled(&mut self, is_enabled: bool) { - if is_enabled { - self.enabled = is_enabled - } else { - // Clear state when disabling IME. - *self = Default::default(); - } - } - - #[inline] - #[allow(unused)] - pub fn is_enabled(&self) -> bool { - self.enabled - } - - #[inline] - pub fn set_preedit(&mut self, preedit: Option) { - self.preedit = preedit; - } - - #[inline] - pub fn preedit(&self) -> Option<&Preedit> { - self.preedit.as_ref() - } -} - -#[derive(Debug, Default, PartialEq, Eq)] -pub struct Preedit { - /// The preedit text. - pub text: String, - - /// Byte offset for cursor start into the preedit text. - /// - /// `None` means that the cursor is invisible. - pub cursor_byte_offset: Option, - - /// The cursor offset from the end of the preedit in char width. - pub cursor_end_offset: Option, -} - -impl Preedit { - pub fn new(text: String, cursor_byte_offset: Option) -> Self { - let cursor_end_offset = if let Some(byte_offset) = cursor_byte_offset { - // Convert byte offset into char offset. - let cursor_end_offset = text[byte_offset..] - .chars() - .fold(0, |acc, ch| acc + ch.width().unwrap_or(1)); - - Some(cursor_end_offset) - } else { - None - }; - - Self { - text, - cursor_byte_offset, - cursor_end_offset, - } - } -} diff --git a/frontends/rioterm-beta/src/logger.rs b/frontends/rioterm-beta/src/logger.rs deleted file mode 100644 index 8704aa36fd..0000000000 --- a/frontends/rioterm-beta/src/logger.rs +++ /dev/null @@ -1,29 +0,0 @@ -use log::{Metadata, Record}; - -pub struct Logger; - -impl log::Log for Logger { - fn enabled(&self, _metadata: &Metadata) -> bool { - // If declarative wants to ignore trace - // metadata.level() <= log::Level::Debug - - true - } - - fn log(&self, record: &Record) { - // For cases where wants to validate if log is enabled - // if self.enabled(record.metadata()) { - // println!("{}", record.level()); - // } - - let line = format!( - "\x1b[35m[{}]\x1b[0m \x1b[34m{}\x1b[0m {}\0", - record.level(), - record.target(), - record.args() - ); - println!("{line}"); - } - - fn flush(&self) {} -} diff --git a/frontends/rioterm-beta/src/main.rs b/frontends/rioterm-beta/src/main.rs deleted file mode 100644 index a981b06df2..0000000000 --- a/frontends/rioterm-beta/src/main.rs +++ /dev/null @@ -1,138 +0,0 @@ -// With the default subsystem, 'console', windows creates an additional console -// window for the program. -// This is silently ignored on non-windows systems. -// See https://msdn.microsoft.com/en-us/library/4cc7ya5b.aspx for more details. -#![windows_subsystem = "windows"] -#![cfg(target_os = "macos")] - -mod cli; -mod ime; -mod logger; -mod messenger; -#[cfg(windows)] -mod panic; -mod platform; -mod renderer; -mod router; -mod scheduler; -mod state; -mod watcher; - -use clap::Parser; -use log::{info, LevelFilter, SetLoggerError}; -use logger::Logger; -use rio_backend::{ansi, crosswords, event, performer, selection}; -use std::str::FromStr; - -#[cfg(windows)] -use windows_sys::Win32::System::Console::{ - AttachConsole, FreeConsole, ATTACH_PARENT_PROCESS, -}; - -pub fn setup_environment_variables(config: &rio_backend::config::Config) { - #[cfg(unix)] - let terminfo = if teletypewriter::terminfo_exists("rio") { - "rio" - } else { - "xterm-256color" - }; - - #[cfg(unix)] - { - info!("[setup_environment_variables] terminfo: {terminfo}"); - std::env::set_var("TERM", terminfo); - } - - // https://github.com/raphamorim/rio/issues/200 - std::env::set_var("TERM_PROGRAM", "rio"); - std::env::set_var("TERM_PROGRAM_VERSION", env!("CARGO_PKG_VERSION")); - - std::env::set_var("COLORTERM", "truecolor"); - std::env::remove_var("DESKTOP_STARTUP_ID"); - #[cfg(target_os = "macos")] - { - platform::macos::set_locale_environment(); - std::env::set_current_dir(dirs::home_dir().unwrap()).unwrap(); - } - - // Set env vars from config. - for env_config in config.env_vars.iter() { - let env_vec: Vec<&str> = env_config.split('=').collect(); - - if env_vec.len() == 2 { - std::env::set_var(env_vec[0], env_vec[1]); - } - } -} - -static LOGGER: Logger = Logger; - -fn setup_logs_by_filter_level(log_level: &str) -> Result<(), SetLoggerError> { - let mut filter_level = LevelFilter::from_str(log_level).unwrap_or(LevelFilter::Off); - - if let Ok(data) = std::env::var("RIO_LOG_LEVEL") { - if !data.is_empty() { - filter_level = LevelFilter::from_str(&data).unwrap_or(filter_level); - } - } - - info!("[setup_logs_by_filter_level] log_level: {log_level}"); - log::set_logger(&LOGGER).map(|()| log::set_max_level(filter_level)) -} - -#[tokio::main] -async fn main() -> Result<(), Box> { - #[cfg(windows)] - panic::attach_handler(); - - // When linked with the windows subsystem windows won't automatically attach - // to the console of the parent process, so we do it explicitly. This fails - // silently if the parent has no console. - #[cfg(windows)] - unsafe { - AttachConsole(ATTACH_PARENT_PROCESS); - } - - // Load command line options. - let args = cli::Cli::parse(); - - let (mut config, config_error) = match rio_backend::config::Config::try_load() { - Ok(config) => (config, None), - Err(err) => (rio_backend::config::Config::default(), Some(err)), - }; - - { - if setup_logs_by_filter_level(&config.developer.log_level).is_err() { - eprintln!("unable to configure log level"); - } - - if let Some(command) = args.window_options.terminal_options.command() { - config.shell = command; - config.use_fork = false; - } - - if let Some(working_dir_cli) = args.window_options.terminal_options.working_dir { - config.working_dir = Some(working_dir_cli); - } - } - - #[cfg(target_os = "linux")] - { - // If running inside a flatpak sandbox. - // Rio will never use use_fork configuration as true - if std::path::PathBuf::from("/.flatpak-info").exists() { - config.use_fork = false; - } - } - - setup_environment_variables(&config); - - let _ = router::run(config, config_error).await; - - #[cfg(windows)] - unsafe { - FreeConsole(); - } - - Ok(()) -} diff --git a/frontends/rioterm-beta/src/panic.rs b/frontends/rioterm-beta/src/panic.rs deleted file mode 100644 index b60144a931..0000000000 --- a/frontends/rioterm-beta/src/panic.rs +++ /dev/null @@ -1,33 +0,0 @@ -// panic.rs was retired originally from https://github.com/alacritty/alacritty/blob/e35e5ad14fce8456afdd89f2b392b9924bb27471/alacritty/src/panic.rs -// which is licensed under Apache 2.0 license. - -use std::ffi::OsStr; -use std::io::Write; -use std::iter::once; -use std::os::windows::ffi::OsStrExt; -use std::{io, panic}; - -use windows_sys::Win32::UI::WindowsAndMessaging::{ - MessageBoxW, MB_ICONERROR, MB_OK, MB_SETFOREGROUND, MB_TASKMODAL, -}; - -pub fn win32_string + ?Sized>(value: &S) -> Vec { - OsStr::new(value).encode_wide().chain(once(0)).collect() -} - -// Install a panic handler that renders the panic in a classical Windows error -// dialog box as well as writes the panic to STDERR. -pub fn attach_handler() { - panic::set_hook(Box::new(|panic_info| { - let _ = writeln!(io::stderr(), "{}", panic_info); - let msg = format!("{}\n\nPress Ctrl-C to Copy", panic_info); - unsafe { - MessageBoxW( - 0isize, - win32_string(&msg).as_ptr(), - win32_string("Rio: Runtime Error").as_ptr(), - MB_ICONERROR | MB_OK | MB_SETFOREGROUND | MB_TASKMODAL, - ); - } - })); -} diff --git a/frontends/rioterm-beta/src/platform/macos/mod.rs b/frontends/rioterm-beta/src/platform/macos/mod.rs deleted file mode 100644 index 3c586c1470..0000000000 --- a/frontends/rioterm-beta/src/platform/macos/mod.rs +++ /dev/null @@ -1,100 +0,0 @@ -// Retired from https://github.com/alacritty/alacritty/blob/6e7f466c68b387f41726757eed4f3e70d05479d2/alacritty/src/macos/locale.rs -// which is licensed under Apache 2.0 license. - -use std::ffi::{CStr, CString}; -use std::os::raw::c_char; -use std::{env, slice, str}; - -use libc::{setlocale, LC_ALL, LC_CTYPE}; -use log::debug; -use objc::runtime::{Class, Object}; -use objc::{msg_send, sel, sel_impl}; - -const FALLBACK_LOCALE: &str = "UTF-8"; - -pub fn set_locale_environment() { - let env_locale_c = CString::new("").unwrap(); - let env_locale_ptr = unsafe { setlocale(LC_ALL, env_locale_c.as_ptr()) }; - if !env_locale_ptr.is_null() { - let env_locale = unsafe { CStr::from_ptr(env_locale_ptr).to_string_lossy() }; - - // Assume `C` locale means unchanged, since it is the default anyways. - if env_locale != "C" { - debug!("Using environment locale: {}", env_locale); - return; - } - } - - let system_locale = system_locale(); - - // Set locale to system locale. - let system_locale_c = - CString::new(system_locale.clone()).expect("nul byte in system locale"); - let lc_all = unsafe { setlocale(LC_ALL, system_locale_c.as_ptr()) }; - - // Check if system locale was valid or not. - if lc_all.is_null() { - // Use fallback locale. - debug!("Using fallback locale: {}", FALLBACK_LOCALE); - - let fallback_locale_c = CString::new(FALLBACK_LOCALE).unwrap(); - unsafe { setlocale(LC_CTYPE, fallback_locale_c.as_ptr()) }; - - env::set_var("LC_CTYPE", FALLBACK_LOCALE); - } else { - // Use system locale. - debug!("Using system locale: {}", system_locale); - - env::set_var("LC_ALL", system_locale); - } -} - -/// Determine system locale based on language and country code. -fn system_locale() -> String { - unsafe { - let locale_class = Class::get("NSLocale").unwrap(); - let locale: *const Object = msg_send![locale_class, currentLocale]; - let _: () = msg_send![locale_class, release]; - - // `localeIdentifier` returns extra metadata with the locale (including currency and - // collator) on newer versions of macOS. This is not a valid locale, so we use - // `languageCode` and `countryCode`, if they're available (macOS 10.12+): - // - // https://developer.apple.com/documentation/foundation/nslocale/1416263-localeidentifier?language=objc - // https://developer.apple.com/documentation/foundation/nslocale/1643060-countrycode?language=objc - // https://developer.apple.com/documentation/foundation/nslocale/1643026-languagecode?language=objc - let is_language_code_supported: bool = - msg_send![locale, respondsToSelector: sel!(languageCode)]; - let is_country_code_supported: bool = - msg_send![locale, respondsToSelector: sel!(countryCode)]; - let locale_id = if is_language_code_supported && is_country_code_supported { - let language_code: *const Object = msg_send![locale, languageCode]; - let language_code_str = nsstring_as_str(language_code).to_owned(); - let _: () = msg_send![language_code, release]; - - let country_code: *const Object = msg_send![locale, countryCode]; - let country_code_str = nsstring_as_str(country_code).to_owned(); - let _: () = msg_send![country_code, release]; - - format!("{}_{}.UTF-8", &language_code_str, &country_code_str) - } else { - let identifier: *const Object = msg_send![locale, localeIdentifier]; - let identifier_str = nsstring_as_str(identifier).to_owned(); - let _: () = msg_send![identifier, release]; - - identifier_str + ".UTF-8" - }; - - let _: () = msg_send![locale, release]; - - locale_id - } -} - -const UTF8_ENCODING: usize = 4; - -unsafe fn nsstring_as_str<'a>(nsstring: *const Object) -> &'a str { - let cstr: *const c_char = msg_send![nsstring, UTF8String]; - let len: usize = msg_send![nsstring, lengthOfBytesUsingEncoding: UTF8_ENCODING]; - str::from_utf8(slice::from_raw_parts(cstr as *const u8, len)).unwrap() -} diff --git a/frontends/rioterm-beta/src/platform/mod.rs b/frontends/rioterm-beta/src/platform/mod.rs deleted file mode 100644 index 221a0d47a4..0000000000 --- a/frontends/rioterm-beta/src/platform/mod.rs +++ /dev/null @@ -1,42 +0,0 @@ -use std::fs::File; -use std::io::Write; - -#[cfg(target_os = "macos")] -pub mod macos; - -pub fn create_config_file() { - let default_file_path = rio_backend::config::config_file_path(); - if default_file_path.exists() { - return; - } - - let default_dir_path = rio_backend::config::config_dir_path(); - match std::fs::create_dir_all(&default_dir_path) { - Ok(_) => { - log::info!("configuration path created {}", default_dir_path.display()); - } - Err(err_message) => { - log::error!("could not create config directory: {err_message}"); - } - } - - match File::create(&default_file_path) { - Err(err_message) => { - log::error!( - "could not create config file {}: {err_message}", - default_file_path.display() - ) - } - Ok(mut created_file) => { - log::info!("configuration file created {}", default_file_path.display()); - - if let Err(err_message) = writeln!( - created_file, - "{}", - rio_backend::config::config_file_content() - ) { - log::error!("could not update config file with defaults: {err_message}") - } - } - } -} diff --git a/frontends/rioterm-beta/src/router/constants.rs b/frontends/rioterm-beta/src/router/constants.rs deleted file mode 100644 index 787cda640a..0000000000 --- a/frontends/rioterm-beta/src/router/constants.rs +++ /dev/null @@ -1,33 +0,0 @@ -#![allow(unused)] - -#[cfg(not(any(target_os = "macos")))] -pub const PADDING_Y: f32 = 2.0; - -#[cfg(not(any(target_os = "macos")))] -pub const PADDING_Y_WITH_TAB_ON_TOP: f32 = 15.0; - -#[cfg(target_os = "macos")] -pub const PADDING_Y: f32 = 15.; - -#[cfg(not(any(target_os = "macos")))] -pub const INACTIVE_TAB_WIDTH_SIZE: f32 = 4.; - -#[cfg(target_os = "macos")] -pub const INACTIVE_TAB_WIDTH_SIZE: f32 = 16.; - -#[cfg(not(any(target_os = "macos")))] -pub const ACTIVE_TAB_WIDTH_SIZE: f32 = 8.; - -#[cfg(target_os = "macos")] -pub const ACTIVE_TAB_WIDTH_SIZE: f32 = 26.; - -#[cfg(target_os = "macos")] -pub const DEADZONE_START_Y: f64 = 30.; - -#[cfg(target_os = "macos")] -pub const DEADZONE_END_Y: f64 = -2.0; - -#[cfg(target_os = "macos")] -pub const DEADZONE_START_X: f64 = 80.; - -pub const PADDING_X_COLLAPSED_TABS: f32 = 30.; diff --git a/frontends/rioterm-beta/src/router/mouse.rs b/frontends/rioterm-beta/src/router/mouse.rs deleted file mode 100644 index 5e85da65e1..0000000000 --- a/frontends/rioterm-beta/src/router/mouse.rs +++ /dev/null @@ -1,573 +0,0 @@ -use crate::crosswords::pos::Column; -use crate::crosswords::pos::Line; -use crate::crosswords::pos::Side; -use crate::event::ClickState; -use rio_backend::crosswords::pos::Pos; -use std::time::Instant; -use wa::MouseButton; - -#[derive(Default, Debug)] -pub struct AccumulatedScroll { - /// Scroll we should perform along `x` axis. - pub x: f64, - - /// Scroll we should perform along `y` axis. - pub y: f64, -} - -#[derive(Debug)] -pub struct Mouse { - pub multiplier: f64, - pub divider: f64, - pub left_button_state: bool, - pub middle_button_state: bool, - pub right_button_state: bool, - pub last_click_timestamp: Instant, - pub last_click_button: MouseButton, - pub click_state: ClickState, - pub accumulated_scroll: AccumulatedScroll, - pub square_side: Side, - pub lines_scrolled: f32, - pub inside_text_area: bool, - pub x: usize, - pub y: usize, -} - -impl Default for Mouse { - fn default() -> Mouse { - Mouse { - multiplier: 3.0, - divider: 1.0, - last_click_timestamp: Instant::now(), - last_click_button: MouseButton::Left, - left_button_state: false, - middle_button_state: false, - right_button_state: false, - click_state: ClickState::None, - square_side: Side::Left, - inside_text_area: Default::default(), - lines_scrolled: Default::default(), - accumulated_scroll: AccumulatedScroll::default(), - x: Default::default(), - y: Default::default(), - } - } -} - -impl Mouse { - pub fn new(multiplier: f64, divider: f64) -> Self { - Self { - multiplier, - divider, - ..Default::default() - } - } - - #[inline] - pub fn set_multiplier_and_divider(&mut self, multiplier: f64, divider: f64) { - self.multiplier = multiplier; - self.divider = divider; - } -} - -#[inline] -pub fn calculate_mouse_position( - mouse: &Mouse, - display_offset: usize, - scale_factor: f32, - config_columns_rows: (usize, usize), - margin_x_left: f32, - margin_y_top: f32, - cell_dimension: (f32, f32), -) -> Pos { - let cell_width = cell_dimension.0.round() as usize; - let cell_height = cell_dimension.1.round() as usize; - let scaled_margin_x = (margin_x_left * scale_factor).round() as usize; - - let col: Column = if (scaled_margin_x + cell_width) > mouse.x { - Column(0) - } else { - let col = (mouse.x - scaled_margin_x) / cell_width; - std::cmp::min(Column(col), Column(config_columns_rows.0 - 1)) - }; - - // TODO: Refactor row position - let row = mouse - .y - .saturating_sub((margin_y_top * 2. * scale_factor) as usize) - / cell_height; - let calc_row = std::cmp::min(row, config_columns_rows.1 - 1); - let row = Line(calc_row as i32) - (display_offset); - - Pos::new(row, col) -} - -#[cfg(test)] -pub mod test { - use super::*; - - #[test] - fn test_pos_calc_moving_mouse_x_with_scale_1() { - let display_offset = 0; - let scale_factor = 1.0; - let columns = 10; - let lines = 5; - let margin_x_left = 0.0; - let margin_y_top = 0.0; - let cell_dimension_width = 9.4; - let cell_dimension_height = 18.0; - - let mouse = Mouse { - x: 8, - ..Default::default() - }; - let pos = calculate_mouse_position( - &mouse, - display_offset, - scale_factor, - (columns, lines), - margin_x_left, - margin_y_top, - (cell_dimension_width, cell_dimension_height), - ); - assert_eq!(pos, Pos::new(Line(0), Column(0))); - - let mouse = Mouse { - x: 8, - ..Default::default() - }; - let pos = calculate_mouse_position( - &mouse, - display_offset, - scale_factor, - (columns, lines), - margin_x_left, - margin_y_top, - (cell_dimension_width, cell_dimension_height), - ); - assert_eq!(pos, Pos::new(Line(0), Column(0))); - - let mouse = Mouse { - x: 9, - ..Default::default() - }; - let pos = calculate_mouse_position( - &mouse, - display_offset, - scale_factor, - (columns, lines), - margin_x_left, - margin_y_top, - (cell_dimension_width, cell_dimension_height), - ); - assert_eq!(pos, Pos::new(Line(0), Column(1))); - - let mouse = Mouse { - x: 17, - ..Default::default() - }; - let pos = calculate_mouse_position( - &mouse, - display_offset, - scale_factor, - (columns, lines), - margin_x_left, - margin_y_top, - (cell_dimension_width, cell_dimension_height), - ); - assert_eq!(pos, Pos::new(Line(0), Column(1))); - - let mouse = Mouse { - x: 19, - ..Default::default() - }; - let pos = calculate_mouse_position( - &mouse, - display_offset, - scale_factor, - (columns, lines), - margin_x_left, - margin_y_top, - (cell_dimension_width, cell_dimension_height), - ); - assert_eq!(pos, Pos::new(Line(0), Column(2))); - } - - #[test] - fn test_pos_calc_moving_mouse_x_with_scale_2() { - let display_offset = 0; - let scale_factor = 2.0; - let columns = 10; - let lines = 5; - let margin_x_left = 0.0; - let margin_y_top = 0.0; - let cell_dimension_width = 9.4; - let cell_dimension_height = 18.0; - - let mouse = Mouse { - x: 8, - ..Default::default() - }; - let pos = calculate_mouse_position( - &mouse, - display_offset, - scale_factor, - (columns, lines), - margin_x_left, - margin_y_top, - (cell_dimension_width, cell_dimension_height), - ); - assert_eq!(pos, Pos::new(Line(0), Column(0))); - - let mouse = Mouse { - x: 8, - ..Default::default() - }; - let pos = calculate_mouse_position( - &mouse, - display_offset, - scale_factor, - (columns, lines), - margin_x_left, - margin_y_top, - (cell_dimension_width, cell_dimension_height), - ); - assert_eq!(pos, Pos::new(Line(0), Column(0))); - - let mouse = Mouse { - x: 9, - ..Default::default() - }; - let pos = calculate_mouse_position( - &mouse, - display_offset, - scale_factor, - (columns, lines), - margin_x_left, - margin_y_top, - (cell_dimension_width, cell_dimension_height), - ); - assert_eq!(pos, Pos::new(Line(0), Column(1))); - - let mouse = Mouse { - x: 17, - ..Default::default() - }; - let pos = calculate_mouse_position( - &mouse, - display_offset, - scale_factor, - (columns, lines), - margin_x_left, - margin_y_top, - (cell_dimension_width, cell_dimension_height), - ); - assert_eq!(pos, Pos::new(Line(0), Column(1))); - - let mouse = Mouse { - x: 19, - ..Default::default() - }; - let pos = calculate_mouse_position( - &mouse, - display_offset, - scale_factor, - (columns, lines), - margin_x_left, - margin_y_top, - (cell_dimension_width, cell_dimension_height), - ); - assert_eq!(pos, Pos::new(Line(0), Column(2))); - } - - #[test] - fn test_pos_calc_moving_mouse_x_with_scale_1_with_margin_10() { - let display_offset = 0; - let scale_factor = 1.0; - let columns = 10; - let lines = 5; - let margin_x_left = 10.0; - let margin_y_top = 0.0; - let cell_dimension_width = 9.4; - let cell_dimension_height = 18.0; - - let mouse = Mouse { - x: 8, - ..Default::default() - }; - let pos = calculate_mouse_position( - &mouse, - display_offset, - scale_factor, - (columns, lines), - margin_x_left, - margin_y_top, - (cell_dimension_width, cell_dimension_height), - ); - assert_eq!(pos, Pos::new(Line(0), Column(0))); - - let mouse = Mouse { - x: 9, - ..Default::default() - }; - let pos = calculate_mouse_position( - &mouse, - display_offset, - scale_factor, - (columns, lines), - margin_x_left, - margin_y_top, - (cell_dimension_width, cell_dimension_height), - ); - assert_eq!(pos, Pos::new(Line(0), Column(0))); - - let mouse = Mouse { - x: 18, - ..Default::default() - }; - let pos = calculate_mouse_position( - &mouse, - display_offset, - scale_factor, - (columns, lines), - margin_x_left, - margin_y_top, - (cell_dimension_width, cell_dimension_height), - ); - assert_eq!(pos, Pos::new(Line(0), Column(0))); - - let mouse = Mouse { - x: 19, - ..Default::default() - }; - let pos = calculate_mouse_position( - &mouse, - display_offset, - scale_factor, - (columns, lines), - margin_x_left, - margin_y_top, - (cell_dimension_width, cell_dimension_height), - ); - assert_eq!(pos, Pos::new(Line(0), Column(1))); - - let mouse = Mouse { - x: 27, - ..Default::default() - }; - let pos = calculate_mouse_position( - &mouse, - display_offset, - scale_factor, - (columns, lines), - margin_x_left, - margin_y_top, - (cell_dimension_width, cell_dimension_height), - ); - assert_eq!(pos, Pos::new(Line(0), Column(1))); - - let mouse = Mouse { - x: 28, - ..Default::default() - }; - let pos = calculate_mouse_position( - &mouse, - display_offset, - scale_factor, - (columns, lines), - margin_x_left, - margin_y_top, - (cell_dimension_width, cell_dimension_height), - ); - assert_eq!(pos, Pos::new(Line(0), Column(2))); - - let mouse = Mouse { - x: 36, - ..Default::default() - }; - let pos = calculate_mouse_position( - &mouse, - display_offset, - scale_factor, - (columns, lines), - margin_x_left, - margin_y_top, - (cell_dimension_width, cell_dimension_height), - ); - assert_eq!(pos, Pos::new(Line(0), Column(2))); - - let mouse = Mouse { - x: 37, - ..Default::default() - }; - let pos = calculate_mouse_position( - &mouse, - display_offset, - scale_factor, - (columns, lines), - margin_x_left, - margin_y_top, - (cell_dimension_width, cell_dimension_height), - ); - assert_eq!(pos, Pos::new(Line(0), Column(3))); - } - - #[test] - fn test_pos_calc_moving_mouse_x_with_scale_2_with_margin_10() { - let display_offset = 0; - let scale_factor = 2.0; - let columns = 10; - let lines = 5; - let margin_x_left = 10.0; - let margin_y_top = 0.0; - let cell_dimension_width = 9.4; - let cell_dimension_height = 18.0; - - let mouse = Mouse { - x: 9, - ..Default::default() - }; - let pos = calculate_mouse_position( - &mouse, - display_offset, - scale_factor, - (columns, lines), - margin_x_left, - margin_y_top, - (cell_dimension_width, cell_dimension_height), - ); - assert_eq!(pos, Pos::new(Line(0), Column(0))); - - let mouse = Mouse { - x: 19, - ..Default::default() - }; - let pos = calculate_mouse_position( - &mouse, - display_offset, - scale_factor, - (columns, lines), - margin_x_left, - margin_y_top, - (cell_dimension_width, cell_dimension_height), - ); - assert_eq!(pos, Pos::new(Line(0), Column(0))); - - let mouse = Mouse { - x: 28, - ..Default::default() - }; - let pos = calculate_mouse_position( - &mouse, - display_offset, - scale_factor, - (columns, lines), - margin_x_left, - margin_y_top, - (cell_dimension_width, cell_dimension_height), - ); - assert_eq!(pos, Pos::new(Line(0), Column(0))); - - let mouse = Mouse { - x: 29, - ..Default::default() - }; - let pos = calculate_mouse_position( - &mouse, - display_offset, - scale_factor, - (columns, lines), - margin_x_left, - margin_y_top, - (cell_dimension_width, cell_dimension_height), - ); - assert_eq!(pos, Pos::new(Line(0), Column(1))); - } - - #[test] - fn test_pos_calc_font_size_12_6_moving_mouse_x_with_scale_1_with_margin_10() { - let display_offset = 0; - let scale_factor = 1.0; - let columns = 10; - let lines = 5; - let margin_x_left = 10.0; - let margin_y_top = 0.0; - let cell_dimension_width = 12.6; - let cell_dimension_height = 18.0; - - let mouse = Mouse { - x: 10, - ..Default::default() - }; - let pos = calculate_mouse_position( - &mouse, - display_offset, - scale_factor, - (columns, lines), - margin_x_left, - margin_y_top, - (cell_dimension_width, cell_dimension_height), - ); - assert_eq!(pos, Pos::new(Line(0), Column(0))); - - let mouse = Mouse { - x: 22, - ..Default::default() - }; - let pos = calculate_mouse_position( - &mouse, - display_offset, - scale_factor, - (columns, lines), - margin_x_left, - margin_y_top, - (cell_dimension_width, cell_dimension_height), - ); - assert_eq!(pos, Pos::new(Line(0), Column(0))); - - let mouse = Mouse { - x: 23, - ..Default::default() - }; - let pos = calculate_mouse_position( - &mouse, - display_offset, - scale_factor, - (columns, lines), - margin_x_left, - margin_y_top, - (cell_dimension_width, cell_dimension_height), - ); - assert_eq!(pos, Pos::new(Line(0), Column(1))); - - let mouse = Mouse { - x: 35, - ..Default::default() - }; - let pos = calculate_mouse_position( - &mouse, - display_offset, - scale_factor, - (columns, lines), - margin_x_left, - margin_y_top, - (cell_dimension_width, cell_dimension_height), - ); - assert_eq!(pos, Pos::new(Line(0), Column(1))); - - let mouse = Mouse { - x: 36, - ..Default::default() - }; - let pos = calculate_mouse_position( - &mouse, - display_offset, - scale_factor, - (columns, lines), - margin_x_left, - margin_y_top, - (cell_dimension_width, cell_dimension_height), - ); - assert_eq!(pos, Pos::new(Line(0), Column(2))); - } -} diff --git a/frontends/rioterm-beta/src/scheduler.rs b/frontends/rioterm-beta/src/scheduler.rs deleted file mode 100644 index 50e288c885..0000000000 --- a/frontends/rioterm-beta/src/scheduler.rs +++ /dev/null @@ -1,124 +0,0 @@ -// // scheduler.rs was retired originally from https://github.com/alacritty/alacritty/blob/e35e5ad14fce8456afdd89f2b392b9924bb27471/alacritty/src/scheduler.rs -// // which is licensed under Apache 2.0 license. - -// use crate::event::EventPayload; -// use rio_backend::event::EventListener; -// use rio_backend::superloop::Superloop; -// use std::collections::VecDeque; -// use std::time::{Duration, Instant}; - -// /// ID uniquely identifying a timer. -// #[derive(Copy, Clone, Debug, PartialEq, Eq)] -// pub struct TimerId { -// topic: Topic, -// id: u16, -// } - -// impl TimerId { -// pub fn new(topic: Topic, id: u16) -> Self { -// Self { topic, id } -// } -// } - -// /// Available timer topics. -// #[derive(Copy, Clone, Debug, PartialEq, Eq)] -// pub enum Topic { -// Render, -// } - -// /// Event scheduled to be emitted at a specific time. -// #[derive(Debug)] -// pub struct Timer { -// pub deadline: Instant, -// pub event: EventPayload, -// pub id: TimerId, - -// #[allow(unused)] -// interval: Option, -// } - -// /// Scheduler tracking all pending timers. -// pub struct Scheduler { -// timers: VecDeque, -// event_proxy: Superloop, -// } - -// impl Scheduler { -// pub fn new(event_proxy: Superloop) -> Self { -// Self { -// timers: VecDeque::new(), -// event_proxy, -// } -// } - -// /// Process all pending timers. -// /// -// /// If there are still timers pending after all ready events have been processed, the closest -// /// pending deadline will be returned. -// pub fn update(&mut self) -> Option { -// let now = Instant::now(); - -// while !self.timers.is_empty() && self.timers[0].deadline <= now { -// if let Some(timer) = self.timers.pop_front() { -// // Automatically repeat the event. -// if let Some(interval) = timer.interval { -// self.schedule(timer.event.clone(), interval, true, timer.id); -// } -// self.event_proxy.send_event(timer.event.payload, timer.id.id); -// } -// } - -// self.timers.get(0).map(|timer| timer.deadline) -// } - -// /// Schedule a new event. -// pub fn schedule( -// &mut self, -// event: EventPayload, -// interval: Duration, -// repeat: bool, -// timer_id: TimerId, -// ) { -// let deadline = Instant::now() + interval; - -// // Get insert position in the schedule. -// let index = self -// .timers -// .iter() -// .position(|timer| timer.deadline > deadline) -// .unwrap_or(self.timers.len()); - -// // Set the automatic event repeat rate. -// let interval = if repeat { Some(interval) } else { None }; - -// self.timers.insert( -// index, -// Timer { -// interval, -// deadline, -// event, -// id: timer_id, -// }, -// ); -// } - -// /// Cancel a scheduled event. -// pub fn unschedule(&mut self, id: TimerId) -> Option { -// let index = self.timers.iter().position(|timer| timer.id == id)?; -// self.timers.remove(index) -// } - -// /// Check if a timer is already scheduled. -// pub fn scheduled(&mut self, id: TimerId) -> bool { -// self.timers.iter().any(|timer| timer.id == id) -// } - -// /// Remove all timers scheduled for a tab. -// /// -// /// This must be called when a tab is removed to ensure that timers on intervals do not -// /// stick around forever and cause a memory leak. -// #[allow(dead_code)] -// pub fn unschedule_tab(&mut self, id: u16) { -// self.timers.retain(|timer| timer.id.id != id); -// } -// } diff --git a/frontends/rioterm-beta/src/watcher.rs b/frontends/rioterm-beta/src/watcher.rs deleted file mode 100644 index 8cf63bd6f0..0000000000 --- a/frontends/rioterm-beta/src/watcher.rs +++ /dev/null @@ -1,50 +0,0 @@ -use crate::event::RioEvent; -use notify::{Config, EventKind, RecommendedWatcher, RecursiveMode, Watcher}; -use rio_backend::event::EventListener; -use rio_backend::superloop::Superloop; -use std::time::Duration; - -const CONFIG_POLLING_TIMEOUT: Duration = Duration::from_secs(2); - -pub fn configuration_file_updates(event_proxy: Superloop) -> notify::Result<()> { - let path = rio_backend::config::config_dir_path(); - let (tx, rx) = std::sync::mpsc::channel(); - - // Automatically select the best implementation for your platform. - // You can also access each implementation directly e.g. INotifyWatcher. - let mut watcher = RecommendedWatcher::new( - tx, - Config::default().with_poll_interval(CONFIG_POLLING_TIMEOUT), - )?; - - tokio::spawn(async move { - // Add a path to be watched. All files and directories at that path and - // below will be monitored for changes. - if let Err(err_message) = - watcher.watch(path.as_ref(), RecursiveMode::NonRecursive) - { - log::warn!("unable to watch config directory {err_message:?}"); - }; - - for res in rx { - match res { - Ok(event) => match event.kind { - EventKind::Any - | EventKind::Create(_) - | EventKind::Modify(_) - | EventKind::Other => { - log::info!("config directory has dispatched an event {event:?}"); - // TODO: Refactor to send_global_event - event_proxy.send_event(RioEvent::UpdateConfig, 0); - } - _ => (), - }, - Err(err_message) => { - log::error!("unable to watch config directory: {err_message:?}") - } - } - } - }); - - Ok(()) -} diff --git a/frontends/rioterm/Cargo.toml b/frontends/rioterm/Cargo.toml index 2d9e05b0d6..ceba07dc86 100644 --- a/frontends/rioterm/Cargo.toml +++ b/frontends/rioterm/Cargo.toml @@ -16,9 +16,9 @@ name = "rio" path = "src/main.rs" [dependencies] -winit = { workspace = true } log = { workspace = true } -rio-backend = { workspace = true, features = ["winit"] } +wgpu = { workspace = true } +rio-backend = { workspace = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] bitflags = "2.4.1" @@ -37,13 +37,14 @@ teletypewriter = { workspace = true } tokio = { workspace = true } unicode-width = { workspace = true } copa = { workspace = true } -wgpu = { workspace = true } + +[target.'cfg(not(target_os = "macos"))'.dependencies] +winit = { workspace = true } [target.'cfg(target_os = "macos")'.dependencies] -objc = "0.2.2" -cocoa = "0.25.0" -objc2 = { version = "=0.3.0-beta.0" } -objc2-foundation = { version = "=0.2.0-alpha.5" } +objc = "0.2.7" +smol_str = "0.2.0" +wa = { workspace = true } [target.'cfg(windows)'.dependencies] ahash = { version = "0.8.2", default-features = false, features = ["std"] } diff --git a/frontends/rioterm-beta/src/router/menu/macos.rs b/frontends/rioterm/src/app/menu/macos.rs similarity index 100% rename from frontends/rioterm-beta/src/router/menu/macos.rs rename to frontends/rioterm/src/app/menu/macos.rs diff --git a/frontends/rioterm-beta/src/router/menu/mod.rs b/frontends/rioterm/src/app/menu/mod.rs similarity index 100% rename from frontends/rioterm-beta/src/router/menu/mod.rs rename to frontends/rioterm/src/app/menu/mod.rs diff --git a/frontends/rioterm-beta/src/router/mod.rs b/frontends/rioterm/src/app/mod.rs similarity index 96% rename from frontends/rioterm-beta/src/router/mod.rs rename to frontends/rioterm/src/app/mod.rs index 23a04e59f5..be97d490c3 100644 --- a/frontends/rioterm-beta/src/router/mod.rs +++ b/frontends/rioterm/src/app/mod.rs @@ -1,14 +1,10 @@ -pub mod bindings; -pub mod constants; mod menu; -pub mod mouse; mod route; -mod routes; use crate::event::RioEvent; -use crate::ime::{Ime, Preedit}; +use crate::ime::Preedit; +use crate::routes::RoutePath; // use crate::scheduler::{Scheduler, TimerId, Topic}; -use crate::watcher; use rio_backend::error::RioError; use rio_backend::event::EventListener; use rio_backend::sugarloaf::font::loader; @@ -178,7 +174,7 @@ impl EventHandler for Router { RioEvent::MouseCursorDirty => { if let Some(current) = &mut self.route { current.mouse.accumulated_scroll = - mouse::AccumulatedScroll::default(); + crate::mouse::AccumulatedScroll::default(); } } RioEvent::Scroll(scroll) => { @@ -286,7 +282,7 @@ impl EventHandler for Router { fn ime_event(&mut self, ime_state: ImeState) { if let Some(current) = &mut self.route { - if current.path != routes::RoutePath::Terminal { + if current.path != RoutePath::Terminal { return; } @@ -352,7 +348,7 @@ impl EventHandler for Router { } fn mouse_motion_event(&mut self, x: f32, y: f32) { if let Some(current) = &mut self.route { - if current.path != routes::RoutePath::Terminal { + if current.path != RoutePath::Terminal { return; } @@ -396,7 +392,7 @@ impl EventHandler for Router { } fn mouse_wheel_event(&mut self, mut x: f32, mut y: f32) { if let Some(current) = &mut self.route { - if current.path != routes::RoutePath::Terminal { + if current.path != RoutePath::Terminal { return; } @@ -430,7 +426,7 @@ impl EventHandler for Router { } fn mouse_button_down_event(&mut self, button: MouseButton, x: f32, y: f32) { if let Some(current) = &mut self.route { - if current.path != routes::RoutePath::Terminal { + if current.path != RoutePath::Terminal { return; } @@ -443,7 +439,7 @@ impl EventHandler for Router { } fn mouse_button_up_event(&mut self, button: MouseButton, x: f32, y: f32) { if let Some(current) = &mut self.route { - if current.path != routes::RoutePath::Terminal { + if current.path != RoutePath::Terminal { return; } @@ -497,7 +493,7 @@ impl EventHandler for Router { } if let Some(current) = &mut self.route { - if current.path != routes::RoutePath::Terminal { + if current.path != RoutePath::Terminal { return; } @@ -551,9 +547,9 @@ pub async fn run( config: rio_backend::config::Config, _config_error: Option, ) -> Result<(), Box> { - let superloop = Superloop::new(); + // let superloop = Superloop::new(); let app_loop = Looper::new(config); - let _ = watcher::configuration_file_updates(superloop.clone()); + // let _ = crate::watcher::configuration_file_updates(superloop.clone()); // let scheduler = Scheduler::new(superloop.clone()); diff --git a/frontends/rioterm-beta/src/router/route.rs b/frontends/rioterm/src/app/route.rs similarity index 99% rename from frontends/rioterm-beta/src/router/route.rs rename to frontends/rioterm/src/app/route.rs index e5063b7eb4..397fbfc62b 100644 --- a/frontends/rioterm-beta/src/router/route.rs +++ b/frontends/rioterm/src/app/route.rs @@ -8,23 +8,19 @@ // // A router is a window, but it can contain a sub route that is a panel // For example /:window-id/:panel-one - -use crate::crosswords::{grid::Scroll, vi_mode::ViMotion, Mode}; -use crate::renderer::{padding_bottom_from_config, padding_top_from_config}; -use crate::router::bindings::{ +use crate::bindings::{ self, Action as Act, BindingKey, BindingMode, KeyBindings, MouseBinding, ViAction, }; -use crate::router::loader; -use crate::router::mouse::{calculate_mouse_position, Mouse}; -use crate::router::routes::{ +use crate::context::{self, ContextManager}; +use crate::crosswords::{grid::Scroll, vi_mode::ViMotion, Mode}; +use crate::ime::Ime; +use crate::mouse::{calculate_mouse_position, Mouse}; +use crate::renderer::{padding_bottom_from_config, padding_top_from_config}; +use crate::routes::{ assistant::{self, Assistant}, welcome, RoutePath, }; -use crate::router::{constants, Ime, RioEvent, Superloop}; -use crate::state::{ - context::{self, ContextManager}, - State, -}; +use crate::state::State; use rio_backend::clipboard::{Clipboard, ClipboardType}; use rio_backend::config::renderer::{ Backend as RendererBackend, Performance as RendererPerformance, @@ -34,11 +30,14 @@ use rio_backend::crosswords::{ }; use rio_backend::error::{RioError, RioErrorType}; use rio_backend::event::EventListener; +use rio_backend::event::RioEvent; use rio_backend::selection::SelectionType; +use rio_backend::sugarloaf::font::loader; use rio_backend::sugarloaf::{ layout::SugarloafLayout, Sugarloaf, SugarloafErrors, SugarloafRenderer, SugarloafWindow, SugarloafWindowSize, }; +use rio_backend::superloop::Superloop; use std::borrow::Cow; use std::ffi::OsStr; use std::fmt::Debug; @@ -552,8 +551,9 @@ impl Route { let step = (SELECTION_SCROLLING_STEP * scale_factor) as f64; // Compute the height of the scrolling areas. - let end_top = std::cmp::min(min_height, constants::PADDING_Y as i32) as f64; - let text_area_bottom = (constants::PADDING_Y + let end_top = + std::cmp::min(min_height, crate::constants::PADDING_Y as i32) as f64; + let text_area_bottom = (crate::constants::PADDING_Y + self.sugarloaf.layout.lines as f32) * self.sugarloaf.layout.font_size; let start_bottom = std::cmp::min( diff --git a/frontends/rioterm-beta/src/router/bindings/kitty_keyboard_protocol.rs b/frontends/rioterm/src/bindings/bindings_wa/kitty_keyboard_protocol.rs similarity index 100% rename from frontends/rioterm-beta/src/router/bindings/kitty_keyboard_protocol.rs rename to frontends/rioterm/src/bindings/bindings_wa/kitty_keyboard_protocol.rs diff --git a/frontends/rioterm-beta/src/router/bindings/mod.rs b/frontends/rioterm/src/bindings/bindings_wa/mod.rs similarity index 100% rename from frontends/rioterm-beta/src/router/bindings/mod.rs rename to frontends/rioterm/src/bindings/bindings_wa/mod.rs diff --git a/frontends/rioterm/src/screen/bindings/kitty_keyboard_protocol.rs b/frontends/rioterm/src/bindings/bindings_winit/kitty_keyboard_protocol.rs similarity index 99% rename from frontends/rioterm/src/screen/bindings/kitty_keyboard_protocol.rs rename to frontends/rioterm/src/bindings/bindings_winit/kitty_keyboard_protocol.rs index 5480f4a8d0..a1e3ff2ce1 100644 --- a/frontends/rioterm/src/screen/bindings/kitty_keyboard_protocol.rs +++ b/frontends/rioterm/src/bindings/bindings_winit/kitty_keyboard_protocol.rs @@ -1,3 +1,6 @@ +// build_key_sequence was originally taken from alacritty +// which is licensed under Apache 2.0 license. + use crate::screen::ElementState; use crate::screen::Mode; use crate::screen::ModifiersState; diff --git a/frontends/rioterm/src/screen/bindings/mod.rs b/frontends/rioterm/src/bindings/bindings_winit/mod.rs similarity index 100% rename from frontends/rioterm/src/screen/bindings/mod.rs rename to frontends/rioterm/src/bindings/bindings_winit/mod.rs diff --git a/frontends/rioterm/src/bindings/mod.rs b/frontends/rioterm/src/bindings/mod.rs new file mode 100644 index 0000000000..a7fc8292a7 --- /dev/null +++ b/frontends/rioterm/src/bindings/mod.rs @@ -0,0 +1,11 @@ +#[cfg(target_os = "macos")] +pub mod bindings_wa; + +#[cfg(target_os = "macos")] +pub use bindings_wa::*; + +#[cfg(not(target_os = "macos"))] +pub mod bindings_winit; + +#[cfg(not(target_os = "macos"))] +pub use bindings_winit::*; diff --git a/frontends/rioterm/src/screen/constants.rs b/frontends/rioterm/src/constants.rs similarity index 72% rename from frontends/rioterm/src/screen/constants.rs rename to frontends/rioterm/src/constants.rs index 41b62ad24b..30e648b603 100644 --- a/frontends/rioterm/src/screen/constants.rs +++ b/frontends/rioterm/src/constants.rs @@ -19,13 +19,13 @@ pub const ACTIVE_TAB_WIDTH_SIZE: f32 = 8.; #[cfg(target_os = "macos")] pub const ACTIVE_TAB_WIDTH_SIZE: f32 = 26.; -#[cfg(target_os = "macos")] -pub const DEADZONE_START_Y: f64 = 30.; +// #[cfg(target_os = "macos")] +// pub const DEADZONE_START_Y: f64 = 30.; -#[cfg(target_os = "macos")] -pub const DEADZONE_END_Y: f64 = -2.0; +// #[cfg(target_os = "macos")] +// pub const DEADZONE_END_Y: f64 = -2.0; -#[cfg(target_os = "macos")] -pub const DEADZONE_START_X: f64 = 80.; +// #[cfg(target_os = "macos")] +// pub const DEADZONE_START_X: f64 = 80.; pub const PADDING_X_COLLAPSED_TABS: f32 = 30.; diff --git a/frontends/rioterm-beta/src/state/context.rs b/frontends/rioterm/src/context.rs similarity index 98% rename from frontends/rioterm-beta/src/state/context.rs rename to frontends/rioterm/src/context.rs index c578d6aa4d..ecb8ff42da 100644 --- a/frontends/rioterm-beta/src/state/context.rs +++ b/frontends/rioterm/src/context.rs @@ -673,13 +673,11 @@ pub mod test { #[test] fn test_capacity() { let context_manager = - ContextManager::start_with_capacity(5, Superloop::new(), 0_u16.into()) - .unwrap(); + ContextManager::start_with_capacity(5, Superloop::new(), 0).unwrap(); assert_eq!(context_manager.capacity, 5); let mut context_manager = - ContextManager::start_with_capacity(5, Superloop::new(), 0_u16.into()) - .unwrap(); + ContextManager::start_with_capacity(5, Superloop::new(), 0).unwrap(); context_manager.increase_capacity(3); assert_eq!(context_manager.capacity, 8); } @@ -687,8 +685,7 @@ pub mod test { #[test] fn test_add_context() { let mut context_manager = - ContextManager::start_with_capacity(5, Superloop::new(), 0_u16.into()) - .unwrap(); + ContextManager::start_with_capacity(5, Superloop::new(), 0).unwrap(); assert_eq!(context_manager.capacity, 5); assert_eq!(context_manager.current_index, 0); @@ -714,8 +711,7 @@ pub mod test { #[test] fn test_add_context_start_with_capacity_limit() { let mut context_manager = - ContextManager::start_with_capacity(3, Superloop::new(), 0_u16.into()) - .unwrap(); + ContextManager::start_with_capacity(3, Superloop::new(), 0).unwrap(); assert_eq!(context_manager.capacity, 3); assert_eq!(context_manager.current_index, 0); let should_redirect = false; @@ -747,8 +743,7 @@ pub mod test { #[test] fn test_set_current() { let mut context_manager = - ContextManager::start_with_capacity(8, Superloop::new(), 0_u16.into()) - .unwrap(); + ContextManager::start_with_capacity(8, Superloop::new(), 0).unwrap(); let should_redirect = true; context_manager.add_context( @@ -783,8 +778,7 @@ pub mod test { #[test] fn test_close_context() { let mut context_manager = - ContextManager::start_with_capacity(3, Superloop::new(), 0_u16.into()) - .unwrap(); + ContextManager::start_with_capacity(3, Superloop::new(), 0).unwrap(); let should_redirect = false; context_manager.add_context( @@ -813,8 +807,7 @@ pub mod test { #[test] fn test_close_context_upcoming_ids() { let mut context_manager = - ContextManager::start_with_capacity(5, Superloop::new(), 0_u16.into()) - .unwrap(); + ContextManager::start_with_capacity(5, Superloop::new(), 0).unwrap(); let should_redirect = false; context_manager.add_context( @@ -863,8 +856,7 @@ pub mod test { #[test] fn test_close_last_context() { let mut context_manager = - ContextManager::start_with_capacity(2, Superloop::new(), 0_u16.into()) - .unwrap(); + ContextManager::start_with_capacity(2, Superloop::new(), 0).unwrap(); let should_redirect = false; context_manager.add_context( @@ -891,8 +883,7 @@ pub mod test { #[test] fn test_switch_to_next() { let mut context_manager = - ContextManager::start_with_capacity(5, Superloop::new(), 0_u16.into()) - .unwrap(); + ContextManager::start_with_capacity(5, Superloop::new(), 0).unwrap(); let should_redirect = false; context_manager.add_context( diff --git a/frontends/rioterm/src/main.rs b/frontends/rioterm/src/main.rs index 67effea041..60c2388fda 100644 --- a/frontends/rioterm/src/main.rs +++ b/frontends/rioterm/src/main.rs @@ -4,25 +4,37 @@ // See https://msdn.microsoft.com/en-us/library/4cc7ya5b.aspx for more details. #![windows_subsystem = "windows"] +#[cfg(target_os = "macos")] +mod app; +mod bindings; mod cli; +mod constants; +mod context; mod ime; mod logger; +mod messenger; +mod mouse; #[cfg(windows)] mod panic; mod platform; +#[cfg(target_os = "macos")] +mod renderer; +#[cfg(not(target_os = "macos"))] mod router; +mod routes; +#[cfg(not(target_os = "macos"))] mod scheduler; +#[cfg(not(target_os = "macos"))] mod screen; +#[cfg(not(target_os = "macos"))] mod sequencer; -mod ui; -mod watch; +mod state; +mod watcher; -use crate::event::EventPayload; -use crate::sequencer::Sequencer; use clap::Parser; use log::{info, LevelFilter, SetLoggerError}; use logger::Logger; -use rio_backend::{ansi, clipboard, crosswords, event, performer, selection}; +use rio_backend::{ansi, crosswords, event, performer, selection}; use std::str::FromStr; #[cfg(windows)] @@ -129,13 +141,20 @@ async fn main() -> Result<(), Box> { setup_environment_variables(&config); - let window_event_loop = - winit::event_loop::EventLoopBuilder::::with_user_event() - .build() - .unwrap(); + #[cfg(not(target_os = "macos"))] + { + let window_event_loop = winit::event_loop::EventLoopBuilder::< + crate::event::EventPayload, + >::with_user_event() + .build() + .unwrap(); + + let mut sequencer = crate::sequencer::Sequencer::new(config, config_error); + let _ = sequencer.run(window_event_loop).await; + } - let mut sequencer = Sequencer::new(config, config_error); - let _ = sequencer.run(window_event_loop).await; + #[cfg(target_os = "macos")] + let _ = app::run(config, config_error).await; #[cfg(windows)] unsafe { diff --git a/frontends/rioterm-beta/src/messenger.rs b/frontends/rioterm/src/messenger.rs similarity index 100% rename from frontends/rioterm-beta/src/messenger.rs rename to frontends/rioterm/src/messenger.rs diff --git a/frontends/rioterm/src/screen/mouse.rs b/frontends/rioterm/src/mouse/mod.rs similarity index 95% rename from frontends/rioterm/src/screen/mouse.rs rename to frontends/rioterm/src/mouse/mod.rs index cfe5ab5753..32384d3b37 100644 --- a/frontends/rioterm/src/screen/mouse.rs +++ b/frontends/rioterm/src/mouse/mod.rs @@ -2,11 +2,17 @@ use crate::crosswords::pos::Column; use crate::crosswords::pos::Line; use crate::crosswords::pos::Side; use crate::event::ClickState; -use crate::screen::Pos; +use rio_backend::crosswords::pos::Pos; use std::time::Instant; + +#[cfg(not(target_os = "macos"))] use winit::event::ElementState; +#[cfg(not(target_os = "macos"))] use winit::event::MouseButton; +#[cfg(target_os = "macos")] +use wa::MouseButton; + #[derive(Default, Debug)] pub struct AccumulatedScroll { /// Scroll we should perform along `x` axis. @@ -20,9 +26,18 @@ pub struct AccumulatedScroll { pub struct Mouse { pub multiplier: f64, pub divider: f64, + #[cfg(not(target_os = "macos"))] pub left_button_state: ElementState, + #[cfg(not(target_os = "macos"))] pub middle_button_state: ElementState, + #[cfg(not(target_os = "macos"))] pub right_button_state: ElementState, + #[cfg(target_os = "macos")] + pub left_button_state: bool, + #[cfg(target_os = "macos")] + pub middle_button_state: bool, + #[cfg(target_os = "macos")] + pub right_button_state: bool, pub last_click_timestamp: Instant, pub last_click_button: MouseButton, pub click_state: ClickState, @@ -41,9 +56,18 @@ impl Default for Mouse { divider: 1.0, last_click_timestamp: Instant::now(), last_click_button: MouseButton::Left, + #[cfg(not(target_os = "macos"))] left_button_state: ElementState::Released, + #[cfg(not(target_os = "macos"))] middle_button_state: ElementState::Released, + #[cfg(not(target_os = "macos"))] right_button_state: ElementState::Released, + #[cfg(target_os = "macos")] + left_button_state: false, + #[cfg(target_os = "macos")] + middle_button_state: false, + #[cfg(target_os = "macos")] + right_button_state: false, click_state: ClickState::None, square_side: Side::Left, inside_text_area: Default::default(), diff --git a/frontends/rioterm/src/platform/macos/mod.rs b/frontends/rioterm/src/platform/macos/mod.rs index c7899b57ab..281fc5b8f8 100644 --- a/frontends/rioterm/src/platform/macos/mod.rs +++ b/frontends/rioterm/src/platform/macos/mod.rs @@ -1,4 +1,5 @@ -// Retired from https://github.com/alacritty/alacritty/blob/6e7f466c68b387f41726757eed4f3e70d05479d2/alacritty/src/macos/locale.rs +// set_locale_environment and nsstring_as_str were originally taken from alacritty +// https://github.com/alacritty/alacritty/blob/94ede16ee4af8869fd6415b3530c7e12c8681578/alacritty/src/macos/locale.rs#L50 // which is licensed under Apache 2.0 license. use std::ffi::{CStr, CString}; @@ -7,8 +8,9 @@ use std::{env, slice, str}; use libc::{setlocale, LC_ALL, LC_CTYPE}; use log::debug; -use objc2::runtime::{Class, Object}; -use objc2::{msg_send, sel}; +use objc::runtime::{Class, Object}; +use objc::{msg_send, sel, sel_impl}; + const FALLBACK_LOCALE: &str = "UTF-8"; pub fn set_locale_environment() { diff --git a/frontends/rioterm/src/platform/mod.rs b/frontends/rioterm/src/platform/mod.rs index ed50e7d475..221a0d47a4 100644 --- a/frontends/rioterm/src/platform/mod.rs +++ b/frontends/rioterm/src/platform/mod.rs @@ -1,2 +1,42 @@ +use std::fs::File; +use std::io::Write; + #[cfg(target_os = "macos")] pub mod macos; + +pub fn create_config_file() { + let default_file_path = rio_backend::config::config_file_path(); + if default_file_path.exists() { + return; + } + + let default_dir_path = rio_backend::config::config_dir_path(); + match std::fs::create_dir_all(&default_dir_path) { + Ok(_) => { + log::info!("configuration path created {}", default_dir_path.display()); + } + Err(err_message) => { + log::error!("could not create config directory: {err_message}"); + } + } + + match File::create(&default_file_path) { + Err(err_message) => { + log::error!( + "could not create config file {}: {err_message}", + default_file_path.display() + ) + } + Ok(mut created_file) => { + log::info!("configuration file created {}", default_file_path.display()); + + if let Err(err_message) = writeln!( + created_file, + "{}", + rio_backend::config::config_file_content() + ) { + log::error!("could not update config file with defaults: {err_message}") + } + } + } +} diff --git a/frontends/rioterm-beta/src/renderer/mod.rs b/frontends/rioterm/src/renderer/mod.rs similarity index 95% rename from frontends/rioterm-beta/src/renderer/mod.rs rename to frontends/rioterm/src/renderer/mod.rs index 00ce568988..3ff71ea0b5 100644 --- a/frontends/rioterm-beta/src/renderer/mod.rs +++ b/frontends/rioterm/src/renderer/mod.rs @@ -1,4 +1,4 @@ -use crate::router::constants; +use crate::constants; #[inline] pub fn padding_top_from_config(config: &rio_backend::config::Config) -> f32 { diff --git a/frontends/rioterm/src/router/assistant.rs b/frontends/rioterm/src/router/assistant.rs deleted file mode 100644 index 67d98ac96f..0000000000 --- a/frontends/rioterm/src/router/assistant.rs +++ /dev/null @@ -1,109 +0,0 @@ -use rio_backend::error::{RioError, RioErrorLevel}; -use rio_backend::sugarloaf::components::rect::Rect; -use rio_backend::sugarloaf::font::FONT_ID_BUILTIN; -use rio_backend::sugarloaf::Sugarloaf; - -pub struct Assistant { - pub inner: Option, -} - -impl Assistant { - pub fn new() -> Assistant { - Assistant { inner: None } - } - - #[inline] - pub fn set(&mut self, report: RioError) { - self.inner = Some(report); - } - - #[inline] - pub fn clear(&mut self) { - self.inner = None; - } - - #[inline] - pub fn is_warning(&self) -> bool { - if let Some(report) = &self.inner { - if report.level == RioErrorLevel::Error { - return false; - } - } - - true - } -} - -#[inline] -pub fn screen(sugarloaf: &mut Sugarloaf, assistant: &Assistant) { - let blue = [0.1764706, 0.6039216, 1.0, 1.0]; - let yellow = [0.9882353, 0.7294118, 0.15686275, 1.0]; - let red = [1.0, 0.07058824, 0.38039216, 1.0]; - - let assistant_background = vec![ - // Rect { - // position: [30., 0.0], - // color: self.named_colors.background.0, - // size: [sugarloaf.layout.width, sugarloaf.layout.height], - // }, - Rect { - position: [0., 30.0], - color: blue, - size: [30., sugarloaf.layout.height], - }, - Rect { - position: [15., sugarloaf.layout.margin.top_y + 40.], - color: yellow, - size: [30., sugarloaf.layout.height], - }, - Rect { - position: [30., sugarloaf.layout.margin.top_y + 120.], - color: red, - size: [30., sugarloaf.layout.height], - }, - ]; - - sugarloaf.pile_rects(assistant_background); - - sugarloaf.text( - (70., sugarloaf.layout.margin.top_y + 50.), - String::from("Woops! Rio got errors"), - FONT_ID_BUILTIN, - 28., - [1., 1., 1., 1.], - true, - ); - - if let Some(report) = &assistant.inner { - if report.level == RioErrorLevel::Error { - sugarloaf.text( - (70., sugarloaf.layout.margin.top_y + 80.), - String::from("after fix it, restart the terminal"), - FONT_ID_BUILTIN, - 18., - [1., 1., 1., 1.], - true, - ); - } - - if report.level == RioErrorLevel::Warning { - sugarloaf.text( - (70., sugarloaf.layout.margin.top_y + 80.), - String::from("(press enter to continue)"), - FONT_ID_BUILTIN, - 18., - [1., 1., 1., 1.], - true, - ); - } - - sugarloaf.text( - (70., sugarloaf.layout.margin.top_y + 170.), - report.report.to_string(), - FONT_ID_BUILTIN, - 14., - [1., 1., 1., 1.], - false, - ); - } -} diff --git a/frontends/rioterm/src/router/mod.rs b/frontends/rioterm/src/router/mod.rs index 2562b4b17d..ed66c959df 100644 --- a/frontends/rioterm/src/router/mod.rs +++ b/frontends/rioterm/src/router/mod.rs @@ -1,10 +1,8 @@ -pub mod assistant; -pub mod dialog; -pub mod welcome; mod window; use crate::event::{EventPayload, EventProxy}; use crate::router::window::{configure_window, create_window_builder}; +use crate::routes::{assistant, dialog, welcome}; use crate::screen::Screen; use assistant::Assistant; use rio_backend::config::Config as RioConfig; diff --git a/frontends/rioterm/src/router/welcome.rs b/frontends/rioterm/src/router/welcome.rs deleted file mode 100644 index 10f87f0c45..0000000000 --- a/frontends/rioterm/src/router/welcome.rs +++ /dev/null @@ -1,128 +0,0 @@ -use rio_backend::sugarloaf::components::rect::Rect; -use rio_backend::sugarloaf::Sugarloaf; - -#[inline] -pub fn screen(sugarloaf: &mut Sugarloaf) { - let blue = [0.1764706, 0.6039216, 1.0, 1.0]; - let yellow = [0.9882353, 0.7294118, 0.15686275, 1.0]; - let red = [1.0, 0.07058824, 0.38039216, 1.0]; - - let width = sugarloaf.layout.width / sugarloaf.layout.scale_factor; - - let assistant_background = vec![ - Rect { - position: [0., 30.0], - color: blue, - size: [30., sugarloaf.layout.height], - }, - Rect { - position: [15., sugarloaf.layout.margin.top_y + 40.], - color: yellow, - size: [30., sugarloaf.layout.height], - }, - Rect { - position: [30., sugarloaf.layout.margin.top_y + 120.], - color: red, - size: [30., sugarloaf.layout.height], - }, - ]; - - sugarloaf.pile_rects(assistant_background); - - if width <= 440. { - sugarloaf.text( - (70., sugarloaf.layout.margin.top_y + 50.), - String::from("Welcome to\nRio Terminal"), - 8, - 28., - [1., 1., 1., 1.], - false, - ); - - sugarloaf.text( - (70., sugarloaf.layout.margin.top_y + 100.), - String::from("(enter to continue)"), - 8, - 18., - yellow, - false, - ); - - sugarloaf.text( - (width - 50., sugarloaf.layout.margin.top_y + 320.), - String::from("󰌑"), - 7, - 26., - yellow, - true, - ); - - sugarloaf.text( - (width - 50., sugarloaf.layout.margin.top_y + 340.), - String::from("nice"), - 8, - 14., - yellow, - true, - ); - - return; - } - - sugarloaf.text( - (70., sugarloaf.layout.margin.top_y + 50.), - String::from("Welcome to Rio Terminal"), - 8, - 28., - [1., 1., 1., 1.], - true, - ); - - sugarloaf.text( - (70., sugarloaf.layout.margin.top_y + 80.), - String::from("(press enter to continue)"), - 8, - 18., - yellow, - true, - ); - - sugarloaf.text( - (70., sugarloaf.layout.margin.top_y + 220.), - welcome_content(), - 8, - 18., - [1., 1., 1., 1.], - false, - ); - - sugarloaf.text( - (width - 50., sugarloaf.layout.margin.top_y + 320.), - String::from("󰌑"), - 7, - 26., - yellow, - true, - ); - - sugarloaf.text( - (width - 50., sugarloaf.layout.margin.top_y + 340.), - String::from("nice"), - 8, - 14., - yellow, - true, - ); -} - -#[inline] -fn welcome_content() -> String { - #[cfg(target_os = "macos")] - let shortcut = "\"Command\" + \",\" (comma)"; - - #[cfg(not(target_os = "macos"))] - let shortcut = "\"Control\" + \"Shift\" + \",\" (comma)"; - - format!("Your configuration file will be created in\n{}\n\nTo open settings menu use\n{}\n\n\n\nMore info in raphamorim.io/rio/docs - ", rio_backend::config::config_file_path().display(), shortcut) -} diff --git a/frontends/rioterm-beta/src/router/routes/assistant.rs b/frontends/rioterm/src/routes/assistant.rs similarity index 100% rename from frontends/rioterm-beta/src/router/routes/assistant.rs rename to frontends/rioterm/src/routes/assistant.rs diff --git a/frontends/rioterm/src/router/dialog.rs b/frontends/rioterm/src/routes/dialog.rs similarity index 100% rename from frontends/rioterm/src/router/dialog.rs rename to frontends/rioterm/src/routes/dialog.rs diff --git a/frontends/rioterm-beta/src/router/routes/mod.rs b/frontends/rioterm/src/routes/mod.rs similarity index 74% rename from frontends/rioterm-beta/src/router/routes/mod.rs rename to frontends/rioterm/src/routes/mod.rs index 8b29616f19..b0996e718c 100644 --- a/frontends/rioterm-beta/src/router/routes/mod.rs +++ b/frontends/rioterm/src/routes/mod.rs @@ -1,4 +1,6 @@ pub mod assistant; +#[cfg(not(target_os = "macos"))] +pub mod dialog; pub mod welcome; #[derive(PartialEq)] diff --git a/frontends/rioterm-beta/src/router/routes/welcome.rs b/frontends/rioterm/src/routes/welcome.rs similarity index 100% rename from frontends/rioterm-beta/src/router/routes/welcome.rs rename to frontends/rioterm/src/routes/welcome.rs diff --git a/frontends/rioterm/src/scheduler.rs b/frontends/rioterm/src/scheduler.rs index 24c31b48b9..4fd1d2e983 100644 --- a/frontends/rioterm/src/scheduler.rs +++ b/frontends/rioterm/src/scheduler.rs @@ -1,12 +1,13 @@ // scheduler.rs was retired originally from https://github.com/alacritty/alacritty/blob/e35e5ad14fce8456afdd89f2b392b9924bb27471/alacritty/src/scheduler.rs // which is licensed under Apache 2.0 license. +#![cfg(not(target_os = "macos"))] + +use crate::event::EventPayload; use std::collections::VecDeque; use std::time::{Duration, Instant}; use winit::event_loop::EventLoopProxy; -use crate::event::EventPayload; - /// ID uniquely identifying a timer. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct TimerId { diff --git a/frontends/rioterm/src/screen/context.rs b/frontends/rioterm/src/screen/context.rs index 165fea6bce..2655f043af 100644 --- a/frontends/rioterm/src/screen/context.rs +++ b/frontends/rioterm/src/screen/context.rs @@ -2,9 +2,9 @@ use crate::ansi::CursorShape; use crate::crosswords::pos::CursorState; use crate::event::sync::FairMutex; use crate::event::{EventListener, RioEvent}; +use crate::messenger::Messenger; use crate::performer::Machine; use crate::screen::Crosswords; -use crate::screen::Messenger; use crate::screen::SugarloafLayout; use rio_backend::config::Shell; use rio_backend::crosswords::{CrosswordsSize, MIN_COLUMNS, MIN_LINES}; diff --git a/frontends/rioterm/src/screen/messenger.rs b/frontends/rioterm/src/screen/messenger.rs deleted file mode 100644 index b4e052951f..0000000000 --- a/frontends/rioterm/src/screen/messenger.rs +++ /dev/null @@ -1,50 +0,0 @@ -use crate::event::Msg; -use std::borrow::Cow; -use teletypewriter::WinsizeBuilder; - -pub struct Messenger { - channel: corcovado::channel::Sender, -} - -impl Messenger { - pub fn new(channel: corcovado::channel::Sender) -> Messenger { - Messenger { channel } - } - - #[inline] - pub fn send_bytes(&mut self, string: Vec) { - self.send_write(string); - } - - #[inline] - pub fn send_write>>(&self, data: B) { - let bytes = data.into(); - // terminal hangs if we send 0 bytes through. - if bytes.len() == 0 { - return; - } - - let _ = self.channel.send(Msg::Input(bytes)); - } - - #[inline] - pub fn send_resize( - &self, - width: u16, - height: u16, - cols: u16, - rows: u16, - ) -> Result<&str, String> { - let new_size = WinsizeBuilder { - rows, - cols, - width, - height, - }; - - match self.channel.send(Msg::Resize(new_size)) { - Ok(..) => Ok("Resized"), - Err(..) => Err("Error sending message".to_string()), - } - } -} diff --git a/frontends/rioterm/src/screen/mod.rs b/frontends/rioterm/src/screen/mod.rs index 0a8aa6cf10..0b08e1d79a 100644 --- a/frontends/rioterm/src/screen/mod.rs +++ b/frontends/rioterm/src/screen/mod.rs @@ -6,16 +6,17 @@ // were retired from https://github.com/alacritty/alacritty/blob/c39c3c97f1a1213418c3629cc59a1d46e34070e0/alacritty/src/input.rs // which is licensed under Apache 2.0 license. -mod bindings; -mod constants; mod context; -mod messenger; -mod mouse; mod navigation; mod state; pub mod touch; +use crate::bindings::{ + Action as Act, BindingKey, BindingMode, FontSizeAction, MouseBinding, ViAction, +}; use crate::clipboard::{Clipboard, ClipboardType}; +#[cfg(target_os = "macos")] +use crate::constants::{DEADZONE_END_Y, DEADZONE_START_X, DEADZONE_START_Y}; use crate::crosswords::{ grid::{Dimensions, Scroll}, pos::{Column, Pos, Side}, @@ -25,20 +26,12 @@ use crate::crosswords::{ }; use crate::event::{ClickState, EventProxy}; use crate::ime::Ime; +use crate::messenger::Messenger; +use crate::mouse::{calculate_mouse_position, Mouse}; use crate::router; -use crate::screen::bindings::MouseBinding; -use crate::screen::bindings::ViAction; -#[cfg(target_os = "macos")] -use crate::screen::constants::{DEADZONE_END_Y, DEADZONE_START_X, DEADZONE_START_Y}; -use crate::screen::{ - bindings::{Action as Act, BindingKey, BindingMode, FontSizeAction}, - context::ContextManager, - mouse::{calculate_mouse_position, Mouse}, - touch::TouchPurpose, -}; +use crate::screen::{context::ContextManager, touch::TouchPurpose}; use crate::selection::{Selection, SelectionType}; use core::fmt::Debug; -use messenger::Messenger; use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; use rio_backend::config::{ colors::{term::List, ColorWGPU}, @@ -73,7 +66,7 @@ fn padding_top_from_config(config: &rio_backend::config::Config) -> f32 { #[cfg(not(target_os = "macos"))] { if config.navigation.is_placed_on_top() { - return constants::PADDING_Y_WITH_TAB_ON_TOP; + return crate::constants::PADDING_Y_WITH_TAB_ON_TOP; } } @@ -84,7 +77,7 @@ fn padding_top_from_config(config: &rio_backend::config::Config) -> f32 { } } - constants::PADDING_Y + crate::constants::PADDING_Y } #[inline] @@ -97,11 +90,11 @@ fn padding_bottom_from_config(config: &rio_backend::config::Config) -> f32 { } pub struct Screen { - bindings: bindings::KeyBindings, + bindings: crate::bindings::KeyBindings, mouse_bindings: Vec, clipboard: Clipboard, pub modifiers: Modifiers, - pub mouse: Mouse, + pub mouse: crate::Mouse, pub touchpurpose: TouchPurpose, pub ime: Ime, pub state: State, @@ -192,7 +185,7 @@ impl Screen { let clipboard = unsafe { Clipboard::new(raw_display_handle.into()) }; - let bindings = bindings::default_key_bindings( + let bindings = crate::bindings::default_key_bindings( config.bindings.keys.to_owned(), config.navigation.has_navigation_key_bindings(), config.keyboard, @@ -224,12 +217,12 @@ impl Screen { )?; Ok(Screen { - mouse_bindings: bindings::default_mouse_bindings(), + mouse_bindings: crate::bindings::default_mouse_bindings(), modifiers: Modifiers::default(), context_manager, ime, sugarloaf, - mouse: Mouse::new(config.scroll.multiplier, config.scroll.divider), + mouse: crate::Mouse::new(config.scroll.multiplier, config.scroll.divider), touchpurpose: TouchPurpose::default(), state, bindings, @@ -254,7 +247,7 @@ impl Screen { #[inline] pub fn reset_mouse(&mut self) { - self.mouse.accumulated_scroll = mouse::AccumulatedScroll::default(); + self.mouse.accumulated_scroll = crate::mouse::AccumulatedScroll::default(); } #[inline] @@ -500,7 +493,7 @@ impl Screen { Key::Named(NamedKey::Enter) => [b'\r'].as_slice().into(), Key::Named(NamedKey::Delete) => [b'\x7f'].as_slice().into(), Key::Named(NamedKey::Escape) => [b'\x1b'].as_slice().into(), - _ => bindings::kitty_keyboard_protocol::build_key_sequence( + _ => crate::bindings::kitty_keyboard_protocol::build_key_sequence( key, mods, mode, ) .into(), @@ -811,7 +804,9 @@ impl Screen { bytes } else { // Otherwise we should build the key sequence for the given input. - bindings::kitty_keyboard_protocol::build_key_sequence(key, mods, mode) + crate::bindings::kitty_keyboard_protocol::build_key_sequence( + key, mods, mode, + ) } }; @@ -1064,8 +1059,8 @@ impl Screen { let step = (SELECTION_SCROLLING_STEP * scale_factor) as f64; // Compute the height of the scrolling areas. - let end_top = max(min_height, constants::PADDING_Y as i32) as f64; - let text_area_bottom = (constants::PADDING_Y + let end_top = max(min_height, crate::constants::PADDING_Y as i32) as f64; + let text_area_bottom = (crate::constants::PADDING_Y + self.sugarloaf.layout.lines as f32) * self.sugarloaf.layout.font_size; let start_bottom = min( diff --git a/frontends/rioterm/src/screen/navigation.rs b/frontends/rioterm/src/screen/navigation.rs index 6d8a721204..b830928e93 100644 --- a/frontends/rioterm/src/screen/navigation.rs +++ b/frontends/rioterm/src/screen/navigation.rs @@ -1,4 +1,4 @@ -use crate::screen::constants::*; +use crate::constants::*; use rio_backend::config::navigation::NavigationMode; use rio_backend::sugarloaf::components::rect::Rect; use rio_backend::sugarloaf::font::FONT_ID_BUILTIN; diff --git a/frontends/rioterm/src/sequencer.rs b/frontends/rioterm/src/sequencer.rs index 968ba8ac6a..cc0119c099 100644 --- a/frontends/rioterm/src/sequencer.rs +++ b/frontends/rioterm/src/sequencer.rs @@ -4,7 +4,7 @@ use crate::ime::Preedit; use crate::router::{RoutePath, RouteWindow, Router}; use crate::scheduler::{Scheduler, TimerId, Topic}; use crate::screen::touch::on_touch; -use crate::watch::watch; +use crate::watcher::configuration_file_updates; use rio_backend::config::colors::ColorRgb; use std::error::Error; use std::rc::Rc; @@ -51,7 +51,7 @@ impl Sequencer { ) -> Result<(), Box> { let proxy = event_loop.create_proxy(); self.event_proxy = Some(EventProxy::new(proxy.clone())); - let _ = watch( + let _ = configuration_file_updates( rio_backend::config::config_dir_path(), self.event_proxy.clone().unwrap(), ); @@ -405,10 +405,7 @@ impl Sequencer { } Event::NewEvents(StartCause::Init) => { - #[cfg(target_os = "macos")] - { - crate::ui::appkit::create_toolbar(); - } + // noop } Event::Resumed => { diff --git a/frontends/rioterm-beta/src/state/mod.rs b/frontends/rioterm/src/state/mod.rs similarity index 92% rename from frontends/rioterm-beta/src/state/mod.rs rename to frontends/rioterm/src/state/mod.rs index f174cf8d72..df5901fdc6 100644 --- a/frontends/rioterm-beta/src/state/mod.rs +++ b/frontends/rioterm/src/state/mod.rs @@ -1,5 +1,4 @@ -pub mod context; -pub mod navigation; +mod navigation; use crate::ansi::CursorShape; use crate::crosswords::grid::row::Row; @@ -8,19 +7,19 @@ use crate::crosswords::pos::CursorState; use crate::crosswords::square::{Flags, Square}; use crate::ime::Preedit; use crate::selection::SelectionRange; -use crate::state::navigation::ScreenNavigation; -use rio_backend::superloop::Superloop; -// use rio_backend::ansi::graphics::UpdateQueues; +use navigation::ScreenNavigation; use rio_backend::config::colors::{ term::{List, TermColors}, AnsiColor, ColorArray, Colors, NamedColor, }; use rio_backend::config::Config; use rio_backend::sugarloaf::core::{Sugar, SugarDecoration, SugarStack, SugarStyle}; -use rio_backend::sugarloaf::SugarGraphic; -use rio_backend::sugarloaf::Sugarloaf; +use rio_backend::sugarloaf::{SugarGraphic, Sugarloaf}; +use rio_backend::superloop::Superloop; use std::collections::HashMap; use std::time::{Duration, Instant}; +#[cfg(not(target_os = "macos"))] +use winit::window::Theme; struct Cursor { state: CursorState, @@ -50,22 +49,46 @@ pub struct State { } impl State { - pub fn new(config: &Config, appearance: wa::Appearance) -> State { + pub fn new( + #[cfg(not(target_os = "macos"))] config: &std::rc::Rc, + #[cfg(target_os = "macos")] config: &Config, + #[cfg(not(target_os = "macos"))] current_theme: Option, + #[cfg(target_os = "macos")] appearance: wa::Appearance, + ) -> State { let term_colors = TermColors::default(); let colors = List::from(&term_colors); let mut named_colors = config.colors; - if let Some(adaptive_colors) = &config.adaptive_colors { - match appearance { - wa::Appearance::Light => { - named_colors = adaptive_colors.light.unwrap_or(named_colors); + #[cfg(not(target_os = "macos"))] + { + if let Some(theme) = current_theme { + if let Some(adaptive_colors) = &config.adaptive_colors { + match theme { + Theme::Light => { + named_colors = adaptive_colors.light.unwrap_or(named_colors); + } + Theme::Dark => { + named_colors = adaptive_colors.dark.unwrap_or(named_colors); + } + } } - wa::Appearance::Dark => { - named_colors = adaptive_colors.dark.unwrap_or(named_colors); + } + } + + #[cfg(target_os = "macos")] + { + if let Some(adaptive_colors) = &config.adaptive_colors { + match appearance { + wa::Appearance::Light => { + named_colors = adaptive_colors.light.unwrap_or(named_colors); + } + wa::Appearance::Dark => { + named_colors = adaptive_colors.dark.unwrap_or(named_colors); + } + // TODO + wa::Appearance::LightHighContrast => {} + wa::Appearance::DarkHighContrast => {} } - // TODO - wa::Appearance::LightHighContrast => {} - wa::Appearance::DarkHighContrast => {} } } @@ -312,6 +335,16 @@ impl State { stack } + #[inline] + pub fn decrease_foreground_opacity(&mut self, acc: f32) { + self.foreground_opacity -= acc; + } + + #[inline] + pub fn increase_foreground_opacity(&mut self, acc: f32) { + self.foreground_opacity += acc; + } + #[inline] fn compute_fg_color(&self, square: &Square) -> ColorArray { let mut color = match square.fg { @@ -388,16 +421,6 @@ impl State { color } - #[inline] - pub fn decrease_foreground_opacity(&mut self, acc: f32) { - self.foreground_opacity -= acc; - } - - #[inline] - pub fn increase_foreground_opacity(&mut self, acc: f32) { - self.foreground_opacity += acc; - } - #[inline] fn compute_bg_color(&self, square: &Square) -> ColorArray { let mut color = match square.bg { @@ -468,12 +491,12 @@ impl State { for column in 0..columns { let square = &row.inner[column]; - if square.flags.contains(Flags::GRAPHICS) { - stack.push(self.create_graphic_sugar(square)); + if square.flags.contains(Flags::WIDE_CHAR_SPACER) { continue; } - if square.flags.contains(Flags::WIDE_CHAR_SPACER) { + if square.flags.contains(Flags::GRAPHICS) { + stack.push(self.create_graphic_sugar(square)); continue; } @@ -492,6 +515,26 @@ impl State { stack } + #[inline] + fn create_graphic_sugar(&self, square: &Square) -> Sugar { + let foreground_color = self.compute_fg_color(square); + let background_color = self.compute_bg_color(square); + + let media = &square.graphics().unwrap()[0].texture; + Sugar { + content: ' ', + foreground_color, + background_color, + style: None, + decoration: None, + media: Some(SugarGraphic { + id: media.id, + width: media.width, + height: media.height, + }), + } + } + #[inline] fn create_cursor(&self, square: &Square) -> Sugar { let mut sugar = Sugar { @@ -534,26 +577,6 @@ impl State { sugar } - #[inline] - fn create_graphic_sugar(&self, square: &Square) -> Sugar { - let foreground_color = self.compute_fg_color(square); - let background_color = self.compute_bg_color(square); - - let media = &square.graphics().unwrap()[0].texture; - Sugar { - content: ' ', - foreground_color, - background_color, - style: None, - decoration: None, - media: Some(SugarGraphic { - id: media.id, - width: media.width, - height: media.height, - }), - } - } - #[inline] pub fn set_ime(&mut self, ime_preedit: Option<&Preedit>) { if let Some(preedit) = ime_preedit { @@ -584,7 +607,12 @@ impl State { rows: Vec>, cursor: CursorState, sugarloaf: &mut Sugarloaf, - context_manager: &context::ContextManager, + #[cfg(not(target_os = "macos"))] context_manager: &crate::context::ContextManager< + crate::screen::EventProxy, + >, + #[cfg(target_os = "macos")] context_manager: &crate::context::ContextManager< + Superloop, + >, display_offset: i32, has_blinking_enabled: bool, ) { diff --git a/frontends/rioterm-beta/src/state/navigation.rs b/frontends/rioterm/src/state/navigation.rs similarity index 99% rename from frontends/rioterm-beta/src/state/navigation.rs rename to frontends/rioterm/src/state/navigation.rs index 96251f38a0..c824c60567 100644 --- a/frontends/rioterm-beta/src/state/navigation.rs +++ b/frontends/rioterm/src/state/navigation.rs @@ -1,4 +1,4 @@ -use crate::router::constants::*; +use crate::constants::*; use rio_backend::config::navigation::NavigationMode; use rio_backend::sugarloaf::components::rect::Rect; use rio_backend::sugarloaf::font::FONT_ID_BUILTIN; diff --git a/frontends/rioterm/src/ui/appkit/global.rs b/frontends/rioterm/src/ui/appkit/global.rs deleted file mode 100644 index e4fbfccb7d..0000000000 --- a/frontends/rioterm/src/ui/appkit/global.rs +++ /dev/null @@ -1,182 +0,0 @@ -use core::cell::UnsafeCell; - -use objc2::rc::{AutoreleasePool, Id, Shared}; -use objc2::runtime::Bool; -use objc2::{class, msg_send}; -use objc2::{Encoding, Message, RefEncode}; - -use super::menu::NSMenu; -use super::menubar::MenuBar; - -/// Helper to make various functions on the global application object safe. -#[repr(C)] -pub struct InitializedApplication { - /// The application contains syncronization primitives that allows mutable - /// access with an immutable reference, and hence need to be `UnsafeCell`. - /// - /// TODO: Verify this claim. - _priv: UnsafeCell<[u8; 0]>, -} - -unsafe impl RefEncode for InitializedApplication { - const ENCODING_REF: Encoding<'static> = Encoding::Object; -} - -unsafe impl Message for InitializedApplication {} -unsafe impl Sync for InitializedApplication {} - -impl InitializedApplication { - /// # Safety - /// - /// This must not be called before `applicationDidFinishLaunching`. - /// - /// In `winit`, this is at or after - /// [`winit::event::StartCause::Init`] has been emitted. - pub unsafe fn new() -> &'static Self { - msg_send![class!(NSApplication), sharedApplication] - } - - #[allow(unused)] - pub fn menubar<'p>(&self, _pool: &'p AutoreleasePool) -> Option<&'p NSMenu> { - unsafe { msg_send![self, mainMenu] } - } - - /// Setting the menubar to `null` does not work properly, so we don't allow - /// that functionality here! - #[allow(unused)] - pub fn set_menubar(&self, menubar: MenuBar) -> Id { - let menu = menubar.into_raw(); - let _: () = unsafe { msg_send![self, setMainMenu: &*menu] }; - menu.into() - } - - /// Returns the first menu set with [`set_window_menu`] - #[allow(unused)] - pub fn window_menu<'p>(&self, _pool: &'p AutoreleasePool) -> Option<&'p NSMenu> { - unsafe { msg_send![self, windowsMenu] } - } - - /// Set the global window menu. - /// - /// The "Window: menu has items and keyboard shortcuts for entering - /// fullscreen, managing tabs (e.g. "Show Next Tab") and a list of the - /// application's windows. - /// - /// Should be called before [`set_menubar`], otherwise the window menu - /// won't be properly populated. - /// - /// Un-setting the window menu (to `null`) does not work properly, so we - /// don't expose that functionality here. - /// - /// Additionally, you can have luck setting the window menu more than once, - /// though this is not recommended. - #[allow(unused)] - pub fn set_window_menu(&self, menu: &NSMenu) { - // TODO: Is it safe to immutably set this? - unsafe { msg_send![self, setWindowsMenu: menu] } - } - - /// Returns the first menu set with [`set_services_menu`] - #[allow(unused)] - pub fn services_menu<'p>(&self, _pool: &'p AutoreleasePool) -> Option<&'p NSMenu> { - unsafe { msg_send![self, servicesMenu] } - } - - /// Set the global services menu. - /// - /// The user can have a number of system configured services and - /// corresponding keyboard shortcuts that can be accessed from this menu. - /// - /// Un-setting the services menu (to `null`) does not work properly, so we - /// don't expose that functionality here. - /// - /// Additionally, you can sometimes have luck setting the services menu - /// more than once, but this is really flaky. - #[allow(unused)] - pub fn set_services_menu(&self, menu: &NSMenu) { - // TODO: Is it safe to immutably set this? - // TODO: The menu should (must?) not contain any items! - // TODO: Setting this and pressing the close button doesn't work in winit - unsafe { msg_send![self, setServicesMenu: menu] } - } - - // TODO: registerServicesMenuSendTypes - - /// Get the menu that is currently assigned as the help menu, or `None` if the system is configured to autodetect this. - #[allow(unused)] - pub fn help_menu<'p>(&self, _pool: &'p AutoreleasePool) -> Option<&'p NSMenu> { - unsafe { msg_send![self, helpMenu] } - } - - /// Set the global menu that should have the spotlight Help Search - /// functionality at the top of it. - /// - /// If this is set to `None`, the system will place the search bar somewhere - /// else, usually on an item named "Help" (unknown if localization applies). - /// To prevent this, specify a menu that does not appear anywhere. - #[allow(unused)] - pub fn set_help_menu(&self, menu: Option<&NSMenu>) { - // TODO: Is it safe to immutably set this? - unsafe { msg_send![self, setHelpMenu: menu] } - } - - // TODO: applicationDockMenu (the application delegate should implement this function) - - #[allow(unused)] - pub fn menubar_visible(&self) -> bool { - let visible: Bool = unsafe { msg_send![class!(NSMenu), menuBarVisible] }; - visible.is_true() - } - - /// Hide or show the menubar for the entire application. - /// This also hides or shows the yellow minimize button. - /// - /// Might silently fail to set the menubar visible if in fullscreen mode or similar. - #[allow(unused)] - pub fn set_menubar_visible(&self, visible: bool) { - let visible = Bool::new(visible); - unsafe { msg_send![class!(NSMenu), setMenuBarVisible: visible] } - } - - // Only available on the global menu bar object - // pub fn global_height(&self) -> f64 { - // let height: CGFloat = unsafe { msg_send![self, menuBarHeight] }; - // height - // } -} - -#[cfg(test)] -mod tests { - use objc2::rc::autoreleasepool; - use objc2::rc::Owned; - - use super::*; - - fn init_app() -> &'static InitializedApplication { - unimplemented!() - } - - fn create_menu() -> Id { - unimplemented!() - } - - #[test] - #[ignore = "not implemented"] - fn test_services_menu() { - let app = init_app(); - let menu1 = create_menu(); - let menu2 = create_menu(); - - autoreleasepool(|pool| { - assert!(app.services_menu(pool).is_none()); - - app.set_services_menu(&menu1); - assert_eq!(app.services_menu(pool).unwrap(), &*menu1); - - app.set_services_menu(&menu2); - assert_eq!(app.services_menu(pool).unwrap(), &*menu2); - - // At this point `menu1` still shows as a services menu... - }); - } -} diff --git a/frontends/rioterm/src/ui/appkit/menu.rs b/frontends/rioterm/src/ui/appkit/menu.rs deleted file mode 100644 index caf7b037ac..0000000000 --- a/frontends/rioterm/src/ui/appkit/menu.rs +++ /dev/null @@ -1,549 +0,0 @@ -use core::fmt; -use core::marker::PhantomData; - -use objc2::ffi::{NSInteger, NSUInteger}; -use objc2::rc::{autoreleasepool, AutoreleasePool, Id, Owned, Shared}; -use objc2::runtime::Object; -use objc2::{class, msg_send}; -use objc2::{Encoding, Message, RefEncode}; -use objc2_foundation::NSString; - -use super::menuitem::NSMenuItem; - -#[allow(unused)] -struct MenuDelegate; - -#[allow(unused)] -struct USize { - height: f64, - width: f64, -} - -/// The maximum number of items a menu can hold is 65534 -#[repr(C)] -pub struct NSMenu { - _priv: [u8; 0], -} - -unsafe impl RefEncode for NSMenu { - const ENCODING_REF: Encoding<'static> = Encoding::Object; -} - -unsafe impl Message for NSMenu {} - -unsafe impl Send for NSMenu {} -unsafe impl Sync for NSMenu {} - -/// Creating menus -impl NSMenu { - fn alloc() -> *mut Self { - unsafe { msg_send![class!(NSMenu), alloc] } - } - - pub fn new() -> Id { - let ptr = Self::alloc(); - unsafe { Id::new(msg_send![ptr, init]).unwrap() } - } - - // Public only locally to allow for construction in Menubar - pub(super) fn new_with_title(title: &str) -> Id { - let title = NSString::from_str(title); - let ptr = Self::alloc(); - unsafe { Id::new(msg_send![ptr, initWithTitle: &*title]).unwrap() } - } - - // Title (only useful for MenuBar!) - - pub(super) fn title<'p>(&self, pool: &'p AutoreleasePool) -> &'p str { - let title: &'p NSString = unsafe { msg_send![self, title] }; - title.as_str(pool) - } - - #[allow(unused)] - pub(super) fn set_title(&mut self, title: &str) { - let title = NSString::from_str(title); - unsafe { msg_send![self, setTitle: &*title] } - } -} - -/// Managing items -impl NSMenu { - /// Insert an item at the specified index. - /// - /// Panics if `index > menu.len()`. - // TODO: Reorder arguments to match `Vec::insert`? - #[allow(unused)] - pub fn insert( - &mut self, - item: Id, - index: usize, - ) -> Id { - let length = self.len(); - if index > length { - panic!( - "Failed inserting item: Index {} larger than number of items {}", - index, length - ); - } - // SAFETY: - // - References are valid - // - The item must not exist in another menu!!!!! - // - We need to ensure this somehow, for now we'll just consume the item! - // - Should maybe return a reference to the menu, where the reference is now bound to self? - // - 0 <= index <= self.len() - // TODO: Thread safety! - let _: () = - unsafe { msg_send![self, insertItem: &*item, atIndex: index as NSInteger] }; - // The item is now shared, so it's no longer safe to hold a mutable pointer to it - item.into() - } - - #[allow(unused)] - pub fn add(&mut self, item: Id) -> Id { - // Same safety concerns as above - let _: () = unsafe { msg_send![self, addItem: &*item] }; - // The item is now shared, so it's no longer safe to hold a mutable pointer to it - item.into() - } - - // There exists `addItemWithTitle_action_keyEquivalent` - - // Can't use this yet, we need to find a way to let users have references to menu items safely! - // fn remove(&mut self, item: &mut NSMenuItem) { - // unsafe { msg_send![self, removeItem: item] } - // } - // fn remove_at_index(&mut self, at: isize) { - // unimplemented!() - // } - - /// Does not post notifications. - #[allow(unused)] - pub fn remove_all(&mut self) { - // SAFETY: Reference is valid - unsafe { msg_send![self, removeAllItems] } - } - - // Finding items - - #[allow(unused)] - fn find_by_tag<'p>( - &self, - _pool: &'p AutoreleasePool, - _tag: isize, - ) -> Option<&'p NSMenuItem> { - unimplemented!() - } - - #[allow(unused)] - fn find_by_title<'p>( - &self, - _pool: &'p AutoreleasePool, - _title: &str, - ) -> Option<&'p NSMenuItem> { - unimplemented!() - } - - #[allow(unused)] - unsafe fn get_at_index<'p>( - &self, - _pool: &'p AutoreleasePool, - _at: isize, - ) -> &'p NSMenuItem { - unimplemented!() - } - - // Getting all items - - /// Number of items in this menu, including separators - #[allow(unused)] - pub fn len(&self) -> usize { - let number_of_items: NSInteger = unsafe { msg_send![self, numberOfItems] }; - number_of_items as usize - } - - #[inline] - #[allow(unused)] - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - #[allow(unused)] - fn get_all_items<'p>(&self, _pool: &'p AutoreleasePool) -> &'p [&'p NSMenuItem] { - unimplemented!() - } - - #[allow(unused)] - pub fn iter<'p>( - &self, - _pool: &'p AutoreleasePool, - ) -> impl Iterator + 'p { - let array: *const Object = unsafe { msg_send![self, itemArray] }; - let enumerator: *mut Object = unsafe { msg_send![array, objectEnumerator] }; - Iter { - array, - enumerator, - _p: PhantomData, - } - } - - // Finding indices of elements - - #[allow(unused)] - fn index_of(&self, _item: &NSMenuItem) -> Option { - unimplemented!() - } - - #[allow(unused)] - fn index_of_by_title(&self, _title: &str) -> Option { - unimplemented!() - } - - #[allow(unused)] - fn index_of_by_tag(&self, _tag: isize) -> Option { - unimplemented!() - } - - // fn index_of_by_action_and_target(&self, ...) -> isize {} - // fn index_of_item_by_represented_object(&self, ...) -> isize {} - - #[allow(unused)] - fn index_of_submenu(&self, _submenu: &NSMenu) -> Option { - unimplemented!() - } - - // Managing submenus - - // Unsure about this! - #[allow(unused)] - fn set_submenu(&self, _submenu: &mut NSMenu, _for_item: &mut NSMenuItem) { - unimplemented!() - } - - // fn submenuAction(&self) {} // Overridable! - - #[allow(unused)] - fn get_parent<'p>(&self, _pool: &'p AutoreleasePool) -> Option<&'p NSMenu> { - unimplemented!() - } - - // Has more deprecated methods! - - // Enable/disable items - - /// Default on - #[allow(unused)] - fn autoenables_items(&self) -> bool { - unimplemented!() - } - - #[allow(unused)] - fn set_autoenables_items(&mut self, _state: bool) { - unimplemented!() - } - - #[allow(unused)] - fn update_enabled_state_of_items(&self) { - unimplemented!() - } - - // Control fonts for this and subitems - - // fn font() -> Font {} - - // fn set_font(&mut self, font: Font) {} - - // Handling keyboard events - - // fn perform_key_equivalent(&self, event: KeyEvent) -> bool {} - - // Simulating mouse clicks - - // fn perform_action_for_item_at(&self, index: isize) {} - - // Size - - #[allow(unused)] - fn min_width(&self) -> Option { - // None / zero when not set - unimplemented!() - } - - #[allow(unused)] - fn set_min_width(&mut self, _width: Option) { - // None ~= zero - unimplemented!() - } - - #[allow(unused)] - fn size(&self) -> USize { - unimplemented!() - } - - #[allow(unused)] - fn set_size(&mut self, _size: USize) { - // Might change the size if too big (or small?) - unimplemented!() - } - - // propertiesToUpdate - for efficiency when updating items - - #[allow(unused)] - fn allows_context_menu_plug_ins(&self) -> bool { - unimplemented!() - } - - #[allow(unused)] - fn set_allows_context_menu_plug_ins(&mut self, _state: bool) { - unimplemented!() - } - - // fn displayPopUpContextMenu(&mut self, event: Event, view: Option<&View>) {} - // fn displayPopUpContextMenuWithFont(&mut self, event: Event, view: Option<&View>, font: Font) {} - // fn displayPopUpAtMenuPositioningItem(&mut self, position_item: Option<&NSMenuItem>, event: Event, view: Option<&View>) - - // Whether the menu displays the state column (the "Checkmark" column for items?) - #[allow(unused)] - fn show_state_column(&self) -> bool { - unimplemented!() - } - - #[allow(unused)] - fn set_show_state_column(&mut self, _show: bool) { - unimplemented!() - } - - #[allow(unused)] - fn currently_highlighted_item<'p>( - &self, - _pool: &'p AutoreleasePool, - ) -> Option<&'p NSMenuItem> { - unimplemented!() - } - - // Should honestly probably not be changed! (userInterfaceLayoutDirection) - // fn layout_direction() {} - // fn set_layout_direction() {} - - // You can use the delegate to populate a menu just before it is drawn - // and to check for key equivalents without creating a menu item. - #[allow(unused)] - fn delegate(&self) -> &MenuDelegate { - // Tied to a pool or the current item? - unimplemented!() - - // Events / things this delegate can respond to - // - menuHasKeyEquivalent:forEvent:target:action: - // - menu:updateItem:atIndex:shouldCancel: (update_item_before_displayed) - // - confinementRectForMenu:onScreen: (display_location) - // - menu:willHighlightItem: (before_highlight_item) - // - menuWillOpen: (before_open) - // - menuDidClose: (after_close) - // - numberOfItemsInMenu: // Works together with updateItemBeforeDisplayed - // Newly created items are blank, and then updateItemBeforeDisplayed populates them - // - menuNeedsUpdate: // Alternatively, if the population can happen basically instantly - // (and don't need to do a lot of processing beforehand), this can just be used - } - - #[allow(unused)] - fn set_delegate(&mut self, _delegate: &mut MenuDelegate) { - unimplemented!() - } - - // Handling tracking? Perhaps just means closing/dismissing the menu? - - #[allow(unused)] - fn cancel_tracking(&mut self) { - unimplemented!() - } - - #[allow(unused)] - fn cancel_tracking_without_animation(&mut self) { - unimplemented!() - } - - // "Notifications" - not sure what these are yet! - // - https://developer.apple.com/documentation/foundation/nsnotificationcenter?language=objc - // - https://developer.apple.com/documentation/foundation/nsnotificationname?language=objc - // - https://developer.apple.com/documentation/foundation/nsnotification?language=objc - - // Conforms to protocols: - // NSAccessibility - wow big guy [...] - // NSAccessibilityElement - // NSAppearanceCustomization - we should probably not allow editing this? - // NSCoding - // NSCopying - // NSUserInterfaceItemIdentification - May become important! -} - -impl PartialEq for NSMenu { - /// Pointer equality - #[inline] - fn eq(&self, other: &Self) -> bool { - core::ptr::eq(self, other) - } -} - -impl fmt::Debug for NSMenu { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - autoreleasepool(|pool| { - f.debug_struct("NSMenu") - .field("id", &(self as *const Self)) - .field("title", &self.title(pool)) - // TODO: parent? - // TODO: size and stuff - .field("items", &self.iter(pool).collect::>()) - .finish() - }) - } -} - -struct Iter<'p> { - array: *const Object, - enumerator: *mut Object, - _p: PhantomData<&'p [&'p NSMenuItem]>, -} - -impl<'p> Iterator for Iter<'p> { - type Item = &'p NSMenuItem; - - fn next(&mut self) -> Option { - let item: *const NSMenuItem = unsafe { msg_send![self.enumerator, nextObject] }; - - if item.is_null() { - None - } else { - Some(unsafe { &*item }) - } - } - - fn size_hint(&self) -> (usize, Option) { - let length: NSUInteger = unsafe { msg_send![self.array, count] }; - (length as usize, Some(length)) - } -} - -impl ExactSizeIterator for Iter<'_> {} - -impl std::iter::FusedIterator for Iter<'_> {} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_title() { - autoreleasepool(|pool| { - let strings: [&str; 5] = [ - "", - "🤖", - "test", - "abcαβγ", - "ศไทย中华Việt Nam β-release 🐱123", - ]; - let mut menu = NSMenu::new(); - assert_eq!(menu.title(pool), ""); - strings.iter().for_each(|&title| { - menu.set_title(title); - assert_eq!(menu.title(pool), title); - }); - }); - } - - #[test] - fn test_title_init() { - let strings: [&str; 5] = [ - "", - "🤖", - "test", - "abcαβγ", - "ศไทย中华Việt Nam β-release 🐱123", - ]; - autoreleasepool(|pool| { - strings.iter().for_each(|&title| { - let menu = NSMenu::new_with_title(title); - assert_eq!(menu.title(pool), title); - }); - }); - } - - #[test] - fn test_length() { - autoreleasepool(|_pool| { - let mut menu = NSMenu::new(); - assert_eq!(menu.len(), 0); - menu.add(NSMenuItem::new_empty()); - assert_eq!(menu.len(), 1); - menu.add(NSMenuItem::new_separator()); - assert_eq!(menu.len(), 2); - menu.add(NSMenuItem::new("test", "", None)); - assert_eq!(menu.len(), 3); - menu.insert(NSMenuItem::new("test", "", None), 2); - assert_eq!(menu.len(), 4); - menu.remove_all(); - assert_eq!(menu.len(), 0); - }); - } - - #[test] - fn test_iter() { - autoreleasepool(|pool| { - let mut menu = NSMenu::new(); - assert!(menu.iter(pool).next().is_none()); - - // A few different iterations - menu.add(NSMenuItem::new_empty()); - menu.add(NSMenuItem::new_empty()); - menu.add(NSMenuItem::new_separator()); - let mut iter = menu.iter(pool); - assert_eq!(iter.size_hint(), (3, Some(3))); - assert!(!iter.next().unwrap().separator()); - assert!(!iter.next().unwrap().separator()); - assert!(iter.next().unwrap().separator()); - assert!(iter.next().is_none()); - - // Modifying after creating the iterator (the iterator is unaffected) - let mut iter = menu.iter(pool); - - menu.add(NSMenuItem::new_empty()); - assert_eq!(iter.size_hint(), (3, Some(3))); - assert!(!iter.next().unwrap().separator()); - - menu.add(NSMenuItem::new_separator()); - assert_eq!(iter.size_hint(), (3, Some(3))); - assert!(!iter.next().unwrap().separator()); - - menu.remove_all(); - assert_eq!(iter.size_hint(), (3, Some(3))); - assert!(iter.next().unwrap().separator()); - - menu.add(NSMenuItem::new_separator()); - assert_eq!(iter.size_hint(), (3, Some(3))); - assert!(iter.next().is_none()); - - // Test fused-ness - assert!(iter.next().is_none()); - assert!(iter.next().is_none()); - assert!(iter.next().is_none()); - assert!(iter.next().is_none()); - }); - } - - #[test] - fn test_max_count() { - autoreleasepool(|_| { - let mut menu = NSMenu::new(); - const COUNT: usize = 65534; - for i in 1..=COUNT { - menu.add(NSMenuItem::new(&format!("item {}", i), "", None)); - } - assert_eq!(menu.len(), COUNT); - - // The menu, if we could render it at this point, should render fine - - menu.add(NSMenuItem::new(&format!("item {}", COUNT + 1), "", None)); - - // The menu item should fail rendering, and we should get an error similar to the following logged: - // 2021-01-01 00:00:00.000 my_program[12345:678901] InsertMenuItemTextWithCFString(_principalMenuRef, (useAccessibilityTitleDescriptionTrick ? CFSTR("") : (CFStringRef)title), carbonIndex - 1, attributes, [self _menuItemCommandID]) returned error -108 on line 2638 in -[NSCarbonMenuImpl _carbonMenuInsertItem:atCarbonIndex:] - }); - } -} diff --git a/frontends/rioterm/src/ui/appkit/menubar.rs b/frontends/rioterm/src/ui/appkit/menubar.rs deleted file mode 100644 index aceb43d6c3..0000000000 --- a/frontends/rioterm/src/ui/appkit/menubar.rs +++ /dev/null @@ -1,52 +0,0 @@ -use super::menu::NSMenu; -use super::menuitem::NSMenuItem; -use objc2::rc::{Id, Owned, Shared}; - -/// Helper to make constructing the menu bar easier -#[derive(Debug)] -pub struct MenuBar(Id); - -impl MenuBar { - #[allow(unused)] - pub unsafe fn from_raw(menu: Id) -> Self { - Self(menu) - } - - pub fn into_raw(self) -> Id { - self.0 - } - - pub fn new(f: impl FnOnce(&mut NSMenu)) -> Self { - // The root menu title is irrelevant - let menu = NSMenu::new(); - let mut menubar = Self(menu); - // The first item's title is irrelevant. - // Not sure if this is the best way to represent this? - let mut first = NSMenu::new(); - f(&mut first); - menubar.add_menu(first); - menubar - } - - fn add_menu(&mut self, menu: Id) -> Id { - // All parameters on menu items irrelevant in the menu bar - let mut item = NSMenuItem::new_empty(); - let menu = item.set_submenu(Some(menu)).unwrap(); - let _item = self.0.add(item); - menu - } - - pub fn add( - &mut self, - title: &str, - f: impl FnOnce(&mut NSMenu), - ) -> Id { - let mut menu = NSMenu::new_with_title(title); - f(&mut menu); - self.add_menu(menu) - } - - // How do we handle this??? - // pub fn title(index) {} - // pub fn set_title(index, title) {} -} diff --git a/frontends/rioterm/src/ui/appkit/menuitem.rs b/frontends/rioterm/src/ui/appkit/menuitem.rs deleted file mode 100644 index 0a93ccaa39..0000000000 --- a/frontends/rioterm/src/ui/appkit/menuitem.rs +++ /dev/null @@ -1,482 +0,0 @@ -use core::{ffi, fmt, ptr}; -use objc2::ffi::NSInteger; -use objc2::rc::{autoreleasepool, AutoreleasePool, Id, Owned, Shared}; -// use objc2::runtime; -use objc2::runtime::{Bool, Object}; -use objc2::{class, msg_send}; -use objc2::{Encoding, Message, RefEncode}; -use objc2_foundation::NSString; -use std::ptr::NonNull; - -use super::menu::NSMenu; -// pub type SEL = runtime::Sel; - -// #[allow(non_camel_case_types)] -// pub type id = *mut runtime::Object; - -// #[inline] -// pub fn selector(name: &str) -> SEL { -// runtime::Sel::register(name) -// } - -#[allow(dead_code)] -struct Target; // Normal NSObject. Should return YES in worksWhenModal. -struct ActionSelector; // objc::Sel - a method selector -#[allow(dead_code)] -struct Image; - -#[derive(Debug, PartialEq)] -pub enum MenuItemState { - /// Checked - On, - Mixed, - /// Unchecked - Off, -} - -#[repr(C)] -pub struct NSMenuItem { - _priv: [u8; 0], -} - -unsafe impl RefEncode for NSMenuItem { - const ENCODING_REF: Encoding<'static> = Encoding::Object; -} - -unsafe impl Message for NSMenuItem {} - -unsafe impl Send for NSMenuItem {} -unsafe impl Sync for NSMenuItem {} - -impl NSMenuItem { - // Defaults: - // State: NSOffState - // On-state image: Check mark - // Mixed-state image: Dash - - fn alloc() -> *mut Self { - unsafe { msg_send![class!(NSMenuItem), alloc] } - } - - // Public only locally to allow for construction in Menubar - pub(super) fn new_empty() -> Id { - let ptr = Self::alloc(); - unsafe { Id::new(msg_send![ptr, init]).unwrap() } - } - - pub fn new( - title: &str, - key_equivalent: &str, - action: Option>, - ) -> Id { - let title = NSString::from_str(title); - let key_equivalent = NSString::from_str(key_equivalent); - let action = if let Some(p) = action { - p.as_ptr() - } else { - ptr::null_mut() - }; - let ptr = Self::alloc(); - unsafe { - Id::new(msg_send![ - ptr, - initWithTitle: &*title, - action: action, - keyEquivalent: &*key_equivalent, - ]) - .unwrap() - } - } - - pub fn new_separator() -> Id { - let ptr: *mut Self = unsafe { msg_send![class!(NSMenuItem), separatorItem] }; - // TODO: Find an ergonomic API where we don't need to retain. Also, - // this has a memory leak if there's no `autoreleasepool` to release - // the returned pointer. - unsafe { Id::retain(ptr).unwrap_unchecked() } - } - - // fn new_separator<'p>(pool: &'p AutoreleasePool) -> &'p mut Self { - // unsafe { msg_send![class!(NSMenuItem), separatorItem] } - // } - - // Enabling - - #[allow(dead_code)] - fn enabled(&self) -> bool { - unimplemented!() - } - - #[allow(dead_code)] - pub fn set_enabled(&mut self, state: bool) { - unsafe { msg_send![self, setEnabled: Bool::new(state)] } - } - - // Managing Hidden Status - - /// Whether the menu item is hidden or not. - /// - /// If hidden, it does not appear in a menu and does not participate in command key matching. - pub fn hidden(&self) -> bool { - let hidden: Bool = unsafe { msg_send![self, isHidden] }; - hidden.is_true() - } - - #[allow(dead_code)] - pub fn set_hidden(&mut self, hidden: bool) { - let hidden = Bool::new(hidden); - unsafe { msg_send![self, setHidden: hidden] } - } - - // fn hidden_or_has_hidden_ancestor(&self) -> bool { - // unimplemented!() - // } - - // Target and action - #[allow(unused)] - fn target(&self) -> Target { - unimplemented!() - } - - // pub fn set_target(&mut self, target: ) { - // unsafe { msg_send![self, setTarget: target] } - // } - - #[allow(unused)] - fn action(&self) -> ActionSelector { - unimplemented!() - } - - #[allow(dead_code)] - fn set_action(&mut self, _action: ActionSelector) { - unimplemented!() - } - - // Title - #[allow(unused)] - pub fn title<'p>(&self, pool: &'p AutoreleasePool) -> &'p str { - let title: &NSString = unsafe { msg_send![self, title] }; - title.as_str(pool) - } - - #[allow(unused)] - pub fn set_title(&mut self, title: &str) { - let title = NSString::from_str(title); - unsafe { msg_send![self, setTitle: &*title] } - } - - // pub fn attributed_title(&self) -> ??? { unimplemented!() } - // pub fn set_attributed_title(&mut self, title: ???) { unimplemented!() } - - // Tag - - #[allow(unused)] - fn tag(&self) -> isize { - unimplemented!() - } - - #[allow(unused)] - fn set_tag(&mut self, _tag: isize) { - unimplemented!() - } - - /// Get the menu item's state - pub fn state(&self) -> MenuItemState { - let state: NSInteger = unsafe { msg_send![self, state] }; - match state { - 1 => MenuItemState::On, - -1 => MenuItemState::Mixed, - 0 => MenuItemState::Off, - _ => unreachable!(), - } - } - - /// Set the menu item's state - #[allow(dead_code)] - pub fn set_state(&mut self, state: MenuItemState) { - // TODO: Link or something to these? - // static const NSControlStateValue NSControlStateValueMixed = -1; - // static const NSControlStateValue NSControlStateValueOff = 0; - // static const NSControlStateValue NSControlStateValueOn = 1; - - let state: NSInteger = match state { - MenuItemState::On => 1, - MenuItemState::Mixed => -1, - MenuItemState::Off => 0, - }; - unsafe { msg_send![self, setState: state] } - } - - // Images - #[allow(unused)] - fn image(&self) -> Option<&Image> { - unimplemented!() - } - - #[allow(unused)] - fn set_image(&mut self, _image: Option<&Image>) { - unimplemented!() - } - - #[allow(unused)] - fn image_for_state<'p>( - &self, - _pool: &'p AutoreleasePool, - _state: MenuItemState, - ) -> Option<&'p Image> { - unimplemented!() - } - - #[allow(unused)] - fn set_image_for_state(&mut self, _state: MenuItemState, _image: Option<&Image>) { - unimplemented!() - } - - // Submenus - - pub fn submenu<'p>(&self, _pool: &'p AutoreleasePool) -> Option<&'p NSMenu> { - unsafe { msg_send![self, submenu] } - } - - pub fn set_submenu( - &mut self, - mut menu: Option>, - ) -> Option> { - // The submenu must not already have a parent! - let ptr = match menu { - Some(ref mut menu) => &mut **menu as *mut NSMenu, - None => ptr::null_mut(), - }; - let _: () = unsafe { msg_send![self, setSubmenu: ptr] }; - menu.map(|obj| obj.into()) - } - #[allow(unused)] - fn has_submenu(&self) -> bool { - unimplemented!() - } - - /// The parent submenu's menuitem#[allow(unused)] - #[allow(unused)] - fn parent_item<'p>(&self, _pool: &'p AutoreleasePool) -> Option<&'p NSMenuItem> { - unimplemented!() - } - #[allow(unused)] - pub fn separator(&self) -> bool { - // TODO: Maybe call this is_separator? - let is_separator: Bool = unsafe { msg_send![self, isSeparatorItem] }; - is_separator.is_true() - } - - // Owning menu - #[allow(unused)] - fn parent_menu<'p>(&self, _pool: &'p AutoreleasePool) -> &'p NSMenu { - unimplemented!() - } - #[allow(unused)] - fn set_parent_menu(&mut self, _menu: &mut NSMenu) { - unimplemented!() - } - - // Handling keyboard events - // fn key_equvalent() - // fn key_equvalent_something_modifiers() - // fn something_user_key_equvalents - // fn user_key_equvalent() (readonly) - - // Marks the menu item as an alternate to the previous menu item - #[allow(unused)] - fn alternate(&self) -> bool { - unimplemented!() - } - #[allow(unused)] - fn set_alternate(&mut self, _alternate: bool) { - unimplemented!() - } - - // Indentation level (0-15) - #[allow(unused)] - fn indentation_level(&self) -> isize { - unimplemented!() - } - #[allow(unused)] - fn set_indentation_level(&mut self, _level: isize) { - unimplemented!() - } - - // Tooltop / help tag - #[allow(unused)] - fn tooltip(&self) -> &str { - unimplemented!() - } - #[allow(unused)] - fn set_tooltip(&mut self, _tooltip: &str) { - unimplemented!() - } - - // Represented object (kinda like tags) - #[allow(unused)] - fn represented_object(&self) -> *const Object { - unimplemented!() - } - #[allow(unused)] - fn set_represented_object(&mut self, _tooltip: *mut Object) { - unimplemented!() - } - - // View - most other attributes are ignore if this is set - #[allow(unused)] - fn view(&self) -> *const Object { - unimplemented!() - } - #[allow(unused)] - fn set_view(&mut self, _tooltip: *mut Object) { - unimplemented!() - } - - /// Get whether the menu should be drawn highlighted - /// - /// You should probably use the [`NSMenu`] delegate method "willHighlightItem" - #[allow(unused)] - fn highlighted(&self) -> bool { - unimplemented!() - } - - // Protocols: Same as NSMenu + "NSValidatedUserInterfaceItem" - // This will have to be researched, is the way for the system to - // automatically enable and disable items based on context -} - -impl PartialEq for NSMenuItem { - /// Pointer equality - #[inline] - fn eq(&self, other: &Self) -> bool { - core::ptr::eq(self, other) - } -} - -impl fmt::Debug for NSMenuItem { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - autoreleasepool(|pool| { - f.debug_struct("NSMenuItem") - .field("id", &(self as *const Self)) - .field("separator", &self.separator()) - .field("title", &self.title(pool)) - .field("hidden", &self.hidden()) - .field("state", &self.state()) - .field("submenu", &self.submenu(pool)) - // TODO: parent? - .finish() - }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - fn for_each_item(_pool: &AutoreleasePool, mut f: impl FnMut(&mut NSMenuItem)) { - f(&mut NSMenuItem::new_separator()); - f(&mut NSMenuItem::new_empty()); - f(&mut NSMenuItem::new("", "", None)); - } - - #[test] - fn test_hidden() { - autoreleasepool(|pool| { - for_each_item(pool, |item| { - assert!(!item.hidden()); - item.set_hidden(true); - assert!(item.hidden()); - item.set_hidden(false); - assert!(!item.hidden()); - }) - }); - } - - #[test] - fn test_title() { - autoreleasepool(|pool| { - let strings: [&str; 5] = [ - "", - "🤖", - "test", - "abcαβγ", - "ศไทย中华Việt Nam β-release 🐱123", - ]; - for_each_item(pool, |item| { - strings.iter().for_each(|&title| { - item.set_title(title); - assert_eq!(item.title(pool), title); - }); - }); - }); - } - - #[test] - fn test_title_init() { - autoreleasepool(|pool| { - let strings: [&str; 5] = [ - "", - "🤖", - "test", - "abcαβγ", - "ศไทย中华Việt Nam β-release 🐱123", - ]; - strings.iter().for_each(|&title| { - let item = NSMenuItem::new(title, "", None); - assert_eq!(item.title(pool), title); - }); - }); - } - - #[test] - fn test_title_default() { - autoreleasepool(|pool| { - let item = NSMenuItem::new_empty(); - assert_eq!(item.title(pool), "NSMenuItem"); - let item = NSMenuItem::new_separator(); - assert_eq!(item.title(pool), ""); - }); - } - - #[test] - fn test_separator() { - autoreleasepool(|_| { - let item = NSMenuItem::new_separator(); - assert!(item.separator()); - let item = NSMenuItem::new_empty(); - assert!(!item.separator()); - let item = NSMenuItem::new("", "", None); - assert!(!item.separator()); - }); - } - - #[test] - fn test_state() { - autoreleasepool(|pool| { - for_each_item(pool, |item| { - assert_eq!(item.state(), MenuItemState::Off); - item.set_state(MenuItemState::On); - assert_eq!(item.state(), MenuItemState::On); - item.set_state(MenuItemState::Mixed); - assert_eq!(item.state(), MenuItemState::Mixed); - item.set_state(MenuItemState::Off); - assert_eq!(item.state(), MenuItemState::Off); - }); - }); - } - - #[test] - fn test_submenu() { - autoreleasepool(|pool| { - for_each_item(pool, |item| { - assert!(item.submenu(pool).is_none()); - let menu = NSMenu::new(); - let menu = item.set_submenu(Some(menu)); - assert_eq!(item.submenu(pool), menu.as_deref()); - item.set_submenu(None); - assert!(item.submenu(pool).is_none()); - }) - }); - } -} diff --git a/frontends/rioterm/src/ui/appkit/mod.rs b/frontends/rioterm/src/ui/appkit/mod.rs deleted file mode 100644 index cdb29bf77f..0000000000 --- a/frontends/rioterm/src/ui/appkit/mod.rs +++ /dev/null @@ -1,69 +0,0 @@ -pub mod global; -pub mod menu; -pub mod menubar; -pub mod menuitem; - -// use core::ptr::NonNull; -use objc2::rc::autoreleasepool; - -pub use self::menubar::MenuBar; -pub use global::InitializedApplication; -pub use menu::NSMenu; -pub use menuitem::{MenuItemState, NSMenuItem}; - -// We need the Objectice-C symbols like NSString, NSMenu and so on to be available -#[link(name = "AppKit", kind = "framework")] -extern "C" {} -#[link(name = "Foundation", kind = "framework")] -extern "C" {} - -pub fn create_toolbar() { - autoreleasepool(|_pool| { - let app = unsafe { InitializedApplication::new() }; - // let menubar = app.menubar(pool).unwrap(); - // // Yeah, this is not ok but we'll do it for now - // let menubar: Id = - // unsafe { Id::retain(NonNull::from(menubar)) }; - // let mut menubar = unsafe { MenuBar::from_raw(menubar) }; - let mut menubar = MenuBar::new(|menu| { - menu.add(NSMenuItem::new(env!("CARGO_PKG_VERSION"), "", None)); - menu.add(NSMenuItem::new_separator()); - menu.add(NSMenuItem::new("Hide Rio", "h", None)); - menu.add(NSMenuItem::new("Quit Rio", "q", None)); - }); - - // let oi = NonNull::new(print as *mut std::ffi::c_void); - - // menubar.add("Shell", |menu| { - // let mut item = NSMenuItem::new("Will be above the window data", "a", oi); - // item.set_enabled(true); - // item.set_target(1) - // menu.add(item); - // }); - - // menubar.add("Edit", |menu| { - // menu.add(NSMenuItem::new("Will be above the window data", "", None)); - // }); - - menubar.add("View", |_menu| { - // menu.add(NSMenuItem::new("Will be above the window data", "", None)); - }); - - let window_menu = menubar.add("Window", |_menu| { - // menu.add(NSMenuItem::new("Will be above the window data", "", None)); - }); - - let help_menu = menubar.add("Help", |_menu| { - // menu.add(NSMenuItem::new("Item 2 : 1", "", None)); - // menu.add(NSMenuItem::new( - // "Search or report issue on Github", - // "", - // None, - // )); - }); - - app.set_window_menu(&window_menu); - app.set_help_menu(Some(&help_menu)); - app.set_menubar(menubar); - }); -} diff --git a/frontends/rioterm/src/ui/mod.rs b/frontends/rioterm/src/ui/mod.rs deleted file mode 100644 index 17518a2062..0000000000 --- a/frontends/rioterm/src/ui/mod.rs +++ /dev/null @@ -1,78 +0,0 @@ -#[cfg(target_os = "macos")] -pub mod appkit; - -#[cfg(windows)] -pub mod win32; - -use core::fmt; - -/// Error that can occur during operation of `menubar`. -pub struct Error(Box); - -enum Impl { - /// Standard input/output error. - #[allow(unused)] - Io(std::io::Error), - - /// A menu already exists. - #[allow(unused)] - MenuExists, - - /// This isn't the window type we expected. - #[allow(unused)] - UnexpectedWindowType, -} - -impl fmt::Debug for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match &*self.0 { - Impl::Io(io) => fmt::Debug::fmt(io, f), - Impl::MenuExists => f.write_str("MenuExists"), - Impl::UnexpectedWindowType => f.write_str("UnexpectedWindowType"), - } - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match &*self.0 { - Impl::Io(io) => fmt::Display::fmt(io, f), - Impl::MenuExists => { - f.write_str("a menu already exists for the given menu target") - } - Impl::UnexpectedWindowType => f.write_str("unexpected window type"), - } - } -} - -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match &*self.0 { - Impl::Io(io) => Some(io), - _ => None, - } - } -} - -impl Error { - #[allow(unused)] - fn last_io_error() -> Self { - Impl::Io(std::io::Error::last_os_error()).into() - } - - #[allow(unused)] - fn menu_exists() -> Self { - Impl::MenuExists.into() - } - - #[allow(unused)] - fn unexpected_window_type() -> Self { - Impl::UnexpectedWindowType.into() - } -} - -impl From for Error { - fn from(value: Impl) -> Self { - Self(Box::new(value)) - } -} diff --git a/frontends/rioterm/src/ui/win32.rs b/frontends/rioterm/src/ui/win32.rs deleted file mode 100644 index 35164b9ea0..0000000000 --- a/frontends/rioterm/src/ui/win32.rs +++ /dev/null @@ -1,503 +0,0 @@ -//! Win32 implementation of menubars. - -use crate::ui::Error; - -use std::cell::{Cell, RefCell}; -use std::collections::HashMap; -use std::ffi::CString; -use std::fmt; -use std::marker::PhantomData; -use std::mem; -use std::num::NonZeroIsize; -use std::ptr; -use std::rc::Rc; - -use windows_sys::Win32::Foundation::{HWND, LPARAM, LRESULT, WPARAM}; - -use windows_sys::Win32::UI::Shell::{DefSubclassProc, SetWindowSubclass}; - -use windows_sys::Win32::UI::WindowsAndMessaging::{ - AppendMenuA, AppendMenuW, CreateMenu, CreatePopupMenu, DestroyMenu, GetMenu, - InsertMenuItemA, SetMenu, SetMenuInfo, MIIM_ID, -}; -use windows_sys::Win32::UI::WindowsAndMessaging::{HMENU, MENUINFO, MENUITEMINFOA}; -use windows_sys::Win32::UI::WindowsAndMessaging::{ - MFT_SEPARATOR, MFT_STRING, MF_POPUP, MF_SEPARATOR, MF_STRING, MIIM_DATA, MIIM_FTYPE, - MIIM_STRING, MIIM_SUBMENU, MIIM_TYPE, MIM_STYLE, MNS_NOTIFYBYPOS, WM_COMMAND, - WM_MENUCOMMAND, WM_NCDESTROY, -}; - -macro_rules! syscall { - // The null value (0) is an error. - (nul $fname: ident $($args: tt)*) => {{ - let res = unsafe { $fname $($args)* }; - if res == 0 { - return Err(crate::ui::Error::last_io_error()); - } else { - res - } - }} -} - -// No one else should use this very unique ID. -const SUBCLASS_ID: usize = 4 * 8 * 15 * 16 * 23 * 42; - -/// A handle that manages a menu key. -struct MenuKeyHandle { - key: MenuKey, - unsend: PhantomData<*mut ()>, -} - -impl MenuKeyHandle { - /// Create a new menu key handle. - fn new() -> Self { - std::thread_local! { - static SLOT_LIST: RefCell = RefCell::new(SlotList { - menu_keys: Vec::new(), - next_key: 0, - len: 0, - }); - } - - struct SlotList { - /// A list of menu keys indicating whether they have been freed or not. - menu_keys: Vec, - - /// The next menu key to use. - next_key: u16, - - /// The current number of occupied slots in the menu key list. - len: u16, - } - - enum Slot { - /// This slot is occupied. - Occupied, - - /// This slot is free. - /// - /// The value inside is the value that should be set to `NEXT_KEY` - /// when this slot is occupied. - Vacant(u16), - } - - impl Drop for MenuKeyHandle { - fn drop(&mut self) { - // Free a slot in the key list. - let _ = SLOT_LIST.try_with(|slot_list| { - let mut slot_list = slot_list.borrow_mut(); - - // Decrement length by one. - let new_len = slot_list - .len - .checked_sub(1) - .expect("menu key list is corrupt"); - slot_list.len = new_len; - - // Mark the slot at vacant. - let our_key = self.key.0; - slot_list.menu_keys[our_key as usize] = - Slot::Vacant(slot_list.next_key); - slot_list.next_key = our_key; - }); - } - } - - SLOT_LIST.with(|slot_list| { - let mut slot_list = slot_list.borrow_mut(); - let our_key = slot_list.next_key; - - // Increment length by one. - { - let new_len = slot_list.len.checked_add(1).expect("too many menu keys"); - slot_list.len = new_len; - } - - if slot_list.next_key == slot_list.menu_keys.len() as _ { - // Allocate a new slot at the end of the list. - slot_list.menu_keys.push(Slot::Occupied); - slot_list.next_key += 1; - } else { - // Take the vacant slot. - slot_list.next_key = match slot_list.menu_keys.get(our_key as usize) { - Some(Slot::Vacant(next_key)) => *next_key, - _ => panic!("menu key list is corrupt"), - }; - slot_list.menu_keys[our_key as usize] = Slot::Occupied; - } - - MenuKeyHandle { - key: MenuKey(our_key), - unsend: PhantomData, - } - }) - } - - /// Get the underlying menu key. - fn key(&self) -> MenuKey { - self.key - } -} - -/// Key given to a menu. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -struct MenuKey(u16); - -/// Key given to an item in a menu. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -struct ItemKey(u32); - -impl ItemKey { - /// Create a new `ItemKey` from a menu handle and an item ID. - fn new(menu: MenuKey, id: u16) -> Self { - ItemKey(((menu.0 as u32) << 16) | (id as u32)) - } - - /// Get the menu key. - fn menu(&self) -> MenuKey { - MenuKey((self.0 >> 16) as u16) - } - - /// Get the item ID. - fn id(&self) -> u16 { - self.0 as u16 - } -} - -unsafe extern "system" fn menu_subclass_proc( - hwnd: HWND, - msg: u32, - wparam: WPARAM, - lparam: LPARAM, - uidsubclass: usize, - refdata: usize, -) -> LRESULT { - abort_on_panic(move || { - // Early out. - macro_rules! early_out { - () => {{ - return DefSubclassProc(hwnd, msg, wparam, lparam); - }}; - } - - macro_rules! leap { - ($e: expr) => {{ - match $e { - Some(e) => e, - None => early_out!(), - } - }}; - } - - // If we are being destroyed, free our refdata. - if msg == WM_NCDESTROY { - drop(Box::from_raw(refdata as *mut WindowData)); - early_out!(); - } else if msg == WM_COMMAND { - // Get a reference to the hash map containing our menu item data. - let map_cell = &*(refdata as *const WindowData); - - // Shouldn't be called reentrantly. - let mut map = map_cell.data.borrow_mut(); - - // Get the item key. - let key = ItemKey(wparam as u32); - - // Get the item data. - let data = leap!(map.get_mut(&key)); - - // Call the handler. - (data.handler)(); - } - - early_out!(); - }) -} - -#[doc(hidden)] -pub enum Empty {} - -pub struct Hotkey<'a> { - // TODO - key: &'a str, -} - -type DataTable = HashMap; - -struct WindowData { - /// The table of menu item data. - data: RefCell, - - /// The menu IDs we are currently holding. - _ids: Vec, -} - -/// A menu to be attached to a window. -pub struct Menu { - /// Handle to the menu. - /// - /// This is semantically an `HMENU`, but we use `NonZeroIsize` to avoid - /// allocating an extra pointer here. - /// - /// This is also used to uniquely identify the menu. - menu: Option, - - /// Data associated with the menu. - data: DataTable, - - /// The menu IDs we are currently holding. - menu_id: Vec, - - /// The next item ID to use. - next_id: u16, - - /// Menus are not thread-safe. - _marker: PhantomData<*mut ()>, -} - -impl Menu { - /// Create a new menu. - pub fn new() -> Result { - // Create the menu. - let menu = syscall!(nul CreateMenu()); - - unsafe { Ok(Menu::from_hmenu(menu)) } - } - - /// Create a new, empty popup menu. - pub fn new_popup() -> Result { - // Create the menu. - let menu = syscall!(nul CreatePopupMenu()); - - unsafe { Ok(Menu::from_hmenu(menu)) } - } - - unsafe fn from_hmenu(menu: HMENU) -> Self { - Menu { - menu: Some(NonZeroIsize::new_unchecked(menu)), - data: DataTable::with_hasher(ahash::RandomState::new()), - menu_id: vec![MenuKeyHandle::new()], - next_id: 0, - _marker: PhantomData, - } - } - - /// Add a new menu item to the menu. - pub fn push<'t, 'h, H: MenuItemHandler>( - &mut self, - item: impl Into>, - ) -> Result<(), Error> { - // Create the menu item. - let item = item.into(); - let hmenu = self.menu.unwrap().get(); - - match item.inner { - Inner::Separator => { - syscall!(nul AppendMenuA(hmenu, MF_SEPARATOR, 0, ptr::null_mut())); - } - - Inner::Submenu { text, mut submenu } => { - // Menu item is a submenu. - let handle = submenu.menu.take().unwrap().get(); - let items = mem::replace( - &mut submenu.data, - DataTable::with_hasher(ahash::RandomState::new()), - ); - - // Append items to our items. - self.data.extend(items.into_iter()); - - let text = CString::new(text).unwrap(); - syscall!(nul AppendMenuA(hmenu, MF_POPUP, handle as _, text.as_ptr().cast())); - } - - Inner::Item { - text, - hotkey, - mut handler, - } => { - // Create a new item key. - let key = { - let new_key = ItemKey::new(self.menu_id[0].key, self.next_id); - - let next_id = - self.next_id.checked_add(1).expect("menu item ID overflow"); - self.next_id = next_id; - - new_key - }; - - // Add this key to our map. - self.data.insert( - key, - MenuItemData { - handler: Box::new(move || handler.invoke()), - }, - ); - - let text = CString::new(text).unwrap(); - syscall!(nul AppendMenuA(hmenu, MF_STRING, key.0 as _, text.as_ptr().cast())); - } - }; - - Ok(()) - } - - /// Apply this menu to a raw window handle. - pub fn apply( - self, - handle: impl raw_window_handle::HasRawWindowHandle, - ) -> Result<(), Error> { - match handle.raw_window_handle() { - raw_window_handle::RawWindowHandle::Win32(handle) => unsafe { - if handle.hwnd.is_null() { - return Err(Error::unexpected_window_type()); - } - - self.apply_to_hwnd(handle.hwnd as _) - }, - _ => Err(Error::unexpected_window_type()), - } - } - - /// Apply this menu to a window. - unsafe fn apply_to_hwnd(mut self, hwnd: HWND) -> Result<(), Error> { - // If the window already has a menu, error out. We don't want to step on any toes. - let old_menu = GetMenu(hwnd); - if old_menu != 0 { - return Err(Error::menu_exists()); - } - - // Set the menu. - SetMenu(hwnd, self.menu.take().unwrap().get()); - - // Add a subclass to the window. - let data = mem::replace( - &mut self.data, - HashMap::with_hasher(ahash::RandomState::new()), - ); - let data = Box::into_raw(Box::new(data)); - SetWindowSubclass(hwnd, Some(menu_subclass_proc), SUBCLASS_ID, data as _); - - Ok(()) - } -} - -impl Drop for Menu { - fn drop(&mut self) { - if let Some(menu) = self.menu { - unsafe { - DestroyMenu(menu.get()); - } - } - } -} - -/// Data associated with each menu item. -struct MenuItemData { - /// The handler for the menu item. - handler: Box, -} - -/// A menu item. -pub struct MenuItem<'txt, 'hotkey, Handler = Empty> { - inner: Inner<'txt, 'hotkey, Handler>, -} - -enum Inner<'txt, 'hotkey, Handler> { - /// This is a regular menu item. - Item { - /// The text of the menu item. - text: &'txt str, - - /// Handler for the menu item. - hotkey: Option>, - - /// Handler for the menu item. - handler: Handler, - }, - - /// This is a separator. - Separator, - - /// This is a submenu. - Submenu { - /// The text of the menu item. - text: &'txt str, - - /// The handle to the submenu. - submenu: Menu, - }, -} - -impl MenuItem<'static, 'static> { - /// Create a new separator. - pub fn separator() -> Self { - MenuItem { - inner: Inner::Separator, - } - } -} - -impl<'txt> MenuItem<'txt, 'static> { - /// Create a drop-down menu item. - pub fn submenu(text: &'txt str, submenu: Menu) -> Self { - MenuItem { - inner: Inner::Submenu { text, submenu }, - } - } -} - -impl<'txt, 'hotkey, Handler: MenuItemHandler> MenuItem<'txt, 'hotkey, Handler> { - /// Create a new menu item. - pub fn new( - text: &'txt str, - hotkey: Option>, - handler: Handler, - ) -> Self { - MenuItem { - inner: Inner::Item { - text, - hotkey, - handler, - }, - } - } -} - -/// Callback for invoking a menu item's functionality. -/// -/// This is implemented for all `F` where `F: FnMut()`. -pub trait MenuItemHandler: 'static { - fn invoke(&mut self); -} - -impl MenuItemHandler for Empty { - fn invoke(&mut self) { - match *self {} - } -} - -impl MenuItemHandler for F -where - F: FnMut() + 'static, -{ - fn invoke(&mut self) { - self(); - } -} - -fn abort_on_panic(f: impl FnOnce() -> R) -> R { - struct Bomb; - - impl Drop for Bomb { - fn drop(&mut self) { - std::process::abort(); - } - } - - let bomb = Bomb; - let r = f(); - mem::forget(bomb); - r -} diff --git a/frontends/rioterm/src/watch.rs b/frontends/rioterm/src/watcher.rs similarity index 82% rename from frontends/rioterm/src/watch.rs rename to frontends/rioterm/src/watcher.rs index 78570be260..8c6898be5c 100644 --- a/frontends/rioterm/src/watch.rs +++ b/frontends/rioterm/src/watcher.rs @@ -2,11 +2,11 @@ use crate::event::{EventListener, RioEvent}; use notify::{Config, EventKind, RecommendedWatcher, RecursiveMode, Watcher}; use std::path::Path; use std::time::Duration; -use winit::window::WindowId; const POLLING_TIMEOUT: Duration = Duration::from_secs(2); -pub fn watch< +#[allow(unused)] +pub fn configuration_file_updates< P: AsRef + std::marker::Send + 'static, T: EventListener + std::marker::Send + 'static, >( @@ -39,8 +39,13 @@ pub fn watch< | EventKind::Modify(_) | EventKind::Other => { log::info!("config directory has dispatched an event {event:?}"); - // TODO: Refactor to send_global_event - event_proxy.send_event(RioEvent::UpdateConfig, WindowId::from(0)); + #[cfg(target_os = "macos")] + event_proxy.send_event(RioEvent::UpdateConfig, 0); + #[cfg(not(target_os = "macos"))] + event_proxy.send_event( + RioEvent::UpdateConfig, + rio_backend::event::WindowId::from(0), + ); } _ => (), }, diff --git a/rio-backend/Cargo.toml b/rio-backend/Cargo.toml index 1b56c4e2cd..33d14a6f6a 100644 --- a/rio-backend/Cargo.toml +++ b/rio-backend/Cargo.toml @@ -37,12 +37,12 @@ copa = { workspace = true } wgpu = { workspace = true } cursor-icon = { version = "1.0.0", default-features = false } smallvec = { version = "1.9.0", default-features = false } -winit = { workspace = true, optional = true } + +[target.'cfg(not(target_os = "macos"))'.dependencies] +winit = { workspace = true } [features] default = ["wayland", "x11"] -wa = [] -winit = ["dep:winit"] x11 = [ "copypasta/x11" ] diff --git a/rio-backend/src/crosswords/mod.rs b/rio-backend/src/crosswords/mod.rs index b331aa437b..fbb14b569c 100644 --- a/rio-backend/src/crosswords/mod.rs +++ b/rio-backend/src/crosswords/mod.rs @@ -475,10 +475,10 @@ impl Crosswords { // Damage everything if display offset changed. if old_display_offset != self.grid.display_offset() { self.mark_fully_damaged(); - #[cfg(not(feature = "winit"))] + #[cfg(target_os = "macos")] self.event_proxy.send_redraw(self.window_id); - #[cfg(feature = "winit")] + #[cfg(not(target_os = "macos"))] self.event_proxy .send_event(RioEvent::Wakeup, self.window_id); } @@ -2741,9 +2741,9 @@ mod tests { #[test] fn scroll_up() { let size = CrosswordsSize::new(1, 10); - #[cfg(feature = "winit")] + #[cfg(not(target_os = "macos"))] let window_id = crate::event::WindowId::from(0); - #[cfg(not(feature = "winit"))] + #[cfg(target_os = "macos")] let window_id = 0; let mut cw = Crosswords::new(size, CursorShape::Block, VoidListener {}, window_id); @@ -2778,9 +2778,9 @@ mod tests { #[test] fn test_linefeed() { let size = CrosswordsSize::new(1, 1); - #[cfg(feature = "winit")] + #[cfg(not(target_os = "macos"))] let window_id = crate::event::WindowId::from(0); - #[cfg(not(feature = "winit"))] + #[cfg(target_os = "macos")] let window_id = 0; let mut cw = @@ -2795,9 +2795,9 @@ mod tests { fn test_linefeed_moving_cursor() { let size = CrosswordsSize::new(1, 3); - #[cfg(feature = "winit")] + #[cfg(not(target_os = "macos"))] let window_id = crate::event::WindowId::from(0); - #[cfg(not(feature = "winit"))] + #[cfg(target_os = "macos")] let window_id = 0; let mut cw = @@ -2824,9 +2824,9 @@ mod tests { #[test] fn test_input() { let size = CrosswordsSize::new(5, 10); - #[cfg(feature = "winit")] + #[cfg(not(target_os = "macos"))] let window_id = crate::event::WindowId::from(0); - #[cfg(not(feature = "winit"))] + #[cfg(target_os = "macos")] let window_id = 0; let mut cw = @@ -2849,9 +2849,9 @@ mod tests { #[test] fn simple_selection_works() { let size = CrosswordsSize::new(5, 5); - #[cfg(feature = "winit")] + #[cfg(not(target_os = "macos"))] let window_id = crate::event::WindowId::from(0); - #[cfg(not(feature = "winit"))] + #[cfg(target_os = "macos")] let window_id = 0; let mut term = @@ -2927,9 +2927,9 @@ mod tests { #[test] fn line_selection_works() { let size = CrosswordsSize::new(5, 1); - #[cfg(feature = "winit")] + #[cfg(not(target_os = "macos"))] let window_id = crate::event::WindowId::from(0); - #[cfg(not(feature = "winit"))] + #[cfg(target_os = "macos")] let window_id = 0; let mut term = @@ -2957,9 +2957,9 @@ mod tests { #[test] fn block_selection_works() { let size = CrosswordsSize::new(5, 5); - #[cfg(feature = "winit")] + #[cfg(not(target_os = "macos"))] let window_id = crate::event::WindowId::from(0); - #[cfg(not(feature = "winit"))] + #[cfg(target_os = "macos")] let window_id = 0; let mut term = @@ -3035,9 +3035,9 @@ mod tests { #[test] fn test_search_nearest_hyperlink_from_pos_on_single_line() { let size = CrosswordsSize::new(20, 3); - #[cfg(feature = "winit")] + #[cfg(not(target_os = "macos"))] let window_id = crate::event::WindowId::from(0); - #[cfg(not(feature = "winit"))] + #[cfg(target_os = "macos")] let window_id = 0; let mut term = Crosswords::new(size, CursorShape::Block, VoidListener {}, window_id); @@ -3172,9 +3172,9 @@ mod tests { #[test] fn test_search_nearest_hyperlink_from_pos_on_multiple_lines() { let size = CrosswordsSize::new(4, 4); - #[cfg(feature = "winit")] + #[cfg(not(target_os = "macos"))] let window_id = crate::event::WindowId::from(0); - #[cfg(not(feature = "winit"))] + #[cfg(target_os = "macos")] let window_id = 0; let mut term = Crosswords::new(size, CursorShape::Block, VoidListener {}, window_id); @@ -3255,9 +3255,9 @@ mod tests { #[test] fn test_search_nearest_hyperlink_from_pos_on_existent_hyperlink() { let size = CrosswordsSize::new(4, 4); - #[cfg(feature = "winit")] + #[cfg(not(target_os = "macos"))] let window_id = crate::event::WindowId::from(0); - #[cfg(not(feature = "winit"))] + #[cfg(target_os = "macos")] let window_id = 0; let mut term = Crosswords::new(size, CursorShape::Block, VoidListener {}, window_id); diff --git a/rio-backend/src/crosswords/vi_mode.rs b/rio-backend/src/crosswords/vi_mode.rs index 58559efcff..85fbef4510 100644 --- a/rio-backend/src/crosswords/vi_mode.rs +++ b/rio-backend/src/crosswords/vi_mode.rs @@ -421,9 +421,9 @@ mod tests { size, CursorShape::Underline, VoidListener, - #[cfg(feature = "winit")] + #[cfg(not(target_os = "macos"))] crate::event::WindowId::from(0), - #[cfg(not(feature = "winit"))] + #[cfg(target_os = "macos")] 0, ) } diff --git a/rio-backend/src/event/mod.rs b/rio-backend/src/event/mod.rs index 60859efc98..267dca0483 100644 --- a/rio-backend/src/event/mod.rs +++ b/rio-backend/src/event/mod.rs @@ -10,16 +10,16 @@ use std::fmt::Formatter; use std::sync::Arc; use teletypewriter::WinsizeBuilder; -#[cfg(feature = "winit")] +#[cfg(not(target_os = "macos"))] use winit::event_loop::EventLoopProxy; -#[cfg(feature = "winit")] +#[cfg(not(target_os = "macos"))] pub type WindowId = winit::window::WindowId; -#[cfg(not(feature = "winit"))] +#[cfg(target_os = "macos")] pub type WindowId = u16; -#[cfg(feature = "winit")] +#[cfg(not(target_os = "macos"))] #[derive(Debug, Clone)] pub enum RioEventType { Rio(RioEvent), @@ -28,7 +28,7 @@ pub enum RioEventType { BlinkCursorTimeout, } -#[cfg(not(feature = "winit"))] +#[cfg(target_os = "macos")] pub type RioEventType = RioEvent; #[derive(Debug)] @@ -225,7 +225,7 @@ pub trait EventListener { #[derive(Clone)] pub struct VoidListener; -#[cfg(feature = "winit")] +#[cfg(not(target_os = "macos"))] impl From for RioEventType { fn from(rio_event: RioEvent) -> Self { Self::Rio(rio_event) @@ -238,13 +238,13 @@ impl EventListener for VoidListener { } } -#[cfg(feature = "winit")] +#[cfg(not(target_os = "macos"))] #[derive(Debug, Clone)] pub struct EventProxy { proxy: EventLoopProxy, } -#[cfg(feature = "winit")] +#[cfg(not(target_os = "macos"))] impl EventProxy { pub fn new(proxy: EventLoopProxy) -> Self { Self { proxy } @@ -260,7 +260,7 @@ impl EventProxy { // } } -#[cfg(feature = "winit")] +#[cfg(not(target_os = "macos"))] impl EventListener for EventProxy { fn event(&self) -> (std::option::Option, bool) { (None, false) diff --git a/rio-backend/src/lib.rs b/rio-backend/src/lib.rs index a3a982cd7d..6ecf3622f4 100644 --- a/rio-backend/src/lib.rs +++ b/rio-backend/src/lib.rs @@ -7,7 +7,7 @@ pub mod event; pub mod performer; pub mod selection; -#[cfg(not(feature = "winit"))] +#[cfg(target_os = "macos")] pub mod superloop; pub use sugarloaf; diff --git a/rio-backend/src/performer/mod.rs b/rio-backend/src/performer/mod.rs index e2f58adb47..838707d7bd 100644 --- a/rio-backend/src/performer/mod.rs +++ b/rio-backend/src/performer/mod.rs @@ -2,7 +2,7 @@ pub mod handler; use crate::crosswords::Crosswords; use crate::event::sync::FairMutex; -#[cfg(feature = "winit")] +#[cfg(not(target_os = "macos"))] use crate::event::RioEvent; use crate::event::{EventListener, Msg, WindowId}; use corcovado::channel; @@ -190,10 +190,10 @@ where // Queue terminal redraw unless all processed bytes were synchronized. if state.parser.sync_bytes_count() < processed && processed > 0 { - #[cfg(not(feature = "winit"))] + #[cfg(target_os = "macos")] self.event_proxy.send_redraw(self.window_id); - #[cfg(feature = "winit")] + #[cfg(not(target_os = "macos"))] self.event_proxy .send_event(RioEvent::Wakeup, self.window_id); } @@ -310,10 +310,10 @@ where // Handle synchronized update timeout. if events.is_empty() { state.parser.stop_sync(&mut *self.terminal.lock()); - #[cfg(not(feature = "winit"))] + #[cfg(target_os = "macos")] self.event_proxy.send_redraw(self.window_id); - #[cfg(feature = "winit")] + #[cfg(not(target_os = "macos"))] self.event_proxy .send_event(RioEvent::Wakeup, self.window_id); @@ -345,10 +345,10 @@ where // self.event_proxy // .send_event(RioEvent::Wakeup, self.window_id); - #[cfg(not(feature = "winit"))] + #[cfg(target_os = "macos")] self.event_proxy.send_redraw(self.window_id); - #[cfg(feature = "winit")] + #[cfg(not(target_os = "macos"))] self.event_proxy .send_event(RioEvent::Wakeup, self.window_id); diff --git a/rio-backend/src/selection.rs b/rio-backend/src/selection.rs index 278473129c..9d5b966037 100644 --- a/rio-backend/src/selection.rs +++ b/rio-backend/src/selection.rs @@ -448,9 +448,9 @@ mod tests { fn term(height: usize, width: usize) -> Crosswords { let size = CrosswordsSize::new(width, height); - #[cfg(feature = "winit")] + #[cfg(not(target_os = "macos"))] let window_id = crate::event::WindowId::from(0); - #[cfg(not(feature = "winit"))] + #[cfg(target_os = "macos")] let window_id = 0; Crosswords::new(size, CursorShape::Block, VoidListener {}, window_id)