1
0
mirror of https://github.com/emilk/egui.git synced 2026-06-26 14:49:06 -04:00

egui_inspection: add transport connect/Listener helpers

Add transport::connect (dial + split) and a sync transport::Listener
(bind + accept) so both ends of the inspection connection build streams
identically without depending on interprocess directly. Plugin now dials
via transport::connect. These back the kittest harness moving onto the
same local socket as the live plugin.
This commit is contained in:
lucasmerlin
2026-05-26 16:25:55 +02:00
parent 72389ed5a8
commit b27bc2b9ea
2 changed files with 49 additions and 8 deletions

View File

@@ -30,10 +30,9 @@ use std::sync::{Arc, Mutex, OnceLock};
use std::thread;
use egui::{Context, FullOutput, RawInput};
use interprocess::local_socket::{RecvHalf, SendHalf, Stream, prelude::*};
use crate::INSPECTION_SOCKET_ENV_VAR;
use crate::transport::socket_name;
use crate::transport::{self, RecvHalf, SendHalf};
use crate::protocol::{
Capabilities, Frame, FrameScreenshot, HarnessMessage, InspectorCommand, PROTOCOL_VERSION,
PeerHello, PeerKind, read_message, write_message,
@@ -122,9 +121,8 @@ impl InspectionPlugin {
/// # Errors
/// When the socket can't be dialed or a thread can't be spawned.
pub fn attach(socket: &str, label: Option<String>) -> Result<Self, InspectionError> {
let name = socket_name(socket).map_err(InspectionError::Connect)?;
let stream = Stream::connect(name).map_err(InspectionError::Connect)?;
let (reader_stream, writer_stream) = stream.split();
let (reader_stream, writer_stream) =
transport::connect(socket).map_err(InspectionError::Connect)?;
let shared_ctx: SharedCtx = Arc::new(OnceLock::new());

View File

@@ -7,11 +7,15 @@
use std::io;
use interprocess::local_socket::Name;
use interprocess::local_socket::{ListenerOptions, Name, prelude::*};
#[cfg(windows)]
use interprocess::local_socket::{GenericNamespaced, ToNsName as _};
use interprocess::local_socket::GenericNamespaced;
#[cfg(not(windows))]
use interprocess::local_socket::{GenericFilePath, ToFsName as _};
use interprocess::local_socket::GenericFilePath;
/// The two halves of a connected local-socket stream, re-exported so consumers build
/// reader/writer threads without depending on `interprocess` directly.
pub use interprocess::local_socket::{RecvHalf, SendHalf};
/// Build a platform-appropriate local-socket [`Name`] from the env-var string produced by
/// [`generate_socket_target`].
@@ -68,3 +72,42 @@ pub fn generate_socket_target() -> io::Result<SocketTarget> {
Ok(SocketTarget { name })
}
}
/// Dial an already-listening inspection socket and split the stream into read / write halves.
///
/// The connector side of the connection: the live plugin, or the kittest harness when
/// [`crate::INSPECTION_SOCKET_ENV_VAR`] is set.
///
/// # Errors
/// When `raw` isn't a valid local-socket name, or the socket can't be dialed.
pub fn connect(raw: &str) -> io::Result<(RecvHalf, SendHalf)> {
use interprocess::local_socket::Stream;
let stream = Stream::connect(socket_name(raw)?)?;
Ok(stream.split())
}
/// A bound synchronous local-socket listener — the listener side of the connection (kittest
/// harness in spawn mode, where it binds and then spawns an inspector pointed at the socket).
///
/// The MCP server uses the tokio listener directly; this sync wrapper exists for the
/// thread-based kittest harness.
pub struct Listener(interprocess::local_socket::Listener);
impl Listener {
/// Bind a listener at the given target name (from [`generate_socket_target`]).
///
/// # Errors
/// When `raw` isn't a valid local-socket name, or the socket can't be bound.
pub fn bind(raw: &str) -> io::Result<Self> {
let listener = ListenerOptions::new().name(socket_name(raw)?).create_sync()?;
Ok(Self(listener))
}
/// Block until a peer connects, then split the accepted stream into read / write halves.
///
/// # Errors
/// When accepting the inbound connection fails.
pub fn accept(&self) -> io::Result<(RecvHalf, SendHalf)> {
Ok(self.0.accept()?.split())
}
}