Initial public release of Warp.

Repo-Sync-Origin: warpdotdev/warp-internal@12af1d983b
This commit is contained in:
David Stern
2026-04-28 08:43:33 -05:00
commit 0dbd3d567a
4982 changed files with 1431549 additions and 0 deletions
@@ -0,0 +1,149 @@
//! Session management for the XDG RemoteDesktop and ScreenCast portals.
//!
//! This module handles creating and maintaining a portal session that enables
//! input emulation via the RemoteDesktop portal and provides stream IDs for
//! absolute pointer positioning via the ScreenCast portal.
use ashpd::desktop::PersistMode;
use ashpd::desktop::remote_desktop::{DeviceType, RemoteDesktop};
use ashpd::desktop::screencast::{CursorMode, Screencast, SourceType};
use ashpd::enumflags2::BitFlags;
/// A portal session that provides input emulation capabilities.
///
/// This session combines RemoteDesktop (for input) and ScreenCast (for absolute
/// positioning coordinates) into a single session with one permission dialog.
pub struct PortalSession<'a> {
remote_desktop: RemoteDesktop<'a>,
session: ashpd::desktop::Session<'a, RemoteDesktop<'a>>,
/// The PipeWire stream node ID for the primary monitor.
/// Used for absolute pointer positioning.
stream_id: u32,
/// The device types that were granted by the user.
granted_devices: BitFlags<DeviceType>,
}
impl<'a> PortalSession<'a> {
/// Creates and starts a new portal session.
///
/// This will show a permission dialog to the user (unless permissions are
/// already persisted from a previous session).
pub async fn new() -> Result<Self, String> {
let remote_desktop = RemoteDesktop::new()
.await
.map_err(|e| format!("Failed to create RemoteDesktop proxy: {e}"))?;
let screencast = Screencast::new()
.await
.map_err(|e| format!("Failed to create Screencast proxy: {e}"))?;
// Create a RemoteDesktop session. This session is shared with ScreenCast.
let session = remote_desktop
.create_session()
.await
.map_err(|e| format!("Failed to create RemoteDesktop session: {e}"))?;
// Select input devices (keyboard and pointer).
// Note: Some portals don't support persistence for remote desktop sessions,
// so we use DoNot to avoid errors.
remote_desktop
.select_devices(
&session,
DeviceType::Keyboard | DeviceType::Pointer,
None, // No restore token.
PersistMode::DoNot,
)
.await
.map_err(|e| format!("Failed to select devices: {e}"))?;
// Select screencast sources (monitors) to get stream IDs for absolute positioning.
// We use CursorMode::Metadata since we only need the stream ID, not the video.
screencast
.select_sources(
&session,
CursorMode::Metadata,
SourceType::Monitor.into(),
true, // Allow multiple monitors.
None, // No restore token.
PersistMode::DoNot,
)
.await
.map_err(|e| format!("Failed to select screencast sources: {e}"))?;
// Start the session. This shows the permission dialog to the user.
let response = remote_desktop
.start(&session, None)
.await
.map_err(|e| format!("Failed to start session request: {e}"))?
.response()
.map_err(|e| format!("Session start failed: {e}"))?;
// Extract the stream ID from the response.
let streams = response
.streams()
.ok_or("No streams returned from ScreenCast")?;
if streams.is_empty() {
return Err("No monitors available for screen casting".to_string());
}
// Use the first stream (primary monitor) for now.
let stream_id = streams[0].pipe_wire_node_id();
// Get the devices that were actually granted by the user.
let granted_devices = response.devices();
Ok(Self {
remote_desktop,
session,
stream_id,
granted_devices,
})
}
/// Returns a reference to the RemoteDesktop proxy.
pub fn remote_desktop(&self) -> &RemoteDesktop<'a> {
&self.remote_desktop
}
/// Returns a reference to the session.
pub fn session(&self) -> &ashpd::desktop::Session<'a, RemoteDesktop<'a>> {
&self.session
}
/// Returns the stream ID for the primary monitor.
///
/// This ID is used for absolute pointer positioning via
/// `notify_pointer_motion_absolute`.
pub fn stream_id(&self) -> u32 {
self.stream_id
}
/// Validates that keyboard input permission was granted.
///
/// Returns an error with a clear message for the agent if permission was denied.
pub fn require_keyboard(&self) -> Result<(), String> {
if self.granted_devices.contains(DeviceType::Keyboard) {
Ok(())
} else {
Err(
"Keyboard input permission was not granted. \
The user must allow keyboard input in the portal dialog to perform keyboard actions."
.to_string(),
)
}
}
/// Validates that pointer/mouse input permission was granted.
///
/// Returns an error with a clear message for the agent if permission was denied.
pub fn require_pointer(&self) -> Result<(), String> {
if self.granted_devices.contains(DeviceType::Pointer) {
Ok(())
} else {
Err("Mouse input permission was not granted. \
The user must allow mouse input in the portal dialog to perform mouse actions."
.to_string())
}
}
}