From ff3cdf8be1e4d40592034dfe6598e9423e8392cc Mon Sep 17 00:00:00 2001 From: Oleg Orlov Date: Thu, 5 Feb 2026 21:14:08 +0300 Subject: [PATCH 01/26] Add scroll_to_bottom() method to TerminalBackend This method allows applications to reset the terminal scroll position to the bottom, which is useful after clearing the terminal screen. Fixes scrolling issues after clear command. --- src/backend/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 4b3e43c..9d6f2d7 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -302,6 +302,12 @@ impl TerminalBackend { self.pty_id } + pub fn scroll_to_bottom(&mut self) { + let term = self.term.clone(); + let mut term = term.lock(); + term.scroll_display(Scroll::Bottom); + } + fn process_link_action( &mut self, terminal: &Term, From edf4f02707e1770a3047c143ffc22bd9f633e00d Mon Sep 17 00:00:00 2001 From: Oleg Orlov Date: Thu, 5 Feb 2026 21:24:23 +0300 Subject: [PATCH 02/26] Add clear_history() method to TerminalBackend This method allows clearing the terminal scrollback history, which is useful after clear command to reset terminal size and scroll position. --- src/backend/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 9d6f2d7..75ae83d 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -308,6 +308,12 @@ impl TerminalBackend { term.scroll_display(Scroll::Bottom); } + pub fn clear_history(&mut self) { + let term = self.term.clone(); + let mut term = term.lock(); + term.grid_mut().clear_history(); + } + fn process_link_action( &mut self, terminal: &Term, From 86e4379bc9033c1972f09de803d35beab0a89fee Mon Sep 17 00:00:00 2001 From: Oleg Orlov Date: Thu, 5 Feb 2026 22:34:29 +0300 Subject: [PATCH 03/26] Fix scroll handling in alternate screen mode with MOUSE_MODE Fixes mouse wheel scrolling in ncurses-based applications (opencode, tmux, less, vim) that use alternate screen mode. Changes: - Fix terminal mode priority: MOUSE_MODE now takes precedence over ALTERNATE_SCROLL - Separate keyboard/mouse event handling: keyboard requires focus, mouse only requires pointer over widget - Rewrite process_mouse_wheel() to send correct SGR mouse wheel reports when MOUSE_MODE is active - Simplify backend::scroll() to only handle viewport scrolling - Invert scroll direction to match standard behavior This matches alacrity's scroll handling priority order and allows applications with mouse support to receive proper mouse wheel events instead of arrow key sequences. --- notes/ALTERNATE_SCROLL.md | 81 ++++++++++++++++++++++++ src/backend/mod.rs | 18 +----- src/view.rs | 128 +++++++++++++++++++++++++------------- 3 files changed, 166 insertions(+), 61 deletions(-) create mode 100644 notes/ALTERNATE_SCROLL.md diff --git a/notes/ALTERNATE_SCROLL.md b/notes/ALTERNATE_SCROLL.md new file mode 100644 index 0000000..70ca57b --- /dev/null +++ b/notes/ALTERNATE_SCROLL.md @@ -0,0 +1,81 @@ +# ALTERNATE_SCROLL Fix + +## Problem + +Mouse wheel scrolling was not working correctly in applications using ncurses with alternate screen mode (e.g., opencode, tmux, less, vim). Instead of scrolling the content within the application, the terminal was sending arrow key sequences (ESC O A / ESC O B). + +## Root Cause + +The issue was in the scroll handling logic in `src/view.rs` and `src/backend/mod.rs`: + +1. **Incorrect priority check**: The condition `terminal.mode().contains(TermMode::ALTERNATE_SCROLL | TermMode::ALT_SCREEN)` would return `true` even when `MOUSE_MODE` was active, because `contains()` checks if ANY of the flags are present. + +2. **Missing terminal mode priority**: When `MOUSE_MODE` (specifically `SGR_MOUSE`) is active in ncurses applications, the application expects mouse wheel events via SGR protocol, not arrow key sequences. + +3. **Focus/pointer check issue**: All input events were blocked if either focus was lost OR pointer was outside widget, which prevented scroll from working even when the mouse was over the widget. + +## Solution + +### 1. Corrected terminal mode priority + +Updated scroll handling in `src/view.rs` to follow alacrity's priority order: + +```rust +// Priority 1: MOUSE_MODE (SGR_MOUSE active) → Send mouse wheel report +if terminal_mode.intersects(TermMode::MOUSE_MODE) { + // Send SGR mouse wheel report +} +// Priority 2: ALT_SCREEN | ALTERNATE_SCROLL → Send arrow keys +else if terminal_mode.contains(TermMode::ALT_SCREEN | TermMode::ALTERNATE_SCROLL) { + // Send ESC O A / ESC O B +} +// Priority 3: Default → Scroll viewport +else { + // Scroll terminal viewport +} +``` + +Key changes: +- Use `intersects()` for `MOUSE_MODE` check (it's a combination of multiple flags) +- Ensure MOUSE_MODE is checked BEFORE ALTERNATE_SCROLL +- Use `contains()` for ALT_SCREEN | ALTERNATE_SCROLL combination + +### 2. Separated keyboard and mouse event handling + +Modified `process_input()` in `src/view.rs` to have different focus requirements: + +- **Keyboard events** (Text, Key, Copy, Paste): Require `layout.has_focus()` +- **Mouse events** (MouseWheel, PointerButton, PointerMoved): Require `layout.contains_pointer()` + +This allows mouse wheel to work even when keyboard focus is on different widgets. + +### 3. Inverted scroll direction + +Adjusted scroll direction to match standard behavior: + +- **Mouse wheel up** → Scroll content up (send ScrollUp / ESC O A) +- **Mouse wheel down** → Scroll content down (send ScrollDown / ESC O B) + +## Files Changed + +- `src/view.rs`: + - Modified `process_input()` to separate keyboard/mouse event handling + - Rewrote `process_mouse_wheel()` with correct terminal mode priority + - Removed rigid focus check at the start + +- `src/backend/mod.rs`: + - Simplified `scroll()` function to only handle viewport scrolling + +## Testing + +Tested with opencode (ncurses-based application) in alternate screen mode: +- ✅ Mouse wheel now correctly scrolls application content +- ✅ Mouse wheel events are sent via SGR protocol when MOUSE_MODE is active +- ✅ Scroll direction matches standard behavior +- ✅ Works without requiring explicit focus + +## References + +- Alacrity scroll handling: `alacritty/src/input/mod.rs` - `scroll_terminal()` function +- Terminal mode definitions: `alacritty_terminal/src/term/mod.rs` - `TermMode` enum +- DEC Private Mode 1007 (ALTERNATE_SCROLL): Controls arrow key substitution for scroll events diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 75ae83d..89d205b 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -522,23 +522,7 @@ impl TerminalBackend { fn scroll(&mut self, terminal: &mut Term, delta_value: i32) { if delta_value != 0 { let scroll = Scroll::Delta(delta_value); - if terminal - .mode() - .contains(TermMode::ALTERNATE_SCROLL | TermMode::ALT_SCREEN) - { - let line_cmd = if delta_value > 0 { b'A' } else { b'B' }; - let mut content = vec![]; - - for _ in 0..delta_value.abs() { - content.push(0x1b); - content.push(b'O'); - content.push(line_cmd); - } - - self.notifier.notify(content); - } else { - terminal.grid_mut().scroll_display(scroll); - } + terminal.grid_mut().scroll_display(scroll); } } diff --git a/src/view.rs b/src/view.rs index cdbccdc..a6a8394 100644 --- a/src/view.rs +++ b/src/view.rs @@ -144,10 +144,6 @@ impl<'a> TerminalView<'a> { layout: &Response, state: &mut TerminalViewState, ) -> Self { - if !layout.has_focus() || !layout.contains_pointer() { - return self; - } - let modifiers = layout.ctx.input(|i| i.modifiers); let events = layout.ctx.input(|i| i.events.clone()); for event in events { @@ -158,44 +154,58 @@ impl<'a> TerminalView<'a> { | egui::Event::Key { .. } | egui::Event::Copy | egui::Event::Paste(_) => { - input_actions.push(process_keyboard_event( - event, - self.backend, - &self.bindings_layout, - modifiers, - )) + if layout.has_focus() { + input_actions.push(process_keyboard_event( + event, + self.backend, + &self.bindings_layout, + modifiers, + )) + } + }, + egui::Event::MouseWheel { unit, delta, .. } => { + let terminal_mode = + self.backend.last_content().terminal_mode; + if layout.contains_pointer() { + input_actions.push(process_mouse_wheel( + state, + self.font.font_type().size, + unit, + delta, + self.backend, + )) + } }, - egui::Event::MouseWheel { unit, delta, .. } => input_actions - .push(process_mouse_wheel( - state, - self.font.font_type().size, - unit, - delta, - )), egui::Event::PointerButton { button, pressed, modifiers, pos, .. - } => input_actions.push(process_button_click( - state, - layout, - self.backend, - &self.bindings_layout, - button, - pos, - &modifiers, - pressed, - )), + } => { + if layout.contains_pointer() { + input_actions.push(process_button_click( + state, + layout, + self.backend, + &self.bindings_layout, + button, + pos, + &modifiers, + pressed, + )) + } + }, egui::Event::PointerMoved(pos) => { - input_actions = process_mouse_move( - state, - layout, - self.backend, - pos, - &modifiers, - ) + if layout.contains_pointer() { + input_actions = process_mouse_move( + state, + layout, + self.backend, + pos, + &modifiers, + ) + } }, _ => {}, }; @@ -463,23 +473,53 @@ fn process_mouse_wheel( font_size: f32, unit: MouseWheelUnit, delta: Vec2, + backend: &TerminalBackend, ) -> InputAction { - match unit { + let lines = match unit { MouseWheelUnit::Line => { - let lines = delta.y.signum() * delta.y.abs().ceil(); - InputAction::BackendCall(BackendCommand::Scroll(lines as i32)) + (delta.y.signum() * delta.y.abs().ceil()) as i32 }, MouseWheelUnit::Point => { state.scroll_pixels -= delta.y; - let lines = (state.scroll_pixels / font_size).trunc(); + let lines = (state.scroll_pixels / font_size).trunc() as i32; state.scroll_pixels %= font_size; - if lines != 0.0 { - InputAction::BackendCall(BackendCommand::Scroll(-lines as i32)) - } else { - InputAction::Ignore - } + lines }, - MouseWheelUnit::Page => InputAction::Ignore, + MouseWheelUnit::Page => 0, + }; + + if lines == 0 { + return InputAction::Ignore; + } + + let terminal_mode = backend.last_content().terminal_mode; + + if terminal_mode.intersects(crate::backend::TerminalMode::MOUSE_MODE) { + let mouse_btn = if lines > 0 { + crate::backend::MouseButton::ScrollDown + } else { + crate::backend::MouseButton::ScrollUp + }; + InputAction::BackendCall(BackendCommand::MouseReport( + mouse_btn, + egui::Modifiers::NONE, + state.current_mouse_position_on_grid, + true, + )) + } else if terminal_mode.contains( + crate::backend::TerminalMode::ALT_SCREEN + | crate::backend::TerminalMode::ALTERNATE_SCROLL, + ) { + let line_cmd = if lines > 0 { b'B' } else { b'A' }; + let mut content = vec![]; + for _ in 0..lines.abs() { + content.push(0x1b); + content.push(b'O'); + content.push(line_cmd); + } + InputAction::BackendCall(BackendCommand::Write(content)) + } else { + InputAction::BackendCall(BackendCommand::Scroll(lines)) } } From 5e0674d119fc033346e9f288ba391b49fba3a16d Mon Sep 17 00:00:00 2001 From: Oleg Orlov Date: Thu, 5 Feb 2026 23:31:14 +0300 Subject: [PATCH 04/26] Cleanup unused code --- src/view.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/view.rs b/src/view.rs index a6a8394..fb7498b 100644 --- a/src/view.rs +++ b/src/view.rs @@ -164,8 +164,6 @@ impl<'a> TerminalView<'a> { } }, egui::Event::MouseWheel { unit, delta, .. } => { - let terminal_mode = - self.backend.last_content().terminal_mode; if layout.contains_pointer() { input_actions.push(process_mouse_wheel( state, From d31aa9e284cbf7f62d342a21b3d346448e5ba602 Mon Sep 17 00:00:00 2001 From: Oleg Orlov Date: Fri, 6 Feb 2026 14:09:41 +0300 Subject: [PATCH 05/26] Add shell argument support to BackendSettings and use --login for bash in tabs example - Allow passing shell arguments via BackendSettings.args API - Tabs example now uses SHELL env var with fallback to bash - Automatically adds --login argument for bash shells - Fix scroll behavior when display_offset is not zero --- examples/tabs/src/app.rs | 9 ++++++++- src/backend/mod.rs | 4 +++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/examples/tabs/src/app.rs b/examples/tabs/src/app.rs index 6966540..e6829ca 100644 --- a/examples/tabs/src/app.rs +++ b/examples/tabs/src/app.rs @@ -161,16 +161,23 @@ impl Tab { ) -> Self { #[cfg(unix)] let system_shell = - std::env::var("SHELL").expect("SHELL variable is not defined"); + std::env::var("SHELL").unwrap_or_else(|_| "bash".to_string()); #[cfg(windows)] let system_shell = "cmd.exe".to_string(); + let shell_args = if system_shell.contains("bash") { + vec!["--login".to_string()] + } else { + vec![] + }; + let backend = TerminalBackend::new( id, ctx, command_sender, egui_term::BackendSettings { shell: system_shell, + args: shell_args, ..Default::default() }, ) diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 89d205b..7596524 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -222,7 +222,9 @@ impl TerminalBackend { match cmd { BackendCommand::Write(input) => { self.write(input); - term.scroll_display(Scroll::Bottom); + if term.grid().display_offset() == 0 { + term.scroll_display(Scroll::Bottom); + } }, BackendCommand::Scroll(delta) => { self.scroll(&mut term, delta); From 954385cc1728c577697cc4e4e2657c344fb85f05 Mon Sep 17 00:00:00 2001 From: Oleg Orlov Date: Fri, 6 Feb 2026 16:34:01 +0300 Subject: [PATCH 06/26] Add env support to BackendSettings with default TERM and COLORTERM --- examples/tabs/input_link | 1 + src/backend/mod.rs | 1 + src/backend/settings.rs | 7 +++++++ 3 files changed, 9 insertions(+) create mode 120000 examples/tabs/input_link diff --git a/examples/tabs/input_link b/examples/tabs/input_link new file mode 120000 index 0000000..16193db --- /dev/null +++ b/examples/tabs/input_link @@ -0,0 +1 @@ +/home/oleg/alacritty/alacritty/src/input/ \ No newline at end of file diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 7596524..f36f693 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -153,6 +153,7 @@ impl TerminalBackend { let pty_config = tty::Options { shell: Some(tty::Shell::new(settings.shell, settings.args)), working_directory: settings.working_directory, + env: settings.env, ..tty::Options::default() }; let config = term::Config::default(); diff --git a/src/backend/settings.rs b/src/backend/settings.rs index 3c58f8e..b980ed2 100644 --- a/src/backend/settings.rs +++ b/src/backend/settings.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::path::PathBuf; const DEFAULT_SHELL: &str = "/bin/bash"; @@ -7,14 +8,20 @@ pub struct BackendSettings { pub shell: String, pub args: Vec, pub working_directory: Option, + pub env: HashMap, } impl Default for BackendSettings { fn default() -> Self { + let mut env = HashMap::new(); + env.insert("TERM".to_string(), "xterm-256color".to_string()); + env.insert("COLORTERM".to_string(), "truecolor".to_string()); + Self { shell: DEFAULT_SHELL.to_string(), args: vec![], working_directory: None, + env, } } } From 825a69c4462394c522a9f62cb16f9e4b5b9ab32a Mon Sep 17 00:00:00 2001 From: Oleg Orlov Date: Sun, 8 Feb 2026 23:03:43 +0300 Subject: [PATCH 07/26] Updates for UP, DOWN --- src/backend/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/backend/mod.rs b/src/backend/mod.rs index f36f693..5572dde 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -311,6 +311,12 @@ impl TerminalBackend { term.scroll_display(Scroll::Bottom); } + pub fn scroll_to_top(&mut self) { + let term = self.term.clone(); + let mut term = term.lock(); + term.scroll_display(Scroll::Top); + } + pub fn clear_history(&mut self) { let term = self.term.clone(); let mut term = term.lock(); From a2468e5913583aab1303be375dbe442bbbfa0240 Mon Sep 17 00:00:00 2001 From: Oleg Orlov Date: Sun, 8 Feb 2026 23:49:54 +0300 Subject: [PATCH 08/26] Autoscrolling while input --- src/backend/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 5572dde..349d394 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -223,7 +223,7 @@ impl TerminalBackend { match cmd { BackendCommand::Write(input) => { self.write(input); - if term.grid().display_offset() == 0 { + if term.grid().total_lines() > term.grid().screen_lines() { term.scroll_display(Scroll::Bottom); } }, From 241c7658e1838e2721f8a7cdc2aeca7e70debe19 Mon Sep 17 00:00:00 2001 From: Oleg Orlov Date: Mon, 9 Feb 2026 00:36:21 +0300 Subject: [PATCH 09/26] Add ScrollPageUp and ScrollPageDown support --- src/backend/mod.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 349d394..95afaec 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -33,6 +33,8 @@ pub type SelectionType = AlacrittySelectionType; pub enum BackendCommand { Write(Vec), Scroll(i32), + ScrollPageUp, + ScrollPageDown, Resize(Size, Size), SelectStart(SelectionType, f32, f32), SelectUpdate(f32, f32), @@ -230,6 +232,12 @@ impl TerminalBackend { BackendCommand::Scroll(delta) => { self.scroll(&mut term, delta); }, + BackendCommand::ScrollPageUp => { + term.scroll_display(Scroll::PageUp); + }, + BackendCommand::ScrollPageDown => { + term.scroll_display(Scroll::PageDown); + }, BackendCommand::Resize(layout_size, font_size) => { self.resize(&mut term, layout_size, font_size); }, From ae799dc3f7dec289044cbd5ebb0f579ab6764fbf Mon Sep 17 00:00:00 2001 From: Oleg Orlov Date: Mon, 9 Feb 2026 01:53:25 +0300 Subject: [PATCH 10/26] Add Ctrl+Shift+PageUp/Down bindings for terminal scrolling --- src/bindings.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/bindings.rs b/src/bindings.rs index b33d803..a44617e 100644 --- a/src/bindings.rs +++ b/src/bindings.rs @@ -303,8 +303,10 @@ fn default_keyboard_bindings() -> Vec<(Binding, BindingAction)> { // CTRL + ALT End, Modifiers::CTRL | Modifiers::ALT; BindingAction::Esc("\x1b[1;7F".into()); Home, Modifiers::CTRL | Modifiers::ALT; BindingAction::Esc("\x1b[1;7H".into()); - PageUp, Modifiers::CTRL | Modifiers::ALT; BindingAction::Esc("\x1b[5;7~".into()); - PageDown, Modifiers::CTRL | Modifiers::ALT; BindingAction::Esc("\x1b[6;7~".into()); + PageUp, Modifiers::CTRL | Modifiers::ALT; BindingAction::Esc("\x1b[5;7~".into()); + PageDown, Modifiers::CTRL | Modifiers::ALT; BindingAction::Esc("\x1b[6;7~".into()); + PageUp, Modifiers::SHIFT | Modifiers::CTRL; BindingAction::Esc("\x1b[5;6~".into()); + PageDown, Modifiers::SHIFT | Modifiers::CTRL; BindingAction::Esc("\x1b[6;6~".into()); ArrowUp, Modifiers::CTRL | Modifiers::ALT; BindingAction::Esc("\x1b[1;7A".into()); ArrowDown, Modifiers::CTRL | Modifiers::ALT; BindingAction::Esc("\x1b[1;7B".into()); ArrowLeft, Modifiers::CTRL | Modifiers::ALT; BindingAction::Esc("\x1b[1;7D".into()); From 91eb79958c843f408f2cb4778faf5d185906c8ba Mon Sep 17 00:00:00 2001 From: Oleg Orlov Date: Mon, 9 Feb 2026 11:30:08 +0300 Subject: [PATCH 11/26] Fix: kill child process when TerminalBackend is dropped - Store JoinHandle for threads to prevent them from being detached - Add Drop implementation that kills child process with SIGKILL - Add libc dependency for kill system call - This fixes orphaned opencode processes when closing tabs --- Cargo.lock | 1 + Cargo.toml | 1 + src/backend/mod.rs | 52 +++++++++++++++++++++++++++++++++------------- 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 498cadf..19ba5a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1051,6 +1051,7 @@ dependencies = [ "alacritty_terminal", "anyhow", "egui", + "libc", "open", ] diff --git a/Cargo.toml b/Cargo.toml index 4964299..e0e3928 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,3 +29,4 @@ egui = { workspace = true } alacritty_terminal = "0.25.1" anyhow = "1.0.100" open = "5.3.2" +libc = "0.2" diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 95afaec..7902ec4 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -143,6 +143,16 @@ pub struct TerminalBackend { size: TerminalSize, notifier: Notifier, last_content: RenderableContent, + _event_loop_thread: Option>, + _event_loop_thread_pty: Option< + std::thread::JoinHandle<( + alacritty_terminal::event_loop::EventLoop< + alacritty_terminal::tty::Pty, + EventProxy, + >, + alacritty_terminal::event_loop::State, + )>, + >, } impl TerminalBackend { @@ -189,23 +199,30 @@ impl TerminalBackend { let notifier = Notifier(pty_event_loop.channel()); let pty_notifier = Notifier(pty_event_loop.channel()); let url_regex = RegexSearch::new(r#"(ipfs:|ipns:|magnet:|mailto:|gemini://|gopher://|https://|http://|news:|file://|git://|ssh:|ftp://)[^\u{0000}-\u{001F}\u{007F}-\u{009F}<>"\s{-}\^⟨⟩`]+"#).unwrap(); - let _pty_event_loop_thread = pty_event_loop.spawn(); - let _pty_event_subscription = std::thread::Builder::new() + let event_loop_thread = pty_event_loop.spawn(); + let event_subscription_thread = std::thread::Builder::new() .name(format!("pty_event_subscription_{}", id)) - .spawn(move || loop { - if let Ok(event) = event_receiver.recv() { - pty_event_proxy_sender - .send((id, event.clone())) - .unwrap_or_else(|_| { - panic!("pty_event_subscription_{}: sending PtyEvent is failed", id) - }); - app_context.clone().request_repaint(); - match event { - Event::Exit => break, - Event::PtyWrite(pty) => pty_notifier.notify(pty.into_bytes()), - _ => {} + .spawn(move || { + eprintln!("pty_event_subscription_{}: started, pty_id={}", id, pty_id); + loop { + if let Ok(event) = event_receiver.recv() { + pty_event_proxy_sender + .send((id, event.clone())) + .unwrap_or_else(|_| { + panic!("pty_event_subscription_{}: sending PtyEvent is failed", id) + }); + app_context.clone().request_repaint(); + match event { + Event::Exit => { + eprintln!("pty_event_subscription_{}: received Exit event", id); + break; + } + Event::PtyWrite(pty) => pty_notifier.notify(pty.into_bytes()), + _ => {} + } } } + eprintln!("pty_event_subscription_{}: thread exiting", id); })?; Ok(Self { @@ -216,6 +233,8 @@ impl TerminalBackend { size: terminal_size, notifier, last_content: initial_content, + _event_loop_thread: Some(event_subscription_thread), + _event_loop_thread_pty: Some(event_loop_thread), }) } @@ -600,7 +619,10 @@ impl Default for RenderableContent { impl Drop for TerminalBackend { fn drop(&mut self) { - let _ = self.notifier.0.send(Msg::Shutdown); + eprintln!("TerminalBackend::drop(): killing pid={}", self.pty_id); + unsafe { + let _ = libc::kill(self.pty_id as i32, libc::SIGKILL); + } } } From 5f6065f594b29462f082d57d962f569a5ee34f98 Mon Sep 17 00:00:00 2001 From: Oleg Orlov Date: Tue, 10 Feb 2026 23:26:40 +0300 Subject: [PATCH 12/26] Fix Ctrl+X handling in terminal Add Event::Cut handling to properly send ^X (0x18) to terminal when Ctrl+X is pressed without text selection. This fixes the issue where Ctrl+X was intercepted by egui and never reached the terminal. --- src/view.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/view.rs b/src/view.rs index fb7498b..83bec5d 100644 --- a/src/view.rs +++ b/src/view.rs @@ -153,6 +153,7 @@ impl<'a> TerminalView<'a> { egui::Event::Text(_) | egui::Event::Key { .. } | egui::Event::Copy + | egui::Event::Cut | egui::Event::Paste(_) => { if layout.has_focus() { input_actions.push(process_keyboard_event( @@ -391,6 +392,21 @@ fn process_keyboard_event( InputAction::WriteToClipboard(content) } }, + egui::Event::Cut => { + #[cfg(not(any(target_os = "ios", target_os = "macos")))] + if modifiers.contains(Modifiers::COMMAND | Modifiers::SHIFT) { + let content = backend.selectable_content(); + InputAction::WriteToClipboard(content) + } else { + // Hotfix - Send ^X when there's not selection on view. + InputAction::BackendCall(BackendCommand::Write([0x18].to_vec())) + } + #[cfg(any(target_os = "ios", target_os = "macos"))] + { + let content = backend.selectable_content(); + InputAction::WriteToClipboard(content) + } + }, egui::Event::Key { key, pressed, From 771ea503e41d8eca13f3df7f1c0d19c17d97fe81 Mon Sep 17 00:00:00 2001 From: Oleg Orlov Date: Wed, 11 Feb 2026 00:09:40 +0300 Subject: [PATCH 13/26] Fix bracketed paste support in terminal Replace the broken ^V hotfix with proper bracketed paste implementation: - Detect when terminal is in BRACKETED_PASTE mode - Wrap pasted text with \e[200~ and \e[201~ markers - Filter escape sequences that could terminate paste early - In normal mode, replace newlines with carriage returns This fixes the issue where pasting multi-line text would insert it character-by-character instead of as a block. --- src/view.rs | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/src/view.rs b/src/view.rs index 83bec5d..741e0cf 100644 --- a/src/view.rs +++ b/src/view.rs @@ -364,19 +364,28 @@ fn process_keyboard_event( egui::Event::Text(text) => { process_text_event(&text, modifiers, backend, bindings_layout) }, - egui::Event::Paste(text) => InputAction::BackendCall( - #[cfg(not(any(target_os = "ios", target_os = "macos")))] - if modifiers.contains(Modifiers::COMMAND | Modifiers::SHIFT) { - BackendCommand::Write(text.as_bytes().to_vec()) - } else { - // Hotfix - Send ^V when there's not selection on view. - BackendCommand::Write([0x16].to_vec()) - }, - #[cfg(any(target_os = "ios", target_os = "macos"))] - { - BackendCommand::Write(text.as_bytes().to_vec()) - }, - ), + egui::Event::Paste(text) => { + let terminal_mode = backend.last_content().terminal_mode; + InputAction::BackendCall( + if terminal_mode.contains(TermMode::BRACKETED_PASTE) { + // Bracketed paste mode: wrap text with markers and filter escape sequences + let mut payload = Vec::new(); + payload.extend_from_slice(b"\x1b[200~"); + // Filter out escape sequences that could terminate the paste early + for byte in text.bytes() { + if byte != 0x1b && byte != 0x03 { + payload.push(byte); + } + } + payload.extend_from_slice(b"\x1b[201~"); + BackendCommand::Write(payload) + } else { + // Normal mode: replace newlines with carriage returns + let processed = text.replace("\r\n", "\r").replace('\n', "\r"); + BackendCommand::Write(processed.into_bytes()) + }, + ) + } egui::Event::Copy => { #[cfg(not(any(target_os = "ios", target_os = "macos")))] if modifiers.contains(Modifiers::COMMAND | Modifiers::SHIFT) { From 00613fbca7505824d7a4eb4730ca99b2d05c0532 Mon Sep 17 00:00:00 2001 From: Oleg Orlov Date: Wed, 11 Feb 2026 10:20:49 +0300 Subject: [PATCH 14/26] Fix: add newline characters when copying multi-line terminal text Previously, selectable_content() only appended characters without adding newlines between rows, causing multi-line selections to copy as a single line. Now adds '\n' when transitioning to a new line in the terminal grid. --- src/backend/mod.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 7902ec4..aa7b338 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -294,8 +294,15 @@ impl TerminalBackend { let content = self.last_content(); let mut result = String::new(); if let Some(range) = content.selectable_range { + let mut prev_line: Option = None; for indexed in content.grid.display_iter() { if range.contains(indexed.point) { + if let Some(prev) = prev_line { + if indexed.point.line.0 != prev { + result.push('\n'); + } + } + prev_line = Some(indexed.point.line.0); result.push(indexed.c); } } From eb217029478306c0b46dec9203e79121ab2369ac Mon Sep 17 00:00:00 2001 From: Oleg Orlov Date: Wed, 11 Feb 2026 11:09:18 +0300 Subject: [PATCH 15/26] docs: update README with fork differences --- README.md | 79 +++++++++++++------------------------------------------ 1 file changed, 18 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index 138e744..02add33 100644 --- a/README.md +++ b/README.md @@ -1,67 +1,24 @@ - -
+# egui_term fork gives you: -# egui_term +## Bug Fixes -[![GitHub License][license-badge]][license-link] -[![crates.io][crates.io-badge]][crates.io-link] -[![docs.rs][docs.rs-badge]][docs.rs-link] -[![Rust CI][ci-badge]][ci-link] +- **Copying multi-line text** — added newline characters when copying terminal text +- **Bracketed paste** — fixed proper bracketed paste support +- **Ctrl+X** — fixed handling (was intercepted by egui) +- **Kill child process** — proper termination when TerminalBackend is dropped +- **Alternate screen scroll** — fixed scroll handling in MOUSE_MODE -Terminal emulator widget powered by EGUI framework and alacritty terminal backend. +## New Features - - - - - - +- **Ctrl+Shift+PageUp/Down** — bindings for terminal scrolling +- **ScrollPageUp/ScrollPageDown** — page scrolling support +- **Autoscrolling** — automatic scrolling while input +- **UP/DOWN** — navigation key improvements +- **clear_history()** — method to clear terminal history +- **scroll_to_bottom()** — method to scroll to bottom +- **Shell arguments** — support for shell arguments (--login for bash) +- **ENV support** — TERM and COLORTERM in BackendSettings -
+--- -## Features - -The widget is currently under development and does not provide full terminal features make sure that widget is covered everything you want. - -- PTY content rendering -- Multiple instance support -- Basic keyboard input -- Adding custom keyboard or mouse bindings -- Resizing -- Scrolling -- Focusing -- Selecting -- Changing Font/Color scheme -- Hyperlinks processing (hover/open) - -This widget was tested on MacOS, Linux, and Windows. - -## Examples - -You can also look at [examples](./examples) directory for more information about widget using. - -- [full_screen](./examples/full_screen/) - The basic example of terminal emulator. -- [tabs](./examples/tabs/) - The example with tab widget that show how multiple instance feature work. -- [custom_bindings](./examples/custom_bindings/) - The example that show how you can add custom keyboard or mouse bindings to your terminal emulator app. -- [themes](./examples/themes/) - The example that show how you can change terminal color scheme. -- [fonts](./examples/fonts/) - The examples that show how you can change font type or font size in your terminal emulator app. - -## Dependencies - -[![dependency status][deps.rs-badge]][deps.rs-link] - -- [alacritty_terminal](https://github.com/alacritty/alacritty) (Apache-2.0) -- [anyhow](https://github.com/dtolnay/anyhow) (MIT OR Apache-2.0) -- [egui](https://github.com/emilk/egui) (MIT OR Apache-2.0) -- [open](https://github.com/Byron/open-rs) (MIT) - -[license-badge]: https://img.shields.io/github/license/Harzu/egui_term -[license-link]: https://github.com/Harzu/egui_term/blob/main/LICENSE -[crates.io-badge]: https://img.shields.io/crates/v/egui_term -[crates.io-link]: https://crates.io/crates/egui_term -[docs.rs-badge]: https://img.shields.io/docsrs/egui_term -[docs.rs-link]: https://docs.rs/egui_term -[ci-badge]: https://github.com/Harzu/egui_term/actions/workflows/rust.yml/badge.svg -[ci-link]: https://github.com/Harzu/egui_term/actions/workflows/rust.yml -[deps.rs-badge]: https://img.shields.io/deps-rs/egui_term/latest?style=for-the-badge&label=dependency%20status -[deps.rs-link]: https://deps.rs/crate/egui_term +Forked from [Harzu/egui_term](https://github.com/Harzu/egui_term) From cc0c25156060692389c70da12de599e441266f83 Mon Sep 17 00:00:00 2001 From: Oleg Orlov Date: Thu, 12 Feb 2026 18:32:24 +0300 Subject: [PATCH 16/26] feat: add search functionality with CTRL+F toggle - Add SearchState to track search query, matches, and current match - Add search panel with text input, Search Up/Down buttons - Add orange highlight for search matches - Support CTRL+F to toggle search panel - Support F3/Shift+F3 and Enter/Shift+Enter for navigation - Support Escape to close search - Auto-scroll to found matches --- src/backend/mod.rs | 171 ++++++++++++++++++++++++++++++++++++++++++++- src/lib.rs | 4 +- src/view.rs | 138 ++++++++++++++++++++++++++++++++++-- 3 files changed, 304 insertions(+), 9 deletions(-) diff --git a/src/backend/mod.rs b/src/backend/mod.rs index aa7b338..0a2c0a9 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -4,7 +4,7 @@ use crate::types::Size; use alacritty_terminal::event::{ Event, EventListener, Notify, OnResize, WindowSize, }; -use alacritty_terminal::event_loop::{EventLoop, Msg, Notifier}; +use alacritty_terminal::event_loop::{EventLoop, Notifier}; use alacritty_terminal::grid::{Dimensions, Scroll}; use alacritty_terminal::index::{Column, Direction, Line, Point, Side}; use alacritty_terminal::selection::{ @@ -81,6 +81,106 @@ pub enum LinkAction { Open, } +#[derive(Clone, Debug, Default)] +pub struct SearchState { + pub query: String, + pub regex: Option, + pub matches: Vec, + pub current_match_index: usize, + pub active: bool, + pub no_match: bool, +} + +impl SearchState { + pub fn new() -> Self { + Self::default() + } + + pub fn set_query(&mut self, query: &str) { + self.query = query.to_string(); + if query.is_empty() { + self.regex = None; + self.matches.clear(); + self.no_match = false; + return; + } + + match RegexSearch::new(query) { + Ok(regex) => { + self.regex = Some(regex); + }, + Err(_) => { + self.regex = None; + self.matches.clear(); + self.no_match = true; + }, + } + } + + pub fn update_matches(&mut self, term: &Term) { + if let Some(ref mut regex) = self.regex { + let viewport_start = Line(-(term.grid().display_offset() as i32)); + let viewport_end = viewport_start + term.bottommost_line(); + let mut start = + term.line_search_left(Point::new(viewport_start, Column(0))); + let mut end = + term.line_search_right(Point::new(viewport_end, Column(0))); + start.line = start.line.max(viewport_start - 100); + end.line = end.line.min(viewport_end + 100); + + self.matches = + RegexIter::new(start, end, Direction::Right, term, regex) + .skip_while(|rm| rm.end().line < viewport_start) + .take_while(|rm| rm.start().line <= viewport_end) + .collect(); + + self.no_match = self.matches.is_empty(); + if self.current_match_index >= self.matches.len() { + self.current_match_index = 0; + } + } else { + self.matches.clear(); + self.no_match = !self.query.is_empty(); + } + } + + pub fn next_match(&mut self) -> Option<&Match> { + if self.matches.is_empty() { + return None; + } + let m = self.matches.get(self.current_match_index)?; + self.current_match_index = + (self.current_match_index + 1) % self.matches.len(); + Some(m) + } + + pub fn prev_match(&mut self) -> Option<&Match> { + if self.matches.is_empty() { + return None; + } + if self.current_match_index == 0 { + self.current_match_index = self.matches.len() - 1; + } else { + self.current_match_index -= 1; + } + self.matches.get(self.current_match_index) + } + + pub fn current_match(&self) -> Option<&Match> { + self.matches.get(self.current_match_index) + } + + pub fn point_in_match(&self, point: Point) -> Option { + self.matches.iter().position(|m| m.contains(&point)) + } + + pub fn is_focused_match(&self, point: Point) -> bool { + self.current_match() + .map(|m| m.contains(&point)) + .unwrap_or(false) + } +} + #[derive(Clone, Copy, Debug)] pub struct TerminalSize { pub cell_width: u16, @@ -192,6 +292,7 @@ impl TerminalBackend { terminal_size, cursor: term.grid_mut().cursor_cell().clone(), hovered_hyperlink: None, + search_state: SearchState::default(), }; let term = Arc::new(FairMutex::new(term)); let pty_event_loop = @@ -324,6 +425,11 @@ impl TerminalBackend { self.last_content.cursor = cursor.clone(); self.last_content.terminal_mode = *terminal.mode(); self.last_content.terminal_size = self.size; + + if self.last_content.search_state.active { + self.last_content.search_state.update_matches(&terminal); + } + self.last_content() } @@ -357,6 +463,67 @@ impl TerminalBackend { term.grid_mut().clear_history(); } + pub fn search_set_query(&mut self, query: &str) { + self.last_content.search_state.set_query(query); + let term = self.term.clone(); + let term = term.lock(); + self.last_content.search_state.update_matches(&term); + } + + pub fn search_next(&mut self) -> Option { + let term = self.term.clone(); + let term = term.lock(); + self.last_content.search_state.update_matches(&term); + + if let Some(m) = self.last_content.search_state.next_match() { + let start = *m.start(); + return Some(start); + } + None + } + + pub fn search_prev(&mut self) -> Option { + let term = self.term.clone(); + let term = term.lock(); + self.last_content.search_state.update_matches(&term); + + if let Some(m) = self.last_content.search_state.prev_match() { + let start = *m.start(); + return Some(start); + } + None + } + + pub fn scroll_to_point(&mut self, point: Point) { + let term = self.term.clone(); + let mut term = term.lock(); + let display_offset = term.grid().display_offset(); + let viewport_top = -(display_offset as i32); + let viewport_bottom = viewport_top + (self.size.num_lines as i32 - 1); + + if point.line.0 < viewport_top { + let delta = viewport_top - point.line.0; + term.grid_mut().scroll_display(Scroll::Delta(delta as i32)); + } else if point.line.0 > viewport_bottom { + let delta = point.line.0 - viewport_bottom; + term.grid_mut() + .scroll_display(Scroll::Delta(-(delta as i32))); + } + } + + pub fn search_active(&self) -> bool { + self.last_content.search_state.active + } + + pub fn search_set_active(&mut self, active: bool) { + self.last_content.search_state.active = active; + if !active { + self.last_content.search_state.matches.clear(); + self.last_content.search_state.query.clear(); + self.last_content.search_state.no_match = false; + } + } + fn process_link_action( &mut self, terminal: &Term, @@ -609,6 +776,7 @@ pub struct RenderableContent { pub cursor: Cell, pub terminal_mode: TermMode, pub terminal_size: TerminalSize, + pub search_state: SearchState, } impl Default for RenderableContent { @@ -620,6 +788,7 @@ impl Default for RenderableContent { cursor: Cell::default(), terminal_mode: TermMode::empty(), terminal_size: TerminalSize::default(), + search_state: SearchState::default(), } } } diff --git a/src/lib.rs b/src/lib.rs index 20cbcd3..4e32029 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,9 @@ mod types; mod view; pub use backend::settings::BackendSettings; -pub use backend::{BackendCommand, PtyEvent, TerminalBackend, TerminalMode}; +pub use backend::{ + BackendCommand, PtyEvent, SearchState, TerminalBackend, TerminalMode, +}; pub use bindings::{Binding, BindingAction, InputKind, KeyboardBinding}; pub use font::{FontSettings, TerminalFont}; pub use theme::{ColorPalette, TerminalTheme}; diff --git a/src/view.rs b/src/view.rs index 741e0cf..900fa65 100644 --- a/src/view.rs +++ b/src/view.rs @@ -8,8 +8,8 @@ use egui::MouseWheelUnit; use egui::Shape; use egui::Widget; use egui::{Align2, Painter, Pos2, Rect, Response, Stroke, Vec2}; -use egui::{CornerRadius, Key}; -use egui::{Id, PointerButton}; +use egui::{Button, Color32, CornerRadius, Key}; +use egui::{Id, PointerButton, TextEdit}; use crate::backend::BackendCommand; use crate::backend::TerminalBackend; @@ -22,11 +22,17 @@ use crate::types::Size; const EGUI_TERM_WIDGET_ID_PREFIX: &str = "egui_term::instance::"; +const SEARCH_HIGHLIGHT_COLOR: Color32 = Color32::from_rgb(255, 165, 0); +const SEARCH_FOCUSED_HIGHLIGHT_COLOR: Color32 = Color32::from_rgb(255, 140, 0); + #[derive(Debug, Clone)] enum InputAction { BackendCall(BackendCommand), WriteToClipboard(String), Ignore, + ToggleSearch, + SearchNext, + SearchPrev, } #[derive(Clone, Default)] @@ -34,6 +40,8 @@ pub struct TerminalViewState { is_dragged: bool, scroll_pixels: f32, current_mouse_position_on_grid: TerminalGridPoint, + search_query: String, + search_active: bool, } pub struct TerminalView<'a> { @@ -48,9 +56,6 @@ pub struct TerminalView<'a> { impl Widget for TerminalView<'_> { fn ui(self, ui: &mut egui::Ui) -> Response { - let (layout, painter) = - ui.allocate_painter(self.size, egui::Sense::click()); - let widget_id = self.widget_id; let mut state = ui.memory(|m| { m.data @@ -58,6 +63,59 @@ impl Widget for TerminalView<'_> { .unwrap_or_default() }); + let search_panel_height = if state.search_active { 28.0 } else { 0.0 }; + let terminal_size = + Vec2::new(self.size.x, self.size.y - search_panel_height); + + if state.search_active { + ui.allocate_ui_with_layout( + Vec2::new(self.size.x, search_panel_height), + egui::Layout::left_to_right(egui::Align::Center), + |ui| { + ui.spacing_mut().item_spacing.x = 4.0; + + let query_response = ui.add( + TextEdit::singleline(&mut state.search_query) + .desired_width(150.0) + .hint_text("Search..."), + ); + + if query_response.changed() { + self.backend.search_set_query(&state.search_query); + } + + if ui.add(Button::new("⏶").frame(false)).clicked() { + if let Some(point) = self.backend.search_prev() { + self.backend.scroll_to_point(point); + } + } + + if ui.add(Button::new("⏷").frame(false)).clicked() { + if let Some(point) = self.backend.search_next() { + self.backend.scroll_to_point(point); + } + } + + if ui.add(Button::new("Search").frame(false)).clicked() { + self.backend.search_set_query(&state.search_query); + } + + let content = self.backend.last_content(); + if content.search_state.no_match + && !state.search_query.is_empty() + { + ui.label( + egui::RichText::new("No matches") + .color(Color32::RED), + ); + } + }, + ); + } + + let (layout, painter) = + ui.allocate_painter(terminal_size, egui::Sense::click()); + self.focus(&layout) .resize(&layout) .process_input(&layout, &mut state) @@ -217,6 +275,27 @@ impl<'a> TerminalView<'a> { InputAction::WriteToClipboard(data) => { layout.ctx.copy_text(data); }, + InputAction::ToggleSearch => { + state.search_active = !state.search_active; + self.backend.search_set_active(state.search_active); + if !state.search_active { + state.search_query.clear(); + } + }, + InputAction::SearchNext => { + if self.backend.search_active() { + if let Some(point) = self.backend.search_next() { + self.backend.scroll_to_point(point); + } + } + }, + InputAction::SearchPrev => { + if self.backend.search_active() { + if let Some(point) = self.backend.search_prev() { + self.backend.scroll_to_point(point); + } + } + }, InputAction::Ignore => {}, } } @@ -267,6 +346,10 @@ impl<'a> TerminalView<'a> { r.contains(&indexed.point) && r.contains(&state.current_mouse_position_on_grid) }); + let is_search_match = content.search_state.active + && content.search_state.point_in_match(indexed.point).is_some(); + let is_focused_search_match = content.search_state.active + && content.search_state.is_focused_match(indexed.point); let x = layout_min.x + (cell_width * indexed.point.column.0 as f32); let line_num = @@ -301,6 +384,22 @@ impl<'a> TerminalView<'a> { ))); } + if is_search_match { + let highlight_color = if is_focused_search_match { + SEARCH_FOCUSED_HIGHLIGHT_COLOR + } else { + SEARCH_HIGHLIGHT_COLOR + }; + shapes.push(Shape::Rect(RectShape::filled( + Rect::from_min_size( + Pos2::new(x, y), + Vec2::new(cell_width + 1., cell_height + 1.), + ), + CornerRadius::ZERO, + highlight_color, + ))); + } + // Handle hovered hyperlink underline if is_hovered_hyperling { let underline_height = y + cell_height; @@ -381,11 +480,12 @@ fn process_keyboard_event( BackendCommand::Write(payload) } else { // Normal mode: replace newlines with carriage returns - let processed = text.replace("\r\n", "\r").replace('\n', "\r"); + let processed = + text.replace("\r\n", "\r").replace('\n', "\r"); BackendCommand::Write(processed.into_bytes()) }, ) - } + }, egui::Event::Copy => { #[cfg(not(any(target_os = "ios", target_os = "macos")))] if modifiers.contains(Modifiers::COMMAND | Modifiers::SHIFT) { @@ -469,6 +569,30 @@ fn process_keyboard_key( return InputAction::Ignore; } + if modifiers.command && key == Key::F { + return InputAction::ToggleSearch; + } + + if backend.search_active() { + if key == Key::F3 { + if modifiers.shift { + return InputAction::SearchPrev; + } else { + return InputAction::SearchNext; + } + } + if key == Key::Enter { + if modifiers.shift { + return InputAction::SearchPrev; + } else { + return InputAction::SearchNext; + } + } + if key == Key::Escape { + return InputAction::ToggleSearch; + } + } + let terminal_mode = backend.last_content().terminal_mode; let binding_action = bindings_layout.get_action( InputKind::KeyCode(key), From 2224c63da28190deeaac48ce941e8ba2efad2011 Mon Sep 17 00:00:00 2001 From: Oleg Orlov Date: Thu, 12 Feb 2026 19:30:59 +0300 Subject: [PATCH 17/26] fix: focus handling when search panel is active - Use self.has_focus instead of layout.has_focus() for keyboard events - Only handle search-related keys (Escape, F3, Ctrl+Enter, Ctrl+F) when search is active - Let TextEdit receive regular text input when search panel is open --- src/view.rs | 63 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/src/view.rs b/src/view.rs index 900fa65..451c620 100644 --- a/src/view.rs +++ b/src/view.rs @@ -213,12 +213,13 @@ impl<'a> TerminalView<'a> { | egui::Event::Copy | egui::Event::Cut | egui::Event::Paste(_) => { - if layout.has_focus() { + if self.has_focus { input_actions.push(process_keyboard_event( event, self.backend, &self.bindings_layout, modifiers, + state.search_active, )) } }, @@ -458,7 +459,45 @@ fn process_keyboard_event( backend: &TerminalBackend, bindings_layout: &BindingsLayout, modifiers: Modifiers, + search_active: bool, ) -> InputAction { + if search_active { + return match event { + egui::Event::Key { + key, + pressed, + modifiers, + .. + } => { + if !pressed { + return InputAction::Ignore; + } + if key == Key::Escape { + InputAction::ToggleSearch + } else if key == Key::F3 { + if modifiers.shift { + InputAction::SearchPrev + } else { + InputAction::SearchNext + } + } else if key == Key::Enter + && (modifiers.ctrl || modifiers.command) + { + if modifiers.shift { + InputAction::SearchPrev + } else { + InputAction::SearchNext + } + } else if modifiers.command && key == Key::F { + InputAction::ToggleSearch + } else { + InputAction::Ignore + } + }, + _ => InputAction::Ignore, + }; + } + match event { egui::Event::Text(text) => { process_text_event(&text, modifiers, backend, bindings_layout) @@ -481,7 +520,7 @@ fn process_keyboard_event( } else { // Normal mode: replace newlines with carriage returns let processed = - text.replace("\r\n", "\r").replace('\n', "\r"); + text.replace("\r\n", "\r").replace("\n", "\r"); BackendCommand::Write(processed.into_bytes()) }, ) @@ -573,26 +612,6 @@ fn process_keyboard_key( return InputAction::ToggleSearch; } - if backend.search_active() { - if key == Key::F3 { - if modifiers.shift { - return InputAction::SearchPrev; - } else { - return InputAction::SearchNext; - } - } - if key == Key::Enter { - if modifiers.shift { - return InputAction::SearchPrev; - } else { - return InputAction::SearchNext; - } - } - if key == Key::Escape { - return InputAction::ToggleSearch; - } - } - let terminal_mode = backend.last_content().terminal_mode; let binding_action = bindings_layout.get_action( InputKind::KeyCode(key), From 3f838690938398da03e45979f0523558fb087b84 Mon Sep 17 00:00:00 2001 From: Oleg Orlov Date: Thu, 12 Feb 2026 19:36:52 +0300 Subject: [PATCH 18/26] fix: auto-focus search input when panel opens --- src/view.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/view.rs b/src/view.rs index 451c620..5b60f5e 100644 --- a/src/view.rs +++ b/src/view.rs @@ -42,6 +42,7 @@ pub struct TerminalViewState { current_mouse_position_on_grid: TerminalGridPoint, search_query: String, search_active: bool, + search_just_opened: bool, } pub struct TerminalView<'a> { @@ -80,6 +81,11 @@ impl Widget for TerminalView<'_> { .hint_text("Search..."), ); + if state.search_just_opened { + query_response.request_focus(); + state.search_just_opened = false; + } + if query_response.changed() { self.backend.search_set_query(&state.search_query); } @@ -279,7 +285,9 @@ impl<'a> TerminalView<'a> { InputAction::ToggleSearch => { state.search_active = !state.search_active; self.backend.search_set_active(state.search_active); - if !state.search_active { + if state.search_active { + state.search_just_opened = true; + } else { state.search_query.clear(); } }, From fe8eb9fbb9b3400eb62323fbd6572ce7adaaee40 Mon Sep 17 00:00:00 2001 From: Oleg Orlov Date: Thu, 12 Feb 2026 19:43:22 +0300 Subject: [PATCH 19/26] fix: proper focus handling using ctx.memory_mut like settings window - Terminal surrenders focus when search panel is active - Search input requests focus via ctx.memory_mut like settings window - Added id to TextEdit for focus tracking --- src/view.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/view.rs b/src/view.rs index 5b60f5e..25354c3 100644 --- a/src/view.rs +++ b/src/view.rs @@ -69,6 +69,16 @@ impl Widget for TerminalView<'_> { Vec2::new(self.size.x, self.size.y - search_panel_height); if state.search_active { + let search_textedit_id = ui.make_persistent_id(format!( + "{}_search_input", + EGUI_TERM_WIDGET_ID_PREFIX + )); + + if state.search_just_opened { + ui.ctx().memory_mut(|m| m.request_focus(search_textedit_id)); + state.search_just_opened = false; + } + ui.allocate_ui_with_layout( Vec2::new(self.size.x, search_panel_height), egui::Layout::left_to_right(egui::Align::Center), @@ -77,15 +87,11 @@ impl Widget for TerminalView<'_> { let query_response = ui.add( TextEdit::singleline(&mut state.search_query) + .id(search_textedit_id) .desired_width(150.0) .hint_text("Search..."), ); - if state.search_just_opened { - query_response.request_focus(); - state.search_just_opened = false; - } - if query_response.changed() { self.backend.search_set_query(&state.search_query); } @@ -122,7 +128,7 @@ impl Widget for TerminalView<'_> { let (layout, painter) = ui.allocate_painter(terminal_size, egui::Sense::click()); - self.focus(&layout) + self.focus(&layout, state.search_active) .resize(&layout) .process_input(&layout, &mut state) .show(&mut state, &layout, &painter); @@ -184,8 +190,8 @@ impl<'a> TerminalView<'a> { self } - fn focus(self, layout: &Response) -> Self { - if self.has_focus { + fn focus(self, layout: &Response, search_active: bool) -> Self { + if self.has_focus && !search_active { layout.request_focus(); } else { layout.surrender_focus(); From 56ce7163daf8aca403061bd4f9f1c3f33de325cc Mon Sep 17 00:00:00 2001 From: Oleg Orlov Date: Thu, 12 Feb 2026 23:34:32 +0300 Subject: [PATCH 20/26] refactor: remove search UI from egui_term, keep only search API - Removed search panel UI - should be implemented in consumer app - Keep SearchState and backend search methods - Keep search highlight rendering (orange color) - Consumer app can build its own UI using search_set_query, search_next, search_prev, scroll_to_point --- src/view.rs | 154 ++++------------------------------------------------ 1 file changed, 10 insertions(+), 144 deletions(-) diff --git a/src/view.rs b/src/view.rs index 25354c3..a214ae3 100644 --- a/src/view.rs +++ b/src/view.rs @@ -8,8 +8,8 @@ use egui::MouseWheelUnit; use egui::Shape; use egui::Widget; use egui::{Align2, Painter, Pos2, Rect, Response, Stroke, Vec2}; -use egui::{Button, Color32, CornerRadius, Key}; -use egui::{Id, PointerButton, TextEdit}; +use egui::{Color32, CornerRadius, Key}; +use egui::{Id, PointerButton}; use crate::backend::BackendCommand; use crate::backend::TerminalBackend; @@ -21,7 +21,6 @@ use crate::theme::TerminalTheme; use crate::types::Size; const EGUI_TERM_WIDGET_ID_PREFIX: &str = "egui_term::instance::"; - const SEARCH_HIGHLIGHT_COLOR: Color32 = Color32::from_rgb(255, 165, 0); const SEARCH_FOCUSED_HIGHLIGHT_COLOR: Color32 = Color32::from_rgb(255, 140, 0); @@ -30,9 +29,6 @@ enum InputAction { BackendCall(BackendCommand), WriteToClipboard(String), Ignore, - ToggleSearch, - SearchNext, - SearchPrev, } #[derive(Clone, Default)] @@ -40,9 +36,6 @@ pub struct TerminalViewState { is_dragged: bool, scroll_pixels: f32, current_mouse_position_on_grid: TerminalGridPoint, - search_query: String, - search_active: bool, - search_just_opened: bool, } pub struct TerminalView<'a> { @@ -57,6 +50,9 @@ pub struct TerminalView<'a> { impl Widget for TerminalView<'_> { fn ui(self, ui: &mut egui::Ui) -> Response { + let (layout, painter) = + ui.allocate_painter(self.size, egui::Sense::click()); + let widget_id = self.widget_id; let mut state = ui.memory(|m| { m.data @@ -64,71 +60,7 @@ impl Widget for TerminalView<'_> { .unwrap_or_default() }); - let search_panel_height = if state.search_active { 28.0 } else { 0.0 }; - let terminal_size = - Vec2::new(self.size.x, self.size.y - search_panel_height); - - if state.search_active { - let search_textedit_id = ui.make_persistent_id(format!( - "{}_search_input", - EGUI_TERM_WIDGET_ID_PREFIX - )); - - if state.search_just_opened { - ui.ctx().memory_mut(|m| m.request_focus(search_textedit_id)); - state.search_just_opened = false; - } - - ui.allocate_ui_with_layout( - Vec2::new(self.size.x, search_panel_height), - egui::Layout::left_to_right(egui::Align::Center), - |ui| { - ui.spacing_mut().item_spacing.x = 4.0; - - let query_response = ui.add( - TextEdit::singleline(&mut state.search_query) - .id(search_textedit_id) - .desired_width(150.0) - .hint_text("Search..."), - ); - - if query_response.changed() { - self.backend.search_set_query(&state.search_query); - } - - if ui.add(Button::new("⏶").frame(false)).clicked() { - if let Some(point) = self.backend.search_prev() { - self.backend.scroll_to_point(point); - } - } - - if ui.add(Button::new("⏷").frame(false)).clicked() { - if let Some(point) = self.backend.search_next() { - self.backend.scroll_to_point(point); - } - } - - if ui.add(Button::new("Search").frame(false)).clicked() { - self.backend.search_set_query(&state.search_query); - } - - let content = self.backend.last_content(); - if content.search_state.no_match - && !state.search_query.is_empty() - { - ui.label( - egui::RichText::new("No matches") - .color(Color32::RED), - ); - } - }, - ); - } - - let (layout, painter) = - ui.allocate_painter(terminal_size, egui::Sense::click()); - - self.focus(&layout, state.search_active) + self.focus(&layout) .resize(&layout) .process_input(&layout, &mut state) .show(&mut state, &layout, &painter); @@ -190,8 +122,8 @@ impl<'a> TerminalView<'a> { self } - fn focus(self, layout: &Response, search_active: bool) -> Self { - if self.has_focus && !search_active { + fn focus(self, layout: &Response) -> Self { + if self.has_focus { layout.request_focus(); } else { layout.surrender_focus(); @@ -225,13 +157,12 @@ impl<'a> TerminalView<'a> { | egui::Event::Copy | egui::Event::Cut | egui::Event::Paste(_) => { - if self.has_focus { + if layout.has_focus() { input_actions.push(process_keyboard_event( event, self.backend, &self.bindings_layout, modifiers, - state.search_active, )) } }, @@ -288,29 +219,6 @@ impl<'a> TerminalView<'a> { InputAction::WriteToClipboard(data) => { layout.ctx.copy_text(data); }, - InputAction::ToggleSearch => { - state.search_active = !state.search_active; - self.backend.search_set_active(state.search_active); - if state.search_active { - state.search_just_opened = true; - } else { - state.search_query.clear(); - } - }, - InputAction::SearchNext => { - if self.backend.search_active() { - if let Some(point) = self.backend.search_next() { - self.backend.scroll_to_point(point); - } - } - }, - InputAction::SearchPrev => { - if self.backend.search_active() { - if let Some(point) = self.backend.search_prev() { - self.backend.scroll_to_point(point); - } - } - }, InputAction::Ignore => {}, } } @@ -473,45 +381,7 @@ fn process_keyboard_event( backend: &TerminalBackend, bindings_layout: &BindingsLayout, modifiers: Modifiers, - search_active: bool, ) -> InputAction { - if search_active { - return match event { - egui::Event::Key { - key, - pressed, - modifiers, - .. - } => { - if !pressed { - return InputAction::Ignore; - } - if key == Key::Escape { - InputAction::ToggleSearch - } else if key == Key::F3 { - if modifiers.shift { - InputAction::SearchPrev - } else { - InputAction::SearchNext - } - } else if key == Key::Enter - && (modifiers.ctrl || modifiers.command) - { - if modifiers.shift { - InputAction::SearchPrev - } else { - InputAction::SearchNext - } - } else if modifiers.command && key == Key::F { - InputAction::ToggleSearch - } else { - InputAction::Ignore - } - }, - _ => InputAction::Ignore, - }; - } - match event { egui::Event::Text(text) => { process_text_event(&text, modifiers, backend, bindings_layout) @@ -534,7 +404,7 @@ fn process_keyboard_event( } else { // Normal mode: replace newlines with carriage returns let processed = - text.replace("\r\n", "\r").replace("\n", "\r"); + text.replace("\r\n", "\r").replace('\n', "\r"); BackendCommand::Write(processed.into_bytes()) }, ) @@ -622,10 +492,6 @@ fn process_keyboard_key( return InputAction::Ignore; } - if modifiers.command && key == Key::F { - return InputAction::ToggleSearch; - } - let terminal_mode = backend.last_content().terminal_mode; let binding_action = bindings_layout.get_action( InputKind::KeyCode(key), From ae6e3486257543b8082ce05db8665a61ea82ff6c Mon Sep 17 00:00:00 2001 From: Oleg Orlov Date: Fri, 1 May 2026 10:40:37 +0300 Subject: [PATCH 21/26] fix: kill entire process group on tab close to prevent orphaned children Sending SIGKILL only to the shell process left child processes (cargo, gradle, java) alive. They would try I/O on the closed PTY slave, receive SIGTTIN/SIGTTOU from the kernel, and get stopped (state Tl). Stopped processes held file locks (e.g. lock.android) causing subsequent builds to hang forever. Now we kill the entire process group via kill(-pgid, ...) with SIGHUP first for clean session teardown, then SIGKILL to guarantee termination. --- src/backend/mod.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 0a2c0a9..1cd73bf 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -795,9 +795,14 @@ impl Default for RenderableContent { impl Drop for TerminalBackend { fn drop(&mut self) { - eprintln!("TerminalBackend::drop(): killing pid={}", self.pty_id); + eprintln!( + "TerminalBackend::drop(): killing process group {}", + self.pty_id + ); unsafe { - let _ = libc::kill(self.pty_id as i32, libc::SIGKILL); + let pid = self.pty_id as i32; + let _ = libc::kill(-pid, libc::SIGHUP); + let _ = libc::kill(-pid, libc::SIGKILL); } } } From 01c3aeaba0a458c2d65b44333bcdfffb7e336ad5 Mon Sep 17 00:00:00 2001 From: Oleg Orlov Date: Sat, 30 May 2026 10:22:47 +0300 Subject: [PATCH 22/26] Fixes for windows --- Cargo.lock | 1 + Cargo.toml | 3 +++ src/backend/mod.rs | 16 ++++++++++++---- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 19ba5a6..6d90015 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1053,6 +1053,7 @@ dependencies = [ "egui", "libc", "open", + "windows-sys 0.59.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e0e3928..d8e34e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,3 +30,6 @@ alacritty_terminal = "0.25.1" anyhow = "1.0.100" open = "5.3.2" libc = "0.2" + +[target.'cfg(windows)'.dependencies] +windows-sys = { version = "0.59", features = ["Win32_Foundation", "Win32_System_Threading"] } diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 1cd73bf..6b2a934 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -795,15 +795,23 @@ impl Default for RenderableContent { impl Drop for TerminalBackend { fn drop(&mut self) { - eprintln!( - "TerminalBackend::drop(): killing process group {}", - self.pty_id - ); + eprintln!("TerminalBackend::drop(): killing pid={}", self.pty_id); + #[cfg(unix)] unsafe { let pid = self.pty_id as i32; let _ = libc::kill(-pid, libc::SIGHUP); let _ = libc::kill(-pid, libc::SIGKILL); } + #[cfg(windows)] + unsafe { + use windows_sys::Win32::Foundation::CloseHandle; + use windows_sys::Win32::System::Threading::{OpenProcess, TerminateProcess, PROCESS_TERMINATE}; + let handle = OpenProcess(PROCESS_TERMINATE, 0, self.pty_id); + if handle != 0 { + let _ = TerminateProcess(handle, 1); + let _ = CloseHandle(handle); + } + } } } From 48fbc5daeea222fe862f0958f637817e784bd7e3 Mon Sep 17 00:00:00 2001 From: Oleg Orlov Date: Sat, 30 May 2026 11:43:26 +0300 Subject: [PATCH 23/26] Fixes --- src/backend/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 6b2a934..380f8a7 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -807,7 +807,7 @@ impl Drop for TerminalBackend { use windows_sys::Win32::Foundation::CloseHandle; use windows_sys::Win32::System::Threading::{OpenProcess, TerminateProcess, PROCESS_TERMINATE}; let handle = OpenProcess(PROCESS_TERMINATE, 0, self.pty_id); - if handle != 0 { + if !handle.is_null() { let _ = TerminateProcess(handle, 1); let _ = CloseHandle(handle); } From d7fa9d1bff47dda2a095c4c9a932e4d1170efe38 Mon Sep 17 00:00:00 2001 From: Oleg Orlov Date: Sat, 6 Jun 2026 13:28:06 +0300 Subject: [PATCH 24/26] Update dependencies: egui 0.34, eframe 0.34, wgpu 29, egui_dock 0.19.1; adapt to eframe 0.34 App::ui API --- Cargo.lock | 850 ++++++++++++++++------------ Cargo.toml | 6 +- examples/custom_bindings/src/app.rs | 3 +- examples/fonts/src/app.rs | 3 +- examples/full_screen/src/app.rs | 3 +- examples/split_view/Cargo.toml | 2 +- examples/split_view/src/app.rs | 3 +- examples/tabs/src/app.rs | 3 +- examples/themes/src/app.rs | 3 +- 9 files changed, 514 insertions(+), 362 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6d90015..df1cdd2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,47 +20,58 @@ checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" [[package]] name = "accesskit" -version = "0.21.1" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf203f9d3bd8f29f98833d1fbef628df18f759248a547e7e01cfbf63cda36a99" +checksum = "5351dcebb14b579ccab05f288596b2ae097005be7ee50a7c3d4ca9d0d5a66f6a" dependencies = [ "enumn", "serde", + "uuid", ] [[package]] name = "accesskit_atspi_common" -version = "0.14.2" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "890d241cf51fc784f0ac5ac34dfc847421f8d39da6c7c91a0fcc987db62a8267" +checksum = "1e8c61bee90b42a772d39d06a740207dc71a4e780004ace1db8d99fb1baaa954" dependencies = [ "accesskit", - "accesskit_consumer", + "accesskit_consumer 0.36.0", "atspi-common", + "phf", "serde", - "thiserror 1.0.69", "zvariant", ] [[package]] name = "accesskit_consumer" -version = "0.31.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db81010a6895d8707f9072e6ce98070579b43b717193d2614014abd5cb17dd43" +checksum = "53cf47daed85312e763fbf85ceca136e0d7abc68e0a7e12abe11f48172bc3b10" dependencies = [ "accesskit", - "hashbrown 0.15.5", + "hashbrown 0.16.1", +] + +[[package]] +name = "accesskit_consumer" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25e0d7e25d06f4dc21d1774d67146e9e80d6789216cbd4d1e88185b0095dba60" +dependencies = [ + "accesskit", + "hashbrown 0.16.1", ] [[package]] name = "accesskit_macos" -version = "0.22.2" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0089e5c0ac0ca281e13ea374773898d9354cc28d15af9f0f7394d44a495b575" +checksum = "4c5c87e8d94f2ec10cce590aadff24c76f576dab5502d45d0aed9fc3065d4451" dependencies = [ "accesskit", - "accesskit_consumer", - "hashbrown 0.15.5", + "accesskit_consumer 0.36.0", + "hashbrown 0.16.1", "objc2 0.5.2", "objc2-app-kit 0.2.2", "objc2-foundation 0.2.2", @@ -68,9 +79,9 @@ dependencies = [ [[package]] name = "accesskit_unix" -version = "0.17.2" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301e55b39cfc15d9c48943ce5f572204a551646700d0e8efa424585f94fec528" +checksum = "b016ca8db0ea0ea2ceff29a9d6240391492d960716aa471967c00e8cc8cb197c" dependencies = [ "accesskit", "accesskit_atspi_common", @@ -86,23 +97,23 @@ dependencies = [ [[package]] name = "accesskit_windows" -version = "0.29.2" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2d63dd5041e49c363d83f5419a896ecb074d309c414036f616dc0b04faca971" +checksum = "eff7009f1a532e917d66970a1e80c965140c6cfbbabbdde3d64e5431e6c78e21" dependencies = [ "accesskit", - "accesskit_consumer", - "hashbrown 0.15.5", + "accesskit_consumer 0.35.0", + "hashbrown 0.16.1", "static_assertions", - "windows 0.61.3", - "windows-core 0.61.2", + "windows", + "windows-core", ] [[package]] name = "accesskit_winit" -version = "0.29.2" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8cfabe59d0eaca7412bfb1f70198dd31e3b0496fee7e15b066f9c36a1a140a0" +checksum = "1fe9a94394896352cc4660ca2288bd4ef883d83238853c038b44070c8f134313" dependencies = [ "accesskit", "accesskit_macos", @@ -166,6 +177,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "android-activity" version = "0.6.0" @@ -267,7 +284,7 @@ dependencies = [ "clipboard-win", "image", "log", - "objc2 0.6.3", + "objc2 0.6.4", "objc2-app-kit 0.3.2", "objc2-core-foundation", "objc2-core-graphics", @@ -444,20 +461,19 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "atspi" -version = "0.25.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c83247582e7508838caf5f316c00791eee0e15c0bf743e6880585b867e16815c" +checksum = "c77886257be21c9cd89a4ae7e64860c6f0eefca799bb79127913052bd0eefb3d" dependencies = [ "atspi-common", - "atspi-connection", "atspi-proxies", ] [[package]] name = "atspi-common" -version = "0.9.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33dfc05e7cdf90988a197803bf24f5788f94f7c94a69efa95683e8ffe76cfdfb" +checksum = "20c5617155740c98003016429ad13fe43ce7a77b007479350a9f8bf95a29f63d" dependencies = [ "enumflags2", "serde", @@ -469,23 +485,11 @@ dependencies = [ "zvariant", ] -[[package]] -name = "atspi-connection" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4193d51303d8332304056ae0004714256b46b6635a5c556109b319c0d3784938" -dependencies = [ - "atspi-common", - "atspi-proxies", - "futures-lite", - "zbus", -] - [[package]] name = "atspi-proxies" -version = "0.9.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2eebcb9e7e76f26d0bcfd6f0295e1cd1e6f33bedbc5698a971db8dc43d7751c" +checksum = "2230e48787ed3eb4088996eab66a32ca20c0b67bbd4fd6cdfe79f04f1f04c9fc" dependencies = [ "atspi-common", "serde", @@ -506,18 +510,18 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bit-set" -version = "0.8.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +checksum = "34ddef2995421ab6a5c779542c81ee77c115206f4ad9d5a8e05f4ff49716a3dd" dependencies = [ "bit-vec", ] [[package]] name = "bit-vec" -version = "0.8.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" +checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51" [[package]] name = "bitflags" @@ -534,12 +538,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" - [[package]] name = "block2" version = "0.5.1" @@ -549,6 +547,15 @@ dependencies = [ "objc2 0.5.2", ] +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2 0.6.4", +] + [[package]] name = "blocking" version = "1.6.2" @@ -701,15 +708,24 @@ dependencies = [ [[package]] name = "codespan-reporting" -version = "0.12.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" +checksum = "af491d569909a7e4dee0ad7db7f5341fef5c614d5b8ec8cf765732aba3cff681" dependencies = [ "serde", "termcolor", "unicode-width", ] +[[package]] +name = "color" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18ef4657441fb193b65f34dc39b3781f0dfec23d3bd94d0eeb4e88cde421edb" +dependencies = [ + "bytemuck", +] + [[package]] name = "colorchoice" version = "1.0.4" @@ -769,7 +785,7 @@ checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" dependencies = [ "bitflags 1.3.2", "core-foundation 0.9.4", - "core-graphics-types 0.1.3", + "core-graphics-types", "foreign-types", "libc", ] @@ -785,17 +801,6 @@ dependencies = [ "libc", ] -[[package]] -name = "core-graphics-types" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" -dependencies = [ - "bitflags 2.10.0", - "core-foundation 0.10.1", - "libc", -] - [[package]] name = "crc32fast" version = "1.5.0" @@ -848,7 +853,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" dependencies = [ "bitflags 2.10.0", - "objc2 0.6.3", + "objc2 0.6.4", ] [[package]] @@ -905,9 +910,9 @@ dependencies = [ [[package]] name = "ecolor" -version = "0.33.3" +version = "0.34.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71ddb8ac7643d1dba1bb02110e804406dd459a838efcb14011ced10556711a8e" +checksum = "a05fbfa222ffb51989d5ccf33e5f7aebfcf96c5023413856b0c3618a7f79896e" dependencies = [ "bytemuck", "emath", @@ -916,9 +921,9 @@ dependencies = [ [[package]] name = "eframe" -version = "0.33.3" +version = "0.34.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "457481173e6db5ca9fa2be93a58df8f4c7be639587aeb4853b526c6cf87db4e6" +checksum = "f98fe83b2589105b69dd25ca1e0fa2135a6e864d502fd8e08978f937e128cfef" dependencies = [ "ahash", "bytemuck", @@ -933,9 +938,9 @@ dependencies = [ "image", "js-sys", "log", - "objc2 0.5.2", - "objc2-app-kit 0.2.2", - "objc2-foundation 0.2.2", + "objc2 0.6.4", + "objc2-app-kit 0.3.2", + "objc2-foundation 0.3.2", "parking_lot", "percent-encoding", "pollster", @@ -955,9 +960,9 @@ dependencies = [ [[package]] name = "egui" -version = "0.33.3" +version = "0.34.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9b567d356674e9a5121ed3fedfb0a7c31e059fe71f6972b691bcd0bfc284e3" +checksum = "42112be0ae157289312b92b3dfaf20e911b5a3c4c65d4aab0e7c47fbc0ce16e3" dependencies = [ "accesskit", "ahash", @@ -975,9 +980,9 @@ dependencies = [ [[package]] name = "egui-wgpu" -version = "0.33.3" +version = "0.34.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e4d209971c84b2352a06174abdba701af1e552ce56b144d96f2bd50a3c91236" +checksum = "9f0c0559ac5598a1b887a6206dccbab7e3e6246c57cb00ae287262bd44776c9c" dependencies = [ "ahash", "bytemuck", @@ -986,7 +991,7 @@ dependencies = [ "epaint", "log", "profiling", - "thiserror 2.0.17", + "thiserror 2.0.18", "type-map", "web-time", "wgpu", @@ -995,18 +1000,18 @@ dependencies = [ [[package]] name = "egui-winit" -version = "0.33.3" +version = "0.34.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec6687e5bb551702f4ad10ac428bab12acf9d53047ebb1082d4a0ed8c6251a29" +checksum = "967c5b323625d46d46a59b5daba3fef742248d27693cc18972458619858c4239" dependencies = [ "accesskit_winit", "arboard", "bytemuck", "egui", "log", - "objc2 0.5.2", - "objc2-foundation 0.2.2", - "objc2-ui-kit", + "objc2 0.6.4", + "objc2-foundation 0.3.2", + "objc2-ui-kit 0.3.2", "profiling", "raw-window-handle", "serde", @@ -1018,20 +1023,21 @@ dependencies = [ [[package]] name = "egui_dock" -version = "0.18.0" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd0f689738efbd4afb811433b873379c6a8f3af48f2d296d9849fe70ee021d6" +checksum = "db51b9023b9d65cb4d561fd981946f987c54f25413c75b2c8e634c774c3d70ad" dependencies = [ "duplicate", "egui", "paste", + "thiserror 2.0.18", ] [[package]] name = "egui_glow" -version = "0.33.3" +version = "0.34.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6420863ea1d90e750f75075231a260030ad8a9f30a7cef82cdc966492dc4c4eb" +checksum = "62b652957fa7e1ab01e8fecbfbf4e35f6e43a53fa98af8a562b50d5403cd44b9" dependencies = [ "bytemuck", "egui", @@ -1058,9 +1064,9 @@ dependencies = [ [[package]] name = "emath" -version = "0.33.3" +version = "0.34.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "491bdf728bf25ddd9ad60d4cf1c48588fa82c013a2440b91aa7fc43e34a07c32" +checksum = "b53f0d33a479321da6b0caa71366c9f67e8a2c149762d90bdc0d16e601ee8ecb" dependencies = [ "bytemuck", "serde", @@ -1129,28 +1135,32 @@ dependencies = [ [[package]] name = "epaint" -version = "0.33.3" +version = "0.34.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "009d0dd3c2163823a0abdb899451ecbc78798dec545ee91b43aff1fa790bab62" +checksum = "6675898a291ec212fc3df04f537d177fce8496120244590e6359dcaa4c25da79" dependencies = [ - "ab_glyph", "ahash", "bytemuck", "ecolor", "emath", "epaint_default_fonts", + "font-types", "log", "nohash-hasher", "parking_lot", "profiling", + "self_cell", "serde", + "skrifa", + "smallvec", + "vello_cpu", ] [[package]] name = "epaint_default_fonts" -version = "0.33.3" +version = "0.34.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c4fbe202b6578d3d56428fa185cdf114a05e49da05f477b3c7f0fbb221f1862" +checksum = "f8970033a4282a7bcf899b38b5ed3a58b732fe093d03785d58648515d8d309da" [[package]] name = "equivalent" @@ -1174,6 +1184,15 @@ version = "3.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" +[[package]] +name = "euclid" +version = "0.22.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a05365e3b1c6d1650318537c7460c6923f1abdd272ad6842baa2b509957a06" +dependencies = [ + "num-traits", +] + [[package]] name = "event-listener" version = "5.4.1" @@ -1230,6 +1249,15 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "fearless_simd" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fb2907d1f08b2b316b9223ced5b0e89d87028ba8deae9764741dba8ff7f3903" +dependencies = [ + "bytemuck", +] + [[package]] name = "find-msvc-tools" version = "0.1.5" @@ -1258,6 +1286,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" +[[package]] +name = "font-types" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b38ad915f6dadd993ced50848a8291a543bd41ca62bc10740d5e64e2ab4cfd7" +dependencies = [ + "bytemuck", + "serde", +] + [[package]] name = "fonts" version = "0.1.0" @@ -1381,7 +1419,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" dependencies = [ "rustix 1.1.2", - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -1409,9 +1447,9 @@ dependencies = [ [[package]] name = "glow" -version = "0.16.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e5ea60d70410161c8bf5da3fdfeaa1c72ed2c15f8bbb9d19fe3a4fad085f08" +checksum = "29038e1c483364cc6bb3cf78feee1816002e127c331a1eec55a4d202b9e1adb5" dependencies = [ "js-sys", "slotmap", @@ -1433,7 +1471,7 @@ dependencies = [ "glutin_glx_sys", "glutin_wgl_sys", "libloading", - "objc2 0.6.3", + "objc2 0.6.4", "objc2-app-kit 0.3.2", "objc2-core-foundation", "objc2-foundation 0.3.2", @@ -1485,35 +1523,18 @@ dependencies = [ "gl_generator", ] -[[package]] -name = "gpu-alloc" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" -dependencies = [ - "bitflags 2.10.0", - "gpu-alloc-types", -] - -[[package]] -name = "gpu-alloc-types" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" -dependencies = [ - "bitflags 2.10.0", -] - [[package]] name = "gpu-allocator" -version = "0.27.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd" +checksum = "51255ea7cfaadb6c5f1528d43e92a82acb2b96c43365989a28b2d44ee38f8795" dependencies = [ + "ash", + "hashbrown 0.16.1", "log", "presser", - "thiserror 1.0.69", - "windows 0.58.0", + "thiserror 2.0.18", + "windows", ] [[package]] @@ -1559,10 +1580,12 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ + "allocator-api2", + "equivalent", "foldhash 0.2.0", ] @@ -1722,7 +1745,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" dependencies = [ "equivalent", - "hashbrown 0.16.0", + "hashbrown 0.16.1", ] [[package]] @@ -1808,10 +1831,12 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.82" +version = "0.3.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" +checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" dependencies = [ + "cfg-if", + "futures-util", "once_cell", "wasm-bindgen", ] @@ -1833,6 +1858,18 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" +[[package]] +name = "kurbo" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b60dfc32f652b926df6192e55525b16d186c69d47876c3ead4da5cc9f8450e2" +dependencies = [ + "arrayvec", + "euclid", + "polycool", + "smallvec", +] + [[package]] name = "libc" version = "0.2.177" @@ -1846,7 +1883,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -1866,6 +1903,12 @@ dependencies = [ "redox_syscall 0.5.18", ] +[[package]] +name = "linebender_resource_handle" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a5ff6bcca6c4867b1c4fd4ef63e4db7436ef363e0ad7531d1558856bae64f4" + [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -1905,15 +1948,6 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - [[package]] name = "memchr" version = "2.7.6" @@ -1938,21 +1972,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "metal" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00c15a6f673ff72ddcc22394663290f870fb224c1bfce55734a75c414150e605" -dependencies = [ - "bitflags 2.10.0", - "block", - "core-graphics-types 0.2.0", - "foreign-types", - "log", - "objc", - "paste", -] - [[package]] name = "miniz_oxide" version = "0.8.9" @@ -1984,9 +2003,9 @@ dependencies = [ [[package]] name = "naga" -version = "27.0.3" +version = "29.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "066cf25f0e8b11ee0df221219010f213ad429855f57c494f995590c861a9a7d8" +checksum = "0dd91265cc2454558f659b3b4b9640f0ddb8cc6521277f166b8a8c181c898079" dependencies = [ "arrayvec", "bit-set", @@ -1995,7 +2014,7 @@ dependencies = [ "cfg_aliases", "codespan-reporting", "half", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "hexf-parse", "indexmap", "libm", @@ -2004,7 +2023,7 @@ dependencies = [ "once_cell", "rustc-hash 1.1.0", "spirv", - "thiserror 2.0.17", + "thiserror 2.0.18", "unicode-ident", ] @@ -2089,15 +2108,6 @@ dependencies = [ "syn", ] -[[package]] -name = "objc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" -dependencies = [ - "malloc_buf", -] - [[package]] name = "objc-sys" version = "0.3.5" @@ -2116,9 +2126,9 @@ dependencies = [ [[package]] name = "objc2" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" dependencies = [ "objc2-encode", ] @@ -2130,13 +2140,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ "bitflags 2.10.0", - "block2", + "block2 0.5.1", "libc", "objc2 0.5.2", "objc2-core-data", "objc2-core-image", "objc2-foundation 0.2.2", - "objc2-quartz-core", + "objc2-quartz-core 0.2.2", ] [[package]] @@ -2146,7 +2156,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ "bitflags 2.10.0", - "objc2 0.6.3", + "objc2 0.6.4", "objc2-core-foundation", "objc2-core-graphics", "objc2-foundation 0.3.2", @@ -2159,7 +2169,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ "bitflags 2.10.0", - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-core-location", "objc2-foundation 0.2.2", @@ -2171,7 +2181,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -2183,7 +2193,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ "bitflags 2.10.0", - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -2196,7 +2206,7 @@ checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ "bitflags 2.10.0", "dispatch2", - "objc2 0.6.3", + "objc2 0.6.4", ] [[package]] @@ -2207,7 +2217,7 @@ checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" dependencies = [ "bitflags 2.10.0", "dispatch2", - "objc2 0.6.3", + "objc2 0.6.4", "objc2-core-foundation", "objc2-io-surface", ] @@ -2218,10 +2228,10 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", - "objc2-metal", + "objc2-metal 0.2.2", ] [[package]] @@ -2230,7 +2240,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-contacts", "objc2-foundation 0.2.2", @@ -2249,7 +2259,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ "bitflags 2.10.0", - "block2", + "block2 0.5.1", "dispatch", "libc", "objc2 0.5.2", @@ -2262,7 +2272,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ "bitflags 2.10.0", - "objc2 0.6.3", + "block2 0.6.2", + "objc2 0.6.4", "objc2-core-foundation", ] @@ -2273,7 +2284,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" dependencies = [ "bitflags 2.10.0", - "objc2 0.6.3", + "objc2 0.6.4", "objc2-core-foundation", ] @@ -2283,7 +2294,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-app-kit 0.2.2", "objc2-foundation 0.2.2", @@ -2296,11 +2307,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ "bitflags 2.10.0", - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", ] +[[package]] +name = "objc2-metal" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0125f776a10d00af4152d74616409f0d4a2053a6f57fa5b7d6aa2854ac04794" +dependencies = [ + "bitflags 2.10.0", + "block2 0.6.2", + "objc2 0.6.4", + "objc2-foundation 0.3.2", +] + [[package]] name = "objc2-quartz-core" version = "0.2.2" @@ -2308,10 +2331,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ "bitflags 2.10.0", - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", - "objc2-metal", + "objc2-metal 0.2.2", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags 2.10.0", + "objc2 0.6.4", + "objc2-core-foundation", + "objc2-foundation 0.3.2", + "objc2-metal 0.3.2", ] [[package]] @@ -2331,7 +2367,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ "bitflags 2.10.0", - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-cloud-kit", "objc2-core-data", @@ -2339,19 +2375,31 @@ dependencies = [ "objc2-core-location", "objc2-foundation 0.2.2", "objc2-link-presentation", - "objc2-quartz-core", + "objc2-quartz-core 0.2.2", "objc2-symbols", "objc2-uniform-type-identifiers", "objc2-user-notifications", ] +[[package]] +name = "objc2-ui-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" +dependencies = [ + "bitflags 2.10.0", + "objc2 0.6.4", + "objc2-core-foundation", + "objc2-foundation 0.3.2", +] + [[package]] name = "objc2-uniform-type-identifiers" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -2363,7 +2411,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ "bitflags 2.10.0", - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-core-location", "objc2-foundation 0.2.2", @@ -2455,7 +2503,7 @@ dependencies = [ "libc", "redox_syscall 0.5.18", "smallvec", - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -2470,12 +2518,68 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" +[[package]] +name = "peniko" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2b6aadb221872732e87d465213e9be5af2849b0e8cc5300a8ba98fffa2e00a" +dependencies = [ + "bytemuck", + "color", + "kurbo", + "linebender_resource_handle", + "smallvec", +] + [[package]] name = "percent-encoding" version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros", + "phf_shared", + "serde", +] + +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared", +] + +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project" version = "1.1.10" @@ -2558,6 +2662,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" +[[package]] +name = "polycool" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50596ddc09eb5ad5f75cacd40209568e66df71baf86e1499a0e99c4cff12a5a6" +dependencies = [ + "arrayvec", +] + [[package]] name = "portable-atomic" version = "1.11.1" @@ -2685,6 +2798,28 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" +[[package]] +name = "raw-window-metal" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40d213455a5f1dc59214213c7330e074ddf8114c9a42411eb890c767357ce135" +dependencies = [ + "objc2 0.6.4", + "objc2-core-foundation", + "objc2-foundation 0.3.2", + "objc2-quartz-core 0.3.2", +] + +[[package]] +name = "read-fonts" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b634fabf032fab15307ffd272149b622260f55974d9fad689292a5d33df02e5" +dependencies = [ + "bytemuck", + "font-types", +] + [[package]] name = "redox_syscall" version = "0.4.1" @@ -2740,14 +2875,15 @@ checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" [[package]] name = "ron" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db09040cc89e461f1a265139777a2bde7f8d8c67c4936f700c63ce3e2904d468" +checksum = "4147b952f3f819eca0e99527022f7d6a8d05f111aeb0a62960c74eb283bec8fc" dependencies = [ - "base64", "bitflags 2.10.0", + "once_cell", "serde", "serde_derive", + "typeid", "unicode-ident", ] @@ -2840,6 +2976,12 @@ dependencies = [ "tiny-skia", ] +[[package]] +name = "self_cell" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89" + [[package]] name = "serde" version = "1.0.228" @@ -2912,6 +3054,22 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "siphasher" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" + +[[package]] +name = "skrifa" +version = "0.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fbdfe3d2475fbd7ddd1f3e5cf8288a30eb3e5f95832829570cd88115a7434ac" +dependencies = [ + "bytemuck", + "read-fonts", +] + [[package]] name = "slab" version = "0.4.11" @@ -2932,6 +3090,9 @@ name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +dependencies = [ + "serde", +] [[package]] name = "smithay-client-toolkit" @@ -2972,7 +3133,7 @@ dependencies = [ "log", "memmap2", "rustix 1.1.2", - "thiserror 2.0.17", + "thiserror 2.0.18", "wayland-backend", "wayland-client", "wayland-csd-frame", @@ -3007,9 +3168,9 @@ dependencies = [ [[package]] name = "spirv" -version = "0.3.0+sdk-1.3.268.0" +version = "0.4.0+sdk-1.4.341.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" +checksum = "d9571ea910ebd84c86af4b3ed27f9dbdc6ad06f17c5f96146b2b671e2976744f" dependencies = [ "bitflags 2.10.0", ] @@ -3125,11 +3286,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 2.0.17", + "thiserror-impl 2.0.18", ] [[package]] @@ -3145,9 +3306,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", @@ -3280,6 +3441,12 @@ dependencies = [ "rustc-hash 2.1.1", ] +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + [[package]] name = "uds_windows" version = "1.1.0" @@ -3344,6 +3511,32 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "vello_common" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bd1a4c633ce09e7d713df1a6e036644a125e15e0c169cfb5180ddf5836ca04b" +dependencies = [ + "bytemuck", + "fearless_simd", + "hashbrown 0.16.1", + "log", + "peniko", + "skrifa", + "smallvec", +] + +[[package]] +name = "vello_cpu" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0162bfe48aabf6a9fdcd401b628c7d9f260c2cbabb343c70a65feba6f7849edc" +dependencies = [ + "bytemuck", + "hashbrown 0.16.1", + "vello_common", +] + [[package]] name = "version_check" version = "0.9.5" @@ -3385,9 +3578,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.105" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" +checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" dependencies = [ "cfg-if", "once_cell", @@ -3398,22 +3591,19 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.55" +version = "0.4.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0" +checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f" dependencies = [ - "cfg-if", "js-sys", - "once_cell", "wasm-bindgen", - "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.105" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" +checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3421,9 +3611,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.105" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" +checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" dependencies = [ "bumpalo", "proc-macro2", @@ -3434,9 +3624,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.105" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" +checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" dependencies = [ "unicode-ident", ] @@ -3578,9 +3768,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.82" +version = "0.3.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" +checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436" dependencies = [ "js-sys", "wasm-bindgen", @@ -3606,7 +3796,7 @@ dependencies = [ "jni", "log", "ndk-context", - "objc2 0.6.3", + "objc2 0.6.4", "objc2-foundation 0.3.2", "url", "web-sys", @@ -3620,16 +3810,17 @@ checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" [[package]] name = "wgpu" -version = "27.0.1" +version = "29.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfe68bac7cde125de7a731c3400723cadaaf1703795ad3f4805f187459cd7a77" +checksum = "bb3feacc458f7bee8bc1737149b42b6c731aa461039a4264a67bb6681646b250" dependencies = [ "arrayvec", "bitflags 2.10.0", + "bytemuck", "cfg-if", "cfg_aliases", "document-features", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "js-sys", "log", "naga", @@ -3649,9 +3840,9 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "27.0.3" +version = "29.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27a75de515543b1897b26119f93731b385a19aea165a1ec5f0e3acecc229cae7" +checksum = "02da3ad1b568337f25513b317870960ef87073ea0945502e44b864b67a8c77b7" dependencies = [ "arrayvec", "bit-set", @@ -3660,7 +3851,7 @@ dependencies = [ "bytemuck", "cfg_aliases", "document-features", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "indexmap", "log", "naga", @@ -3671,72 +3862,84 @@ dependencies = [ "raw-window-handle", "rustc-hash 1.1.0", "smallvec", - "thiserror 2.0.17", + "thiserror 2.0.18", "wgpu-core-deps-apple", "wgpu-core-deps-emscripten", + "wgpu-core-deps-wasm", "wgpu-core-deps-windows-linux-android", "wgpu-hal", + "wgpu-naga-bridge", "wgpu-types", ] [[package]] name = "wgpu-core-deps-apple" -version = "27.0.0" +version = "29.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0772ae958e9be0c729561d5e3fd9a19679bcdfb945b8b1a1969d9bfe8056d233" +checksum = "62e51b5447e144b3dbba4feb01f80f4fa21696fa0cd99afb2c3df1affd6fdb28" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-emscripten" -version = "27.0.0" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3487cd6293a963bc5c0c0396f6a2192043c50003c07f4efdccbad3d90ec9d819" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-core-deps-wasm" +version = "29.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06ac3444a95b0813ecfd81ddb2774b66220b264b3e2031152a4a29fda4da6b5" +checksum = "0c2f2fb042f36920771deb0b966543c5751b18f3d327760ffc90f74e20b2dcd4" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-windows-linux-android" -version = "27.0.0" +version = "29.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71197027d61a71748e4120f05a9242b2ad142e3c01f8c1b47707945a879a03c3" +checksum = "1bfb01076d0aa08b0ba9bd741e178b5cc440f5abe99d9581323a4c8b5d1a1916" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-hal" -version = "27.0.4" +version = "29.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b21cb61c57ee198bc4aff71aeadff4cbb80b927beb912506af9c780d64313ce" +checksum = "31f8e1a9e7a8512f276f7c62e018c7fa8d60954303fed2e5750114332049193f" dependencies = [ "android_system_properties", "arrayvec", "ash", "bit-set", "bitflags 2.10.0", - "block", + "block2 0.6.2", "bytemuck", "cfg-if", "cfg_aliases", - "core-graphics-types 0.2.0", "glow", "glutin_wgl_sys", - "gpu-alloc", "gpu-allocator", "gpu-descriptor", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "js-sys", "khronos-egl", "libc", "libloading", "log", - "metal", "naga", "ndk-sys", - "objc", + "objc2 0.6.4", + "objc2-core-foundation", + "objc2-foundation 0.3.2", + "objc2-metal 0.3.2", + "objc2-quartz-core 0.3.2", "once_cell", "ordered-float", "parking_lot", @@ -3745,27 +3948,41 @@ dependencies = [ "profiling", "range-alloc", "raw-window-handle", + "raw-window-metal", "renderdoc-sys", "smallvec", - "thiserror 2.0.17", + "thiserror 2.0.18", "wasm-bindgen", + "wayland-sys", "web-sys", + "wgpu-naga-bridge", + "wgpu-types", + "windows", + "windows-core", + "windows-result", +] + +[[package]] +name = "wgpu-naga-bridge" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59c654c483f058800972c3645e95388a7eca31bf9fe1933bc20e036588a0be02" +dependencies = [ + "naga", "wgpu-types", - "windows 0.58.0", - "windows-core 0.58.0", ] [[package]] name = "wgpu-types" -version = "27.0.1" +version = "29.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afdcf84c395990db737f2dd91628706cb31e86d72e53482320d368e52b5da5eb" +checksum = "a9bcc31518a0e9735aefebedb5f7a9ef3ed1c42549c9f4c882fa9060ceaac639" dependencies = [ "bitflags 2.10.0", "bytemuck", "js-sys", "log", - "thiserror 2.0.17", + "raw-window-handle", "web-sys", ] @@ -3802,84 +4019,49 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.58.0" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" -dependencies = [ - "windows-core 0.58.0", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows" -version = "0.61.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" dependencies = [ "windows-collections", - "windows-core 0.61.2", + "windows-core", "windows-future", - "windows-link 0.1.3", "windows-numerics", ] [[package]] name = "windows-collections" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" -dependencies = [ - "windows-core 0.61.2", -] - -[[package]] -name = "windows-core" -version = "0.58.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" dependencies = [ - "windows-implement 0.58.0", - "windows-interface 0.58.0", - "windows-result 0.2.0", - "windows-strings 0.1.0", - "windows-targets 0.52.6", + "windows-core", ] [[package]] name = "windows-core" -version = "0.61.2" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ - "windows-implement 0.60.2", - "windows-interface 0.59.3", - "windows-link 0.1.3", - "windows-result 0.3.4", - "windows-strings 0.4.2", + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", ] [[package]] name = "windows-future" -version = "0.2.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", + "windows-core", + "windows-link", "windows-threading", ] -[[package]] -name = "windows-implement" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "windows-implement" version = "0.60.2" @@ -3891,17 +4073,6 @@ dependencies = [ "syn", ] -[[package]] -name = "windows-interface" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "windows-interface" version = "0.59.3" @@ -3913,12 +4084,6 @@ dependencies = [ "syn", ] -[[package]] -name = "windows-link" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" - [[package]] name = "windows-link" version = "0.2.1" @@ -3927,49 +4092,30 @@ checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-numerics" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" -dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", -] - -[[package]] -name = "windows-result" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" dependencies = [ - "windows-targets 0.52.6", + "windows-core", + "windows-link", ] [[package]] name = "windows-result" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" -dependencies = [ - "windows-link 0.1.3", -] - -[[package]] -name = "windows-strings" -version = "0.1.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-result 0.2.0", - "windows-targets 0.52.6", + "windows-link", ] [[package]] name = "windows-strings" -version = "0.4.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link 0.1.3", + "windows-link", ] [[package]] @@ -4014,7 +4160,7 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -4054,7 +4200,7 @@ version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows-link 0.2.1", + "windows-link", "windows_aarch64_gnullvm 0.53.1", "windows_aarch64_msvc 0.53.1", "windows_i686_gnu 0.53.1", @@ -4067,11 +4213,11 @@ dependencies = [ [[package]] name = "windows-threading" -version = "0.1.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" dependencies = [ - "windows-link 0.1.3", + "windows-link", ] [[package]] @@ -4214,15 +4360,15 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winit" -version = "0.30.12" +version = "0.30.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66d4b9ed69c4009f6321f762d6e61ad8a2389cd431b97cb1e146812e9e6c732" +checksum = "a6755fa58a9f8350bd1e472d4c3fcc25f824ec358933bba33306d0b63df5978d" dependencies = [ "ahash", "android-activity", "atomic-waker", "bitflags 2.10.0", - "block2", + "block2 0.5.1", "bytemuck", "calloop 0.13.0", "cfg_aliases", @@ -4238,7 +4384,7 @@ dependencies = [ "objc2 0.5.2", "objc2-app-kit 0.2.2", "objc2-foundation 0.2.2", - "objc2-ui-kit", + "objc2-ui-kit 0.2.2", "orbclient", "percent-encoding", "pin-project", diff --git a/Cargo.toml b/Cargo.toml index d8e34e2..a104d6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,8 +3,8 @@ members = ["examples/*"] # dependencies that are shared throughout the workspace (mostly for examples) [workspace.dependencies] -egui = "0.33" -eframe = { version = "0.33", default-features = false, features = [ +egui = "0.34" +eframe = { version = "0.34", default-features = false, features = [ "accesskit", # Make egui compatible with screen readers. NOTE: adds a lot of dependencies. "default_fonts", # Embed the default egui fonts. "wgpu", # Use the glow rendering backend. Alternative: "wgpu". @@ -12,7 +12,7 @@ eframe = { version = "0.33", default-features = false, features = [ ] } env_logger = "0.11.6" log = "0.4" -wgpu = "27" # enables the wgpu default features so we get the default backends +wgpu = "29" # enables the wgpu default features so we get the default backends [package] name = "egui_term" diff --git a/examples/custom_bindings/src/app.rs b/examples/custom_bindings/src/app.rs index 11f2489..33d660d 100644 --- a/examples/custom_bindings/src/app.rs +++ b/examples/custom_bindings/src/app.rs @@ -80,7 +80,8 @@ impl App { } impl eframe::App for App { - fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { + fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) { + let ctx = ui.ctx(); if let Ok((_, PtyEvent::Exit)) = self.pty_proxy_receiver.try_recv() { ctx.send_viewport_cmd(egui::ViewportCommand::Close); return; diff --git a/examples/fonts/src/app.rs b/examples/fonts/src/app.rs index f361683..bdc1c06 100644 --- a/examples/fonts/src/app.rs +++ b/examples/fonts/src/app.rs @@ -82,7 +82,8 @@ impl App { } impl eframe::App for App { - fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { + fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) { + let ctx = ui.ctx(); if let Ok((_, PtyEvent::Exit)) = self.pty_proxy_receiver.try_recv() { ctx.send_viewport_cmd(egui::ViewportCommand::Close); return; diff --git a/examples/full_screen/src/app.rs b/examples/full_screen/src/app.rs index 0278ac6..168df9f 100644 --- a/examples/full_screen/src/app.rs +++ b/examples/full_screen/src/app.rs @@ -35,7 +35,8 @@ impl App { } impl eframe::App for App { - fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { + fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) { + let ctx = ui.ctx(); if let Ok((_, PtyEvent::Exit)) = self.pty_proxy_receiver.try_recv() { ctx.send_viewport_cmd(egui::ViewportCommand::Close); return; diff --git a/examples/split_view/Cargo.toml b/examples/split_view/Cargo.toml index 7cc2077..2464c62 100644 --- a/examples/split_view/Cargo.toml +++ b/examples/split_view/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] egui = { workspace = true } -egui_dock = "0.18.0" +egui_dock = "0.19.1" eframe = { workspace = true } log = { workspace = true } egui_term = { path = "../../" } diff --git a/examples/split_view/src/app.rs b/examples/split_view/src/app.rs index 9179d7c..b6b8967 100644 --- a/examples/split_view/src/app.rs +++ b/examples/split_view/src/app.rs @@ -124,7 +124,8 @@ impl App { } impl eframe::App for App { - fn update(&mut self, ctx: &Context, _frame: &mut eframe::Frame) { + fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) { + let ctx = ui.ctx(); DockArea::new(&mut self.dock_state) .show_add_buttons(false) .style(Style::from_egui(ctx.style().as_ref())) diff --git a/examples/tabs/src/app.rs b/examples/tabs/src/app.rs index e6829ca..b097d67 100644 --- a/examples/tabs/src/app.rs +++ b/examples/tabs/src/app.rs @@ -22,7 +22,8 @@ impl App { } impl eframe::App for App { - fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { + fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) { + let ctx = ui.ctx(); if ctx.input(|i| i.viewport().close_requested()) { self.tab_manager.clear(); } diff --git a/examples/themes/src/app.rs b/examples/themes/src/app.rs index dcd4b69..c0bd79e 100644 --- a/examples/themes/src/app.rs +++ b/examples/themes/src/app.rs @@ -39,7 +39,8 @@ impl App { } impl eframe::App for App { - fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { + fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) { + let ctx = ui.ctx(); if let Ok((_, PtyEvent::Exit)) = self.pty_proxy_receiver.try_recv() { ctx.send_viewport_cmd(egui::ViewportCommand::Close); return; From 77af64ebde361752edfdd0ad9d2455e8c5601b1b Mon Sep 17 00:00:00 2001 From: Oleg Orlov Date: Wed, 24 Jun 2026 16:05:38 +0300 Subject: [PATCH 25/26] fix: avoid UI thread blocking on terminal FairMutex during sync() Use try_lock_unfair() in sync() so the UI thread paints the previous frame instead of waiting for the PTY reader thread to finish processing a batch of output. This prevents random freezes in the main window when terminal output is bursty. --- src/backend/mod.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 380f8a7..e4e3d68 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -413,7 +413,14 @@ impl TerminalBackend { pub fn sync(&mut self) -> &RenderableContent { let term = self.term.clone(); - let mut terminal = term.lock(); + let mut terminal = match term.try_lock_unfair() { + Some(guard) => guard, + // The PTY reader thread currently holds the terminal lock while + // processing a batch of output. Block the UI only briefly: paint + // the previous frame's content instead of waiting for the lock. + None => return &self.last_content, + }; + let selectable_range = match &terminal.selection { Some(s) => s.to_range(&terminal), None => None, From 34de807e449a536c90db223c0dae9d109a6b1f08 Mon Sep 17 00:00:00 2001 From: Oleg Orlov Date: Fri, 3 Jul 2026 23:51:26 +0300 Subject: [PATCH 26/26] feat: support transparent terminal background (#RRGGBBAA) - Parse #RRGGBBAA hex colors in TerminalTheme. - Skip drawing the global background rect when alpha is 0. --- src/theme.rs | 11 +++++++++-- src/view.rs | 15 ++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/theme.rs b/src/theme.rs index 63bef9e..5ecba2b 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -206,13 +206,20 @@ impl TerminalTheme { } fn hex_to_color(hex: &str) -> anyhow::Result { - if hex.len() != 7 { + let is_solid = hex.len() == 7; + let is_transparent = hex.len() == 9; + if !is_solid && !is_transparent || !hex.starts_with('#') { return Err(anyhow::format_err!("input string is in non valid format")); } let r = u8::from_str_radix(&hex[1..3], 16)?; let g = u8::from_str_radix(&hex[3..5], 16)?; let b = u8::from_str_radix(&hex[5..7], 16)?; + let a = if is_transparent { + u8::from_str_radix(&hex[7..9], 16)? + } else { + 255 + }; - Ok(Color32::from_rgb(r, g, b)) + Ok(Color32::from_rgba_unmultiplied(r, g, b, a)) } diff --git a/src/view.rs b/src/view.rs index a214ae3..3499fc6 100644 --- a/src/view.rs +++ b/src/view.rs @@ -241,11 +241,16 @@ impl<'a> TerminalView<'a> { let global_bg = self.theme.get_color(Color::Named(NamedColor::Background)); - let mut shapes = vec![Shape::Rect(RectShape::filled( - Rect::from_min_max(layout_min, layout_max), - CornerRadius::ZERO, - global_bg, - ))]; + let mut shapes = Vec::new(); + + // Only draw the global background when it is not fully transparent. + if global_bg.a() > 0 { + shapes.push(Shape::Rect(RectShape::filled( + Rect::from_min_max(layout_min, layout_max), + CornerRadius::ZERO, + global_bg, + ))); + } for indexed in content.grid.display_iter() { let flags = indexed.cell.flags;