diff options
Diffstat (limited to 'src/server/headless.rs')
| -rw-r--r-- | src/server/headless.rs | 233 |
1 files changed, 25 insertions, 208 deletions
diff --git a/src/server/headless.rs b/src/server/headless.rs index bba59498..fe8a67e8 100644 --- a/src/server/headless.rs +++ b/src/server/headless.rs @@ -76,8 +76,8 @@ mod bootstrap; mod client_views; mod endpoint_requests; mod lifecycle; +mod native_graphics; mod notifications; -mod pane_graphics; mod render; mod retained_surface; mod surface_interest; @@ -132,31 +132,16 @@ enum LoopEvent { RenderRequested, } -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] +/// Presentation work caused by a server event. +/// +/// Keeping this classification separate from event delivery makes the input +/// contract explicit: events may reach a PTY without necessarily repainting. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] enum RenderImpact { - #[default] None, - Graphics, Full, } -impl RenderImpact { - fn merge(&mut self, other: Self) { - *self = (*self).max(other); - } -} - -fn record_render_impact(source: &'static str, impact: RenderImpact) { - let event = match (source, impact) { - ("api_requests", RenderImpact::Graphics) => "graphics_render_cause.api_requests", - ("api_requests", RenderImpact::Full) => "full_render_cause.api_requests", - ("server_events", RenderImpact::Graphics) => "graphics_render_cause.server_events", - ("server_events", RenderImpact::Full) => "full_render_cause.server_events", - _ => return, - }; - crate::render_prof::event(event); -} - // --------------------------------------------------------------------------- // Constants // --------------------------------------------------------------------------- @@ -205,6 +190,7 @@ pub struct HeadlessServer { client_socket_path: PathBuf, client_socket_identity: SocketFileIdentity, clients: HashMap<u64, ClientConnection>, + native_graphics: native_graphics::NativeGraphics, #[cfg(unix)] next_client_id: u64, /// The client currently driving session-wide host presentation and side effects. @@ -350,6 +336,7 @@ impl HeadlessServer { client_socket_path: client_path, client_socket_identity, clients: HashMap::new(), + native_graphics: Default::default(), #[cfg(unix)] next_client_id: 1, foreground_client_id: None, @@ -463,22 +450,7 @@ impl HeadlessServer { } // 3. Drain API requests. - if self.pane_graphics_runtime_active() { - let api_impact = self.drain_api_requests_with_render_impact(); - record_render_impact("api_requests", api_impact); - match api_impact { - RenderImpact::None => {} - RenderImpact::Graphics => { - needs_render = true; - needs_graphics_render = true; - } - RenderImpact::Full => { - needs_render = true; - needs_full_render = true; - needs_graphics_render = false; - } - } - } else if self.drain_api_requests_with_shutdown_check() { + if self.drain_api_requests_with_shutdown_check() { needs_render = true; needs_full_render = true; crate::render_prof::event("full_render_cause.api_requests"); @@ -494,22 +466,7 @@ impl HeadlessServer { self.accept_client_connections()?; // 5. Drain server events from client threads. - if self.pane_graphics_runtime_active() { - let server_impact = self.drain_server_events_with_render_impact(); - record_render_impact("server_events", server_impact); - match server_impact { - RenderImpact::None => {} - RenderImpact::Graphics => { - needs_render = true; - needs_graphics_render = true; - } - RenderImpact::Full => { - needs_render = true; - needs_full_render = true; - needs_graphics_render = false; - } - } - } else if self.drain_server_events() { + if self.drain_server_events() { needs_render = true; needs_full_render = true; crate::render_prof::event("full_render_cause.server_events"); @@ -541,13 +498,10 @@ impl HeadlessServer { crate::render_prof::event("full_render_cause.default_workspace"); } - if self.retain_live_pane_graphics() { - needs_render = true; - needs_graphics_render = true; - } - if self.expire_direct_graphics(now) { + if self.expire_native_graphics(now) { needs_render = true; - needs_graphics_render = true; + needs_full_render = true; + needs_graphics_render = false; } self.drain_client_config_reload_request(); @@ -689,43 +643,13 @@ impl HeadlessServer { } } LoopEvent::Api(msg) => { - if self.pane_graphics_runtime_active() { - let impact = self.handle_api_request_with_render_impact(*msg); - record_render_impact("api_requests", impact); - match impact { - RenderImpact::None => {} - RenderImpact::Graphics => { - needs_render = true; - needs_graphics_render = true; - } - RenderImpact::Full => { - needs_render = true; - needs_full_render = true; - needs_graphics_render = false; - } - } - } else if self.handle_api_request_with_shutdown_check(*msg) { + if self.handle_api_request_with_shutdown_check(*msg) { needs_render = true; needs_full_render = true; } } LoopEvent::ServerEvent(ev) => { - if self.pane_graphics_runtime_active() { - let impact = self.handle_server_event_with_render_impact(ev); - record_render_impact("server_events", impact); - match impact { - RenderImpact::None => {} - RenderImpact::Graphics => { - needs_render = true; - needs_graphics_render = true; - } - RenderImpact::Full => { - needs_render = true; - needs_full_render = true; - needs_graphics_render = false; - } - } - } else if self.handle_server_event(ev) { + if self.handle_server_event_with_render_impact(ev) == RenderImpact::Full { needs_render = true; needs_full_render = true; } @@ -825,15 +749,11 @@ impl HeadlessServer { } fn sync_foreground_client_state(&mut self) { - self.app.direct_graphics_available = self.direct_graphics_available(); self.app.pixel_mouse_available = self.foreground_client_id.is_some_and(|id| { self.clients .get(&id) .is_some_and(|client| client.pixel_mouse) }); - if !self.app.direct_graphics_available { - self.retire_all_direct_graphics(); - } let Some(client_id) = self.foreground_client_id else { self.effective_size = self.headless_size; self.app.state.outer_terminal_focus = None; @@ -966,53 +886,12 @@ impl HeadlessServer { }) } - fn existing_direct_graphics_client(&self) -> Option<u64> { - self.app - .pane_graphics - .slots - .values() - .filter_map(crate::app::pane_graphics::Slot::direct_client) - .filter_map(|client_id| { - let client = self.clients.get(&client_id)?; - self.client_supports_direct_graphics(client_id) - .then_some((client.last_activity, client_id)) - }) - .max() - .map(|(_, client_id)| client_id) - } - - fn direct_graphics_client(&self) -> Option<u64> { - self.existing_direct_graphics_client().or_else(|| { - self.foreground_client_id - .filter(|client_id| self.client_supports_direct_graphics(*client_id)) - }) - } - - fn direct_graphics_client_for_key(&self, key: &crate::app::pane_graphics::Key) -> Option<u64> { - match self - .app - .pane_graphics - .slots - .get(key) - .and_then(crate::app::pane_graphics::Slot::direct_client) - { - Some(client_id) => self - .client_supports_direct_graphics(client_id) - .then_some(client_id), - None => self.direct_graphics_client(), - } - } - - fn direct_graphics_available(&self) -> bool { - self.direct_graphics_client().is_some() - } - fn has_app_client(&self) -> bool { self.app_client_count() > 0 } fn remove_client(&mut self, client_id: u64) -> bool { - self.retire_direct_graphics_for_client(client_id); + self.disconnect_native_graphics(client_id); let disconnected_focus = self .clients .get(&client_id) @@ -1052,7 +931,6 @@ impl HeadlessServer { self.send_shell_focus_target(target, crate::ghostty::FocusEvent::Lost); } } - self.app.direct_graphics_available = self.direct_graphics_available(); if was_foreground { self.promote_latest_remaining_client() } else { @@ -1131,31 +1009,17 @@ impl HeadlessServer { } /// Drains server events from the dedicated channel. - /// - /// Uses the original full-render semantics when pane graphics are dormant. fn drain_server_events(&mut self) -> bool { let mut changed = false; while !self.should_quit.load(Ordering::Acquire) { let Ok(ev) = self.server_event_rx.try_recv() else { break; }; - changed |= self.handle_server_event(ev); + changed |= self.handle_server_event_with_render_impact(ev) == RenderImpact::Full; } changed } - /// Returns the strongest render impact from the drained event batch. - fn drain_server_events_with_render_impact(&mut self) -> RenderImpact { - let mut impact = RenderImpact::None; - while !self.should_quit.load(Ordering::Acquire) { - let Ok(ev) = self.server_event_rx.try_recv() else { - break; - }; - impact.merge(self.handle_server_event_with_render_impact(ev)); - } - impact - } - async fn reject_late_client_connections(&mut self) { self.server_event_rx.close(); while let Some(event) = self.server_event_rx.recv().await { @@ -2112,12 +1976,17 @@ impl HeadlessServer { transfer_id, image_id, success, - } => self.complete_direct_graphics(client_id, transfer_id, image_id, success), + } => self + .native_result(client_id, transfer_id, image_id, success) + .unwrap_or(false), ServerEvent::GraphicsTransmissionStarted { client_id, transfer_id, image_id, - } => self.start_direct_graphics_response(client_id, transfer_id, image_id), + } => { + self.native_started(client_id, transfer_id, image_id); + false + } ServerEvent::ClientAttachTerminal { client_id, terminal_id, @@ -2937,35 +2806,6 @@ impl HeadlessServer { } } - fn drain_api_requests_with_render_impact(&mut self) -> RenderImpact { - let mut impact = RenderImpact::None; - while !self.should_quit.load(Ordering::Acquire) { - let Ok(msg) = self.app.api_rx.try_recv() else { - break; - }; - impact.merge(self.handle_api_request_with_render_impact(msg)); - } - impact - } - - fn handle_api_request_with_render_impact( - &mut self, - msg: api::ApiRequestMessage, - ) -> RenderImpact { - if matches!( - &msg.request.method, - api::schema::Method::PaneGraphicsStreamSet(_) - | api::schema::Method::PaneGraphicsStreamDirect(_) - ) { - return self.handle_pane_graphics_stream_frame(msg); - } - if self.handle_api_request_with_shutdown_check(msg) { - RenderImpact::Full - } else { - RenderImpact::None - } - } - fn handle_api_request_with_shutdown_check_inner( &mut self, msg: api::ApiRequestMessage, @@ -2999,12 +2839,6 @@ impl HeadlessServer { }; let metadata_expired = self.app.expire_due_metadata(Instant::now()); - let stream_open = match &msg.request.method { - api::schema::Method::PaneGraphicsStreamOpen(params) => Some(params.clone()), - _ => None, - }; - let stream_active = msg.stream_active.clone(); - if let api::schema::Method::ServerLiveHandoff(params) = &msg.request.method { let handoff_result = self.perform_live_handoff(params.clone()); let handoff_succeeded = handoff_result.is_ok(); @@ -3054,16 +2888,7 @@ impl HeadlessServer { _ => {} } - let pane_graphics_revision_before = matches!( - &msg.request.method, - api::schema::Method::PaneGraphicsSet(_) - | api::schema::Method::PaneGraphicsClear(_) - | api::schema::Method::PaneGraphicsStreamOpen(_) - | api::schema::Method::PaneGraphicsStreamClose(_) - ) - .then_some(self.app.pane_graphics.revision()); - let mut changed = metadata_expired - | (pane_graphics_revision_before.is_none() && api::request_changes_ui(&msg.request)); + let mut changed = metadata_expired | api::request_changes_ui(&msg.request); let skip_default_workspace = skip_default_workspace_for_request || matches!( &msg.request.method, @@ -3184,10 +3009,6 @@ impl HeadlessServer { } } } - if let (Some(params), Some(active)) = (stream_open.as_ref(), stream_active) { - self.app - .attach_pane_graphics_stream_active(params, active, &response); - } if let Some(spec) = alt_screen_read_spec { if let Ok(success) = serde_json::from_str::<api::schema::SuccessResponse>(&response) { if let api::schema::ResponseResult::PaneRead { read } = success.result { @@ -3210,10 +3031,6 @@ impl HeadlessServer { } let _ = msg.respond_to.send(response); - if let Some(revision_before) = pane_graphics_revision_before { - changed |= revision_before != self.app.pane_graphics.revision(); - } - // Forward new toast state only when a client-local delivery mode is selected. // Herdr delivery renders the toast in-frame and must not ask clients to // show a terminal or system notification. |
