mirror of
https://github.com/nestriness/nestri.git
synced 2026-09-27 04:52:25 +03:00
feat(nescope): open the compositor
A headless Wayland compositor for a single fullscreen client, and the second component into this repo. Imported as a tree from `nestrilabs/nescope` for the same reason as the last one: the upstream repo is private, its history has never been reviewed for publication, and a squash is what keeps that history from becoming permanent here. Wired to the workspace — versions from the root, `nesprotocol` by path instead of a sibling directory. 8 tests pass. It knows a lot about Steam, and all of it stays. `steam_app_*` window classes, a launcher that exits before the game it started, a client that shows a login screen with no Vulkan frames in it: that is third-party behaviour a compositor for games has to handle, and describing it reveals nothing about how we are put together. The rule is about topology, not vocabulary. Two comments still name the transport by its old name and are left for the commit that renames it, so that rename reads as one change rather than as noise spread across four imports. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
use std::io::{self, Read};
|
||||
use std::os::unix::io::{AsFd, BorrowedFd};
|
||||
use std::os::unix::net::UnixStream;
|
||||
|
||||
use calloop::{EventSource, Interest, Mode, Poll, PostAction, Readiness, Token, TokenFactory};
|
||||
|
||||
pub struct InputIpcSource {
|
||||
stream: UnixStream,
|
||||
buf: Vec<u8>,
|
||||
}
|
||||
|
||||
impl InputIpcSource {
|
||||
pub fn connect(path: &str) -> io::Result<Self> {
|
||||
let stream = UnixStream::connect(path)?;
|
||||
stream.set_nonblocking(true)?;
|
||||
Ok(Self { stream, buf: Vec::new() })
|
||||
}
|
||||
|
||||
pub fn try_clone(&self) -> io::Result<UnixStream> {
|
||||
self.stream.try_clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsFd for InputIpcSource {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
self.stream.as_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl EventSource for InputIpcSource {
|
||||
type Event = Vec<u8>;
|
||||
type Metadata = ();
|
||||
type Ret = ();
|
||||
type Error = io::Error;
|
||||
|
||||
fn process_events<F>(
|
||||
&mut self,
|
||||
_readiness: Readiness,
|
||||
_token: Token,
|
||||
mut callback: F,
|
||||
) -> Result<PostAction, Self::Error>
|
||||
where
|
||||
F: FnMut(Self::Event, &mut Self::Metadata),
|
||||
{
|
||||
let mut tmp = [0u8; 4096];
|
||||
loop {
|
||||
match self.stream.read(&mut tmp) {
|
||||
Ok(0) => {
|
||||
return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "IPC socket closed"));
|
||||
}
|
||||
Ok(n) => {
|
||||
self.buf.extend_from_slice(&tmp[..n]);
|
||||
}
|
||||
Err(e) if e.kind() == io::ErrorKind::WouldBlock => break,
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
while self.buf.len() >= 2 {
|
||||
let payload_len = u16::from_le_bytes([self.buf[0], self.buf[1]]) as usize;
|
||||
let frame_total = 2 + payload_len;
|
||||
if self.buf.len() < frame_total {
|
||||
break;
|
||||
}
|
||||
let payload = self.buf[2..frame_total].to_vec();
|
||||
self.buf.drain(..frame_total);
|
||||
callback(payload, &mut ());
|
||||
}
|
||||
|
||||
Ok(PostAction::Continue)
|
||||
}
|
||||
|
||||
fn register(&mut self, poll: &mut Poll, factory: &mut TokenFactory) -> calloop::Result<()> {
|
||||
unsafe { poll.register(self.as_fd(), Interest::READ, Mode::Level, factory.token()) }
|
||||
}
|
||||
|
||||
fn reregister(&mut self, poll: &mut Poll, factory: &mut TokenFactory) -> calloop::Result<()> {
|
||||
poll.reregister(self.as_fd(), Interest::READ, Mode::Level, factory.token())
|
||||
}
|
||||
|
||||
fn unregister(&mut self, poll: &mut Poll) -> calloop::Result<()> {
|
||||
poll.unregister(self.as_fd())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user