diff options
| author | Can Celik <ogulcancelik@gmail.com> | 2026-08-09 02:04:39 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-08-09 02:04:39 +0300 |
| commit | 1777e9bba32b953ed1ad203b4a16d01105539000 (patch) | |
| tree | 22096973812c9baba822453d8470bdcfe64fc2a6 /src/app/mod.rs | |
| parent | 36a8d36a7258e08b40c01bf521b1f4006525f143 (diff) | |
feat(graphics): add direct pane frame streaming (#2523)
* feat(graphics): add direct pane frame streaming
* feat(graphics): expose pane visibility
* test(graphics): preserve legacy terminal image output
* fix(graphics): harden streaming lifecycle
* fix(input): preserve mouse releases outside panes
Diffstat (limited to 'src/app/mod.rs')
| -rw-r--r-- | src/app/mod.rs | 61 |
1 files changed, 49 insertions, 12 deletions
diff --git a/src/app/mod.rs b/src/app/mod.rs index 70d5fe93..15f352af 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -16,6 +16,7 @@ mod creation; mod git_refresh; mod ids; mod input; +pub(crate) mod pane_graphics; mod popup; mod runtime; mod runtime_mutations; @@ -95,6 +96,10 @@ impl PaneClickState { pub struct App { pub state: AppState, + pub(crate) pane_graphics: pane_graphics::Runtime, + pub(crate) pane_graphics_files: Arc<crate::pane_graphics_files::FileStore>, + pub(crate) direct_graphics_available: bool, + pub(crate) pixel_mouse_available: bool, pub(crate) terminal_runtimes: crate::terminal::TerminalRuntimeRegistry, pub event_tx: mpsc::Sender<AppEvent>, pub(crate) event_rx: mpsc::Receiver<AppEvent>, @@ -672,9 +677,6 @@ impl App { integration_install_messages: Vec::new(), installed_plugins: load_plugin_registry(no_session), plugin_panes: std::collections::HashMap::new(), - pane_graphics_layers: std::collections::HashMap::new(), - pane_graphics_streams: std::collections::HashMap::new(), - pane_graphics_revision: 0, popup_pane: None, plugin_command_logs: Vec::new(), next_plugin_command_log_id: 1, @@ -682,6 +684,7 @@ impl App { global_menu: state::MenuListState::new(0), host_terminal_theme: crate::terminal_theme::TerminalTheme::default(), host_cell_size: crate::kitty_graphics::HostCellSize::default(), + host_mouse_pixels: None, session_dirty: false, terminal_runtime_shutdowns: Vec::new(), }; @@ -726,6 +729,10 @@ impl App { copy_feedback_deadline: None, last_api_notification_at: None, state, + pane_graphics: pane_graphics::Runtime::default(), + pane_graphics_files: Arc::new(crate::pane_graphics_files::FileStore::default()), + direct_graphics_available: false, + pixel_mouse_available: false, terminal_runtimes: restored_terminal_runtimes, event_tx, event_rx, @@ -1033,6 +1040,10 @@ impl App { needs_render = true; } + if self.pane_graphics.retain_live_panes(&self.state) { + needs_render = true; + } + let now = Instant::now(); self.sync_host_mouse_capture(&mut host_mouse_capture_active)?; self.sync_host_keyboard_report_all(&mut host_keyboard_report_all_active)?; @@ -1048,18 +1059,19 @@ impl App { terminal.swap_buffers(); self.full_redraw_pending = false; } - let mut cell_size = crate::kitty_graphics::HostCellSize::default(); + let mut cell_size = self.state.host_cell_size; terminal.draw(|frame| { let area = frame.area(); if kitty_graphics_enabled { - let observed_cell_size = - crate::kitty_graphics::HostCellSize::try_from_terminal(area); - if let Some(observed_cell_size) = observed_cell_size { + if let Some(observed_cell_size) = + crate::kitty_graphics::HostCellSize::try_from_terminal(area) + { self.state.host_cell_size = observed_cell_size; + cell_size = observed_cell_size; + } else if !cell_size.is_known() { + cell_size = + crate::kitty_graphics::HostCellSize::fallback_for_area(area); } - cell_size = observed_cell_size.unwrap_or_else(|| { - crate::kitty_graphics::HostCellSize::fallback_for_area(area) - }); crate::ui::compute_view_with_cell_size( &mut self.state, &self.terminal_runtimes, @@ -1082,6 +1094,7 @@ impl App { if kitty_graphics_enabled { crate::kitty_graphics::paint_local_pane_graphics( &self.state, + &self.pane_graphics, &self.terminal_runtimes, cell_size, )?; @@ -1468,8 +1481,7 @@ impl App { crate::kitty_graphics::set_enabled(config.experimental.kitty_graphics); if was_kitty_graphics_enabled && !config.experimental.kitty_graphics { let _ = crate::kitty_graphics::clear_all_host_graphics(); - self.state.pane_graphics_layers.clear(); - self.state.pane_graphics_streams.clear(); + self.pane_graphics.clear(); self.state.host_cell_size = crate::kitty_graphics::HostCellSize::default(); } self.state.reveal_hidden_cursor_for_cjk_ime = @@ -1663,6 +1675,31 @@ impl App { self.route_client_events(events, true); } + pub(crate) fn route_client_pixel_mouse( + &mut self, + source_id: InputSourceId, + data: &[u8], + geometry: crate::input::mouse::HostGeometry, + ) -> bool { + let Some((x, y)) = crate::input::mouse::parse_report(data) else { + return false; + }; + let Some((column, row)) = geometry.cell(x, y) else { + return false; + }; + let Some(cell_report) = crate::input::mouse::report_at_cell(data, column, row) else { + return false; + }; + let mut events = crate::raw_input::parse_raw_input_bytes_sync(&cell_report); + if events.len() != 1 || !matches!(events[0], crate::raw_input::RawInputEvent::Mouse(_)) { + return false; + } + self.state.host_mouse_pixels = Some(crate::input::mouse::HostPixels { x, y, geometry }); + self.route_client_events_from(source_id, std::mem::take(&mut events), false); + self.state.host_mouse_pixels = None; + true + } + pub(crate) fn route_client_events( &mut self, events: Vec<crate::raw_input::RawInputEvent>, |
