diff --git a/src/backend/consumer/x11.rs b/src/backend/consumer/x11.rs index 3e1aed5..66dba89 100644 --- a/src/backend/consumer/x11.rs +++ b/src/backend/consumer/x11.rs @@ -2,7 +2,7 @@ use anyhow::{anyhow, Result}; use async_trait::async_trait; use std::ptr; use x11::{ - xlib::{self, XCloseDisplay}, + xlib::{self, XCloseDisplay, XOpenDisplay}, xtest, }; @@ -23,7 +23,7 @@ unsafe impl Send for X11Consumer {} impl X11Consumer { pub fn new() -> Result { let display = unsafe { - match xlib::XOpenDisplay(ptr::null()) { + match XOpenDisplay(ptr::null()) { d if d == ptr::null::() as *mut xlib::Display => { Err(anyhow!("could not open display")) } diff --git a/src/backend/producer/x11.rs b/src/backend/producer/x11.rs index 1811e2c..6abb812 100644 --- a/src/backend/producer/x11.rs +++ b/src/backend/producer/x11.rs @@ -1,19 +1,135 @@ use anyhow::{anyhow, Result}; -use std::io; -use std::task::Poll; +use std::collections::VecDeque; +use std::os::fd::{AsRawFd, RawFd}; +use std::task::{ready, Poll}; +use std::{io, ptr}; use futures_core::Stream; -use crate::event::Event; +use crate::event::{Event, PointerEvent}; use crate::producer::EventProducer; use crate::client::{ClientEvent, ClientHandle}; +use tokio::io::unix::AsyncFd; -pub struct X11Producer {} +use x11::xlib::{ + self, KeyPressMask, KeyReleaseMask, PointerMotionMask, XWhitePixel, XCloseDisplay, + XDefaultScreen, XNextEvent, XOpenDisplay, XPending, + XRootWindow, XFlush, CWOverrideRedirect, CWBackPixel, CWEventMask, XSetWindowAttributes, XCreateWindow, CopyFromParent, XMapRaised, VisibilityChangeMask, ExposureMask, XSetClassHint, XClassHint, +}; + +pub struct X11Producer(AsyncFd); + +struct Inner { + connection_fd: RawFd, + display: *mut xlib::Display, + pending_events: VecDeque<(ClientHandle, Event)>, +} + +impl AsRawFd for Inner { + fn as_raw_fd(&self) -> std::os::unix::prelude::RawFd { + self.connection_fd + } +} impl X11Producer { pub fn new() -> Result { - Err(anyhow!("not implemented")) + let display = unsafe { + match XOpenDisplay(ptr::null()) { + d if d == ptr::null::() as *mut xlib::Display => { + Err(anyhow!("could not open display")) + } + display => Ok(display), + } + }?; + let screen = unsafe { XDefaultScreen(display) }; + log::warn!("screen: {screen}"); + + let root_window = unsafe { XRootWindow(display, screen) }; + log::warn!("root: {root_window}"); + let mut attr: XSetWindowAttributes = unsafe { std::mem::zeroed() }; + attr.override_redirect = true as i32; + attr.background_pixel = unsafe { XWhitePixel(display, screen) }; + attr.event_mask = ExposureMask | VisibilityChangeMask | KeyPressMask | KeyReleaseMask | PointerMotionMask; + let window = unsafe { + XCreateWindow( + display, root_window, + 0, /* x */ + 0, /* y */ + 2560, /* min width */ + 10, /* min height */ + 0, /* border width */ + CopyFromParent, /* depth */ + CopyFromParent as u32, /* class */ + ptr::null_mut(), /* Visual *visual */ + CWOverrideRedirect | CWBackPixel | CWEventMask, + &mut attr as *mut _, + ) + }; + let mut name: String = "lan-mouse".into(); + let name = name.as_mut_ptr(); + + let mut class_hint = XClassHint { + res_name: name as * mut i8, + res_class: name as * mut i8, + }; + unsafe { XSetClassHint(display, window, &mut class_hint as *mut _) }; + log::warn!("window: {window}"); + // unsafe { XSelectInput(display, window, event_mask as i64) }; + unsafe { XMapRaised(display, window) }; + unsafe { XFlush(display) }; + + /* can not fail */ + let connection_fd = unsafe { xlib::XConnectionNumber(display) }; + let pending_events = VecDeque::new(); + let inner = Inner { + connection_fd, + display, + pending_events, + }; + let async_fd = AsyncFd::new(inner)?; + Ok(X11Producer(async_fd)) + } +} + +impl Inner { + fn decode(&self, xevent: xlib::XEvent) -> Option<(u32, Event)> { + let _ = xevent; + Some(( + 0, + Event::Pointer(PointerEvent::Motion { + time: 0, + relative_x: 1., + relative_y: 0., + }), + )) + } + + fn dispatch(&mut self) -> io::Result { + unsafe { + if XPending(self.display) > 0 { + let mut xevent: xlib::XEvent = std::mem::zeroed(); + if XNextEvent(self.display, &mut xevent as *mut _) != 0 { + log::info!("event: {xevent:?}"); + return Err(io::Error::last_os_error().into()); + } + log::info!("event: {xevent:?}"); + if let Some(event) = self.decode(xevent) { + self.pending_events.push_back(event); + } + Ok(true) + } else { + Ok(false) + } + } + } +} + +impl Drop for Inner { + fn drop(&mut self) { + unsafe { + XCloseDisplay(self.display); + } } } @@ -31,9 +147,37 @@ impl Stream for X11Producer { type Item = io::Result<(ClientHandle, Event)>; fn poll_next( - self: std::pin::Pin<&mut Self>, - _cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - Poll::Pending + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> Poll> { + if let Some(event) = self.0.get_mut().pending_events.pop_front() { + return Poll::Ready(Some(Ok(event))); + } + loop { + let mut guard = ready!(self.0.poll_read_ready_mut(cx))?; + { + let inner = guard.get_inner_mut(); + loop { + if match inner.dispatch() { + Ok(event) => event, + Err(e) => { + guard.clear_ready(); + return Poll::Ready(Some(Err(e))); + } + } == false + { + break; + } + } + } + guard.clear_ready(); + + match guard.get_inner_mut().pending_events.pop_front() { + Some(event) => { + return Poll::Ready(Some(Ok(event))); + } + None => continue, + } + } } }