Created
May 15, 2020 16:17
-
-
Save psychon/8c12a9b201b56c72c44a2a832ae1dce4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::os::unix::net::UnixStream; | |
use std::os::unix::io::AsRawFd; | |
use std::io::{Read, Result as IOResult, Write}; | |
use std::error::Error; | |
use std::convert::TryFrom; | |
use nix::poll::{PollFd, PollFlags, poll}; | |
use x11rb::protocol::xproto::{self, SetupRequest, Setup}; | |
use x11rb::x11_utils::Serialize; | |
struct Stream { | |
stream: UnixStream, | |
data_read: Vec<u8>, | |
data_write: Vec<u8>, | |
} | |
impl Stream { | |
// Write some data to the stream | |
pub fn write(&mut self, data: &[u8]) { | |
self.data_write.extend(data); | |
} | |
// Flush the stream | |
pub fn flush(&mut self) -> IOResult<()> { | |
while !self.data_write.is_empty() { | |
let mut fds = [PollFd::new(self.stream.as_raw_fd(), PollFlags::POLLIN | PollFlags::POLLOUT)]; | |
let res = poll(&mut fds, -1) | |
// errno still contains the error code | |
.map_err(|_| std::io::Error::last_os_error())?; | |
// There can be no timeout since we specified no timeout. There can also be no error since that | |
// was handled above. Thus, the FD must now be writable. | |
assert!(res == 1); | |
let revents = fds[0].revents().unwrap(); | |
if revents.contains(PollFlags::POLLIN) { | |
self.do_read()?; | |
} | |
if revents.contains(PollFlags::POLLOUT) { | |
let written = self.stream.write(&self.data_write)?; | |
self.data_write.drain(0..written); | |
} | |
} | |
Ok(()) | |
} | |
// Internal function: Read more data from the other end | |
fn read_more(&mut self) -> IOResult<()> { | |
let mut fds = [PollFd::new(self.stream.as_raw_fd(), PollFlags::POLLIN)]; | |
let res = poll(&mut fds, -1) | |
// errno still contains the error code | |
.map_err(|_| std::io::Error::last_os_error())?; | |
// There can be no timeout since we specified no timeout. There can also be no error since that | |
// was handled above. Thus, the FD must now be writable. | |
assert!(res == 1); | |
assert!(fds[0].revents().unwrap().contains(PollFlags::POLLIN)); | |
self.do_read() | |
} | |
// Internal function: The stream is readable and should now be read. | |
// The data ends up in our internal buffer. | |
fn do_read(&mut self) -> IOResult<()> { | |
let mut data = [0; 1024]; | |
let length = self.stream.read(&mut data)?; | |
self.data_read.extend(&data[..length]); | |
Ok(()) | |
} | |
// Read a packet with the given length. | |
// This blocks until enough data is available. | |
fn read_length(&mut self, length: usize) -> IOResult<Vec<u8>> { | |
while self.data_read.len() < length { | |
self.read_more()?; | |
} | |
Ok(self.data_read.drain(..length).collect()) | |
} | |
} | |
fn connect() -> IOResult<Stream> { | |
let stream = UnixStream::connect("/tmp/.X11-unix/X0")?; | |
stream.set_nonblocking(true)?; | |
let stream = Stream { | |
stream, | |
data_read: Vec::new(), | |
data_write: Vec::new(), | |
}; | |
Ok(stream) | |
} | |
// Send an (empty) X11 SetupRequest and read the Setup response | |
fn do_setup(stream: &mut Stream) -> Result<Setup, Box<dyn Error>> { | |
let request = SetupRequest { | |
byte_order: 0x6c, // little endian | |
protocol_major_version: 11, | |
protocol_minor_version: 0, | |
authorization_protocol_name: Vec::new(), | |
authorization_protocol_data: Vec::new(), | |
}; | |
stream.write(&request.serialize()); | |
stream.flush()?; | |
// Read the response | |
while stream.data_read.len() < 8 { | |
stream.read_more()?; | |
} | |
let length = 8 + usize::from(u16::from_ne_bytes([stream.data_read[6], stream.data_read[7]])) * 4; | |
let setup = stream.read_length(length)?; | |
Ok(Setup::try_from(&setup[..])?) | |
} | |
fn watch_root(stream: &mut Stream, setup: &Setup) -> IOResult<()> { | |
use xproto::EventMask; | |
let root = &setup.roots[0].root; | |
let events = EventMask::PropertyChange | EventMask::FocusChange | EventMask::PointerMotion; | |
// Send a ChangeWindowAttributes request | |
stream.write(&[2, 0, 4, 0]); | |
stream.write(&root.to_ne_bytes()); | |
stream.write(&u32::from(xproto::CW::EventMask).to_ne_bytes()); | |
stream.write(&events.to_ne_bytes()); | |
stream.flush()?; | |
Ok(()) | |
} | |
fn main() -> Result<(), Box<dyn Error>> { | |
let mut stream = connect()?; | |
println!("Hello, world!"); | |
let setup = do_setup(&mut stream)?; | |
println!("Got setup"); | |
//println!("{:?}", setup); | |
watch_root(&mut stream, &setup)?; | |
loop { | |
let event = stream.read_length(32)?; | |
let response_type = event[0] & 0x7f; | |
match response_type { | |
0 => match event[1] { | |
xproto::ACCESS_ERROR => println!("{:?}", xproto::AccessError::try_from(&event[..])?), | |
xproto::ALLOC_ERROR => println!("{:?}", xproto::AllocError::try_from(&event[..])?), | |
xproto::ATOM_ERROR => println!("{:?}", xproto::AtomError::try_from(&event[..])?), | |
xproto::COLORMAP_ERROR => println!("{:?}", xproto::ColormapError::try_from(&event[..])?), | |
xproto::CURSOR_ERROR => println!("{:?}", xproto::CursorError::try_from(&event[..])?), | |
xproto::DRAWABLE_ERROR => println!("{:?}", xproto::DrawableError::try_from(&event[..])?), | |
xproto::FONT_ERROR => println!("{:?}", xproto::FontError::try_from(&event[..])?), | |
xproto::G_CONTEXT_ERROR => println!("{:?}", xproto::GContextError::try_from(&event[..])?), | |
xproto::ID_CHOICE_ERROR => println!("{:?}", xproto::IDChoiceError::try_from(&event[..])?), | |
xproto::IMPLEMENTATION_ERROR => println!("{:?}", xproto::ImplementationError::try_from(&event[..])?), | |
xproto::LENGTH_ERROR => println!("{:?}", xproto::LengthError::try_from(&event[..])?), | |
xproto::MATCH_ERROR => println!("{:?}", xproto::MatchError::try_from(&event[..])?), | |
xproto::NAME_ERROR => println!("{:?}", xproto::NameError::try_from(&event[..])?), | |
xproto::PIXMAP_ERROR => println!("{:?}", xproto::PixmapError::try_from(&event[..])?), | |
xproto::REQUEST_ERROR => println!("{:?}", xproto::RequestError::try_from(&event[..])?), | |
xproto::VALUE_ERROR => println!("{:?}", xproto::ValueError::try_from(&event[..])?), | |
xproto::WINDOW_ERROR => println!("{:?}", xproto::WindowError::try_from(&event[..])?), | |
_ => println!("(unknown error) {:?}", event), | |
}, | |
xproto::BUTTON_PRESS_EVENT => println!("{:?}", xproto::ButtonPressEvent::try_from(&event[..])?), | |
xproto::BUTTON_RELEASE_EVENT => println!("{:?}", xproto::ButtonReleaseEvent::try_from(&event[..])?), | |
xproto::CIRCULATE_NOTIFY_EVENT => println!("{:?}", xproto::CirculateNotifyEvent::try_from(&event[..])?), | |
xproto::CIRCULATE_REQUEST_EVENT => println!("{:?}", xproto::CirculateRequestEvent::try_from(&event[..])?), | |
xproto::CLIENT_MESSAGE_EVENT => println!("{:?}", xproto::ClientMessageEvent::try_from(&event[..])?), | |
xproto::COLORMAP_NOTIFY_EVENT => println!("{:?}", xproto::ColormapNotifyEvent::try_from(&event[..])?), | |
xproto::CONFIGURE_NOTIFY_EVENT => println!("{:?}", xproto::ConfigureNotifyEvent::try_from(&event[..])?), | |
xproto::CONFIGURE_REQUEST_EVENT => println!("{:?}", xproto::ConfigureRequestEvent::try_from(&event[..])?), | |
xproto::CREATE_NOTIFY_EVENT => println!("{:?}", xproto::CreateNotifyEvent::try_from(&event[..])?), | |
xproto::DESTROY_NOTIFY_EVENT => println!("{:?}", xproto::DestroyNotifyEvent::try_from(&event[..])?), | |
xproto::ENTER_NOTIFY_EVENT => println!("{:?}", xproto::EnterNotifyEvent::try_from(&event[..])?), | |
xproto::EXPOSE_EVENT => println!("{:?}", xproto::ExposeEvent::try_from(&event[..])?), | |
xproto::FOCUS_IN_EVENT => println!("{:?}", xproto::FocusInEvent::try_from(&event[..])?), | |
xproto::FOCUS_OUT_EVENT => println!("{:?}", xproto::FocusOutEvent::try_from(&event[..])?), | |
xproto::GRAPHICS_EXPOSURE_EVENT => println!("{:?}", xproto::GraphicsExposureEvent::try_from(&event[..])?), | |
xproto::GRAVITY_NOTIFY_EVENT => println!("{:?}", xproto::GravityNotifyEvent::try_from(&event[..])?), | |
xproto::KEY_PRESS_EVENT => println!("{:?}", xproto::KeyPressEvent::try_from(&event[..])?), | |
xproto::KEY_RELEASE_EVENT => println!("{:?}", xproto::KeyReleaseEvent::try_from(&event[..])?), | |
xproto::KEYMAP_NOTIFY_EVENT => println!("{:?}", xproto::KeymapNotifyEvent::try_from(&event[..])?), | |
xproto::LEAVE_NOTIFY_EVENT => println!("{:?}", xproto::LeaveNotifyEvent::try_from(&event[..])?), | |
xproto::MAP_NOTIFY_EVENT => println!("{:?}", xproto::MapNotifyEvent::try_from(&event[..])?), | |
xproto::MAP_REQUEST_EVENT => println!("{:?}", xproto::MapRequestEvent::try_from(&event[..])?), | |
xproto::MAPPING_NOTIFY_EVENT => println!("{:?}", xproto::MappingNotifyEvent::try_from(&event[..])?), | |
xproto::MOTION_NOTIFY_EVENT => println!("{:?}", xproto::MotionNotifyEvent::try_from(&event[..])?), | |
xproto::NO_EXPOSURE_EVENT => println!("{:?}", xproto::NoExposureEvent::try_from(&event[..])?), | |
xproto::PROPERTY_NOTIFY_EVENT => println!("{:?}", xproto::PropertyNotifyEvent::try_from(&event[..])?), | |
xproto::REPARENT_NOTIFY_EVENT => println!("{:?}", xproto::ReparentNotifyEvent::try_from(&event[..])?), | |
xproto::RESIZE_REQUEST_EVENT => println!("{:?}", xproto::ResizeRequestEvent::try_from(&event[..])?), | |
xproto::SELECTION_CLEAR_EVENT => println!("{:?}", xproto::SelectionClearEvent::try_from(&event[..])?), | |
xproto::SELECTION_NOTIFY_EVENT => println!("{:?}", xproto::SelectionNotifyEvent::try_from(&event[..])?), | |
xproto::SELECTION_REQUEST_EVENT => println!("{:?}", xproto::SelectionRequestEvent::try_from(&event[..])?), | |
xproto::UNMAP_NOTIFY_EVENT => println!("{:?}", xproto::UnmapNotifyEvent::try_from(&event[..])?), | |
xproto::VISIBILITY_NOTIFY_EVENT => println!("{:?}", xproto::VisibilityNotifyEvent::try_from(&event[..])?), | |
_ => println!("(unknown) {:?}", event), | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment