aboutsummaryrefslogtreecommitdiff
path: root/src/client/endpoint/activation/protocol.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/endpoint/activation/protocol.rs')
-rw-r--r--src/client/endpoint/activation/protocol.rs219
1 files changed, 219 insertions, 0 deletions
diff --git a/src/client/endpoint/activation/protocol.rs b/src/client/endpoint/activation/protocol.rs
new file mode 100644
index 00000000..569d53f3
--- /dev/null
+++ b/src/client/endpoint/activation/protocol.rs
@@ -0,0 +1,219 @@
+use super::super::{ClientEndpointId, EndpointRegistry, EndpointSendOutcome};
+use super::model::{ActivationEvidence, EndpointLease};
+
+pub(super) fn focus_result_matches(
+ focus: Option<&crate::client::shell::ClientEndpointFocusTarget>,
+ result: &crate::api::schema::ResponseResult,
+) -> bool {
+ match (focus, result) {
+ (
+ Some(crate::client::shell::ClientEndpointFocusTarget::Pane(expected)),
+ crate::api::schema::ResponseResult::PaneInfo { pane },
+ ) => pane.focused && &pane.pane_id == expected,
+ (
+ Some(crate::client::shell::ClientEndpointFocusTarget::Workspace(expected)),
+ crate::api::schema::ResponseResult::WorkspaceInfo { workspace },
+ ) => workspace.focused && &workspace.workspace_id == expected,
+ (
+ Some(crate::client::shell::ClientEndpointFocusTarget::Tab(expected)),
+ crate::api::schema::ResponseResult::TabInfo { tab },
+ ) => tab.focused && &tab.tab_id == expected,
+ _ => false,
+ }
+}
+
+pub(super) fn endpoint_lease(
+ shell: &crate::client::shell::ClientShellState,
+ endpoints: &EndpointRegistry,
+ endpoint_id: &ClientEndpointId,
+) -> Result<EndpointLease, String> {
+ let connection = endpoints
+ .connection(endpoint_id)
+ .ok_or_else(|| "endpoint connection is unavailable".to_owned())?;
+ let (boot_id, minimum_revision) = shell
+ .endpoint_snapshot_identity(endpoint_id, connection.generation)
+ .ok_or_else(|| "endpoint metadata is not ready for this connection".to_owned())?;
+ Ok(EndpointLease {
+ endpoint_id: endpoint_id.clone(),
+ generation: connection.generation,
+ boot_id: boot_id.to_owned(),
+ minimum_revision,
+ })
+}
+
+pub(super) fn disconnected_endpoint_lease(
+ shell: &crate::client::shell::ClientShellState,
+ endpoint_id: &ClientEndpointId,
+) -> EndpointLease {
+ EndpointLease {
+ endpoint_id: endpoint_id.clone(),
+ generation: 0,
+ boot_id: shell
+ .endpoint_boot_id(endpoint_id)
+ .unwrap_or_default()
+ .to_owned(),
+ minimum_revision: 0,
+ }
+}
+
+pub(super) fn endpoint_matches(
+ lease: &EndpointLease,
+ endpoint_id: &ClientEndpointId,
+ generation: u64,
+ boot_id: &str,
+) -> bool {
+ lease.endpoint_id == *endpoint_id && lease.generation == generation && lease.boot_id == boot_id
+}
+
+pub(super) fn coherent_completion_surface(
+ shell: &crate::client::shell::ClientShellState,
+ lease: &EndpointLease,
+ evidence: &ActivationEvidence,
+ acknowledgement_revision: Option<u64>,
+ geometry: crate::protocol::ClientSurfaceSize,
+) -> Result<crate::protocol::PaneSurfaceFrame, String> {
+ let acknowledgement_revision = acknowledgement_revision.ok_or_else(|| {
+ "endpoint activation completed without a surface acknowledgement".to_owned()
+ })?;
+ let surface = evidence
+ .surface
+ .clone()
+ .ok_or_else(|| "endpoint activation completed without a surface".to_owned())?;
+ if surface.projection_revision < acknowledgement_revision
+ || !surface_matches_geometry(&surface, geometry)
+ {
+ return Err("endpoint activation lost its acknowledged surface evidence".into());
+ }
+ if !shell.endpoint_snapshot_matches(
+ &lease.endpoint_id,
+ lease.generation,
+ &lease.boot_id,
+ surface.projection_revision,
+ ) {
+ return Err("endpoint activation lost its coherent snapshot/surface pair".into());
+ }
+ Ok(surface)
+}
+
+pub(super) fn resize_geometry(
+ message: &crate::protocol::ClientMessage,
+) -> Option<crate::protocol::ClientSurfaceSize> {
+ match message {
+ crate::protocol::ClientMessage::ClientShellResize { surface_size, .. } => {
+ Some(*surface_size)
+ }
+ _ => None,
+ }
+}
+
+pub(super) fn surface_matches_geometry(
+ surface: &crate::protocol::PaneSurfaceFrame,
+ geometry: crate::protocol::ClientSurfaceSize,
+) -> bool {
+ surface.frame.width == geometry.cols && surface.frame.height == geometry.rows
+}
+
+pub(super) fn send_surface_activation(
+ endpoints: &mut EndpointRegistry,
+ target: &EndpointLease,
+ request_id: String,
+ resize: &crate::protocol::ClientMessage,
+ focused: bool,
+) -> Result<(), String> {
+ if endpoints.send_to(&target.endpoint_id, resize) != EndpointSendOutcome::Sent {
+ return Err("endpoint resize could not be sent".into());
+ }
+ let request = surface_interest_request(&target.boot_id, request_id, true)
+ .map_err(|error| error.to_string())?;
+ if endpoints.send_to(&target.endpoint_id, &request) != EndpointSendOutcome::Sent {
+ return Err("endpoint activation could not be sent".into());
+ }
+ // Inactive endpoints reject focus events. Activate first, then establish the host baseline
+ // on the same ordered transport before navigation or presentation can commit.
+ if endpoints.send_to(
+ &target.endpoint_id,
+ &crate::protocol::ClientMessage::ClientShellFocus { focused },
+ ) != EndpointSendOutcome::Sent
+ {
+ return Err("endpoint focus baseline could not be sent".into());
+ }
+ Ok(())
+}
+
+pub(super) fn decode_endpoint_response(
+ request_id: &str,
+ data: &[u8],
+) -> Result<crate::api::schema::ResponseResult, crate::client::shell::ClientShellEndpointError> {
+ crate::client::endpoint_commands::parse_response(request_id, data)
+}
+
+pub(super) fn surface_set_revision(
+ result: &crate::api::schema::ResponseResult,
+ expected_active: bool,
+) -> Result<u64, String> {
+ match result {
+ crate::api::schema::ResponseResult::ClientShellSurfaceSet {
+ active,
+ projection_revision,
+ } if *active == expected_active => Ok(*projection_revision),
+ _ => Err("surface activation returned an invalid acknowledgement".into()),
+ }
+}
+
+pub(super) fn focus_request(
+ boot_id: &str,
+ request_id: String,
+ focus: &crate::client::shell::ClientEndpointFocusTarget,
+) -> std::io::Result<crate::protocol::ClientMessage> {
+ let method = match focus {
+ crate::client::shell::ClientEndpointFocusTarget::Workspace(workspace_id) => {
+ crate::api::schema::Method::WorkspaceFocus(crate::api::schema::WorkspaceTarget {
+ workspace_id: workspace_id.clone(),
+ })
+ }
+ crate::client::shell::ClientEndpointFocusTarget::Pane(pane_id) => {
+ crate::api::schema::Method::PaneFocus(crate::api::schema::PaneTarget {
+ pane_id: pane_id.clone(),
+ })
+ }
+ crate::client::shell::ClientEndpointFocusTarget::Tab(tab_id) => {
+ crate::api::schema::Method::TabFocus(crate::api::schema::TabTarget {
+ tab_id: tab_id.clone(),
+ })
+ }
+ };
+ endpoint_request(
+ boot_id,
+ crate::api::schema::Request {
+ id: request_id,
+ method,
+ },
+ )
+}
+
+pub(super) fn surface_interest_request(
+ boot_id: &str,
+ request_id: String,
+ active: bool,
+) -> std::io::Result<crate::protocol::ClientMessage> {
+ endpoint_request(
+ boot_id,
+ crate::api::schema::Request {
+ id: request_id,
+ method: crate::api::schema::Method::ClientShellSurfaceSet(
+ crate::api::schema::ClientShellSurfaceSetParams { active },
+ ),
+ },
+ )
+}
+
+fn endpoint_request(
+ boot_id: &str,
+ request: crate::api::schema::Request,
+) -> std::io::Result<crate::protocol::ClientMessage> {
+ Ok(crate::protocol::ClientMessage::ClientShellEndpointRequest {
+ boot_id: boot_id.to_owned(),
+ request: serde_json::to_string(&request)
+ .map_err(|error| std::io::Error::new(std::io::ErrorKind::InvalidData, error))?,
+ })
+}