autocommit 2022-03-22 23:24:17 CET
This commit is contained in:
parent
18806a38fb
commit
2ff60ff817
36 changed files with 4934 additions and 237 deletions
|
|
@ -9,6 +9,7 @@ mod enums;
|
|||
mod tokens;
|
||||
mod wire;
|
||||
mod wire_dbus;
|
||||
mod wire_xcon;
|
||||
|
||||
fn open(s: &str) -> io::Result<BufWriter<File>> {
|
||||
let mut path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
|
|
@ -25,6 +26,7 @@ fn open(s: &str) -> io::Result<BufWriter<File>> {
|
|||
fn main() -> anyhow::Result<()> {
|
||||
wire::main()?;
|
||||
wire_dbus::main()?;
|
||||
wire_xcon::main()?;
|
||||
enums::main()?;
|
||||
|
||||
println!("cargo:rerun-if-changed=build/build.rs");
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use anyhow::{bail, Context, Result};
|
||||
use bstr::{BStr, ByteSlice};
|
||||
use bstr::{BStr, BString, ByteSlice};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum TreeDelim {
|
||||
|
|
@ -27,7 +27,9 @@ impl TreeDelim {
|
|||
pub enum Symbol {
|
||||
Comma,
|
||||
Colon,
|
||||
Semicolon,
|
||||
Equals,
|
||||
At,
|
||||
}
|
||||
|
||||
impl Symbol {
|
||||
|
|
@ -36,17 +38,19 @@ impl Symbol {
|
|||
Symbol::Comma => "','",
|
||||
Symbol::Colon => "':'",
|
||||
Symbol::Equals => "'='",
|
||||
Symbol::At => "'@'",
|
||||
Symbol::Semicolon => "';'",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct Token<'a> {
|
||||
pub line: u32,
|
||||
pub kind: TokenKind<'a>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub enum TokenKind<'a> {
|
||||
Ident(&'a BStr),
|
||||
Num(u32),
|
||||
|
|
@ -55,6 +59,7 @@ pub enum TokenKind<'a> {
|
|||
body: Vec<Token<'a>>,
|
||||
},
|
||||
Symbol(Symbol),
|
||||
String(BString),
|
||||
}
|
||||
|
||||
impl TokenKind<'_> {
|
||||
|
|
@ -67,6 +72,7 @@ impl TokenKind<'_> {
|
|||
TreeDelim::Brace => "'{'-tree",
|
||||
},
|
||||
TokenKind::Symbol(s) => s.name(),
|
||||
TokenKind::String(_) => "string",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -153,7 +159,9 @@ impl<'a> Tokenizer<'a> {
|
|||
}
|
||||
b',' => TokenKind::Symbol(Symbol::Comma),
|
||||
b'=' => TokenKind::Symbol(Symbol::Equals),
|
||||
b'@' => TokenKind::Symbol(Symbol::At),
|
||||
b':' => TokenKind::Symbol(Symbol::Colon),
|
||||
b';' => TokenKind::Symbol(Symbol::Semicolon),
|
||||
b'(' => self.tokenize_tree(TreeDelim::Paren)?,
|
||||
b'{' => self.tokenize_tree(TreeDelim::Brace)?,
|
||||
c @ (b')' | b'}') => {
|
||||
|
|
@ -162,6 +170,37 @@ impl<'a> Tokenizer<'a> {
|
|||
}
|
||||
return Ok(false);
|
||||
}
|
||||
b'"' => {
|
||||
let mut res = vec![];
|
||||
let mut escaped = false;
|
||||
while !c.eof() {
|
||||
let char = c.s[c.pos];
|
||||
if char == b'\\' {
|
||||
escaped = true;
|
||||
} else if escaped {
|
||||
escaped = false;
|
||||
if matches!(char, b'"' | b'\\') {
|
||||
res.push(char);
|
||||
} else {
|
||||
bail!(
|
||||
"Unexpected escape sequence '\\{}' in line {}",
|
||||
char,
|
||||
self.line
|
||||
);
|
||||
}
|
||||
} else if char == b'"' {
|
||||
break;
|
||||
} else {
|
||||
res.push(char);
|
||||
}
|
||||
c.pos += 1;
|
||||
}
|
||||
if c.eof() {
|
||||
bail!("Unterminated string in line {}", self.line);
|
||||
}
|
||||
c.pos += 1;
|
||||
TokenKind::String(res.into())
|
||||
}
|
||||
_ => bail!("Unexpected byte {:?} in line {}", b as char, self.line),
|
||||
};
|
||||
self.res.push(Token { line, kind });
|
||||
|
|
|
|||
|
|
@ -747,7 +747,13 @@ pub fn main() -> Result<()> {
|
|||
println!("cargo:rerun-if-changed=wire-dbus");
|
||||
|
||||
let mut f = open("wire_dbus.rs")?;
|
||||
for (_, child) in collect_interfaces()?.children {
|
||||
let mut children: Vec<_> = collect_interfaces()?
|
||||
.children
|
||||
.into_iter()
|
||||
.map(|v| v.1)
|
||||
.collect();
|
||||
children.sort_by(|c1, c2| c1.name.cmp(&c2.name));
|
||||
for child in children {
|
||||
write_element(&mut f, child, "")?;
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
|||
1630
build/wire_xcon.rs
Normal file
1630
build/wire_xcon.rs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,10 +1,16 @@
|
|||
use jay_config::embedded::grab_input_device;
|
||||
use jay_config::keyboard::mods::{Modifiers, ALT, CTRL, SHIFT};
|
||||
use jay_config::keyboard::syms::{SYM_Super_L, SYM_b, SYM_comma, SYM_d, SYM_f, SYM_h, SYM_j, SYM_k, SYM_l, SYM_p, SYM_period, SYM_q, SYM_r, SYM_t, SYM_v, SYM_y, SYM_F1, SYM_F9, SYM_F12, SYM_F11, SYM_F10, SYM_F8, SYM_F7, SYM_F6, SYM_F5, SYM_F4, SYM_F3, SYM_F2};
|
||||
use jay_config::keyboard::syms::{
|
||||
SYM_Super_L, SYM_b, SYM_comma, SYM_d, SYM_f, SYM_h, SYM_j, SYM_k, SYM_l, SYM_p, SYM_period,
|
||||
SYM_q, SYM_r, SYM_t, SYM_v, SYM_y, SYM_F1, SYM_F10, SYM_F11, SYM_F12, SYM_F2, SYM_F3, SYM_F4,
|
||||
SYM_F5, SYM_F6, SYM_F7, SYM_F8, SYM_F9,
|
||||
};
|
||||
use jay_config::theme::{get_title_height, set_title_color, set_title_height, Color};
|
||||
use jay_config::Axis::{Horizontal, Vertical};
|
||||
use jay_config::Direction::{Down, Left, Right, Up};
|
||||
use jay_config::{config, create_seat, input_devices, on_new_input_device, quit, Command, Seat, switch_to_vt};
|
||||
use jay_config::{
|
||||
config, create_seat, input_devices, on_new_input_device, quit, switch_to_vt, Command, Seat,
|
||||
};
|
||||
use rand::Rng;
|
||||
|
||||
const MOD: Modifiers = ALT;
|
||||
|
|
|
|||
|
|
@ -28,8 +28,9 @@ pub enum Phase {
|
|||
EventHandling,
|
||||
Layout,
|
||||
PostLayout,
|
||||
Present,
|
||||
}
|
||||
const NUM_PHASES: usize = 3;
|
||||
const NUM_PHASES: usize = 4;
|
||||
|
||||
pub struct AsyncEngine {
|
||||
wheel: Rc<Wheel>,
|
||||
|
|
|
|||
|
|
@ -237,7 +237,7 @@ impl InputDevice for MetalInputDevice {
|
|||
}
|
||||
|
||||
fn grab(&self, _grab: bool) {
|
||||
log::warn!("Metal backend does not support grabbing devices");
|
||||
// nothing
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
pub mod metal;
|
||||
pub mod xorg;
|
||||
pub mod xorgng;
|
||||
|
|
|
|||
968
src/backends/xorgng.rs
Normal file
968
src/backends/xorgng.rs
Normal file
|
|
@ -0,0 +1,968 @@
|
|||
use crate::async_engine::SpawnedFuture;
|
||||
use crate::backend::{
|
||||
Backend, BackendEvent, InputDevice, InputDeviceId, InputEvent, KeyState, Output, OutputId,
|
||||
ScrollAxis,
|
||||
};
|
||||
use crate::drm::drm::{Drm, DrmError};
|
||||
use crate::drm::gbm::{GbmDevice, GbmError, GBM_BO_USE_RENDERING};
|
||||
use crate::drm::{ModifiedFormat, INVALID_MODIFIER};
|
||||
use crate::fixed::Fixed;
|
||||
use crate::format::XRGB8888;
|
||||
use crate::render::{Framebuffer, RenderContext, RenderError};
|
||||
use crate::utils::clonecell::CloneCell;
|
||||
use crate::utils::copyhashmap::CopyHashMap;
|
||||
use crate::wire_xcon::{
|
||||
ChangeProperty, ChangeWindowAttributes, ConfigureNotify, CreateCursor, CreatePixmap,
|
||||
CreateWindow, CreateWindowValues, DestroyNotify, Dri3Open, Dri3PixmapFromBuffer,
|
||||
Dri3QueryVersion, Extension, FreePixmap, MapWindow, PresentCompleteNotify, PresentIdleNotify,
|
||||
PresentPixmap, PresentQueryVersion, PresentSelectInput, XiButtonPress, XiButtonRelease,
|
||||
XiDeviceInfo, XiEnter, XiEventMask, XiGetDeviceButtonMapping, XiGrabDevice, XiHierarchy,
|
||||
XiKeyPress, XiKeyRelease, XiMotion, XiQueryDevice, XiQueryVersion, XiSelectEvents,
|
||||
XiUngrabDevice, XkbPerClientFlags, XkbUseExtension,
|
||||
};
|
||||
use crate::xcon::consts::{
|
||||
ATOM_STRING, ATOM_WM_CLASS, EVENT_MASK_EXPOSURE, EVENT_MASK_STRUCTURE_NOTIFY,
|
||||
EVENT_MASK_VISIBILITY_CHANGE, GRAB_MODE_ASYNC, GRAB_STATUS_SUCCESS, INPUT_DEVICE_ALL,
|
||||
INPUT_DEVICE_ALL_MASTER, INPUT_DEVICE_TYPE_MASTER_KEYBOARD, INPUT_HIERARCHY_MASK_MASTER_ADDED,
|
||||
INPUT_HIERARCHY_MASK_MASTER_REMOVED, PRESENT_EVENT_MASK_COMPLETE_NOTIFY,
|
||||
PRESENT_EVENT_MASK_IDLE_NOTIFY, PROP_MODE_REPLACE, WINDOW_CLASS_INPUT_OUTPUT,
|
||||
XI_EVENT_MASK_BUTTON_PRESS, XI_EVENT_MASK_BUTTON_RELEASE, XI_EVENT_MASK_ENTER,
|
||||
XI_EVENT_MASK_FOCUS_IN, XI_EVENT_MASK_FOCUS_OUT, XI_EVENT_MASK_HIERARCHY,
|
||||
XI_EVENT_MASK_KEY_PRESS, XI_EVENT_MASK_KEY_RELEASE, XI_EVENT_MASK_LEAVE, XI_EVENT_MASK_MOTION,
|
||||
XI_EVENT_MASK_TOUCH_BEGIN, XI_EVENT_MASK_TOUCH_END, XI_EVENT_MASK_TOUCH_UPDATE,
|
||||
XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT,
|
||||
};
|
||||
use crate::xcon::{Event, XEvent, Xcon, XconError};
|
||||
use crate::{AsyncQueue, ErrorFmt, NumCell, Phase, State};
|
||||
use std::borrow::Cow;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::VecDeque;
|
||||
use std::rc::Rc;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum XorgngBackendError {
|
||||
#[error("Could not connect to the X server")]
|
||||
CannotConnect(#[source] XconError),
|
||||
#[error("Could not enable XInput")]
|
||||
EnableXinput(#[source] XconError),
|
||||
#[error("Could not enable Present")]
|
||||
EnablePresent(#[source] XconError),
|
||||
#[error("Could not enable XKB")]
|
||||
EnableXkb(#[source] XconError),
|
||||
#[error("Could not enable DRI3")]
|
||||
EnableDri3(#[source] XconError),
|
||||
#[error("DriOpen returned an error")]
|
||||
DriOpen(#[source] XconError),
|
||||
#[error("Could not create a pixmap")]
|
||||
CreatePixmap(#[source] XconError),
|
||||
#[error("Could not create a cursor")]
|
||||
CreateCursor(#[source] XconError),
|
||||
#[error("Could not select XInput hierarchy events")]
|
||||
SelectHierarchyEvents(#[source] XconError),
|
||||
#[error("The drm subsystem returned an error")]
|
||||
DrmError(#[from] DrmError),
|
||||
#[error("The gbm subsystem returned an error")]
|
||||
GbmError(#[from] GbmError),
|
||||
#[error("Could not import a dma-buf")]
|
||||
ImportBuffer(#[source] XconError),
|
||||
#[error("Could not create an EGL context")]
|
||||
CreateEgl(#[source] RenderError),
|
||||
#[error("Could not create a framebuffer from a dma-buf")]
|
||||
CreateFramebuffer(#[source] RenderError),
|
||||
#[error("Could not select input events")]
|
||||
CannotSelectInputEvents(#[source] XconError),
|
||||
#[error("Could not select present events")]
|
||||
CannotSelectPresentEvents(#[source] XconError),
|
||||
#[error("libloading returned an error")]
|
||||
Libloading(#[from] libloading::Error),
|
||||
#[error("An unspecified X error occurred")]
|
||||
XconError(#[from] XconError),
|
||||
#[error("Could not create a window")]
|
||||
CreateWindow(#[source] XconError),
|
||||
#[error("Could not set WM_CLASS")]
|
||||
WmClass(#[source] XconError),
|
||||
#[error("Could not select window events")]
|
||||
WindowEvents(#[source] XconError),
|
||||
#[error("Could not map a window")]
|
||||
MapWindow(#[source] XconError),
|
||||
#[error("Could not query device")]
|
||||
QueryDevice(#[source] XconError),
|
||||
}
|
||||
|
||||
pub struct XorgngBackend {
|
||||
_data: Rc<XorgngBackendData>,
|
||||
_events: SpawnedFuture<()>,
|
||||
_present: SpawnedFuture<()>,
|
||||
_grab: SpawnedFuture<()>,
|
||||
}
|
||||
|
||||
impl Backend for XorgngBackend {
|
||||
fn switch_to(&self, _vtnr: u32) {
|
||||
log::error!("Xorg backend cannot switch vts");
|
||||
}
|
||||
}
|
||||
|
||||
pub struct XorgngBackendData {
|
||||
state: Rc<State>,
|
||||
c: Rc<Xcon>,
|
||||
outputs: CopyHashMap<u32, Rc<XorgOutput>>,
|
||||
seats: CopyHashMap<u16, Rc<XorgSeat>>,
|
||||
mouse_seats: CopyHashMap<u16, Rc<XorgSeat>>,
|
||||
ctx: Rc<RenderContext>,
|
||||
gbm: GbmDevice,
|
||||
cursor: u32,
|
||||
root: u32,
|
||||
scheduled_present: AsyncQueue<Rc<XorgOutput>>,
|
||||
grab_requests: AsyncQueue<(Rc<XorgSeat>, bool)>,
|
||||
}
|
||||
|
||||
impl XorgngBackend {
|
||||
pub async fn run(state: &Rc<State>) -> Result<Rc<Self>, XorgngBackendError> {
|
||||
let c = match Xcon::connect(state.eng.clone()).await {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Err(XorgngBackendError::CannotConnect(e)),
|
||||
};
|
||||
if let Err(e) = c
|
||||
.call(&XiQueryVersion {
|
||||
major_version: 2,
|
||||
minor_version: 2,
|
||||
})
|
||||
.await
|
||||
{
|
||||
return Err(XorgngBackendError::EnableXinput(e));
|
||||
}
|
||||
if let Err(e) = c
|
||||
.call(&Dri3QueryVersion {
|
||||
major_version: 1,
|
||||
minor_version: 0,
|
||||
})
|
||||
.await
|
||||
{
|
||||
return Err(XorgngBackendError::EnableDri3(e));
|
||||
}
|
||||
if let Err(e) = c
|
||||
.call(&PresentQueryVersion {
|
||||
major_version: 1,
|
||||
minor_version: 0,
|
||||
})
|
||||
.await
|
||||
{
|
||||
return Err(XorgngBackendError::EnablePresent(e));
|
||||
}
|
||||
if let Err(e) = c
|
||||
.call(&XkbUseExtension {
|
||||
wanted_major: 1,
|
||||
wanted_minor: 0,
|
||||
})
|
||||
.await
|
||||
{
|
||||
return Err(XorgngBackendError::EnableXkb(e));
|
||||
}
|
||||
let root = c.setup().screens[0].root;
|
||||
let drm = {
|
||||
let res = c
|
||||
.call(&Dri3Open {
|
||||
drawable: root,
|
||||
provider: 0,
|
||||
})
|
||||
.await;
|
||||
match res {
|
||||
Ok(r) => Drm::reopen(r.get().device_fd.raw(), false)?,
|
||||
Err(e) => return Err(XorgngBackendError::DriOpen(e)),
|
||||
}
|
||||
};
|
||||
let gbm = GbmDevice::new(&drm)?;
|
||||
let ctx = match RenderContext::from_drm_device(&drm) {
|
||||
Ok(r) => Rc::new(r),
|
||||
Err(e) => return Err(XorgngBackendError::CreateEgl(e)),
|
||||
};
|
||||
let cursor = {
|
||||
let cp = CreatePixmap {
|
||||
depth: 1,
|
||||
pid: c.generate_id()?,
|
||||
drawable: root,
|
||||
width: 1,
|
||||
height: 1,
|
||||
};
|
||||
if let Err(e) = c.call(&cp).await {
|
||||
return Err(XorgngBackendError::CreatePixmap(e));
|
||||
}
|
||||
let cc = CreateCursor {
|
||||
cid: c.generate_id()?,
|
||||
source: cp.pid,
|
||||
mask: 0,
|
||||
fore_red: 0,
|
||||
fore_green: 0,
|
||||
fore_blue: 0,
|
||||
back_red: 0,
|
||||
back_green: 0,
|
||||
back_blue: 0,
|
||||
x: 0,
|
||||
y: 0,
|
||||
};
|
||||
if let Err(e) = c.call(&cc).await {
|
||||
return Err(XorgngBackendError::CreateCursor(e));
|
||||
}
|
||||
cc.cid
|
||||
};
|
||||
{
|
||||
let se = XiSelectEvents {
|
||||
window: c.setup().screens[0].root,
|
||||
masks: Cow::Borrowed(&[XiEventMask {
|
||||
deviceid: INPUT_DEVICE_ALL,
|
||||
mask: &[XI_EVENT_MASK_HIERARCHY],
|
||||
}]),
|
||||
};
|
||||
if let Err(e) = c.call(&se).await {
|
||||
return Err(XorgngBackendError::SelectHierarchyEvents(e));
|
||||
}
|
||||
}
|
||||
|
||||
let data = Rc::new(XorgngBackendData {
|
||||
state: state.clone(),
|
||||
c,
|
||||
outputs: Default::default(),
|
||||
seats: Default::default(),
|
||||
mouse_seats: Default::default(),
|
||||
ctx: ctx.clone(),
|
||||
gbm,
|
||||
cursor,
|
||||
root,
|
||||
scheduled_present: Default::default(),
|
||||
grab_requests: Default::default()
|
||||
});
|
||||
data.add_output().await?;
|
||||
data.query_devices(INPUT_DEVICE_ALL_MASTER).await?;
|
||||
|
||||
let slf = Rc::new(Self {
|
||||
_events: state.eng.spawn(data.clone().event_handler()),
|
||||
_grab: state.eng.spawn(data.clone().grab_handler()),
|
||||
_present: state
|
||||
.eng
|
||||
.spawn2(Phase::Present, data.clone().present_handler()),
|
||||
_data: data,
|
||||
});
|
||||
|
||||
state.set_render_ctx(&ctx);
|
||||
state.backend.set(Some(slf.clone()));
|
||||
|
||||
Ok(slf)
|
||||
}
|
||||
}
|
||||
|
||||
impl XorgngBackendData {
|
||||
async fn event_handler(self: Rc<Self>) {
|
||||
loop {
|
||||
let event = self.c.event().await;
|
||||
if let Err(e) = self.handle_event(&event).await {
|
||||
log::error!("Fatal error: Could not handle an event from the X server: {}", ErrorFmt(e));
|
||||
self.state.el.stop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn present_handler(self: Rc<Self>) {
|
||||
loop {
|
||||
let output = self.scheduled_present.pop().await;
|
||||
self.present(&output).await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn grab_handler(self: Rc<Self>) {
|
||||
loop {
|
||||
let (dev, grab) = self.grab_requests.pop().await;
|
||||
self.handle_grab_request(&dev, grab).await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_grab_request(&self, dev: &XorgSeat, grab: bool) {
|
||||
if grab {
|
||||
let xg = XiGrabDevice {
|
||||
window: self.root,
|
||||
time: 0,
|
||||
cursor: 0,
|
||||
deviceid: dev.kb,
|
||||
mode: GRAB_MODE_ASYNC,
|
||||
paired_device_mode: GRAB_MODE_ASYNC,
|
||||
owner_events: 1,
|
||||
mask: &[],
|
||||
};
|
||||
let res = match self.c.call(&xg).await {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
log::error!("Could not grab device {}: {}", dev.kb, ErrorFmt(e));
|
||||
return;
|
||||
}
|
||||
};
|
||||
let res = res.get();
|
||||
if res.status != GRAB_STATUS_SUCCESS {
|
||||
log::error!("Could not grab device {}: status = {}", dev.kb, res.status);
|
||||
}
|
||||
} else {
|
||||
let ug = XiUngrabDevice {
|
||||
time: 0,
|
||||
deviceid: dev.kb,
|
||||
};
|
||||
if let Err(e) = self.c.call(&ug).await {
|
||||
log::error!("Could not ungrab device {}: {}", dev.kb, ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn create_images(
|
||||
&self,
|
||||
window: u32,
|
||||
width: i32,
|
||||
height: i32,
|
||||
) -> Result<[XorgImage; 2], XorgngBackendError> {
|
||||
let format = ModifiedFormat {
|
||||
format: XRGB8888,
|
||||
modifier: INVALID_MODIFIER,
|
||||
};
|
||||
let mut images = [None, None];
|
||||
for i in 0..2 {
|
||||
let bo = self
|
||||
.gbm
|
||||
.create_bo(width, height, &format, GBM_BO_USE_RENDERING)?;
|
||||
let dma = bo.dma();
|
||||
assert!(dma.planes.len() == 1);
|
||||
let plane = dma.planes.first().unwrap();
|
||||
let size = plane.stride * dma.height as u32;
|
||||
let fb = match self.ctx.dmabuf_fb(dma) {
|
||||
Ok(f) => f,
|
||||
Err(e) => return Err(XorgngBackendError::CreateFramebuffer(e)),
|
||||
};
|
||||
let pixmap = {
|
||||
let pfb = Dri3PixmapFromBuffer {
|
||||
pixmap: self.c.generate_id()?,
|
||||
drawable: window,
|
||||
size,
|
||||
width: dma.width as _,
|
||||
height: dma.height as _,
|
||||
stride: plane.stride as _,
|
||||
depth: 24,
|
||||
bpp: 32,
|
||||
pixmap_fd: plane.fd.clone(),
|
||||
};
|
||||
if let Err(e) = self.c.call(&pfb).await {
|
||||
return Err(XorgngBackendError::ImportBuffer(e));
|
||||
}
|
||||
pfb.pixmap
|
||||
};
|
||||
images[i] = Some(XorgImage {
|
||||
pixmap: Cell::new(pixmap),
|
||||
fb: CloneCell::new(fb),
|
||||
idle: Cell::new(true),
|
||||
render_on_idle: Cell::new(false),
|
||||
last_serial: Cell::new(0),
|
||||
});
|
||||
}
|
||||
Ok([images[0].take().unwrap(), images[1].take().unwrap()])
|
||||
}
|
||||
|
||||
async fn add_output(self: &Rc<Self>) -> Result<(), XorgngBackendError> {
|
||||
const WIDTH: i32 = 800;
|
||||
const HEIGHT: i32 = 600;
|
||||
let window_id = {
|
||||
let cw = CreateWindow {
|
||||
depth: 0,
|
||||
wid: self.c.generate_id()?,
|
||||
parent: self.root,
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: WIDTH as _,
|
||||
height: HEIGHT as _,
|
||||
border_width: 0,
|
||||
class: WINDOW_CLASS_INPUT_OUTPUT,
|
||||
visual: 0,
|
||||
values: Default::default(),
|
||||
};
|
||||
if let Err(e) = self.c.call(&cw).await {
|
||||
return Err(XorgngBackendError::CreateWindow(e));
|
||||
}
|
||||
cw.wid
|
||||
};
|
||||
let images = self.create_images(window_id, WIDTH, HEIGHT).await?;
|
||||
let output = Rc::new(XorgOutput {
|
||||
id: self.state.output_ids.next(),
|
||||
_backend: self.clone(),
|
||||
window: window_id,
|
||||
removed: Cell::new(false),
|
||||
width: Cell::new(0),
|
||||
height: Cell::new(0),
|
||||
serial: Default::default(),
|
||||
next_msc: Cell::new(0),
|
||||
next_image: Default::default(),
|
||||
cb: CloneCell::new(None),
|
||||
images,
|
||||
});
|
||||
{
|
||||
let class = "jay\0jay\0";
|
||||
let cp = ChangeProperty {
|
||||
mode: PROP_MODE_REPLACE,
|
||||
window: window_id,
|
||||
property: ATOM_WM_CLASS,
|
||||
ty: ATOM_STRING,
|
||||
format: 8,
|
||||
data: class.as_bytes(),
|
||||
};
|
||||
if let Err(e) = self.c.call(&cp).await {
|
||||
return Err(XorgngBackendError::WmClass(e));
|
||||
};
|
||||
}
|
||||
{
|
||||
let cwa = ChangeWindowAttributes {
|
||||
window: window_id,
|
||||
values: CreateWindowValues {
|
||||
event_mask: Some(
|
||||
EVENT_MASK_EXPOSURE
|
||||
| EVENT_MASK_STRUCTURE_NOTIFY
|
||||
| EVENT_MASK_VISIBILITY_CHANGE,
|
||||
),
|
||||
cursor: Some(self.cursor),
|
||||
..Default::default()
|
||||
},
|
||||
};
|
||||
if let Err(e) = self.c.call(&cwa).await {
|
||||
return Err(XorgngBackendError::WindowEvents(e));
|
||||
}
|
||||
}
|
||||
if let Err(e) = self.c.call(&MapWindow { window: window_id }).await {
|
||||
return Err(XorgngBackendError::MapWindow(e));
|
||||
}
|
||||
{
|
||||
let mask = 0
|
||||
| XI_EVENT_MASK_MOTION
|
||||
| XI_EVENT_MASK_BUTTON_PRESS
|
||||
| XI_EVENT_MASK_BUTTON_RELEASE
|
||||
| XI_EVENT_MASK_KEY_PRESS
|
||||
| XI_EVENT_MASK_KEY_RELEASE
|
||||
| XI_EVENT_MASK_ENTER
|
||||
| XI_EVENT_MASK_LEAVE
|
||||
| XI_EVENT_MASK_FOCUS_IN
|
||||
| XI_EVENT_MASK_FOCUS_OUT
|
||||
| XI_EVENT_MASK_TOUCH_BEGIN
|
||||
| XI_EVENT_MASK_TOUCH_UPDATE
|
||||
| XI_EVENT_MASK_TOUCH_END;
|
||||
let mask = [XiEventMask {
|
||||
deviceid: INPUT_DEVICE_ALL_MASTER,
|
||||
mask: &[mask],
|
||||
}];
|
||||
let xs = XiSelectEvents {
|
||||
window: window_id,
|
||||
masks: Cow::Borrowed(&mask[..]),
|
||||
};
|
||||
if let Err(e) = self.c.call(&xs).await {
|
||||
return Err(XorgngBackendError::CannotSelectInputEvents(e));
|
||||
}
|
||||
}
|
||||
{
|
||||
let mask = 0 | PRESENT_EVENT_MASK_IDLE_NOTIFY | PRESENT_EVENT_MASK_COMPLETE_NOTIFY;
|
||||
let si = PresentSelectInput {
|
||||
eid: self.c.generate_id()?,
|
||||
window: window_id,
|
||||
event_mask: mask,
|
||||
};
|
||||
if let Err(e) = self.c.call(&si).await {
|
||||
return Err(XorgngBackendError::CannotSelectPresentEvents(e));
|
||||
}
|
||||
}
|
||||
self.outputs.set(window_id, output.clone());
|
||||
self.state
|
||||
.backend_events
|
||||
.push(BackendEvent::NewOutput(output.clone()));
|
||||
self.present(&output).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn query_devices(self: &Rc<Self>, deviceid: u16) -> Result<(), XorgngBackendError> {
|
||||
let reply = match self.c.call(&XiQueryDevice { deviceid }).await {
|
||||
Ok(r) => r,
|
||||
Err(e) => return Err(XorgngBackendError::QueryDevice(e)),
|
||||
};
|
||||
for dev in reply.get().infos.iter() {
|
||||
self.handle_input_device(dev).await;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_input_device(self: &Rc<Self>, info: &XiDeviceInfo<'_>) {
|
||||
if info.ty != INPUT_DEVICE_TYPE_MASTER_KEYBOARD {
|
||||
return;
|
||||
}
|
||||
self.mouse_seats.remove(&info.attachment);
|
||||
if let Some(kb) = self.seats.remove(&info.deviceid) {
|
||||
kb.removed.set(true);
|
||||
kb.kb_changed();
|
||||
kb.mouse_changed();
|
||||
}
|
||||
let pcf = XkbPerClientFlags {
|
||||
device_spec: info.deviceid,
|
||||
change: XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT,
|
||||
value: XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT,
|
||||
ctrls_to_change: 0,
|
||||
auto_ctrls: 0,
|
||||
auto_ctrls_values: 0,
|
||||
};
|
||||
if let Err(e) = self.c.call(&pcf).await {
|
||||
log::warn!(
|
||||
"Could not make auto repeat detectable for keyboard {}: {}",
|
||||
info.deviceid,
|
||||
ErrorFmt(e),
|
||||
);
|
||||
}
|
||||
let seat = Rc::new(XorgSeat {
|
||||
kb_id: self.state.input_device_ids.next(),
|
||||
mouse_id: self.state.input_device_ids.next(),
|
||||
backend: self.clone(),
|
||||
kb: info.deviceid,
|
||||
mouse: info.attachment,
|
||||
removed: Cell::new(false),
|
||||
kb_cb: Default::default(),
|
||||
mouse_cb: Default::default(),
|
||||
kb_events: RefCell::new(Default::default()),
|
||||
mouse_events: RefCell::new(Default::default()),
|
||||
button_map: Default::default(),
|
||||
});
|
||||
seat.update_button_map().await;
|
||||
self.seats.set(info.deviceid, seat.clone());
|
||||
self.mouse_seats.set(info.attachment, seat.clone());
|
||||
self.state
|
||||
.backend_events
|
||||
.push(BackendEvent::NewInputDevice(Rc::new(XorgSeatMouse(
|
||||
seat.clone(),
|
||||
))));
|
||||
self.state
|
||||
.backend_events
|
||||
.push(BackendEvent::NewInputDevice(Rc::new(XorgSeatKeyboard(
|
||||
seat.clone(),
|
||||
))));
|
||||
}
|
||||
|
||||
async fn handle_event(self: &Rc<Self>, event: &Event) -> Result<(), XorgngBackendError> {
|
||||
match event.ext() {
|
||||
Some(ext) => self.handle_ext_event(ext, event).await,
|
||||
_ => self.handle_core_event(event).await,
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_ext_event(
|
||||
self: &Rc<Self>,
|
||||
ext: Extension,
|
||||
event: &Event,
|
||||
) -> Result<(), XorgngBackendError> {
|
||||
match ext {
|
||||
Extension::Present => self.handle_present_event(event),
|
||||
Extension::XInputExtension => self.handle_input_event(event).await,
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_core_event(self: &Rc<Self>, event: &Event) -> Result<(), XorgngBackendError> {
|
||||
match event.code() {
|
||||
ConfigureNotify::OPCODE => self.handle_configure(event).await,
|
||||
DestroyNotify::OPCODE => self.handle_destroy(event),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_present_event(self: &Rc<Self>, event: &Event) -> Result<(), XorgngBackendError> {
|
||||
match event.code() {
|
||||
PresentCompleteNotify::OPCODE => self.handle_present_complete(event)?,
|
||||
PresentIdleNotify::OPCODE => self.handle_present_idle(event)?,
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_present_complete(self: &Rc<Self>, event: &Event) -> Result<(), XorgngBackendError> {
|
||||
let event: PresentCompleteNotify = event.parse()?;
|
||||
let window = event.window;
|
||||
let output = match self.outputs.get(&window) {
|
||||
Some(o) => o,
|
||||
_ => return Ok(()),
|
||||
};
|
||||
output.next_msc.set(event.msc + 1);
|
||||
let image = &output.images[output.next_image.get() % output.images.len()];
|
||||
if image.idle.get() {
|
||||
self.schedule_present(&output);
|
||||
} else {
|
||||
image.render_on_idle.set(true);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_present_idle(self: &Rc<Self>, event: &Event) -> Result<(), XorgngBackendError> {
|
||||
let event: PresentIdleNotify = event.parse()?;
|
||||
let output = match self.outputs.get(&event.window) {
|
||||
Some(o) => o,
|
||||
_ => return Ok(()),
|
||||
};
|
||||
for image in &output.images {
|
||||
if image.last_serial.get() == event.serial {
|
||||
image.idle.set(true);
|
||||
if image.render_on_idle.replace(false) {
|
||||
self.schedule_present(&output);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn schedule_present(&self, output: &Rc<XorgOutput>) {
|
||||
self.scheduled_present.push(output.clone());
|
||||
}
|
||||
|
||||
async fn present(&self, output: &Rc<XorgOutput>) {
|
||||
if output.removed.get() {
|
||||
return;
|
||||
}
|
||||
|
||||
let image = &output.images[output.next_image.fetch_add(1) % output.images.len()];
|
||||
let serial = output.serial.fetch_add(1);
|
||||
|
||||
if let Some(node) = self.state.root.outputs.get(&output.id) {
|
||||
let fb = image.fb.get();
|
||||
fb.render(&*node, &self.state, Some(node.position.get()));
|
||||
}
|
||||
|
||||
let pp = PresentPixmap {
|
||||
window: output.window,
|
||||
pixmap: image.pixmap.get(),
|
||||
serial,
|
||||
valid: 0,
|
||||
update: 0,
|
||||
x_off: 0,
|
||||
y_off: 0,
|
||||
target_crtc: 0,
|
||||
wait_fence: 0,
|
||||
idle_fence: 0,
|
||||
options: 0,
|
||||
target_msc: output.next_msc.get(),
|
||||
divisor: 1,
|
||||
remainder: 0,
|
||||
notifies: Default::default(),
|
||||
};
|
||||
if let Err(e) = self.c.call(&pp).await {
|
||||
log::error!("Could not present image: {:?}", e);
|
||||
return;
|
||||
}
|
||||
image.idle.set(false);
|
||||
image.last_serial.set(serial);
|
||||
}
|
||||
|
||||
async fn handle_input_event(self: &Rc<Self>, event: &Event) -> Result<(), XorgngBackendError> {
|
||||
match event.code() {
|
||||
XiMotion::OPCODE => self.handle_input_motion(event),
|
||||
XiEnter::OPCODE => self.handle_input_enter(event),
|
||||
XiButtonPress::OPCODE => self.handle_input_button_press(event, KeyState::Pressed),
|
||||
XiButtonRelease::OPCODE => self.handle_input_button_press(event, KeyState::Released),
|
||||
XiKeyPress::OPCODE => self.handle_input_key_press(event, KeyState::Pressed),
|
||||
XiKeyRelease::OPCODE => self.handle_input_key_press(event, KeyState::Released),
|
||||
XiHierarchy::OPCODE => self.handle_input_hierarchy(event).await,
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_input_button_press(
|
||||
self: &Rc<Self>,
|
||||
event: &Event,
|
||||
state: KeyState,
|
||||
) -> Result<(), XorgngBackendError> {
|
||||
let event: XiButtonPress = event.parse()?;
|
||||
if let Some(seat) = self.mouse_seats.get(&event.deviceid) {
|
||||
let button = event.detail;
|
||||
// let button = seat.button_map.get(&event.detail).unwrap_or(event.detail);
|
||||
if matches!(button, 4..=7) {
|
||||
if state == KeyState::Pressed {
|
||||
let (axis, val) = match button {
|
||||
4 => (ScrollAxis::Vertical, -15),
|
||||
5 => (ScrollAxis::Vertical, 15),
|
||||
6 => (ScrollAxis::Horizontal, -15),
|
||||
7 => (ScrollAxis::Horizontal, 15),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
seat.mouse_event(InputEvent::Scroll(val, axis));
|
||||
}
|
||||
} else {
|
||||
const BTN_LEFT: u32 = 0x110;
|
||||
const BTN_RIGHT: u32 = 0x111;
|
||||
const BTN_MIDDLE: u32 = 0x112;
|
||||
const BTN_SIDE: u32 = 0x113;
|
||||
let button = match button {
|
||||
0 => return Ok(()),
|
||||
1 => BTN_LEFT,
|
||||
2 => BTN_MIDDLE,
|
||||
3 => BTN_RIGHT,
|
||||
n => BTN_SIDE + n - 8,
|
||||
};
|
||||
seat.mouse_event(InputEvent::Button(button, state));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_input_key_press(
|
||||
self: &Rc<Self>,
|
||||
event: &Event,
|
||||
state: KeyState,
|
||||
) -> Result<(), XorgngBackendError> {
|
||||
let event: XiKeyPress = event.parse()?;
|
||||
if let Some(seat) = self.seats.get(&event.deviceid) {
|
||||
seat.kb_event(InputEvent::Key(event.detail - 8, state));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_input_hierarchy(
|
||||
self: &Rc<Self>,
|
||||
event: &Event,
|
||||
) -> Result<(), XorgngBackendError> {
|
||||
let event: XiHierarchy = event.parse()?;
|
||||
for info in event.infos.iter() {
|
||||
if info.flags & INPUT_HIERARCHY_MASK_MASTER_ADDED != 0 {
|
||||
if let Err(e) = self.query_devices(info.deviceid).await {
|
||||
log::error!("Could not query device {}: {:#}", info.deviceid, e);
|
||||
}
|
||||
} else if info.flags & INPUT_HIERARCHY_MASK_MASTER_REMOVED != 0 {
|
||||
self.mouse_seats.remove(&info.attachment);
|
||||
if let Some(seat) = self.seats.remove(&info.deviceid) {
|
||||
seat.removed.set(true);
|
||||
seat.kb_changed();
|
||||
seat.mouse_changed();
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_input_enter(&self, event: &Event) -> Result<(), XorgngBackendError> {
|
||||
let event: XiEnter = event.parse()?;
|
||||
if let (Some(win), Some(seat)) = (
|
||||
self.outputs.get(&event.event),
|
||||
self.mouse_seats.get(&event.deviceid),
|
||||
) {
|
||||
seat.mouse_event(InputEvent::OutputPosition(
|
||||
win.id,
|
||||
Fixed::from_1616(event.event_x),
|
||||
Fixed::from_1616(event.event_y),
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_input_motion(&self, event: &Event) -> Result<(), XorgngBackendError> {
|
||||
let event: XiMotion = event.parse()?;
|
||||
let (win, seat) = match (
|
||||
self.outputs.get(&event.event),
|
||||
self.mouse_seats.get(&event.deviceid),
|
||||
) {
|
||||
(Some(a), Some(b)) => (a, b),
|
||||
_ => return Ok(()),
|
||||
};
|
||||
seat.mouse_event(InputEvent::OutputPosition(
|
||||
win.id,
|
||||
Fixed::from_1616(event.event_x),
|
||||
Fixed::from_1616(event.event_y),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_destroy(&self, event: &Event) -> Result<(), XorgngBackendError> {
|
||||
self.state.el.stop();
|
||||
let event: DestroyNotify = event.parse()?;
|
||||
let output = match self.outputs.remove(&event.event) {
|
||||
Some(o) => o,
|
||||
_ => return Ok(()),
|
||||
};
|
||||
output.removed.set(true);
|
||||
output.changed();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_configure(&self, event: &Event) -> Result<(), XorgngBackendError> {
|
||||
let event: ConfigureNotify = event.parse()?;
|
||||
let output = match self.outputs.get(&event.event) {
|
||||
Some(o) => o,
|
||||
_ => return Ok(()),
|
||||
};
|
||||
let width = event.width as i32;
|
||||
let height = event.height as i32;
|
||||
let mut changed = false;
|
||||
changed |= output.width.replace(width) != width;
|
||||
changed |= output.height.replace(height) != height;
|
||||
if changed {
|
||||
let images = self.create_images(output.window, width, height).await?;
|
||||
for (new, old) in images.iter().zip(output.images.iter()) {
|
||||
let _ = self.c.call(&FreePixmap {
|
||||
pixmap: old.pixmap.get(),
|
||||
});
|
||||
old.fb.set(new.fb.get());
|
||||
old.pixmap.set(new.pixmap.get());
|
||||
}
|
||||
output.changed();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct XorgOutput {
|
||||
id: OutputId,
|
||||
_backend: Rc<XorgngBackendData>,
|
||||
window: u32,
|
||||
removed: Cell<bool>,
|
||||
width: Cell<i32>,
|
||||
height: Cell<i32>,
|
||||
serial: NumCell<u32>,
|
||||
next_msc: Cell<u64>,
|
||||
next_image: NumCell<usize>,
|
||||
images: [XorgImage; 2],
|
||||
cb: CloneCell<Option<Rc<dyn Fn()>>>,
|
||||
}
|
||||
|
||||
struct XorgImage {
|
||||
pixmap: Cell<u32>,
|
||||
fb: CloneCell<Rc<Framebuffer>>,
|
||||
idle: Cell<bool>,
|
||||
render_on_idle: Cell<bool>,
|
||||
last_serial: Cell<u32>,
|
||||
}
|
||||
|
||||
impl XorgOutput {
|
||||
fn changed(&self) {
|
||||
if let Some(cb) = self.cb.get() {
|
||||
cb();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Output for XorgOutput {
|
||||
fn id(&self) -> OutputId {
|
||||
self.id
|
||||
}
|
||||
|
||||
fn removed(&self) -> bool {
|
||||
self.removed.get()
|
||||
}
|
||||
|
||||
fn width(&self) -> i32 {
|
||||
self.width.get()
|
||||
}
|
||||
|
||||
fn height(&self) -> i32 {
|
||||
self.height.get()
|
||||
}
|
||||
|
||||
fn on_change(&self, cb: Rc<dyn Fn()>) {
|
||||
self.cb.set(Some(cb));
|
||||
}
|
||||
}
|
||||
|
||||
struct XorgSeat {
|
||||
kb_id: InputDeviceId,
|
||||
mouse_id: InputDeviceId,
|
||||
backend: Rc<XorgngBackendData>,
|
||||
kb: u16,
|
||||
mouse: u16,
|
||||
removed: Cell<bool>,
|
||||
kb_cb: CloneCell<Option<Rc<dyn Fn()>>>,
|
||||
mouse_cb: CloneCell<Option<Rc<dyn Fn()>>>,
|
||||
kb_events: RefCell<VecDeque<InputEvent>>,
|
||||
mouse_events: RefCell<VecDeque<InputEvent>>,
|
||||
button_map: CopyHashMap<u32, u32>,
|
||||
}
|
||||
|
||||
struct XorgSeatKeyboard(Rc<XorgSeat>);
|
||||
|
||||
struct XorgSeatMouse(Rc<XorgSeat>);
|
||||
|
||||
impl XorgSeat {
|
||||
fn kb_changed(&self) {
|
||||
if let Some(cb) = self.kb_cb.get() {
|
||||
cb();
|
||||
}
|
||||
}
|
||||
|
||||
fn mouse_changed(&self) {
|
||||
if let Some(cb) = self.mouse_cb.get() {
|
||||
cb();
|
||||
}
|
||||
}
|
||||
|
||||
fn mouse_event(&self, event: InputEvent) {
|
||||
self.mouse_events.borrow_mut().push_back(event);
|
||||
self.mouse_changed();
|
||||
}
|
||||
|
||||
fn kb_event(&self, event: InputEvent) {
|
||||
self.kb_events.borrow_mut().push_back(event);
|
||||
self.kb_changed();
|
||||
}
|
||||
|
||||
async fn update_button_map(&self) {
|
||||
self.button_map.clear();
|
||||
let gdbm = XiGetDeviceButtonMapping {
|
||||
device_id: self.mouse as _,
|
||||
};
|
||||
let reply = match self.backend.c.call(&gdbm).await {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
log::error!(
|
||||
"Could not get Xinput button map of device {}: {}",
|
||||
self.mouse,
|
||||
ErrorFmt(e),
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
for (i, map) in reply.get().map.iter().copied().enumerate().rev() {
|
||||
self.button_map.set(map as u32, i as u32 + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InputDevice for XorgSeatKeyboard {
|
||||
fn id(&self) -> InputDeviceId {
|
||||
self.0.kb_id
|
||||
}
|
||||
|
||||
fn removed(&self) -> bool {
|
||||
self.0.removed.get()
|
||||
}
|
||||
|
||||
fn event(&self) -> Option<InputEvent> {
|
||||
self.0.kb_events.borrow_mut().pop_front()
|
||||
}
|
||||
|
||||
fn on_change(&self, cb: Rc<dyn Fn()>) {
|
||||
self.0.kb_cb.set(Some(cb));
|
||||
}
|
||||
|
||||
fn grab(&self, grab: bool) {
|
||||
self.0.backend.grab_requests.push((self.0.clone(), grab));
|
||||
}
|
||||
}
|
||||
|
||||
impl InputDevice for XorgSeatMouse {
|
||||
fn id(&self) -> InputDeviceId {
|
||||
self.0.mouse_id
|
||||
}
|
||||
|
||||
fn removed(&self) -> bool {
|
||||
self.0.removed.get()
|
||||
}
|
||||
|
||||
fn event(&self) -> Option<InputEvent> {
|
||||
self.0.mouse_events.borrow_mut().pop_front()
|
||||
}
|
||||
|
||||
fn on_change(&self, cb: Rc<dyn Fn()>) {
|
||||
self.0.mouse_cb.set(Some(cb));
|
||||
}
|
||||
|
||||
fn grab(&self, _grab: bool) {
|
||||
log::error!("Cannot grab xorg mouse");
|
||||
}
|
||||
}
|
||||
20
src/dbus.rs
20
src/dbus.rs
|
|
@ -1,10 +1,10 @@
|
|||
use crate::async_engine::{AsyncFd, SpawnedFuture};
|
||||
use crate::dbus::property::GetReply;
|
||||
use crate::dbus::types::{ObjectPath, Signature, Variant};
|
||||
use crate::utils::bufio::{BufIo, BufIoError};
|
||||
use crate::utils::copyhashmap::CopyHashMap;
|
||||
use crate::utils::stack::Stack;
|
||||
use crate::utils::vecstorage::VecStorage;
|
||||
use crate::{AsyncEngine, AsyncError, AsyncQueue, CloneCell, NumCell, RunToplevel};
|
||||
use crate::{AsyncEngine, AsyncError, CloneCell, NumCell, RunToplevel};
|
||||
use ahash::AHashMap;
|
||||
use std::borrow::Cow;
|
||||
use std::cell::{Cell, RefCell};
|
||||
|
|
@ -50,8 +50,6 @@ impl Display for CallError {
|
|||
pub enum DbusError {
|
||||
#[error("Encountered an unknown type in a signature")]
|
||||
UnknownType,
|
||||
#[error("BUS closed the connection")]
|
||||
Closed,
|
||||
#[error("Function call reply does not contain a reply serial")]
|
||||
NoReplySerial,
|
||||
#[error("Signal message contains no interface or member or path")]
|
||||
|
|
@ -108,6 +106,10 @@ pub enum DbusError {
|
|||
InvalidSignatureType,
|
||||
#[error("The signal already has a handler")]
|
||||
AlreadyHandled,
|
||||
#[error(transparent)]
|
||||
BufIoError(#[from] BufIoError),
|
||||
#[error(transparent)]
|
||||
DbusError(Rc<DbusError>),
|
||||
}
|
||||
efrom!(DbusError, AsyncError);
|
||||
|
||||
|
|
@ -145,11 +147,10 @@ unsafe trait ReplyHandler {
|
|||
pub struct DbusSocket {
|
||||
bus_name: &'static str,
|
||||
fd: AsyncFd,
|
||||
bufio: Rc<BufIo>,
|
||||
eng: Rc<AsyncEngine>,
|
||||
next_serial: NumCell<u32>,
|
||||
bufs: Stack<Vec<u8>>,
|
||||
unique_name: CloneCell<Rc<String>>,
|
||||
outgoing: AsyncQueue<DbusMessage>,
|
||||
reply_handlers: CopyHashMap<u32, Box<dyn ReplyHandler>>,
|
||||
incoming: Cell<Option<SpawnedFuture<()>>>,
|
||||
outgoing_: Cell<Option<SpawnedFuture<()>>>,
|
||||
|
|
@ -237,11 +238,6 @@ impl Drop for DbusHolder {
|
|||
}
|
||||
}
|
||||
|
||||
struct DbusMessage {
|
||||
fds: Vec<Rc<OwnedFd>>,
|
||||
buf: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum DynamicType {
|
||||
U8,
|
||||
|
|
@ -354,7 +350,7 @@ impl<T: Message<'static>> Reply<T> {
|
|||
|
||||
impl<T: Message<'static>> Drop for Reply<T> {
|
||||
fn drop(&mut self) {
|
||||
self.socket.bufs.push(mem::take(&mut self.buf));
|
||||
self.socket.bufio.add_buf(mem::take(&mut self.buf));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use crate::dbus::auth::handle_auth;
|
||||
use crate::dbus::{DbusError, DbusHolder, DbusSocket};
|
||||
use crate::utils::bufio::BufIo;
|
||||
use crate::{org, AsyncEngine, ErrorFmt, NumCell, RunToplevel};
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
|
|
@ -46,14 +47,14 @@ fn connect(
|
|||
if let Err(e) = uapi::connect(socket.raw(), &sadr) {
|
||||
return Err(DbusError::Connect(e.into()));
|
||||
}
|
||||
let fd = eng.fd(&Rc::new(socket))?;
|
||||
let socket = Rc::new(DbusSocket {
|
||||
bus_name: name,
|
||||
fd: eng.fd(&Rc::new(socket))?,
|
||||
fd: fd.clone(),
|
||||
bufio: Rc::new(BufIo::new(fd)),
|
||||
eng: eng.clone(),
|
||||
next_serial: NumCell::new(1),
|
||||
bufs: Default::default(),
|
||||
unique_name: Default::default(),
|
||||
outgoing: Default::default(),
|
||||
reply_handlers: Default::default(),
|
||||
incoming: Default::default(),
|
||||
outgoing_: Default::default(),
|
||||
|
|
|
|||
|
|
@ -5,35 +5,24 @@ use super::{
|
|||
use crate::dbus::{
|
||||
CallError, DbusError, DbusSocket, Headers, Parser, MSG_ERROR, MSG_METHOD_RETURN, MSG_SIGNAL,
|
||||
};
|
||||
use crate::utils::bufio::BufIoIncoming;
|
||||
use crate::utils::ptr_ext::{MutPtrExt, PtrExt};
|
||||
use crate::ErrorFmt;
|
||||
use std::cell::UnsafeCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
use uapi::{c, Errno, MaybeUninitSliceExt, MsghdrMut, OwnedFd};
|
||||
|
||||
pub async fn handle_incoming(socket: Rc<DbusSocket>) {
|
||||
let mut incoming = Incoming {
|
||||
incoming: socket.bufio.incoming(),
|
||||
socket,
|
||||
buf: Box::new([MaybeUninit::uninit(); 4096]),
|
||||
buf_start: 0,
|
||||
buf_end: 0,
|
||||
fds: Default::default(),
|
||||
cmsg: Box::new([MaybeUninit::uninit(); 256]),
|
||||
};
|
||||
incoming.run().await;
|
||||
}
|
||||
|
||||
pub struct Incoming {
|
||||
socket: Rc<DbusSocket>,
|
||||
|
||||
buf: Box<[MaybeUninit<u8>; 4096]>,
|
||||
buf_start: usize,
|
||||
buf_end: usize,
|
||||
fds: VecDeque<Rc<OwnedFd>>,
|
||||
cmsg: Box<[MaybeUninit<u8>; 256]>,
|
||||
incoming: BufIoIncoming,
|
||||
}
|
||||
|
||||
impl Incoming {
|
||||
|
|
@ -55,11 +44,12 @@ impl Incoming {
|
|||
}
|
||||
|
||||
async fn handle_msg(&mut self) -> Result<(), DbusError> {
|
||||
let msg_buf_data = UnsafeCell::new(self.socket.bufs.pop().unwrap_or_default());
|
||||
let msg_buf_data = UnsafeCell::new(self.socket.bufio.buf());
|
||||
let msg_buf = unsafe { msg_buf_data.get().deref_mut() };
|
||||
msg_buf.clear();
|
||||
const FIXED_HEADER_SIZE: usize = 16;
|
||||
self.fill_msg_buf(FIXED_HEADER_SIZE, msg_buf).await?;
|
||||
self.incoming
|
||||
.fill_msg_buf(FIXED_HEADER_SIZE, msg_buf)
|
||||
.await?;
|
||||
let endianess = msg_buf[0];
|
||||
if (endianess == b'l') != cfg!(target_endian = "little") {
|
||||
return Err(DbusError::InvalidEndianess);
|
||||
|
|
@ -75,16 +65,18 @@ impl Incoming {
|
|||
let [body_len, _serial, headers_len] = fields2;
|
||||
let dyn_header_len = headers_len + (headers_len.wrapping_neg() & 7);
|
||||
let remaining = dyn_header_len + body_len;
|
||||
self.fill_msg_buf(remaining as usize, msg_buf).await?;
|
||||
self.incoming
|
||||
.fill_msg_buf(remaining as usize, msg_buf)
|
||||
.await?;
|
||||
drop(msg_buf);
|
||||
let msg_buf = unsafe { msg_buf_data.get().deref().deref() };
|
||||
let headers = &msg_buf[FIXED_HEADER_SIZE..FIXED_HEADER_SIZE + headers_len as usize];
|
||||
let headers = self.parse_headers(headers)?;
|
||||
let unix_fds = headers.unix_fds.unwrap_or(0) as usize;
|
||||
if self.fds.len() < unix_fds {
|
||||
if self.incoming.fds.len() < unix_fds {
|
||||
return Err(DbusError::TooFewFds);
|
||||
}
|
||||
let fds: Vec<_> = self.fds.drain(..unix_fds).collect();
|
||||
let fds: Vec<_> = self.incoming.fds.drain(..unix_fds).collect();
|
||||
let mut parser = Parser {
|
||||
buf: &msg_buf,
|
||||
pos: FIXED_HEADER_SIZE + dyn_header_len as usize,
|
||||
|
|
@ -172,7 +164,7 @@ impl Incoming {
|
|||
}
|
||||
let msg_buf = msg_buf_data.into_inner();
|
||||
if msg_buf.capacity() > 0 {
|
||||
self.socket.bufs.push(msg_buf);
|
||||
self.socket.bufio.add_buf(msg_buf);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -199,54 +191,4 @@ impl Incoming {
|
|||
}
|
||||
Ok(headers)
|
||||
}
|
||||
|
||||
async fn fill_msg_buf(&mut self, mut n: usize, buf: &mut Vec<u8>) -> Result<(), DbusError> {
|
||||
while n > 0 {
|
||||
if self.buf_start == self.buf_end {
|
||||
while let Err(e) = self.recvmsg() {
|
||||
if e.0 != c::EAGAIN {
|
||||
return Err(DbusError::ReadError(e.into()));
|
||||
}
|
||||
self.socket.fd.readable().await?;
|
||||
}
|
||||
if self.buf_start == self.buf_end {
|
||||
return Err(DbusError::Closed);
|
||||
}
|
||||
}
|
||||
let read = n.min(self.buf_end - self.buf_start);
|
||||
let buf_start = self.buf_start % self.buf.len();
|
||||
unsafe {
|
||||
buf.extend_from_slice(
|
||||
self.buf[buf_start..buf_start + read].slice_assume_init_ref(),
|
||||
);
|
||||
}
|
||||
n -= read;
|
||||
self.buf_start += read;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn recvmsg(&mut self) -> Result<(), Errno> {
|
||||
self.buf_start = 0;
|
||||
self.buf_end = 0;
|
||||
let mut iov = [&mut self.buf[..]];
|
||||
let mut hdr = MsghdrMut {
|
||||
iov: &mut iov[..],
|
||||
control: Some(&mut self.cmsg[..]),
|
||||
name: uapi::sockaddr_none_mut(),
|
||||
flags: 0,
|
||||
};
|
||||
let (ivec, _, mut cmsg) =
|
||||
uapi::recvmsg(self.socket.fd.raw(), &mut hdr, c::MSG_CMSG_CLOEXEC)?;
|
||||
self.buf_end += ivec.len();
|
||||
while cmsg.len() > 0 {
|
||||
let (_, hdr, body) = uapi::cmsg_read(&mut cmsg)?;
|
||||
if hdr.cmsg_level == c::SOL_SOCKET && hdr.cmsg_type == c::SCM_RIGHTS {
|
||||
for fd in uapi::pod_iter(body)? {
|
||||
self.fds.push_back(Rc::new(OwnedFd::new(fd)));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,118 +1,10 @@
|
|||
use crate::dbus::{DbusMessage, DbusSocket};
|
||||
use crate::utils::vec_ext::{UninitVecExt, VecExt};
|
||||
use crate::utils::vecstorage::VecStorage;
|
||||
use crate::dbus::DbusSocket;
|
||||
use crate::ErrorFmt;
|
||||
use std::collections::VecDeque;
|
||||
use std::mem;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::ptr::NonNull;
|
||||
use std::rc::Rc;
|
||||
use uapi::{c, Errno, Msghdr};
|
||||
|
||||
pub async fn handle_outgoing(socket: Rc<DbusSocket>) {
|
||||
let mut outgoing = Outgoing {
|
||||
socket,
|
||||
msgs: Default::default(),
|
||||
cmsg: vec![],
|
||||
fds: vec![],
|
||||
iovecs: Default::default(),
|
||||
};
|
||||
outgoing.run().await
|
||||
}
|
||||
|
||||
struct DbusMessageOffset {
|
||||
msg: DbusMessage,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
struct Outgoing {
|
||||
socket: Rc<DbusSocket>,
|
||||
|
||||
msgs: VecDeque<DbusMessageOffset>,
|
||||
cmsg: Vec<MaybeUninit<u8>>,
|
||||
fds: Vec<c::c_int>,
|
||||
iovecs: VecStorage<NonNull<[u8]>>,
|
||||
}
|
||||
|
||||
impl Outgoing {
|
||||
async fn run(&mut self) {
|
||||
loop {
|
||||
self.socket.outgoing.non_empty().await;
|
||||
while let Err(e) = self.try_flush() {
|
||||
if e != Errno(c::EAGAIN) {
|
||||
log::error!(
|
||||
"{}: Could not send a message to the bus: {}",
|
||||
self.socket.bus_name,
|
||||
ErrorFmt(e)
|
||||
);
|
||||
self.socket.kill();
|
||||
return;
|
||||
}
|
||||
if let Err(e) = self.socket.fd.writable().await {
|
||||
log::error!(
|
||||
"{}: Cannot wait for fd to become readable: {}",
|
||||
self.socket.bus_name,
|
||||
ErrorFmt(e)
|
||||
);
|
||||
self.socket.kill();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_flush(&mut self) -> Result<(), Errno> {
|
||||
loop {
|
||||
while let Some(msg) = self.socket.outgoing.try_pop() {
|
||||
self.msgs.push_back(DbusMessageOffset { msg, offset: 0 });
|
||||
}
|
||||
if self.msgs.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let mut iovecs = self.iovecs.take_as();
|
||||
let mut fds = &[][..];
|
||||
for msg in &mut self.msgs {
|
||||
if msg.msg.fds.len() > 0 {
|
||||
if fds.len() > 0 || iovecs.len() > 0 {
|
||||
break;
|
||||
}
|
||||
fds = &msg.msg.fds;
|
||||
}
|
||||
iovecs.push(&msg.msg.buf[msg.offset..]);
|
||||
}
|
||||
self.cmsg.clear();
|
||||
if fds.len() > 0 {
|
||||
self.fds.clear();
|
||||
self.fds.extend(fds.iter().map(|f| f.raw()));
|
||||
let cmsg_space = uapi::cmsg_space(fds.len() * mem::size_of::<c::c_int>());
|
||||
self.cmsg.reserve(cmsg_space);
|
||||
let (_, mut spare) = self.cmsg.split_at_spare_mut_bytes_ext();
|
||||
let hdr = c::cmsghdr {
|
||||
cmsg_len: 0,
|
||||
cmsg_level: c::SOL_SOCKET,
|
||||
cmsg_type: c::SCM_RIGHTS,
|
||||
};
|
||||
let len = uapi::cmsg_write(&mut spare, hdr, &self.fds).unwrap();
|
||||
self.cmsg.set_len_safe(len);
|
||||
}
|
||||
let msg = Msghdr {
|
||||
iov: &iovecs[..],
|
||||
control: Some(&self.cmsg[..]),
|
||||
name: uapi::sockaddr_none_ref(),
|
||||
};
|
||||
let mut n = uapi::sendmsg(self.socket.fd.raw(), &msg, c::MSG_DONTWAIT)?;
|
||||
drop(iovecs);
|
||||
self.msgs[0].msg.fds.clear();
|
||||
while n > 0 {
|
||||
let len = self.msgs[0].msg.buf.len() - self.msgs[0].offset;
|
||||
if n < len {
|
||||
self.msgs[0].offset += n;
|
||||
break;
|
||||
}
|
||||
n -= len;
|
||||
let msg = self.msgs.pop_front().unwrap();
|
||||
self.socket.bufs.push(msg.msg.buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Err(e) = socket.bufio.clone().outgoing().await {
|
||||
log::error!("{}: {}", socket.bus_name, ErrorFmt(e));
|
||||
}
|
||||
socket.kill();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
use crate::dbus::property::Get;
|
||||
use crate::dbus::types::{ObjectPath, Signature, Variant};
|
||||
use crate::dbus::{
|
||||
AsyncProperty, AsyncReply, AsyncReplySlot, DbusError, DbusMessage, DbusSocket, DbusType,
|
||||
Formatter, Headers, InterfaceSignalHandlers, Message, MethodCall, Parser, Property, Reply,
|
||||
ReplyHandler, Signal, SignalHandler, SignalHandlerApi, SignalHandlerData, BUS_DEST, BUS_PATH,
|
||||
HDR_DESTINATION, HDR_INTERFACE, HDR_MEMBER, HDR_PATH, HDR_SIGNATURE, HDR_UNIX_FDS,
|
||||
MSG_METHOD_CALL, NO_REPLY_EXPECTED,
|
||||
AsyncProperty, AsyncReply, AsyncReplySlot, DbusError, DbusSocket, DbusType, Formatter, Headers,
|
||||
InterfaceSignalHandlers, Message, MethodCall, Parser, Property, Reply, ReplyHandler, Signal,
|
||||
SignalHandler, SignalHandlerApi, SignalHandlerData, BUS_DEST, BUS_PATH, HDR_DESTINATION,
|
||||
HDR_INTERFACE, HDR_MEMBER, HDR_PATH, HDR_SIGNATURE, HDR_UNIX_FDS, MSG_METHOD_CALL,
|
||||
NO_REPLY_EXPECTED,
|
||||
};
|
||||
use crate::utils::bufio::BufIoMessage;
|
||||
use crate::{org, ErrorFmt};
|
||||
use std::cell::Cell;
|
||||
use std::collections::hash_map::Entry;
|
||||
|
|
@ -243,7 +244,7 @@ impl DbusSocket {
|
|||
msg: &T,
|
||||
) -> u32 {
|
||||
let (msg, serial) = self.format_call(path, destination, flags, msg);
|
||||
self.outgoing.push(msg);
|
||||
self.bufio.send(msg);
|
||||
serial
|
||||
}
|
||||
|
||||
|
|
@ -253,12 +254,11 @@ impl DbusSocket {
|
|||
destination: &str,
|
||||
flags: u8,
|
||||
msg: &T,
|
||||
) -> (DbusMessage, u32) {
|
||||
) -> (BufIoMessage, u32) {
|
||||
let num_fds = msg.num_fds();
|
||||
let mut fds = Vec::with_capacity(num_fds as _);
|
||||
let serial = self.serial();
|
||||
let mut buf = self.bufs.pop().unwrap_or_default();
|
||||
buf.clear();
|
||||
let mut buf = self.bufio.buf();
|
||||
let mut fmt = Formatter::new(&mut fds, &mut buf);
|
||||
self.format_header(
|
||||
&mut fmt,
|
||||
|
|
@ -276,7 +276,7 @@ impl DbusSocket {
|
|||
msg.marshal(&mut fmt);
|
||||
let body_len = (buf.len() - body_start) as u32;
|
||||
buf[4..8].copy_from_slice(uapi::as_bytes(&body_len));
|
||||
(DbusMessage { fds, buf }, serial)
|
||||
(BufIoMessage { fds, buf }, serial)
|
||||
}
|
||||
|
||||
fn format_header(
|
||||
|
|
@ -345,13 +345,22 @@ where
|
|||
) -> Result<(), DbusError> {
|
||||
let msg = <T::Generic<'a> as Message>::unmarshal(parser)?;
|
||||
(self.0)(Ok(&msg));
|
||||
socket.bufs.push(buf);
|
||||
socket.bufio.add_buf(buf);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct AsyncReplyHandler<T: Message<'static>>(Rc<AsyncReplySlot<T>>);
|
||||
|
||||
impl<T: Message<'static>> AsyncReplyHandler<T> {
|
||||
fn complete(self: Box<Self>, res: Result<Reply<T>, DbusError>) {
|
||||
self.0.data.set(Some(res));
|
||||
if let Some(waker) = self.0.waker.take() {
|
||||
waker.wake();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> ReplyHandler for AsyncReplyHandler<T>
|
||||
where
|
||||
T: Message<'static>,
|
||||
|
|
@ -376,16 +385,21 @@ where
|
|||
) -> Result<(), DbusError> {
|
||||
let msg = <T::Generic<'static> as Message<'static>>::unmarshal(unsafe {
|
||||
mem::transmute::<&mut Parser<'a>, &mut Parser<'static>>(parser)
|
||||
})?;
|
||||
});
|
||||
let msg = match msg {
|
||||
Ok(msg) => msg,
|
||||
Err(e) => {
|
||||
let e = Rc::new(e);
|
||||
self.complete(Err(DbusError::DbusError(e.clone())));
|
||||
return Err(DbusError::DbusError(e.clone()));
|
||||
}
|
||||
};
|
||||
let reply = Reply {
|
||||
socket: socket.clone(),
|
||||
buf,
|
||||
t: msg,
|
||||
};
|
||||
self.0.data.set(Some(Ok(reply)));
|
||||
if let Some(waker) = self.0.waker.take() {
|
||||
waker.wake();
|
||||
}
|
||||
self.complete(Ok(reply));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -151,10 +151,10 @@ impl EventLoop {
|
|||
fn run_(&self) -> Result<(), EventLoopError> {
|
||||
self.check_destroyed()?;
|
||||
let mut buf = [c::epoll_event { events: 0, u64: 0 }; 16];
|
||||
while !self.destroyed.get() {
|
||||
'outer: while !self.destroyed.get() {
|
||||
while let Some(id) = self.scheduled.borrow_mut().pop_front() {
|
||||
if self.destroyed.get() {
|
||||
break;
|
||||
break 'outer;
|
||||
}
|
||||
if let Some(entry) = self.entries.get(&id) {
|
||||
if let Err(e) = entry.dispatcher.clone().dispatch(0) {
|
||||
|
|
@ -162,6 +162,9 @@ impl EventLoop {
|
|||
}
|
||||
}
|
||||
}
|
||||
if self.destroyed.get() {
|
||||
break 'outer;
|
||||
}
|
||||
let num = match uapi::epoll_wait(self.epoll.raw(), &mut buf, -1) {
|
||||
Ok(n) => n,
|
||||
Err(Errno(c::EINTR)) => continue,
|
||||
|
|
@ -169,7 +172,7 @@ impl EventLoop {
|
|||
};
|
||||
for event in &buf[..num] {
|
||||
if self.destroyed.get() {
|
||||
break;
|
||||
break 'outer;
|
||||
}
|
||||
let id = event.u64;
|
||||
let entry = match self.entries.get(&id) {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
use crate::dbus::{DbusError, DbusSocket, SignalHandler};
|
||||
use crate::org::freedesktop::login1::seat::SwitchToReply;
|
||||
use crate::org::freedesktop::login1::session::{PauseDevice, ResumeDevice, TakeDeviceReply};
|
||||
use crate::{org, FALSE};
|
||||
use std::rc::Rc;
|
||||
use thiserror::Error;
|
||||
use uapi::c;
|
||||
use crate::org::freedesktop::login1::seat::SwitchToReply;
|
||||
|
||||
const LOGIND_NAME: &str = "org.freedesktop.login1";
|
||||
const MANAGER_PATH: &str = "/org/freedesktop/login1";
|
||||
|
|
@ -53,7 +53,7 @@ impl Session {
|
|||
.get_async::<org::freedesktop::login1::session::Seat>(LOGIND_NAME, &session_path)
|
||||
.await;
|
||||
match seat {
|
||||
Ok(s) => s.get().1.0.to_string(),
|
||||
Ok(s) => s.get().1 .0.to_string(),
|
||||
Err(e) => return Err(LogindError::GetSeatName(e)),
|
||||
}
|
||||
};
|
||||
|
|
@ -126,7 +126,8 @@ impl Session {
|
|||
}
|
||||
|
||||
pub fn switch_to<F>(&self, vtnr: u32, f: F)
|
||||
where F: FnOnce(Result<&SwitchToReply, DbusError>) + 'static
|
||||
where
|
||||
F: FnOnce(Result<&SwitchToReply, DbusError>) + 'static,
|
||||
{
|
||||
self.socket.call(
|
||||
LOGIND_NAME,
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
use crate::acceptor::AcceptorError;
|
||||
use crate::async_engine::{AsyncError, Phase};
|
||||
use crate::backends::metal;
|
||||
use crate::backends::xorg::{XorgBackend, XorgBackendError};
|
||||
use crate::client::Clients;
|
||||
use crate::clientmem::ClientMemError;
|
||||
use crate::dbus::{Dbus, FALSE};
|
||||
|
|
@ -97,6 +96,8 @@ mod utils;
|
|||
mod wheel;
|
||||
mod wire;
|
||||
mod wire_dbus;
|
||||
mod wire_xcon;
|
||||
mod xcon;
|
||||
mod xkbcommon;
|
||||
mod xwayland;
|
||||
|
||||
|
|
@ -127,8 +128,6 @@ enum MainError {
|
|||
WheelError(#[from] WheelError),
|
||||
#[error("The async subsystem caused an error")]
|
||||
AsyncError(#[from] AsyncError),
|
||||
#[error("The xorg backend caused an error")]
|
||||
XorgBackendError(#[from] XorgBackendError),
|
||||
#[error("The render backend caused an error")]
|
||||
RenderError(#[from] RenderError),
|
||||
#[error("The ol' forker caused an error")]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
use crate::async_engine::{AsyncEngine, SpawnedFuture};
|
||||
use crate::backend::{Backend, BackendEvent, InputDevice, InputDeviceId, InputDeviceIds, OutputId, OutputIds};
|
||||
use crate::backend::{
|
||||
Backend, BackendEvent, InputDevice, InputDeviceId, InputDeviceIds, OutputId, OutputIds,
|
||||
};
|
||||
use crate::client::{Client, Clients};
|
||||
use crate::config::ConfigProxy;
|
||||
use crate::cursor::ServerCursors;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,19 @@
|
|||
use crate::{metal, ErrorFmt, State, XorgBackend};
|
||||
use crate::{metal, ErrorFmt, State};
|
||||
use std::future::pending;
|
||||
use std::rc::Rc;
|
||||
use crate::backends::xorgng::XorgngBackend;
|
||||
|
||||
pub async fn start_backend(state: Rc<State>) {
|
||||
log::info!("Trying to start X backend");
|
||||
let e = match XorgBackend::new(&state) {
|
||||
Ok(b) => {
|
||||
state.backend.set(Some(b));
|
||||
// let e = match XorgBackend::new(&state) {
|
||||
// Ok(b) => {
|
||||
// state.backend.set(Some(b));
|
||||
// pending().await
|
||||
// }
|
||||
// Err(e) => e,
|
||||
// };
|
||||
let e = match XorgngBackend::run(&state).await {
|
||||
Ok(_) => {
|
||||
pending().await
|
||||
},
|
||||
Err(e) => e,
|
||||
|
|
|
|||
241
src/utils/bufio.rs
Normal file
241
src/utils/bufio.rs
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
use crate::async_engine::AsyncFd;
|
||||
use crate::utils::oserror::OsError;
|
||||
use crate::utils::stack::Stack;
|
||||
use crate::utils::vec_ext::{UninitVecExt, VecExt};
|
||||
use crate::utils::vecstorage::VecStorage;
|
||||
use crate::{AsyncError, AsyncQueue};
|
||||
use std::collections::VecDeque;
|
||||
use std::mem;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::ptr::NonNull;
|
||||
use std::rc::Rc;
|
||||
use thiserror::Error;
|
||||
use uapi::{c, Errno, MaybeUninitSliceExt, Msghdr, MsghdrMut, OwnedFd};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum BufIoError {
|
||||
#[error("Could not write to the socket")]
|
||||
FlushError(#[source] OsError),
|
||||
#[error("Could not read from the socket")]
|
||||
ReadError(#[source] OsError),
|
||||
#[error("Cannot wait for fd to become writable")]
|
||||
Writable(#[source] AsyncError),
|
||||
#[error("Cannot wait for fd to become readable")]
|
||||
Readable(#[source] AsyncError),
|
||||
#[error("The socket is closed")]
|
||||
Closed,
|
||||
}
|
||||
|
||||
pub struct BufIoMessage {
|
||||
pub fds: Vec<Rc<OwnedFd>>,
|
||||
pub buf: Vec<u8>,
|
||||
}
|
||||
|
||||
struct MessageOffset {
|
||||
msg: BufIoMessage,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
pub struct BufIo {
|
||||
fd: AsyncFd,
|
||||
bufs: Stack<Vec<u8>>,
|
||||
outgoing: AsyncQueue<BufIoMessage>,
|
||||
}
|
||||
|
||||
pub struct BufIoIncoming {
|
||||
bufio: Rc<BufIo>,
|
||||
|
||||
buf: Box<[MaybeUninit<u8>; 4096]>,
|
||||
buf_start: usize,
|
||||
buf_end: usize,
|
||||
pub fds: VecDeque<Rc<OwnedFd>>,
|
||||
cmsg: Box<[MaybeUninit<u8>; 256]>,
|
||||
}
|
||||
|
||||
struct Outgoing {
|
||||
bufio: Rc<BufIo>,
|
||||
|
||||
msgs: VecDeque<MessageOffset>,
|
||||
cmsg: Vec<MaybeUninit<u8>>,
|
||||
fds: Vec<c::c_int>,
|
||||
iovecs: VecStorage<NonNull<[u8]>>,
|
||||
}
|
||||
|
||||
impl BufIo {
|
||||
pub fn new(fd: AsyncFd) -> Self {
|
||||
Self {
|
||||
fd,
|
||||
bufs: Default::default(),
|
||||
outgoing: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shutdown(&self) {
|
||||
let _ = uapi::shutdown(self.fd.raw(), c::SHUT_RDWR);
|
||||
}
|
||||
|
||||
pub fn buf(&self) -> Vec<u8> {
|
||||
let mut buf = self.bufs.pop().unwrap_or_default();
|
||||
buf.clear();
|
||||
buf
|
||||
}
|
||||
|
||||
pub fn add_buf(&self, buf: Vec<u8>) {
|
||||
self.bufs.push(buf);
|
||||
}
|
||||
|
||||
pub fn send(&self, msg: BufIoMessage) {
|
||||
self.outgoing.push(msg);
|
||||
}
|
||||
|
||||
pub async fn outgoing(self: Rc<Self>) -> Result<(), BufIoError> {
|
||||
let mut outgoing = Outgoing {
|
||||
bufio: self,
|
||||
msgs: Default::default(),
|
||||
cmsg: vec![],
|
||||
fds: vec![],
|
||||
iovecs: Default::default(),
|
||||
};
|
||||
outgoing.run().await
|
||||
}
|
||||
|
||||
pub fn incoming(self: &Rc<Self>) -> BufIoIncoming {
|
||||
BufIoIncoming {
|
||||
bufio: self.clone(),
|
||||
buf: Box::new([MaybeUninit::uninit(); 4096]),
|
||||
buf_start: 0,
|
||||
buf_end: 0,
|
||||
fds: Default::default(),
|
||||
cmsg: Box::new([MaybeUninit::uninit(); 256]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BufIoIncoming {
|
||||
pub async fn fill_msg_buf(
|
||||
&mut self,
|
||||
mut n: usize,
|
||||
buf: &mut Vec<u8>,
|
||||
) -> Result<(), BufIoError> {
|
||||
while n > 0 {
|
||||
if self.buf_start == self.buf_end {
|
||||
while let Err(e) = self.recvmsg() {
|
||||
if e.0 != c::EAGAIN {
|
||||
return Err(BufIoError::ReadError(e.into()));
|
||||
}
|
||||
if let Err(e) = self.bufio.fd.readable().await {
|
||||
return Err(BufIoError::Readable(e));
|
||||
}
|
||||
}
|
||||
if self.buf_start == self.buf_end {
|
||||
return Err(BufIoError::Closed);
|
||||
}
|
||||
}
|
||||
let read = n.min(self.buf_end - self.buf_start);
|
||||
let buf_start = self.buf_start % self.buf.len();
|
||||
unsafe {
|
||||
buf.extend_from_slice(
|
||||
self.buf[buf_start..buf_start + read].slice_assume_init_ref(),
|
||||
);
|
||||
}
|
||||
n -= read;
|
||||
self.buf_start += read;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn recvmsg(&mut self) -> Result<(), Errno> {
|
||||
self.buf_start = 0;
|
||||
self.buf_end = 0;
|
||||
let mut iov = [&mut self.buf[..]];
|
||||
let mut hdr = MsghdrMut {
|
||||
iov: &mut iov[..],
|
||||
control: Some(&mut self.cmsg[..]),
|
||||
name: uapi::sockaddr_none_mut(),
|
||||
flags: 0,
|
||||
};
|
||||
let (ivec, _, mut cmsg) =
|
||||
uapi::recvmsg(self.bufio.fd.raw(), &mut hdr, c::MSG_CMSG_CLOEXEC)?;
|
||||
self.buf_end += ivec.len();
|
||||
while cmsg.len() > 0 {
|
||||
let (_, hdr, body) = uapi::cmsg_read(&mut cmsg)?;
|
||||
if hdr.cmsg_level == c::SOL_SOCKET && hdr.cmsg_type == c::SCM_RIGHTS {
|
||||
for fd in uapi::pod_iter(body)? {
|
||||
self.fds.push_back(Rc::new(OwnedFd::new(fd)));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Outgoing {
|
||||
async fn run(&mut self) -> Result<(), BufIoError> {
|
||||
loop {
|
||||
self.bufio.outgoing.non_empty().await;
|
||||
while let Err(e) = self.try_flush() {
|
||||
if e != Errno(c::EAGAIN) {
|
||||
return Err(BufIoError::FlushError(e.into()));
|
||||
}
|
||||
if let Err(e) = self.bufio.fd.writable().await {
|
||||
return Err(BufIoError::Writable(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_flush(&mut self) -> Result<(), Errno> {
|
||||
loop {
|
||||
while let Some(msg) = self.bufio.outgoing.try_pop() {
|
||||
self.msgs.push_back(MessageOffset { msg, offset: 0 });
|
||||
}
|
||||
if self.msgs.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let mut iovecs = self.iovecs.take_as();
|
||||
let mut fds = &[][..];
|
||||
for msg in &mut self.msgs {
|
||||
if msg.msg.fds.len() > 0 {
|
||||
if fds.len() > 0 || iovecs.len() > 0 {
|
||||
break;
|
||||
}
|
||||
fds = &msg.msg.fds;
|
||||
}
|
||||
iovecs.push(&msg.msg.buf[msg.offset..]);
|
||||
}
|
||||
self.cmsg.clear();
|
||||
if fds.len() > 0 {
|
||||
self.fds.clear();
|
||||
self.fds.extend(fds.iter().map(|f| f.raw()));
|
||||
let cmsg_space = uapi::cmsg_space(fds.len() * mem::size_of::<c::c_int>());
|
||||
self.cmsg.reserve(cmsg_space);
|
||||
let (_, mut spare) = self.cmsg.split_at_spare_mut_bytes_ext();
|
||||
let hdr = c::cmsghdr {
|
||||
cmsg_len: 0,
|
||||
cmsg_level: c::SOL_SOCKET,
|
||||
cmsg_type: c::SCM_RIGHTS,
|
||||
};
|
||||
let len = uapi::cmsg_write(&mut spare, hdr, &self.fds[..]).unwrap();
|
||||
self.cmsg.set_len_safe(len);
|
||||
}
|
||||
let msg = Msghdr {
|
||||
iov: &iovecs[..],
|
||||
control: Some(&self.cmsg[..]),
|
||||
name: uapi::sockaddr_none_ref(),
|
||||
};
|
||||
let mut n = uapi::sendmsg(self.bufio.fd.raw(), &msg, c::MSG_DONTWAIT)?;
|
||||
drop(iovecs);
|
||||
self.msgs[0].msg.fds.clear();
|
||||
while n > 0 {
|
||||
let len = self.msgs[0].msg.buf.len() - self.msgs[0].offset;
|
||||
if n < len {
|
||||
self.msgs[0].offset += n;
|
||||
break;
|
||||
}
|
||||
n -= len;
|
||||
let msg = self.msgs.pop_front().unwrap();
|
||||
self.bufio.bufs.push(msg.msg.buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ use crate::utils::ptr_ext::{MutPtrExt, PtrExt};
|
|||
use std::cell::UnsafeCell;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
use std::rc::{Rc, Weak};
|
||||
|
||||
pub struct CloneCell<T: UnsafeCellCloneSafe> {
|
||||
data: UnsafeCell<T>,
|
||||
|
|
@ -60,6 +60,7 @@ pub unsafe trait UnsafeCellCloneSafe: Clone {}
|
|||
unsafe impl<T: UnsafeCellCloneSafe> UnsafeCellCloneSafe for Option<T> {}
|
||||
|
||||
unsafe impl<T: ?Sized> UnsafeCellCloneSafe for Rc<T> {}
|
||||
unsafe impl<T: ?Sized> UnsafeCellCloneSafe for Weak<T> {}
|
||||
|
||||
unsafe impl<T> UnsafeCellCloneSafe for NodeRef<T> {}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ pub mod asyncevent;
|
|||
pub mod bitfield;
|
||||
pub mod bitflags;
|
||||
pub mod buffd;
|
||||
pub mod bufio;
|
||||
pub mod clonecell;
|
||||
pub mod copyhashmap;
|
||||
pub mod debug_fn;
|
||||
|
|
|
|||
9
src/wire_xcon.rs
Normal file
9
src/wire_xcon.rs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#![allow(unused_imports, unused_variables, dead_code, unused_assignments)]
|
||||
|
||||
use crate::xcon::{Formatter, Message, Parser, Request, XEvent, XconError};
|
||||
use bstr::BStr;
|
||||
use std::borrow::Cow;
|
||||
use std::rc::Rc;
|
||||
use uapi::OwnedFd;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/wire_xcon.rs"));
|
||||
590
src/xcon.rs
Normal file
590
src/xcon.rs
Normal file
|
|
@ -0,0 +1,590 @@
|
|||
use crate::async_engine::SpawnedFuture;
|
||||
use crate::utils::bufio::{BufIo, BufIoError, BufIoMessage};
|
||||
use crate::utils::oserror::OsError;
|
||||
use crate::wire_xcon::{
|
||||
Extension, GetInputFocus, ListExtensions, QueryExtension, Setup, EXTENSIONS,
|
||||
};
|
||||
pub use crate::xcon::formatter::Formatter;
|
||||
use crate::xcon::incoming::handle_incoming;
|
||||
use crate::xcon::outgoing::handle_outgoing;
|
||||
pub use crate::xcon::parser::Parser;
|
||||
pub use crate::xcon::wire_type::{Message, Request, XEvent};
|
||||
use crate::xcon::xauthority::{XAuthority, LOCAL, MIT_MAGIC_COOKIE};
|
||||
use crate::{AsyncEngine, AsyncError, AsyncQueue, CloneCell, ErrorFmt, NumCell, Phase};
|
||||
use ahash::AHashMap;
|
||||
use bstr::{BString, ByteSlice};
|
||||
use std::any::TypeId;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::VecDeque;
|
||||
use std::future::Future;
|
||||
use std::io::Write;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::pin::Pin;
|
||||
use std::rc::{Rc, Weak};
|
||||
use std::task::{Context, Poll, Waker};
|
||||
use std::{mem, ptr};
|
||||
use thiserror::Error;
|
||||
use uapi::{c, OwnedFd};
|
||||
|
||||
pub mod consts;
|
||||
mod formatter;
|
||||
mod incoming;
|
||||
mod outgoing;
|
||||
mod parser;
|
||||
mod wire_type;
|
||||
mod xauthority;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum XconError {
|
||||
#[error("Unexpected EOF")]
|
||||
UnexpectedEof,
|
||||
#[error("Buffer slice is not properly aligned")]
|
||||
UnalignedSlice,
|
||||
#[error("Neither XAUTHORITY nor HOME is set")]
|
||||
HomeNotSet,
|
||||
#[error("Could not read Xauthority file")]
|
||||
ReadXAuthority(#[source] std::io::Error),
|
||||
#[error("Display field in Xauthority could not be parsed")]
|
||||
InvalidAuthorityDisplay,
|
||||
#[error("The DISPLAY is not set")]
|
||||
DisplayNotSet,
|
||||
#[error("DISPLAY contains an invalid value")]
|
||||
InvalidDisplayFormat,
|
||||
#[error("Could not create a unix socket")]
|
||||
CreateSocket(#[source] OsError),
|
||||
#[error("Could not connect to Xserver")]
|
||||
ConnectSocket(#[source] OsError),
|
||||
#[error("Could not retrive the hostname")]
|
||||
Hostname(#[source] OsError),
|
||||
#[error("Server did not send enough fds")]
|
||||
NotEnoughFds,
|
||||
#[error("Server rejected our connection attempt: {0}")]
|
||||
Connect(BString),
|
||||
#[error("Server requires additional authentication: {0}")]
|
||||
Authenticate(BString),
|
||||
#[error(transparent)]
|
||||
AsyncError(#[from] AsyncError),
|
||||
#[error(transparent)]
|
||||
BufIoError(#[from] BufIoError),
|
||||
#[error("The server did not send a reply to a request")]
|
||||
MissingReply,
|
||||
#[error("The server did not send fds with a reply")]
|
||||
MissingFds,
|
||||
#[error("The server sent a message with an excessive size")]
|
||||
ExcessiveMessageSize,
|
||||
#[error(transparent)]
|
||||
XconError(Rc<XconError>),
|
||||
#[error("The server does not support the `{0}` extension")]
|
||||
ExtensionUnavailable(&'static str),
|
||||
#[error("The server returned error {0}")]
|
||||
CoreError(u8),
|
||||
#[error("The extension `{}` returned error {1}", .0.name())]
|
||||
ExtensionError(Extension, u8),
|
||||
#[error("The connection to the server has already been closed")]
|
||||
Dead,
|
||||
#[error("Could not query the `{0}` extension")]
|
||||
QueryExtension(BString, #[source] Box<XconError>),
|
||||
#[error("All available xids have been used")]
|
||||
XidExhausted,
|
||||
#[error("Enum contains an unknown variant")]
|
||||
UnknownEnumVariant,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ExtensionIdRange {
|
||||
name: BString,
|
||||
extension: Option<Extension>,
|
||||
first: u8,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct ExtensionData {
|
||||
opcodes: [Option<u8>; EXTENSIONS.len()],
|
||||
ext_by_opcode: AHashMap<u8, Extension>,
|
||||
events: Vec<ExtensionIdRange>,
|
||||
errors: Vec<ExtensionIdRange>,
|
||||
}
|
||||
|
||||
pub struct Xcon {
|
||||
data: Rc<XconData>,
|
||||
outgoing: Cell<Option<SpawnedFuture<()>>>,
|
||||
incoming: Cell<Option<SpawnedFuture<()>>>,
|
||||
setup: Reply<Setup<'static>>,
|
||||
extensions: Rc<ExtensionData>,
|
||||
|
||||
xid_next: Cell<u32>,
|
||||
xid_inc: u32,
|
||||
xid_max: u32,
|
||||
}
|
||||
|
||||
struct XconData {
|
||||
bufio: Rc<BufIo>,
|
||||
next_serial: NumCell<u64>,
|
||||
last_recv_serial: Cell<u64>,
|
||||
reply_handlers: RefCell<VecDeque<Box<dyn ReplyHandler>>>,
|
||||
dead: Cell<bool>,
|
||||
need_sync: Cell<bool>,
|
||||
extensions: CloneCell<Option<Rc<ExtensionData>>>,
|
||||
xorg: CloneCell<Weak<Xcon>>,
|
||||
events: AsyncQueue<Event>,
|
||||
}
|
||||
|
||||
pub struct Reply<T: Message<'static>> {
|
||||
bufio: Rc<BufIo>,
|
||||
buf: Vec<u8>,
|
||||
t: T::Generic<'static>,
|
||||
}
|
||||
|
||||
pub struct Event {
|
||||
bufio: Rc<BufIo>,
|
||||
ext: Option<Extension>,
|
||||
code: u16,
|
||||
buf: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Deref for Event {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.buf
|
||||
}
|
||||
}
|
||||
|
||||
impl Event {
|
||||
pub fn ext(&self) -> Option<Extension> {
|
||||
self.ext
|
||||
}
|
||||
|
||||
pub fn code(&self) -> u16 {
|
||||
self.code
|
||||
}
|
||||
|
||||
pub fn parse<'a, M: Message<'a>>(&'a self) -> Result<M, XconError> {
|
||||
let mut parser = Parser::new(&self.buf, vec![]);
|
||||
M::deserialize(&mut parser)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Event {
|
||||
fn drop(&mut self) {
|
||||
self.bufio.add_buf(mem::take(&mut self.buf));
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Message<'static>> Reply<T> {
|
||||
pub fn get<'a>(&'a self) -> &'a T::Generic<'a> {
|
||||
unsafe { mem::transmute(&self.t) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Message<'static>> Drop for Reply<T> {
|
||||
fn drop(&mut self) {
|
||||
if self.buf.capacity() > 0 {
|
||||
self.bufio.add_buf(mem::take(&mut self.buf));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe trait ReplyHandler {
|
||||
fn has_fds(&self) -> bool;
|
||||
fn serial(&self) -> u64;
|
||||
fn handle_result(
|
||||
self: Box<Self>,
|
||||
bufio: &Rc<BufIo>,
|
||||
parser: &mut Parser<'static>,
|
||||
buf: Vec<u8>,
|
||||
) -> Result<(), XconError>;
|
||||
fn handle_noreply(self: Box<Self>, bufio: &Rc<BufIo>) -> Result<(), XconError>;
|
||||
fn handle_error(self: Box<Self>, error: XconError);
|
||||
}
|
||||
|
||||
struct AsyncReplyHandler<T: Message<'static>> {
|
||||
serial: u64,
|
||||
slot: Weak<AsyncReplySlot<T>>,
|
||||
}
|
||||
|
||||
impl<T: Message<'static>> AsyncReplyHandler<T> {
|
||||
fn done(self: Box<Self>, res: Result<Reply<T>, XconError>) {
|
||||
if let Some(slot) = self.slot.upgrade() {
|
||||
slot.data.set(Some(res));
|
||||
if let Some(waker) = slot.waker.take() {
|
||||
waker.wake();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: Message<'static>> ReplyHandler for AsyncReplyHandler<T> {
|
||||
fn has_fds(&self) -> bool {
|
||||
T::HAS_FDS
|
||||
}
|
||||
|
||||
fn serial(&self) -> u64 {
|
||||
self.serial
|
||||
}
|
||||
|
||||
fn handle_result(
|
||||
self: Box<Self>,
|
||||
bufio: &Rc<BufIo>,
|
||||
parser: &mut Parser<'static>,
|
||||
buf: Vec<u8>,
|
||||
) -> Result<(), XconError> {
|
||||
let msg = <T::Generic<'static> as Message<'static>>::deserialize(parser);
|
||||
let msg = match msg {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
let e = Rc::new(e);
|
||||
self.done(Err(XconError::XconError(e.clone())));
|
||||
return Err(XconError::XconError(e));
|
||||
}
|
||||
};
|
||||
let reply = Reply {
|
||||
bufio: bufio.clone(),
|
||||
buf,
|
||||
t: msg,
|
||||
};
|
||||
self.done(Ok(reply));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_noreply(self: Box<Self>, bufio: &Rc<BufIo>) -> Result<(), XconError> {
|
||||
if TypeId::of::<T::Generic<'static>>() == TypeId::of::<()>() {
|
||||
let reply = Reply {
|
||||
bufio: bufio.clone(),
|
||||
buf: vec![],
|
||||
t: unsafe { ptr::read(&() as *const () as *const T::Generic<'static>) },
|
||||
};
|
||||
self.done(Ok(reply));
|
||||
Ok(())
|
||||
} else {
|
||||
self.done(Err(XconError::MissingReply));
|
||||
Err(XconError::MissingReply)
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_error(self: Box<Self>, error: XconError) {
|
||||
self.done(Err(error))
|
||||
}
|
||||
}
|
||||
|
||||
struct AsyncReplySlot<T: Message<'static>> {
|
||||
data: Cell<Option<Result<Reply<T>, XconError>>>,
|
||||
waker: Cell<Option<Waker>>,
|
||||
}
|
||||
|
||||
pub struct AsyncReply<T: Message<'static>> {
|
||||
slot: Rc<AsyncReplySlot<T>>,
|
||||
xorg: Rc<XconData>,
|
||||
}
|
||||
|
||||
impl<T: Message<'static>> Future for AsyncReply<T> {
|
||||
type Output = Result<Reply<T>, XconError>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
if let Some(d) = self.slot.data.take() {
|
||||
Poll::Ready(d)
|
||||
} else {
|
||||
self.slot.waker.set(Some(cx.waker().clone()));
|
||||
self.xorg.send_sync();
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Xcon {
|
||||
pub fn setup(&self) -> &Setup {
|
||||
self.setup.get()
|
||||
}
|
||||
|
||||
pub async fn event(&self) -> Event {
|
||||
self.data.events.pop().await
|
||||
}
|
||||
|
||||
pub fn generate_id(&self) -> Result<u32, XconError> {
|
||||
if self.xid_next.get() == self.xid_max {
|
||||
return Err(XconError::XidExhausted);
|
||||
}
|
||||
let id = self.xid_next.get();
|
||||
self.xid_next.set(id + self.xid_inc);
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
pub async fn connect(eng: Rc<AsyncEngine>) -> Result<Rc<Self>, XconError> {
|
||||
let authority = match XAuthority::load() {
|
||||
Ok(a) => a,
|
||||
Err(e) => {
|
||||
log::warn!(
|
||||
"Could not parse Xauthority file. Proceeding without authorization: {}",
|
||||
ErrorFmt(e)
|
||||
);
|
||||
vec![]
|
||||
}
|
||||
};
|
||||
let display = parse_display()?;
|
||||
let mut addr = c::sockaddr_un {
|
||||
sun_family: c::AF_UNIX as _,
|
||||
..uapi::pod_zeroed()
|
||||
};
|
||||
{
|
||||
let mut path = uapi::as_bytes_mut(&mut addr.sun_path[..]);
|
||||
let _ = write!(path, "/tmp/.X11-unix/X{}", display);
|
||||
}
|
||||
let fd = match uapi::socket(
|
||||
c::AF_UNIX,
|
||||
c::SOCK_STREAM | c::SOCK_CLOEXEC | c::SOCK_NONBLOCK,
|
||||
0,
|
||||
) {
|
||||
Ok(fd) => Rc::new(fd),
|
||||
Err(e) => return Err(XconError::CreateSocket(e.into())),
|
||||
};
|
||||
if let Err(e) = uapi::connect(fd.raw(), &addr) {
|
||||
return Err(XconError::ConnectSocket(e.into()));
|
||||
}
|
||||
let mut hnbuf = [MaybeUninit::<u8>::uninit(); 256];
|
||||
let hn = match uapi::gethostname(&mut hnbuf[..]) {
|
||||
Ok(hn) => hn.to_bytes(),
|
||||
Err(e) => return Err(XconError::Hostname(e.into())),
|
||||
};
|
||||
let (auth_method, auth_value) = 'auth: {
|
||||
for auth in &authority {
|
||||
if auth.display == display
|
||||
&& auth.family == LOCAL
|
||||
&& auth.host == hn
|
||||
&& auth.method == MIT_MAGIC_COOKIE
|
||||
{
|
||||
break 'auth (&auth.method[..], &auth.value[..]);
|
||||
}
|
||||
}
|
||||
(&[], &[])
|
||||
};
|
||||
Self::connect_to_fd(&eng, &fd, auth_method, auth_value).await
|
||||
}
|
||||
|
||||
pub async fn connect_to_fd(
|
||||
eng: &Rc<AsyncEngine>,
|
||||
fd: &Rc<OwnedFd>,
|
||||
auth_method: &[u8],
|
||||
auth_value: &[u8],
|
||||
) -> Result<Rc<Self>, XconError> {
|
||||
let fd = eng.fd(fd)?;
|
||||
let data = Rc::new(XconData {
|
||||
bufio: Rc::new(BufIo::new(fd)),
|
||||
next_serial: NumCell::new(1),
|
||||
last_recv_serial: Cell::new(0),
|
||||
reply_handlers: Default::default(),
|
||||
dead: Cell::new(false),
|
||||
need_sync: Cell::new(false),
|
||||
extensions: Default::default(),
|
||||
xorg: CloneCell::new(Weak::new()),
|
||||
events: Default::default(),
|
||||
});
|
||||
let outgoing = eng.spawn2(Phase::PostLayout, handle_outgoing(data.clone()));
|
||||
let mut buf = data.bufio.buf();
|
||||
let mut fds = vec![];
|
||||
{
|
||||
let mut formatter = Formatter::new(&mut fds, &mut buf, 0);
|
||||
#[cfg(target_endian = "little")]
|
||||
const ENDIAN: u8 = b'l';
|
||||
#[cfg(target_endian = "big")]
|
||||
const ENDIAN: u8 = b'B';
|
||||
formatter.write_packed(&ENDIAN);
|
||||
formatter.pad(1);
|
||||
formatter.write_packed(&11u16);
|
||||
formatter.write_packed(&0u16);
|
||||
formatter.write_packed(&(auth_method.len() as u16));
|
||||
formatter.write_packed(&(auth_value.len() as u16));
|
||||
formatter.pad(2);
|
||||
formatter.write_packed(auth_method.as_bytes());
|
||||
formatter.align(4);
|
||||
formatter.write_packed(auth_value.as_bytes());
|
||||
formatter.align(4);
|
||||
}
|
||||
data.bufio.send(BufIoMessage { fds, buf });
|
||||
let mut incoming = data.bufio.incoming();
|
||||
let mut buf = data.bufio.buf();
|
||||
incoming.fill_msg_buf(8, &mut buf).await?;
|
||||
let len = u16::from_ne_bytes([buf[6], buf[7]]) as usize * 4;
|
||||
incoming.fill_msg_buf(len, &mut buf).await?;
|
||||
let mut parser = Parser::new(&buf, vec![]);
|
||||
let res: u8 = buf[0];
|
||||
if res == 0 {
|
||||
parser.pad(1)?;
|
||||
let reason_len: u8 = parser.read_pod()?;
|
||||
parser.pad(6)?;
|
||||
let reason = parser.read_string(reason_len as usize)?;
|
||||
return Err(XconError::Connect(reason.to_owned()));
|
||||
}
|
||||
if res == 2 {
|
||||
parser.pad(6)?;
|
||||
let reason_len: u16 = parser.read_pod()?;
|
||||
let reason = parser.read_string(reason_len as usize * 4)?;
|
||||
return Err(XconError::Authenticate(reason.to_owned()));
|
||||
}
|
||||
let setup = Setup::deserialize(&mut parser)?;
|
||||
let incoming = eng.spawn(handle_incoming(data.clone(), incoming));
|
||||
let slf = Rc::new(Self {
|
||||
extensions: data.fetch_extension_data().await?,
|
||||
outgoing: Cell::new(Some(outgoing)),
|
||||
incoming: Cell::new(Some(incoming)),
|
||||
xid_next: Cell::new(setup.resource_id_base),
|
||||
xid_inc: 1 << setup.resource_id_mask.trailing_zeros(),
|
||||
xid_max: setup.resource_id_mask | setup.resource_id_base,
|
||||
setup: Reply {
|
||||
bufio: data.bufio.clone(),
|
||||
t: unsafe { mem::transmute(setup) },
|
||||
buf,
|
||||
},
|
||||
data,
|
||||
});
|
||||
slf.data.xorg.set(Rc::downgrade(&slf));
|
||||
Ok(slf)
|
||||
}
|
||||
|
||||
pub fn call<'a, T: Request<'a>>(self: &Rc<Self>, t: &T) -> AsyncReply<T::Reply> {
|
||||
self.data.call(t, &self.extensions)
|
||||
}
|
||||
}
|
||||
|
||||
impl XconData {
|
||||
fn kill(&self) {
|
||||
self.bufio.shutdown();
|
||||
self.dead.set(true);
|
||||
let handlers = mem::take(self.reply_handlers.borrow_mut().deref_mut());
|
||||
for handler in handlers {
|
||||
handler.handle_error(XconError::Dead);
|
||||
}
|
||||
if let Some(xorg) = self.xorg.get().upgrade() {
|
||||
xorg.outgoing.take();
|
||||
xorg.incoming.take();
|
||||
}
|
||||
}
|
||||
|
||||
fn call<'a, T: Request<'a>>(
|
||||
self: &Rc<Self>,
|
||||
t: &T,
|
||||
extensions: &ExtensionData,
|
||||
) -> AsyncReply<T::Reply> {
|
||||
if self.dead.get() {
|
||||
return AsyncReply {
|
||||
slot: Rc::new(AsyncReplySlot {
|
||||
data: Cell::new(Some(Err(XconError::Dead))),
|
||||
waker: Cell::new(None),
|
||||
}),
|
||||
xorg: self.clone(),
|
||||
};
|
||||
}
|
||||
let opcode = match T::EXTENSION {
|
||||
None => 0,
|
||||
Some(idx) => match extensions.opcodes[idx] {
|
||||
Some(o) => o,
|
||||
_ => {
|
||||
return AsyncReply {
|
||||
slot: Rc::new(AsyncReplySlot {
|
||||
data: Cell::new(Some(Err(XconError::ExtensionUnavailable(
|
||||
EXTENSIONS[idx].name(),
|
||||
)))),
|
||||
waker: Cell::new(None),
|
||||
}),
|
||||
xorg: self.clone(),
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
let mut fds = vec![];
|
||||
let mut buf = self.bufio.buf();
|
||||
let mut formatter = Formatter::new(&mut fds, &mut buf, opcode);
|
||||
t.serialize(&mut formatter);
|
||||
formatter.write_request_length();
|
||||
self.bufio.send(BufIoMessage { fds, buf });
|
||||
let slot = Rc::new(AsyncReplySlot {
|
||||
data: Cell::new(None),
|
||||
waker: Cell::new(None),
|
||||
});
|
||||
let handler = Box::new(AsyncReplyHandler {
|
||||
serial: self.next_serial.fetch_add(1),
|
||||
slot: Rc::downgrade(&slot),
|
||||
});
|
||||
self.reply_handlers.borrow_mut().push_back(handler);
|
||||
self.need_sync.set(T::IS_VOID);
|
||||
AsyncReply {
|
||||
slot,
|
||||
xorg: self.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn send_sync(&self) {
|
||||
if !self.need_sync.replace(false) {
|
||||
return;
|
||||
}
|
||||
let mut fds = vec![];
|
||||
let mut buf = self.bufio.buf();
|
||||
let mut formatter = Formatter::new(&mut fds, &mut buf, 0);
|
||||
GetInputFocus {}.serialize(&mut formatter);
|
||||
formatter.write_request_length();
|
||||
self.bufio.send(BufIoMessage { fds, buf });
|
||||
self.next_serial.fetch_add(1);
|
||||
}
|
||||
|
||||
async fn fetch_extension_data(self: &Rc<Self>) -> Result<Rc<ExtensionData>, XconError> {
|
||||
let mut ext_by_name = AHashMap::new();
|
||||
for e in EXTENSIONS.iter().copied() {
|
||||
ext_by_name.insert(e.name().as_bytes().as_bstr(), e);
|
||||
}
|
||||
let mut ed = ExtensionData::default();
|
||||
let extensions = self.call(&ListExtensions {}, &ed).await?;
|
||||
let mut pending = vec![];
|
||||
for name in extensions.get().names.iter() {
|
||||
pending.push((name.val, self.call(&QueryExtension { name: name.val }, &ed)));
|
||||
}
|
||||
for (name, data) in pending {
|
||||
let data = match data.await {
|
||||
Ok(d) => d,
|
||||
Err(e) => return Err(XconError::QueryExtension(name.to_owned(), Box::new(e))),
|
||||
};
|
||||
let data = data.get();
|
||||
if data.present != 0 {
|
||||
let e = ext_by_name.get(name).copied();
|
||||
if data.first_event > 0 {
|
||||
ed.events.push(ExtensionIdRange {
|
||||
name: name.to_owned(),
|
||||
extension: e,
|
||||
first: data.first_event,
|
||||
});
|
||||
}
|
||||
if data.first_error > 0 {
|
||||
ed.errors.push(ExtensionIdRange {
|
||||
name: name.to_owned(),
|
||||
extension: e,
|
||||
first: data.first_error,
|
||||
});
|
||||
}
|
||||
if let Some(e) = e {
|
||||
ed.opcodes[e as usize] = Some(data.major_opcode);
|
||||
ed.ext_by_opcode.insert(data.major_opcode, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
ed.events.sort_by_key(|e| e.first);
|
||||
ed.errors.sort_by_key(|e| e.first);
|
||||
let ed = Rc::new(ed);
|
||||
self.extensions.set(Some(ed.clone()));
|
||||
Ok(ed)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_display() -> Result<u32, XconError> {
|
||||
let display = match std::env::var("DISPLAY") {
|
||||
Ok(d) => d,
|
||||
_ => return Err(XconError::DisplayNotSet),
|
||||
};
|
||||
let num = match display.strip_prefix(":") {
|
||||
Some(p) => p,
|
||||
_ => return Err(XconError::InvalidDisplayFormat),
|
||||
};
|
||||
let num = match num.parse() {
|
||||
Ok(v) => v,
|
||||
_ => return Err(XconError::InvalidDisplayFormat),
|
||||
};
|
||||
Ok(num)
|
||||
}
|
||||
103
src/xcon/consts.rs
Normal file
103
src/xcon/consts.rs
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
pub const XGE_EVENT: u8 = 35;
|
||||
|
||||
pub const INPUT_DEVICE_ALL: u16 = 0;
|
||||
pub const INPUT_DEVICE_ALL_MASTER: u16 = 1;
|
||||
|
||||
pub const WINDOW_CLASS_INPUT_OUTPUT: u16 = 1;
|
||||
|
||||
pub const PROP_MODE_REPLACE: u8 = 0;
|
||||
|
||||
pub const ATOM_WM_CLASS: u32 = 67;
|
||||
pub const ATOM_STRING: u32 = 31;
|
||||
|
||||
pub const EVENT_MASK_NO_EVENT: u32 = 0;
|
||||
pub const EVENT_MASK_KEY_PRESS: u32 = 1;
|
||||
pub const EVENT_MASK_KEY_RELEASE: u32 = 2;
|
||||
pub const EVENT_MASK_BUTTON_PRESS: u32 = 4;
|
||||
pub const EVENT_MASK_BUTTON_RELEASE: u32 = 8;
|
||||
pub const EVENT_MASK_ENTER_WINDOW: u32 = 16;
|
||||
pub const EVENT_MASK_LEAVE_WINDOW: u32 = 32;
|
||||
pub const EVENT_MASK_POINTER_MOTION: u32 = 64;
|
||||
pub const EVENT_MASK_POINTER_MOTION_HINT: u32 = 128;
|
||||
pub const EVENT_MASK_BUTTON_1_MOTION: u32 = 256;
|
||||
pub const EVENT_MASK_BUTTON_2_MOTION: u32 = 512;
|
||||
pub const EVENT_MASK_BUTTON_3_MOTION: u32 = 1024;
|
||||
pub const EVENT_MASK_BUTTON_4_MOTION: u32 = 2048;
|
||||
pub const EVENT_MASK_BUTTON_5_MOTION: u32 = 4096;
|
||||
pub const EVENT_MASK_BUTTON_MOTION: u32 = 8192;
|
||||
pub const EVENT_MASK_KEYMAP_STATE: u32 = 16384;
|
||||
pub const EVENT_MASK_EXPOSURE: u32 = 32768;
|
||||
pub const EVENT_MASK_VISIBILITY_CHANGE: u32 = 65536;
|
||||
pub const EVENT_MASK_STRUCTURE_NOTIFY: u32 = 131072;
|
||||
pub const EVENT_MASK_RESIZE_REDIRECT: u32 = 262144;
|
||||
pub const EVENT_MASK_SUBSTRUCTURE_NOTIFY: u32 = 524288;
|
||||
pub const EVENT_MASK_SUBSTRUCTURE_REDIRECT: u32 = 1048576;
|
||||
pub const EVENT_MASK_FOCUS_CHANGE: u32 = 2097152;
|
||||
pub const EVENT_MASK_PROPERTY_CHANGE: u32 = 4194304;
|
||||
pub const EVENT_MASK_COLOR_MAP_CHANGE: u32 = 8388608;
|
||||
pub const EVENT_MASK_OWNER_GRAB_BUTTON: u32 = 16777216;
|
||||
|
||||
pub const XI_EVENT_MASK_DEVICE_CHANGED: u32 = 2;
|
||||
pub const XI_EVENT_MASK_KEY_PRESS: u32 = 4;
|
||||
pub const XI_EVENT_MASK_KEY_RELEASE: u32 = 8;
|
||||
pub const XI_EVENT_MASK_BUTTON_PRESS: u32 = 16;
|
||||
pub const XI_EVENT_MASK_BUTTON_RELEASE: u32 = 32;
|
||||
pub const XI_EVENT_MASK_MOTION: u32 = 64;
|
||||
pub const XI_EVENT_MASK_ENTER: u32 = 128;
|
||||
pub const XI_EVENT_MASK_LEAVE: u32 = 256;
|
||||
pub const XI_EVENT_MASK_FOCUS_IN: u32 = 512;
|
||||
pub const XI_EVENT_MASK_FOCUS_OUT: u32 = 1024;
|
||||
pub const XI_EVENT_MASK_HIERARCHY: u32 = 2048;
|
||||
pub const XI_EVENT_MASK_PROPERTY: u32 = 4096;
|
||||
pub const XI_EVENT_MASK_RAW_KEY_PRESS: u32 = 8192;
|
||||
pub const XI_EVENT_MASK_RAW_KEY_RELEASE: u32 = 16384;
|
||||
pub const XI_EVENT_MASK_RAW_BUTTON_PRESS: u32 = 32768;
|
||||
pub const XI_EVENT_MASK_RAW_BUTTON_RELEASE: u32 = 65536;
|
||||
pub const XI_EVENT_MASK_RAW_MOTION: u32 = 131072;
|
||||
pub const XI_EVENT_MASK_TOUCH_BEGIN: u32 = 262144;
|
||||
pub const XI_EVENT_MASK_TOUCH_UPDATE: u32 = 524288;
|
||||
pub const XI_EVENT_MASK_TOUCH_END: u32 = 1048576;
|
||||
pub const XI_EVENT_MASK_TOUCH_OWNERSHIP: u32 = 2097152;
|
||||
pub const XI_EVENT_MASK_RAW_TOUCH_BEGIN: u32 = 4194304;
|
||||
pub const XI_EVENT_MASK_RAW_TOUCH_UPDATE: u32 = 8388608;
|
||||
pub const XI_EVENT_MASK_RAW_TOUCH_END: u32 = 16777216;
|
||||
pub const XI_EVENT_MASK_BARRIER_HIT: u32 = 33554432;
|
||||
pub const XI_EVENT_MASK_BARRIER_LEAVE: u32 = 67108864;
|
||||
|
||||
pub const PRESENT_EVENT_MASK_NO_EVENT: u32 = 0;
|
||||
pub const PRESENT_EVENT_MASK_CONFIGURE_NOTIFY: u32 = 1;
|
||||
pub const PRESENT_EVENT_MASK_COMPLETE_NOTIFY: u32 = 2;
|
||||
pub const PRESENT_EVENT_MASK_IDLE_NOTIFY: u32 = 4;
|
||||
pub const PRESENT_EVENT_MASK_REDIRECT_NOTIFY: u32 = 8;
|
||||
|
||||
pub const INPUT_DEVICE_TYPE_MASTER_POINTER: u16 = 1;
|
||||
pub const INPUT_DEVICE_TYPE_MASTER_KEYBOARD: u16 = 2;
|
||||
pub const INPUT_DEVICE_TYPE_SLAVE_POINTER: u16 = 3;
|
||||
pub const INPUT_DEVICE_TYPE_SLAVE_KEYBOARD: u16 = 4;
|
||||
pub const INPUT_DEVICE_TYPE_FLOATING_SLAVE: u16 = 5;
|
||||
|
||||
pub const XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT: u32 = 1;
|
||||
pub const XKB_PER_CLIENT_FLAG_GRABS_USE_XKB_STATE: u32 = 2;
|
||||
pub const XKB_PER_CLIENT_FLAG_AUTO_RESET_CONTROLS: u32 = 4;
|
||||
pub const XKB_PER_CLIENT_FLAG_LOOKUP_STATE_WHEN_GRABBED: u32 = 8;
|
||||
pub const XKB_PER_CLIENT_FLAG_SEND_EVENT_USES_XKB_STATE: u32 = 16;
|
||||
|
||||
pub const INPUT_HIERARCHY_MASK_MASTER_ADDED: u32 = 1;
|
||||
pub const INPUT_HIERARCHY_MASK_MASTER_REMOVED: u32 = 2;
|
||||
pub const INPUT_HIERARCHY_MASK_SLAVE_ADDED: u32 = 4;
|
||||
pub const INPUT_HIERARCHY_MASK_SLAVE_REMOVED: u32 = 8;
|
||||
pub const INPUT_HIERARCHY_MASK_SLAVE_ATTACHED: u32 = 16;
|
||||
pub const INPUT_HIERARCHY_MASK_SLAVE_DETACHED: u32 = 32;
|
||||
pub const INPUT_HIERARCHY_MASK_DEVICE_ENABLED: u32 = 64;
|
||||
pub const INPUT_HIERARCHY_MASK_DEVICE_DISABLED: u32 = 128;
|
||||
|
||||
pub const GRAB_MODE_SYNC: u8 = 0;
|
||||
pub const GRAB_MODE_ASYNC: u8 = 1;
|
||||
|
||||
pub const GRAB_STATUS_SUCCESS: u8 = 0;
|
||||
pub const GRAB_STATUS_ALREADY_GRABBED: u8 = 1;
|
||||
pub const GRAB_STATUS_INVALID_TIME: u8 = 2;
|
||||
pub const GRAB_STATUS_NOT_VIEWABLE: u8 = 3;
|
||||
pub const GRAB_STATUS_FROZEN: u8 = 4;
|
||||
62
src/xcon/formatter.rs
Normal file
62
src/xcon/formatter.rs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
use crate::xcon::Message;
|
||||
use std::rc::Rc;
|
||||
use uapi::{AssertPacked, OwnedFd, Packed};
|
||||
|
||||
pub struct Formatter<'a> {
|
||||
fds: &'a mut Vec<Rc<OwnedFd>>,
|
||||
buf: &'a mut Vec<u8>,
|
||||
ext_opcode: u8,
|
||||
}
|
||||
|
||||
impl<'a> Formatter<'a> {
|
||||
pub fn new(fds: &'a mut Vec<Rc<OwnedFd>>, buf: &'a mut Vec<u8>, ext_opcode: u8) -> Self {
|
||||
Self {
|
||||
fds,
|
||||
buf,
|
||||
ext_opcode,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ext_opcode(&self) -> u8 {
|
||||
self.ext_opcode
|
||||
}
|
||||
|
||||
pub fn pad(&mut self, pad: usize) {
|
||||
static BUF: [u8; 8] = [0; 8];
|
||||
self.buf.extend_from_slice(&BUF[..pad]);
|
||||
}
|
||||
|
||||
pub fn align(&mut self, alignment: usize) {
|
||||
static BUF: [u8; 8] = [0; 8];
|
||||
let len = self.buf.len().wrapping_neg() & (alignment - 1);
|
||||
self.buf.extend_from_slice(&BUF[..len]);
|
||||
}
|
||||
|
||||
pub fn write_packed<T: Packed + ?Sized>(&mut self, t: &T) {
|
||||
self.buf.extend_from_slice(uapi::as_bytes(t));
|
||||
}
|
||||
|
||||
pub fn write_list<'b, T: Message<'b>>(&mut self, t: &[T]) {
|
||||
if T::IS_POD {
|
||||
self.buf
|
||||
.extend_from_slice(uapi::as_bytes(unsafe { AssertPacked::new(t) }));
|
||||
} else {
|
||||
for t in t {
|
||||
t.serialize(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_bytes(&mut self, b: &[u8]) {
|
||||
self.buf.extend_from_slice(b);
|
||||
}
|
||||
|
||||
pub fn write_request_length(&mut self) {
|
||||
let len: u16 = (self.buf.len() / 4) as u16;
|
||||
self.buf[2..4].copy_from_slice(&len.to_ne_bytes());
|
||||
}
|
||||
|
||||
pub fn add_fd(&mut self, fd: &Rc<OwnedFd>) {
|
||||
self.fds.push(fd.clone());
|
||||
}
|
||||
}
|
||||
222
src/xcon/incoming.rs
Normal file
222
src/xcon/incoming.rs
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
use crate::utils::bufio::BufIoIncoming;
|
||||
use crate::xcon::consts::XGE_EVENT;
|
||||
use crate::xcon::{Event, ExtensionData, ExtensionIdRange, Parser, XconData, XconError};
|
||||
use crate::ErrorFmt;
|
||||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub(super) async fn handle_incoming(xorg: Rc<XconData>, incoming: BufIoIncoming) {
|
||||
let mut incoming = Incoming {
|
||||
incoming,
|
||||
socket: xorg,
|
||||
ed: None,
|
||||
};
|
||||
incoming.run().await;
|
||||
}
|
||||
|
||||
pub struct Incoming {
|
||||
socket: Rc<XconData>,
|
||||
incoming: BufIoIncoming,
|
||||
ed: Option<Rc<ExtensionData>>,
|
||||
}
|
||||
|
||||
impl Incoming {
|
||||
async fn run(&mut self) {
|
||||
loop {
|
||||
if self.socket.dead.get() {
|
||||
return;
|
||||
}
|
||||
if let Err(e) = self.handle_msg().await {
|
||||
log::error!("Could not process an incoming message: {}", ErrorFmt(e));
|
||||
self.socket.kill();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_msg(&mut self) -> Result<(), XconError> {
|
||||
const MAX_LENGTH_UNITS: usize = 0x4000 / 4;
|
||||
const MIN_MSG_SIZE: usize = 32;
|
||||
|
||||
let mut msg_buf = self.socket.bufio.buf();
|
||||
self.incoming
|
||||
.fill_msg_buf(MIN_MSG_SIZE, &mut msg_buf)
|
||||
.await?;
|
||||
let mut serial = 0;
|
||||
const KEYMAP_NOTIFY: u8 = 11;
|
||||
let mut reply_handlers = self.socket.reply_handlers.borrow_mut();
|
||||
if msg_buf[0] & 0x7f != KEYMAP_NOTIFY {
|
||||
let serial_16 = u16::from_ne_bytes([msg_buf[2], msg_buf[3]]);
|
||||
serial = (self.socket.last_recv_serial.get() & !0xffff) | (serial_16 as u64);
|
||||
if serial < self.socket.last_recv_serial.get() {
|
||||
serial += 0x10000;
|
||||
}
|
||||
self.socket.last_recv_serial.set(serial);
|
||||
while let Some(first) = reply_handlers.front() {
|
||||
if first.serial() < serial {
|
||||
let handler = reply_handlers.pop_front().unwrap();
|
||||
drop(reply_handlers);
|
||||
handler.handle_noreply(&self.socket.bufio)?;
|
||||
reply_handlers = self.socket.reply_handlers.borrow_mut();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if self.ed.is_none() {
|
||||
self.ed = self.socket.extensions.get();
|
||||
}
|
||||
match msg_buf[0] & 0x7f {
|
||||
0 => 'handle_error: {
|
||||
let code = msg_buf[1];
|
||||
let (ext, code) = if code < 128 {
|
||||
(None, code)
|
||||
} else if let Some(ed) = &self.ed {
|
||||
let r = match find_range(&ed.errors, code) {
|
||||
Some(r) => r,
|
||||
_ => {
|
||||
log::error!("Received an out of bounds error code {}", code);
|
||||
break 'handle_error;
|
||||
}
|
||||
};
|
||||
match r.extension {
|
||||
Some(e) => (Some(e), code - r.first),
|
||||
None => {
|
||||
log::warn!(
|
||||
"Received an error from an unconfigured extension: `{}`",
|
||||
r.name
|
||||
);
|
||||
break 'handle_error;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log::error!("Received an extension error before extension have been fetched");
|
||||
break 'handle_error;
|
||||
};
|
||||
let e = match ext {
|
||||
Some(e) => XconError::ExtensionError(e, code),
|
||||
_ => XconError::CoreError(code),
|
||||
};
|
||||
if let Some(first) = reply_handlers.front() {
|
||||
if first.serial() == serial {
|
||||
let handler = reply_handlers.pop_front().unwrap();
|
||||
drop(reply_handlers);
|
||||
handler.handle_error(e);
|
||||
break 'handle_error;
|
||||
}
|
||||
}
|
||||
log::error!(
|
||||
"Received an error with no corresponding handler: {}",
|
||||
ErrorFmt(e)
|
||||
);
|
||||
}
|
||||
1 => {
|
||||
if let Some(first) = reply_handlers.front() {
|
||||
if first.serial() == serial {
|
||||
let handler = reply_handlers.pop_front().unwrap();
|
||||
drop(reply_handlers);
|
||||
let mut fds = vec![];
|
||||
if handler.has_fds() {
|
||||
let num_fds = msg_buf[1] as usize;
|
||||
if self.incoming.fds.len() < num_fds {
|
||||
return Err(XconError::MissingFds);
|
||||
}
|
||||
fds.extend(self.incoming.fds.drain(..num_fds));
|
||||
}
|
||||
let length =
|
||||
u32::from_ne_bytes([msg_buf[4], msg_buf[5], msg_buf[6], msg_buf[7]])
|
||||
as usize;
|
||||
if length > MAX_LENGTH_UNITS {
|
||||
return Err(XconError::ExcessiveMessageSize);
|
||||
}
|
||||
let length = length * 4;
|
||||
self.incoming.fill_msg_buf(length, &mut msg_buf).await?;
|
||||
let mut parser = unsafe {
|
||||
let msg_buf = mem::transmute::<&[u8], &'static [u8]>(&msg_buf[..]);
|
||||
Parser::new(msg_buf, fds)
|
||||
};
|
||||
handler.handle_result(
|
||||
&self.socket.bufio,
|
||||
&mut parser,
|
||||
mem::take(&mut msg_buf),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
ev => 'handle_event: {
|
||||
let (ext, code) = if ev == XGE_EVENT {
|
||||
let length =
|
||||
u32::from_ne_bytes([msg_buf[4], msg_buf[5], msg_buf[6], msg_buf[7]])
|
||||
as usize;
|
||||
if length > MAX_LENGTH_UNITS {
|
||||
return Err(XconError::ExcessiveMessageSize);
|
||||
}
|
||||
let length = length * 4;
|
||||
self.incoming.fill_msg_buf(length, &mut msg_buf).await?;
|
||||
let opcode = msg_buf[1];
|
||||
let ext = match &self.ed {
|
||||
Some(ed) => ed.ext_by_opcode.get(&opcode),
|
||||
_ => {
|
||||
log::error!("Received an XGE event before extension have been fetched");
|
||||
break 'handle_event;
|
||||
}
|
||||
};
|
||||
let ext = match ext {
|
||||
Some(ext) => *ext,
|
||||
_ => {
|
||||
log::warn!(
|
||||
"Received an event from an unconfigured extension: `{}`",
|
||||
opcode
|
||||
);
|
||||
break 'handle_event;
|
||||
}
|
||||
};
|
||||
let code = u16::from_ne_bytes([msg_buf[8], msg_buf[9]]);
|
||||
(Some(ext), code)
|
||||
} else if ev < 64 {
|
||||
(None, ev as u16)
|
||||
} else if let Some(ed) = &self.ed {
|
||||
let r = match find_range(&ed.events, ev) {
|
||||
Some(r) => r,
|
||||
_ => {
|
||||
log::error!("Received an out of bounds event {}", ev);
|
||||
break 'handle_event;
|
||||
}
|
||||
};
|
||||
match r.extension {
|
||||
Some(e) => (Some(e), (ev - r.first) as u16),
|
||||
None => {
|
||||
log::warn!(
|
||||
"Received an event from an unconfigured extension: `{}`",
|
||||
r.name
|
||||
);
|
||||
break 'handle_event;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log::error!("Received an extension event before extension have been fetched");
|
||||
break 'handle_event;
|
||||
};
|
||||
self.socket.events.push(Event {
|
||||
bufio: self.socket.bufio.clone(),
|
||||
ext,
|
||||
code,
|
||||
buf: mem::take(&mut msg_buf),
|
||||
});
|
||||
}
|
||||
}
|
||||
if msg_buf.capacity() > 0 {
|
||||
self.socket.bufio.add_buf(msg_buf);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn find_range(codes: &[ExtensionIdRange], code: u8) -> Option<&ExtensionIdRange> {
|
||||
let idx = match codes.binary_search_by_key(&code, |v| v.first) {
|
||||
Ok(v) => v,
|
||||
Err(v) if v > 0 => v - 1,
|
||||
_ => return None,
|
||||
};
|
||||
Some(&codes[idx])
|
||||
}
|
||||
10
src/xcon/outgoing.rs
Normal file
10
src/xcon/outgoing.rs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
use crate::xcon::XconData;
|
||||
use crate::ErrorFmt;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub(super) async fn handle_outgoing(socket: Rc<XconData>) {
|
||||
if let Err(e) = socket.bufio.clone().outgoing().await {
|
||||
log::error!("{}", ErrorFmt(e));
|
||||
}
|
||||
socket.kill();
|
||||
}
|
||||
138
src/xcon/parser.rs
Normal file
138
src/xcon/parser.rs
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
use crate::utils::ptr_ext::PtrExt;
|
||||
use crate::xcon::wire_type::Message;
|
||||
use crate::xcon::XconError;
|
||||
use bstr::{BStr, ByteSlice};
|
||||
use std::borrow::Cow;
|
||||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
use uapi::{OwnedFd, Pod};
|
||||
|
||||
pub struct Parser<'a> {
|
||||
pos: usize,
|
||||
buf: &'a [u8],
|
||||
fds_pos: usize,
|
||||
fds: Vec<Rc<OwnedFd>>,
|
||||
}
|
||||
|
||||
impl<'a> Parser<'a> {
|
||||
pub fn new(buf: &'a [u8], fds: Vec<Rc<OwnedFd>>) -> Self {
|
||||
Self {
|
||||
buf,
|
||||
pos: 0,
|
||||
fds,
|
||||
fds_pos: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eof(&self) -> bool {
|
||||
self.pos == self.buf.len()
|
||||
}
|
||||
|
||||
fn rem(&self) -> usize {
|
||||
self.buf.len() - self.pos
|
||||
}
|
||||
|
||||
pub fn unmarshal<T: Message<'a>>(&mut self) -> Result<T, XconError> {
|
||||
T::deserialize(self)
|
||||
}
|
||||
|
||||
pub fn pad(&mut self, new: usize) -> Result<(), XconError> {
|
||||
if new > self.buf.len() - self.pos {
|
||||
return Err(XconError::UnexpectedEof);
|
||||
}
|
||||
self.pos += new;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn align(&mut self, n: usize) -> Result<(), XconError> {
|
||||
let new = self.pos + (self.pos.wrapping_neg() & (n - 1));
|
||||
if new > self.buf.len() {
|
||||
return Err(XconError::UnexpectedEof);
|
||||
}
|
||||
self.pos = new;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read_fd(&mut self) -> Result<Rc<OwnedFd>, XconError> {
|
||||
if self.fds_pos >= self.fds.len() {
|
||||
return Err(XconError::NotEnoughFds);
|
||||
}
|
||||
self.fds_pos += 1;
|
||||
Ok(self.fds[self.fds_pos - 1].clone())
|
||||
}
|
||||
|
||||
pub fn read_pod<'b, T: Pod>(&mut self) -> Result<T, XconError> {
|
||||
match uapi::pod_read_init(&self.buf[self.pos..]) {
|
||||
Ok(v) => {
|
||||
self.pos += mem::size_of::<T>();
|
||||
Ok(v)
|
||||
}
|
||||
_ => Err(XconError::UnexpectedEof),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_list_slice<T: Message<'a> + Pod>(
|
||||
&mut self,
|
||||
n: Option<usize>,
|
||||
) -> Result<&'a [T], XconError> {
|
||||
let n = match n {
|
||||
Some(n) => n,
|
||||
_ => self.rem() / mem::size_of::<T>(),
|
||||
};
|
||||
let len = mem::size_of::<T>() * n;
|
||||
if len > self.rem() {
|
||||
return Err(XconError::UnexpectedEof);
|
||||
}
|
||||
if self.buf[self.pos..].as_ptr() as usize & (mem::align_of::<T>() - 1) != 0 {
|
||||
return Err(XconError::UnalignedSlice);
|
||||
}
|
||||
let res =
|
||||
unsafe { std::slice::from_raw_parts(self.buf.as_ptr().add(self.pos) as *const T, n) };
|
||||
self.pos += len;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub fn read_list<T: Message<'a> + Clone>(
|
||||
&mut self,
|
||||
n: Option<usize>,
|
||||
) -> Result<Cow<'a, [T]>, XconError> {
|
||||
let mut res = vec![];
|
||||
if let Some(n) = n {
|
||||
for _ in 0..n {
|
||||
res.push(T::deserialize(self)?);
|
||||
}
|
||||
} else {
|
||||
while !self.eof() {
|
||||
res.push(T::deserialize(self)?);
|
||||
}
|
||||
}
|
||||
Ok(res.into())
|
||||
}
|
||||
|
||||
pub fn read_bytes<const N: usize>(&mut self) -> Result<&'a [u8; N], XconError> {
|
||||
if N > self.rem() {
|
||||
return Err(XconError::UnexpectedEof);
|
||||
}
|
||||
let res = unsafe { self.buf.as_ptr().add(self.pos).cast::<[u8; N]>().deref() };
|
||||
self.pos += N;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub fn read_slice(&mut self, n: usize) -> Result<&'a [u8], XconError> {
|
||||
if n > self.rem() {
|
||||
return Err(XconError::UnexpectedEof);
|
||||
}
|
||||
let res = &self.buf[self.pos..self.pos + n];
|
||||
self.pos += n;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub fn read_string(&mut self, n: usize) -> Result<&'a BStr, XconError> {
|
||||
if n > self.rem() {
|
||||
return Err(XconError::UnexpectedEof);
|
||||
}
|
||||
let res = &self.buf[self.pos..self.pos + n];
|
||||
self.pos += n;
|
||||
Ok(res.as_bstr())
|
||||
}
|
||||
}
|
||||
118
src/xcon/wire_type.rs
Normal file
118
src/xcon/wire_type.rs
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
use crate::xcon::formatter::Formatter;
|
||||
use crate::xcon::parser::Parser;
|
||||
use crate::xcon::XconError;
|
||||
use bstr::{BStr, ByteSlice};
|
||||
use std::borrow::Cow;
|
||||
use std::rc::Rc;
|
||||
use uapi::OwnedFd;
|
||||
|
||||
#[cold]
|
||||
fn unimplemented() -> ! {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub unsafe trait Message<'a>: Clone + 'a {
|
||||
type Generic<'b>: Message<'b>;
|
||||
const IS_POD: bool;
|
||||
const HAS_FDS: bool;
|
||||
|
||||
fn serialize(&self, formatter: &mut Formatter) {
|
||||
let _ = formatter;
|
||||
unimplemented()
|
||||
}
|
||||
|
||||
fn deserialize(parser: &mut Parser<'a>) -> Result<Self, XconError> {
|
||||
let _ = parser;
|
||||
unimplemented()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Request<'a>: Message<'a> {
|
||||
type Reply: Message<'static>;
|
||||
const EXTENSION: Option<usize>;
|
||||
const IS_VOID: bool;
|
||||
}
|
||||
|
||||
pub trait XEvent<'a>: Message<'a> {
|
||||
const OPCODE: u16;
|
||||
}
|
||||
|
||||
macro_rules! simple {
|
||||
($ty:ty) => {
|
||||
unsafe impl Message<'_> for $ty {
|
||||
type Generic<'b> = $ty;
|
||||
const IS_POD: bool = true;
|
||||
const HAS_FDS: bool = false;
|
||||
|
||||
fn serialize(&self, formatter: &mut Formatter) {
|
||||
formatter.write_packed(self);
|
||||
}
|
||||
|
||||
fn deserialize(parser: &mut Parser<'_>) -> Result<Self, XconError> {
|
||||
parser.read_pod()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
simple!(u8);
|
||||
simple!(i8);
|
||||
simple!(u16);
|
||||
simple!(i16);
|
||||
simple!(u32);
|
||||
simple!(i32);
|
||||
simple!(u64);
|
||||
simple!(i64);
|
||||
|
||||
unsafe impl<'a> Message<'a> for () {
|
||||
type Generic<'b> = ();
|
||||
const IS_POD: bool = false;
|
||||
const HAS_FDS: bool = false;
|
||||
}
|
||||
|
||||
unsafe impl<'a> Message<'a> for &'a BStr {
|
||||
type Generic<'b> = &'b BStr;
|
||||
const IS_POD: bool = true;
|
||||
const HAS_FDS: bool = false;
|
||||
|
||||
fn serialize(&self, formatter: &mut Formatter) {
|
||||
formatter.write_packed(self.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a, T: Message<'a>> Message<'a> for &'a [T] {
|
||||
type Generic<'b> = &'b [T::Generic<'b>];
|
||||
const IS_POD: bool = false;
|
||||
const HAS_FDS: bool = false;
|
||||
|
||||
fn serialize(&self, formatter: &mut Formatter) {
|
||||
formatter.write_list(self);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a, T> Message<'a> for Cow<'a, [T]>
|
||||
where
|
||||
T: Message<'a>,
|
||||
{
|
||||
type Generic<'b> = Cow<'b, [T::Generic<'b>]>;
|
||||
const IS_POD: bool = false;
|
||||
const HAS_FDS: bool = false;
|
||||
|
||||
fn serialize(&self, formatter: &mut Formatter) {
|
||||
formatter.write_list(self);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> Message<'a> for Rc<OwnedFd> {
|
||||
type Generic<'b> = Rc<OwnedFd>;
|
||||
const IS_POD: bool = false;
|
||||
const HAS_FDS: bool = true;
|
||||
|
||||
fn serialize(&self, formatter: &mut Formatter) {
|
||||
formatter.add_fd(self);
|
||||
}
|
||||
|
||||
fn deserialize(parser: &mut Parser<'a>) -> Result<Self, XconError> {
|
||||
parser.read_fd()
|
||||
}
|
||||
}
|
||||
96
src/xcon/xauthority.rs
Normal file
96
src/xcon/xauthority.rs
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
use crate::xcon::XconError;
|
||||
use bstr::{BString, ByteSlice};
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
|
||||
pub const LOCAL: u16 = 256;
|
||||
|
||||
pub const MIT_MAGIC_COOKIE: &[u8] = b"MIT-MAGIC-COOKIE-1";
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct XAuthority {
|
||||
pub family: u16,
|
||||
pub host: BString,
|
||||
pub display: u32,
|
||||
pub method: BString,
|
||||
pub value: BString,
|
||||
}
|
||||
|
||||
impl XAuthority {
|
||||
pub fn load() -> Result<Vec<XAuthority>, XconError> {
|
||||
let path = 'path: {
|
||||
if let Ok(p) = std::env::var("XAUTHORITY") {
|
||||
break 'path p;
|
||||
}
|
||||
if let Ok(home) = std::env::var("HOME") {
|
||||
break 'path format!("{home}/.Xauthority");
|
||||
}
|
||||
return Err(XconError::HomeNotSet);
|
||||
};
|
||||
let mut buf = vec![];
|
||||
if let Err(e) = File::open(&path).and_then(|mut f| f.read_to_end(&mut buf)) {
|
||||
return Err(XconError::ReadXAuthority(e));
|
||||
}
|
||||
Parser::parse(&buf)
|
||||
}
|
||||
}
|
||||
|
||||
struct Parser<'a> {
|
||||
pos: usize,
|
||||
buf: &'a [u8],
|
||||
}
|
||||
|
||||
impl<'a> Parser<'a> {
|
||||
fn parse(buf: &[u8]) -> Result<Vec<XAuthority>, XconError> {
|
||||
let mut slf = Parser { pos: 0, buf };
|
||||
let mut res = vec![];
|
||||
while slf.rem() > 0 {
|
||||
res.push(slf.parse_one()?);
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn rem(&self) -> usize {
|
||||
self.buf.len() - self.pos
|
||||
}
|
||||
|
||||
fn parse_one(&mut self) -> Result<XAuthority, XconError> {
|
||||
Ok(XAuthority {
|
||||
family: self.read_u16()?,
|
||||
host: self.read_string()?,
|
||||
display: {
|
||||
let s = self.read_string()?;
|
||||
match s.to_str() {
|
||||
Ok(s) => match s.parse() {
|
||||
Ok(v) => v,
|
||||
_ => return Err(XconError::InvalidAuthorityDisplay),
|
||||
},
|
||||
_ => return Err(XconError::InvalidAuthorityDisplay),
|
||||
}
|
||||
},
|
||||
method: self.read_string()?,
|
||||
value: self.read_string()?,
|
||||
})
|
||||
}
|
||||
|
||||
fn read_u16(&mut self) -> Result<u16, XconError> {
|
||||
if self.rem() < 2 {
|
||||
return Err(XconError::UnexpectedEof);
|
||||
}
|
||||
let bytes = [self.buf[self.pos], self.buf[self.pos + 1]];
|
||||
self.pos += 2;
|
||||
Ok(u16::from_be_bytes(bytes))
|
||||
}
|
||||
|
||||
fn read_string(&mut self) -> Result<BString, XconError> {
|
||||
let len = self.read_u16()? as usize;
|
||||
if self.rem() < len {
|
||||
log::info!("rem = {}; len = {}", self.rem(), len);
|
||||
return Err(XconError::UnexpectedEof);
|
||||
}
|
||||
let res = self.buf[self.pos..self.pos + len].to_vec();
|
||||
self.pos += len;
|
||||
let res = res.into();
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
31
wire-xcon/dri3.txt
Normal file
31
wire-xcon/dri3.txt
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
ext "DRI3"
|
||||
|
||||
request Dri3QueryVersion = 0 (
|
||||
major_version: u32,
|
||||
minor_version: u32,
|
||||
) {
|
||||
@pad 1,
|
||||
major_version: u32,
|
||||
minor_version: u32,
|
||||
}
|
||||
|
||||
request Dri3Open = 1 (
|
||||
drawable: u32,
|
||||
provider: u32,
|
||||
) {
|
||||
@pad 1,
|
||||
device_fd: fd,
|
||||
@pad 24,
|
||||
}
|
||||
|
||||
request Dri3PixmapFromBuffer = 2 (
|
||||
pixmap: u32,
|
||||
drawable: u32,
|
||||
size: u32,
|
||||
width: u16,
|
||||
height: u16,
|
||||
stride: u16,
|
||||
depth: u8,
|
||||
bpp: u8,
|
||||
pixmap_fd: fd,
|
||||
);
|
||||
59
wire-xcon/present.txt
Normal file
59
wire-xcon/present.txt
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
ext "Present"
|
||||
|
||||
xge PresentCompleteNotify = 1 {
|
||||
kind: u8,
|
||||
mode: u8,
|
||||
event: u32,
|
||||
window: u32,
|
||||
serial: u32,
|
||||
ust: u64,
|
||||
msc: u64,
|
||||
}
|
||||
|
||||
struct PresentNotify {
|
||||
window: u32,
|
||||
serial: u32,
|
||||
}
|
||||
|
||||
xge PresentIdleNotify = 2 {
|
||||
@pad 2,
|
||||
event: u32,
|
||||
window: u32,
|
||||
serial: u32,
|
||||
pixmap: u32,
|
||||
idle_fence: u32,
|
||||
}
|
||||
|
||||
request PresentQueryVersion = 0 (
|
||||
major_version: u32,
|
||||
minor_version: u32,
|
||||
) {
|
||||
@pad 1,
|
||||
major_version: u32,
|
||||
minor_version: u32,
|
||||
}
|
||||
|
||||
request PresentPixmap = 1 (
|
||||
window: u32,
|
||||
pixmap: u32,
|
||||
serial: u32,
|
||||
valid: u32,
|
||||
update: u32,
|
||||
x_off: i16,
|
||||
y_off: i16,
|
||||
target_crtc: u32,
|
||||
wait_fence: u32,
|
||||
idle_fence: u32,
|
||||
options: u32,
|
||||
@pad 4,
|
||||
target_msc: u64,
|
||||
divisor: u64,
|
||||
remainder: u64,
|
||||
notifies: list(PresentNotify),
|
||||
);
|
||||
|
||||
request PresentSelectInput = 3 (
|
||||
eid: u32,
|
||||
window: u32,
|
||||
event_mask: u32,
|
||||
);
|
||||
214
wire-xcon/xinput.txt
Normal file
214
wire-xcon/xinput.txt
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
ext "XInputExtension"
|
||||
|
||||
struct XiModifierInfo {
|
||||
base: u32,
|
||||
latched: u32,
|
||||
locked: u32,
|
||||
effective: u32,
|
||||
}
|
||||
|
||||
struct XiGroupInfo {
|
||||
base: u8,
|
||||
latched: u8,
|
||||
locked: u8,
|
||||
effective: u8,
|
||||
}
|
||||
|
||||
xge XiKeyPress = 2 {
|
||||
deviceid: u16,
|
||||
time: u32,
|
||||
detail: u32,
|
||||
root: u32,
|
||||
event: u32,
|
||||
child: u32,
|
||||
root_x: i32,
|
||||
root_y: i32,
|
||||
event_x: i32,
|
||||
event_y: i32,
|
||||
buttons_len: u16 = len(button_mask),
|
||||
valuators_len: u16 = len(valuator_mask),
|
||||
sourceid: u16,
|
||||
@pad 2,
|
||||
flags: u32,
|
||||
mods: XiModifierInfo,
|
||||
groups: XiGroupInfo,
|
||||
button_mask: list(u32, field(buttons_len)),
|
||||
valuator_mask: list(u32, field(valuators_len)),
|
||||
axisvalues: list(u32, mul(sum(map(iter(field(valuator_mask)), popcount(it))), literal(2))),
|
||||
}
|
||||
|
||||
eventcopy XiKeyRelease = 3 = XiKeyPress;
|
||||
eventcopy XiButtonPress = 4 = XiKeyPress;
|
||||
eventcopy XiButtonRelease = 5 = XiKeyPress;
|
||||
eventcopy XiMotion = 6 = XiKeyPress;
|
||||
|
||||
xge XiEnter = 7 {
|
||||
deviceid: u16,
|
||||
time: u32,
|
||||
sourceid: u16,
|
||||
mode: u8,
|
||||
detail: u8,
|
||||
root: u32,
|
||||
event: u32,
|
||||
child: u32,
|
||||
root_x: i32,
|
||||
root_y: i32,
|
||||
event_x: i32,
|
||||
event_y: i32,
|
||||
same_screen: u8,
|
||||
focus: u8,
|
||||
buttons_len: u16 = len(buttons),
|
||||
buttons: list(u32, field(buttons_len)),
|
||||
}
|
||||
|
||||
struct XiHierarchyInfo {
|
||||
deviceid: u16,
|
||||
attachment: u16,
|
||||
ty: u8,
|
||||
enabled: u8,
|
||||
@pad 2,
|
||||
flags: u32,
|
||||
}
|
||||
|
||||
xge XiHierarchy = 11 {
|
||||
deviceid: u16,
|
||||
time: u32,
|
||||
flags: u32,
|
||||
num_infos: u16 = len(infos),
|
||||
@pad 10,
|
||||
infos: list(XiHierarchyInfo, field(num_infos)),
|
||||
}
|
||||
|
||||
request XiQueryVersion = 47 (
|
||||
major_version: u16,
|
||||
minor_version: u16,
|
||||
) {
|
||||
@pad 1,
|
||||
major_version: u16,
|
||||
minor_version: u16,
|
||||
@pad 20,
|
||||
}
|
||||
|
||||
struct XiEventMask {
|
||||
deviceid: u16,
|
||||
mask_len: u16 = len(mask),
|
||||
mask: list(u32, field(mask_len)),
|
||||
}
|
||||
|
||||
request XiSelectEvents = 46 (
|
||||
window: u32,
|
||||
num_mask: u16 = len(masks),
|
||||
@pad 2,
|
||||
masks: list(XiEventMask, field(num_mask)),
|
||||
);
|
||||
|
||||
struct XiDeviceClassKey {
|
||||
num_keys: u16 = len(keys),
|
||||
keys: list(u32, field(num_keys)),
|
||||
}
|
||||
|
||||
struct XiDeviceClassButton {
|
||||
num_buttons: u16 = len(labels),
|
||||
state: list(u32, div(plus(field(num_buttons), literal(31)), literal(32))),
|
||||
labels: list(u32, field(num_buttons)),
|
||||
}
|
||||
|
||||
struct XiDeviceClassValuator {
|
||||
number: u16,
|
||||
label: u32,
|
||||
min_int: i32,
|
||||
min_frac: u32,
|
||||
max_int: i32,
|
||||
max_frac: u32,
|
||||
value_int: i32,
|
||||
value_frac: u32,
|
||||
resolution: u32,
|
||||
mode: u8,
|
||||
@pad 3,
|
||||
}
|
||||
|
||||
struct XiDeviceClassScroll {
|
||||
number: u16,
|
||||
scroll_type: u16,
|
||||
@pad 2,
|
||||
flags: u32,
|
||||
increment_int: i32,
|
||||
increment_frac: u32,
|
||||
}
|
||||
|
||||
struct XiDeviceClassTouch {
|
||||
mode: u8,
|
||||
num_touches: u8,
|
||||
}
|
||||
|
||||
enum XiDeviceClassType {
|
||||
Key: XiDeviceClassKey = 0,
|
||||
Button: XiDeviceClassButton = 1,
|
||||
Valuator: XiDeviceClassValuator = 2,
|
||||
Scroll: XiDeviceClassScroll = 3,
|
||||
Touch: XiDeviceClassTouch = 8,
|
||||
}
|
||||
|
||||
struct XiDeviceClass {
|
||||
ty: u16 = variant(data),
|
||||
len: u16,
|
||||
sourceid: u16,
|
||||
data: enum(XiDeviceClassType, field(ty)),
|
||||
}
|
||||
|
||||
struct XiDeviceInfo {
|
||||
deviceid: u16,
|
||||
ty: u16,
|
||||
attachment: u16,
|
||||
num_classes: u16 = len(classes),
|
||||
name_len: u16 = len(name),
|
||||
enabled: u8,
|
||||
@pad 1,
|
||||
name: str(field(name_len)),
|
||||
@align 4,
|
||||
classes: list(XiDeviceClass, field(num_classes)),
|
||||
}
|
||||
|
||||
request XiQueryDevice = 48 (
|
||||
deviceid: u16,
|
||||
@pad 2,
|
||||
) {
|
||||
@pad 1,
|
||||
num_infos: u16 = len(infos),
|
||||
@pad 22,
|
||||
infos: list(XiDeviceInfo, field(num_infos)),
|
||||
}
|
||||
|
||||
request XiGetDeviceButtonMapping = 28 (
|
||||
device_id: u8,
|
||||
@pad 3,
|
||||
) {
|
||||
xi_reply_type: u8,
|
||||
map_size: u8 = len(map),
|
||||
@pad 23,
|
||||
map: list(u8, field(map_size)),
|
||||
@align 4,
|
||||
}
|
||||
|
||||
request XiGrabDevice = 51 (
|
||||
window: u32,
|
||||
time: u32,
|
||||
cursor: u32,
|
||||
deviceid: u16,
|
||||
mode: u8,
|
||||
paired_device_mode: u8,
|
||||
owner_events: u8,
|
||||
@pad 1,
|
||||
mask_len: u16 = len(mask),
|
||||
mask: list(u32, field(mask_len)),
|
||||
) {
|
||||
@pad 1,
|
||||
status: u8,
|
||||
@pad 23,
|
||||
}
|
||||
|
||||
request XiUngrabDevice = 52 (
|
||||
time: u32,
|
||||
deviceid: u16,
|
||||
@pad 2,
|
||||
);
|
||||
28
wire-xcon/xkb.txt
Normal file
28
wire-xcon/xkb.txt
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
ext "XKEYBOARD"
|
||||
|
||||
request XkbUseExtension = 0 (
|
||||
wanted_major: u16,
|
||||
wanted_minor: u16,
|
||||
) {
|
||||
supported: u8,
|
||||
server_major: u16,
|
||||
server_minor: u16,
|
||||
@pad 20,
|
||||
}
|
||||
|
||||
request XkbPerClientFlags = 21 (
|
||||
device_spec: u16,
|
||||
@pad 2,
|
||||
change: u32,
|
||||
value: u32,
|
||||
ctrls_to_change: u32,
|
||||
auto_ctrls: u32,
|
||||
auto_ctrls_values: u32,
|
||||
) {
|
||||
device_id: u8,
|
||||
supported: u32,
|
||||
value: u32,
|
||||
auto_ctrls: u32,
|
||||
auto_ctrls_values: u32,
|
||||
@pad 8,
|
||||
}
|
||||
264
wire-xcon/xproto.txt
Normal file
264
wire-xcon/xproto.txt
Normal file
|
|
@ -0,0 +1,264 @@
|
|||
struct Format {
|
||||
depth: u8,
|
||||
bits_per_pixel: u8,
|
||||
scanline_pad: u8,
|
||||
@pad 5,
|
||||
}
|
||||
|
||||
struct Visualtype {
|
||||
visual_id: u32,
|
||||
class: u8,
|
||||
bits_per_rgb_value: u8,
|
||||
colormap_entries: u16,
|
||||
red_mask: u32,
|
||||
green_mask: u32,
|
||||
blue_mask: u32,
|
||||
@pad 4,
|
||||
}
|
||||
|
||||
struct Depth {
|
||||
depth: u8,
|
||||
@pad 1,
|
||||
num_visualtypes: u16 = len(visuals),
|
||||
@pad 4,
|
||||
visuals: list(Visualtype, field(num_visualtypes)),
|
||||
}
|
||||
|
||||
struct Screen {
|
||||
root: u32,
|
||||
default_colormap: u32,
|
||||
white_pixel: u32,
|
||||
black_pixel: u32,
|
||||
input_mask: u32,
|
||||
width: u16,
|
||||
height: u16,
|
||||
width_mm: u16,
|
||||
height_mm: u16,
|
||||
min_maps: u16,
|
||||
max_maps: u16,
|
||||
root_visual: u32,
|
||||
backing_stores: u8,
|
||||
save_unders: u8,
|
||||
root_depth: u8,
|
||||
number_of_depths: u8 = len(allowed_depths),
|
||||
allowed_depths: list(Depth, field(number_of_depths)),
|
||||
}
|
||||
|
||||
struct Setup {
|
||||
success: u8 = literal(1),
|
||||
@pad 1,
|
||||
protocol_major: u16,
|
||||
protocol_minor: u16,
|
||||
additional_data: u16,
|
||||
release_number: u32,
|
||||
resource_id_base: u32,
|
||||
resource_id_mask: u32,
|
||||
motion_buffer_size: u32,
|
||||
vendor_len: u16 = len(vendor),
|
||||
max_request_length: u16,
|
||||
number_of_screens: u8 = len(screens),
|
||||
number_of_formats: u8 = len(formats),
|
||||
image_byte_order: u8,
|
||||
bitmap_format_bit_order: u8,
|
||||
bitmap_format_scanline_unit: u8,
|
||||
bitmap_format_scanline_pad: u8,
|
||||
min_keycode: u8,
|
||||
max_keycode: u8,
|
||||
@pad 4,
|
||||
vendor: str(field(vendor_len)),
|
||||
@align 4,
|
||||
formats: list(Format, field(number_of_formats)),
|
||||
screens: list(Screen, field(number_of_screens)),
|
||||
}
|
||||
|
||||
struct Str {
|
||||
len: u8 = len(val),
|
||||
val: str(field(len)),
|
||||
}
|
||||
|
||||
request ListExtensions = 99 () {
|
||||
names_len: u8 = len(names),
|
||||
@pad 24,
|
||||
names: list(Str, field(names_len)),
|
||||
}
|
||||
|
||||
request GetInputFocus = 43 () {
|
||||
revert_to: u8,
|
||||
focus: u32,
|
||||
}
|
||||
|
||||
request QueryExtension = 98 (
|
||||
@pad 1,
|
||||
name_len: u16 = len(name),
|
||||
@pad 2,
|
||||
name: str(field(name_len)),
|
||||
@align 4,
|
||||
) {
|
||||
@pad 1,
|
||||
present: u8,
|
||||
major_opcode: u8,
|
||||
first_event: u8,
|
||||
first_error: u8,
|
||||
}
|
||||
|
||||
bitmask CreateWindowValues {
|
||||
background_pixmap: u32 = 0,
|
||||
background_pixel: u32 = 1,
|
||||
border_pixmap: u32 = 2,
|
||||
border_pixel: u32 = 3,
|
||||
bit_gravity: u32 = 4,
|
||||
win_gravity: u32 = 5,
|
||||
backing_store: u32 = 6,
|
||||
backing_planes: u32 = 7,
|
||||
backing_pixel: u32 = 8,
|
||||
override_redirect: u32 = 9,
|
||||
save_under: u32 = 10,
|
||||
event_mask: u32 = 11,
|
||||
do_not_propagate_mask: u32 = 12,
|
||||
colormap: u32 = 13,
|
||||
cursor: u32 = 14,
|
||||
}
|
||||
|
||||
request CreateWindow = 1 (
|
||||
depth: u8,
|
||||
wid: u32,
|
||||
parent: u32,
|
||||
x: i16,
|
||||
y: i16,
|
||||
width: u16,
|
||||
height: u16,
|
||||
border_width: u16,
|
||||
class: u16,
|
||||
visual: u32,
|
||||
value_mask: u32 = bitmask(values),
|
||||
values: bitmask(CreateWindowValues, field(value_mask)),
|
||||
);
|
||||
|
||||
request ChangeWindowAttributes = 2 (
|
||||
@pad 1,
|
||||
window: u32,
|
||||
value_mask: u32 = bitmask(values),
|
||||
values: bitmask(CreateWindowValues, field(value_mask)),
|
||||
);
|
||||
|
||||
request DestroyWindow = 4 (
|
||||
@pad 1,
|
||||
window: u32,
|
||||
);
|
||||
|
||||
request MapWindow = 8 (
|
||||
@pad 1,
|
||||
window: u32,
|
||||
);
|
||||
|
||||
event DestroyNotify = 17 {
|
||||
@pad 1,
|
||||
event: u32,
|
||||
window: u32,
|
||||
}
|
||||
|
||||
event ConfigureNotify = 22 {
|
||||
@pad 1,
|
||||
event: u32,
|
||||
window: u32,
|
||||
above_sibling: u32,
|
||||
x: i16,
|
||||
y: i16,
|
||||
width: u16,
|
||||
height: u16,
|
||||
border_width: u16,
|
||||
override_redirect: u8,
|
||||
@pad 1,
|
||||
}
|
||||
|
||||
request CreatePixmap = 53 (
|
||||
depth: u8,
|
||||
pid: u32,
|
||||
drawable: u32,
|
||||
width: u16,
|
||||
height: u16,
|
||||
);
|
||||
|
||||
request FreePixmap = 54 (
|
||||
@pad 1,
|
||||
pixmap: u32,
|
||||
);
|
||||
|
||||
bitmask GC {
|
||||
function: u32 = 0,
|
||||
plane_mask: u32 = 1,
|
||||
foreground: u32 = 2,
|
||||
background: u32 = 3,
|
||||
line_width: u32 = 4,
|
||||
line_style: u32 = 5,
|
||||
cap_style: u32 = 6,
|
||||
join_style: u32 = 7,
|
||||
fill_style: u32 = 8,
|
||||
fill_rule: u32 = 9,
|
||||
tile: u32 = 10,
|
||||
stipple: u32 = 11,
|
||||
tile_stipple_x_origin: u32 = 12,
|
||||
tile_stipple_y_origin: u32 = 13,
|
||||
font: u32 = 14,
|
||||
subwindow_mode: u32 = 15,
|
||||
graphics_exposures: u32 = 16,
|
||||
clip_x_origin: u32 = 17,
|
||||
clip_y_origin: u32 = 18,
|
||||
clip_mask: u32 = 19,
|
||||
dash_offset: u32 = 20,
|
||||
dashes: u32 = 21,
|
||||
arc_mode: u32 = 22,
|
||||
}
|
||||
|
||||
request CreateGC = 55 (
|
||||
@pad 1,
|
||||
cid: u32,
|
||||
drawable: u32,
|
||||
value_mask: u32 = bitmask(values),
|
||||
values: bitmask(GC, field(value_mask)),
|
||||
);
|
||||
|
||||
request FreeGC = 60 (
|
||||
@pad 1,
|
||||
gc: u32,
|
||||
);
|
||||
|
||||
request PutImage = 72 (
|
||||
format: u8,
|
||||
drawable: u32,
|
||||
gc: u32,
|
||||
width: u16,
|
||||
height: u16,
|
||||
dst_x: i16,
|
||||
dst_y: i16,
|
||||
left_pad: u8,
|
||||
depth: u8,
|
||||
@pad 2,
|
||||
data: list(u8),
|
||||
);
|
||||
|
||||
request CreateCursor = 93 (
|
||||
@pad 1,
|
||||
cid: u32,
|
||||
source: u32,
|
||||
mask: u32,
|
||||
fore_red: u16,
|
||||
fore_green: u16,
|
||||
fore_blue: u16,
|
||||
back_red: u16,
|
||||
back_green: u16,
|
||||
back_blue: u16,
|
||||
x: u16,
|
||||
y: u16,
|
||||
);
|
||||
|
||||
request ChangeProperty = 18 (
|
||||
mode: u8,
|
||||
window: u32,
|
||||
property: u32,
|
||||
ty: u32,
|
||||
format: u8,
|
||||
@pad 3,
|
||||
data_len: u32 = div(mul(len(data), literal(8)), field(format)),
|
||||
data: list(u8, mul(field(data_len), div(field(format), literal(8)))),
|
||||
);
|
||||
Loading…
Add table
Add a link
Reference in a new issue