From 2ff60ff81791c93b1c6d667a2ad327a5ad525ce8 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Tue, 22 Mar 2022 23:24:17 +0100 Subject: [PATCH] autocommit 2022-03-22 23:24:17 CET --- build/build.rs | 2 + build/tokens.rs | 45 +- build/wire_dbus.rs | 8 +- build/wire_xcon.rs | 1630 ++++++++++++++++++++++++++++++++++++ default-config/src/lib.rs | 10 +- src/async_engine.rs | 3 +- src/backends/metal.rs | 2 +- src/backends/mod.rs | 1 + src/backends/xorgng.rs | 968 +++++++++++++++++++++ src/dbus.rs | 20 +- src/dbus/holder.rs | 7 +- src/dbus/incoming.rs | 84 +- src/dbus/outgoing.rs | 116 +-- src/dbus/socket.rs | 46 +- src/event_loop.rs | 9 +- src/logind.rs | 7 +- src/main.rs | 5 +- src/state.rs | 4 +- src/tasks/start_backend.rs | 15 +- src/utils/bufio.rs | 241 ++++++ src/utils/clonecell.rs | 3 +- src/utils/mod.rs | 1 + src/wire_xcon.rs | 9 + src/xcon.rs | 590 +++++++++++++ src/xcon/consts.rs | 103 +++ src/xcon/formatter.rs | 62 ++ src/xcon/incoming.rs | 222 +++++ src/xcon/outgoing.rs | 10 + src/xcon/parser.rs | 138 +++ src/xcon/wire_type.rs | 118 +++ src/xcon/xauthority.rs | 96 +++ wire-xcon/dri3.txt | 31 + wire-xcon/present.txt | 59 ++ wire-xcon/xinput.txt | 214 +++++ wire-xcon/xkb.txt | 28 + wire-xcon/xproto.txt | 264 ++++++ 36 files changed, 4934 insertions(+), 237 deletions(-) create mode 100644 build/wire_xcon.rs create mode 100644 src/backends/xorgng.rs create mode 100644 src/utils/bufio.rs create mode 100644 src/wire_xcon.rs create mode 100644 src/xcon.rs create mode 100644 src/xcon/consts.rs create mode 100644 src/xcon/formatter.rs create mode 100644 src/xcon/incoming.rs create mode 100644 src/xcon/outgoing.rs create mode 100644 src/xcon/parser.rs create mode 100644 src/xcon/wire_type.rs create mode 100644 src/xcon/xauthority.rs create mode 100644 wire-xcon/dri3.txt create mode 100644 wire-xcon/present.txt create mode 100644 wire-xcon/xinput.txt create mode 100644 wire-xcon/xkb.txt create mode 100644 wire-xcon/xproto.txt diff --git a/build/build.rs b/build/build.rs index 3814632c..15e63f5a 100644 --- a/build/build.rs +++ b/build/build.rs @@ -9,6 +9,7 @@ mod enums; mod tokens; mod wire; mod wire_dbus; +mod wire_xcon; fn open(s: &str) -> io::Result> { let mut path = PathBuf::from(env::var("OUT_DIR").unwrap()); @@ -25,6 +26,7 @@ fn open(s: &str) -> io::Result> { fn main() -> anyhow::Result<()> { wire::main()?; wire_dbus::main()?; + wire_xcon::main()?; enums::main()?; println!("cargo:rerun-if-changed=build/build.rs"); diff --git a/build/tokens.rs b/build/tokens.rs index 946e4440..5f6c6e9e 100644 --- a/build/tokens.rs +++ b/build/tokens.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>, }, 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 }); diff --git a/build/wire_dbus.rs b/build/wire_dbus.rs index 3cbd051e..f2185f95 100644 --- a/build/wire_dbus.rs +++ b/build/wire_dbus.rs @@ -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(()) diff --git a/build/wire_xcon.rs b/build/wire_xcon.rs new file mode 100644 index 00000000..7ec5ce8a --- /dev/null +++ b/build/wire_xcon.rs @@ -0,0 +1,1630 @@ +use crate::open; +use crate::tokens::{tokenize, Symbol, Token, TokenKind, TreeDelim}; +use anyhow::{bail, Context, Result}; +use bstr::{BStr, BString, ByteSlice}; +use std::cell::Cell; +use std::collections::HashMap; +use std::io::Write; +use std::mem; +use std::os::unix::ffi::OsStrExt; +use std::rc::Rc; + +struct Parser<'a> { + pos: usize, + tokens: &'a [Token<'a>], + ext_idx: Option, +} + +impl<'a> Parser<'a> { + fn new(tokens: &'a [Token<'a>]) -> Self { + Self { + pos: 0, + tokens, + ext_idx: None, + } + } + + fn parse(&mut self, ext_idx: &mut usize) -> Result { + let mut first = true; + let mut res = Protocol { + extension: None, + structs: vec![], + requests: vec![], + bitmasks: vec![], + enums: vec![], + events: vec![], + eventcopies: vec![], + }; + while !self.eof() { + let (line, ty) = self.expect_ident()?; + match ty.as_bytes() { + b"ext" => { + if !first { + bail!("In line {}: ext must be the first directive", line); + } + res.extension = Some(self.parse_extension(line)?); + self.ext_idx = Some(*ext_idx); + *ext_idx += 1; + } + b"struct" => res.structs.push(self.parse_struct(line)?), + b"request" => res.requests.push(self.parse_request(line)?), + b"bitmask" => res.bitmasks.push(self.parse_bitmask(line)?), + b"event" => res.events.push(self.parse_event(line, false)?), + b"xge" => res.events.push(self.parse_event(line, true)?), + b"eventcopy" => res.eventcopies.push(self.parse_event_copy(line)?), + b"enum" => res.enums.push(self.parse_enum(line)?), + _ => bail!("In line {}: Unexpected entry {:?}", line, ty), + } + first = false; + } + Ok(res) + } + + fn eof(&self) -> bool { + self.pos == self.tokens.len() + } + + fn not_eof(&self) -> Result<()> { + if self.eof() { + bail!("Unexpected eof"); + } + Ok(()) + } + + fn parse_extension(&mut self, line: u32) -> Result { + let res: Result<_> = (|| { + let (_, name) = self.expect_string()?; + Ok(name.to_owned()) + })(); + res.with_context(|| format!("While parsing extension starting in line {}", line)) + } + + fn parse_request(&mut self, line: u32) -> Result { + let res: Result<_> = (|| { + let (_, name) = self.expect_ident()?; + self.expect_symbol(Symbol::Equals)?; + let (_, opcode) = self.expect_num()?; + let (_, body) = self.expect_tree(TreeDelim::Paren)?; + let mut parser = Parser::new(body); + let request = parser.parse_struct_body(name)?; + self.not_eof()?; + let reply = if self.tokens[self.pos].kind == TokenKind::Symbol(Symbol::Semicolon) { + self.pos += 1; + None + } else { + let (_, body) = self.expect_tree(TreeDelim::Brace)?; + let mut parser = Parser::new(body); + Some(parser.parse_struct_body(name)?) + }; + Ok(Request { + opcode, + ext_idx: self.ext_idx, + request, + reply, + }) + })(); + res.with_context(|| format!("While parsing request starting at line {}", line)) + } + + fn parse_struct(&mut self, line: u32) -> Result { + let res: Result<_> = (|| { + let (_, name) = self.expect_ident()?; + let (_, body) = self.expect_tree(TreeDelim::Brace)?; + let mut parser = Parser::new(body); + parser.parse_struct_body(name) + })(); + res.with_context(|| format!("While parsing struct starting at line {}", line)) + } + + fn parse_event_copy(&mut self, line: u32) -> Result { + let res: Result<_> = (|| { + let (_, name) = self.expect_ident()?; + self.expect_symbol(Symbol::Equals)?; + let (_, opcode) = self.expect_num()?; + self.expect_symbol(Symbol::Equals)?; + let (_, original) = self.expect_ident()?; + self.expect_symbol(Symbol::Semicolon)?; + Ok(EventCopy { + name: name.to_owned(), + opcode, + original: original.to_owned(), + }) + })(); + res.with_context(|| format!("While parsing eventcopy starting at line {}", line)) + } + + fn parse_event(&mut self, line: u32, xge: bool) -> Result { + let res: Result<_> = (|| { + let (_, name) = self.expect_ident()?; + self.expect_symbol(Symbol::Equals)?; + let (_, opcode) = self.expect_num()?; + let (_, body) = self.expect_tree(TreeDelim::Brace)?; + let mut parser = Parser::new(body); + let data = parser.parse_struct_body(name)?; + Ok(Event { opcode, xge, data }) + })(); + res.with_context(|| format!("While parsing event starting at line {}", line)) + } + + fn parse_enum(&mut self, line: u32) -> Result { + let res: Result<_> = (|| { + let (_, name) = self.expect_ident()?; + let (_, body) = self.expect_tree(TreeDelim::Brace)?; + let mut parser = Parser::new(body); + let mut variants = vec![]; + while !parser.eof() { + let (_, name) = parser.expect_ident()?; + parser.expect_symbol(Symbol::Colon)?; + let ty = parser.parse_type()?; + parser.expect_symbol(Symbol::Equals)?; + let (_, value) = parser.expect_num()?; + if !parser.eof() { + parser.expect_symbol(Symbol::Comma)?; + } + variants.push(EnumVariant { + name: name.to_owned(), + ty, + value, + }); + } + Ok(Enum { + name: name.to_owned(), + variants, + }) + })(); + res.with_context(|| format!("While parsing enum starting at line {}", line)) + } + + fn parse_bitmask(&mut self, line: u32) -> Result { + let res: Result<_> = (|| { + let (_, name) = self.expect_ident()?; + let (_, body) = self.expect_tree(TreeDelim::Brace)?; + let mut parser = Parser::new(body); + let mut variants = vec![]; + while !parser.eof() { + let (_, name) = parser.expect_ident()?; + parser.expect_symbol(Symbol::Colon)?; + let ty = parser.parse_type()?; + parser.expect_symbol(Symbol::Equals)?; + let (_, bit) = parser.expect_num()?; + if !parser.eof() { + parser.expect_symbol(Symbol::Comma)?; + } + variants.push(BitmaskVariant { + name: name.to_owned(), + ty, + bit, + }); + } + Ok(Bitmask { + name: name.to_owned(), + variants, + }) + })(); + res.with_context(|| format!("While parsing bitmask starting at line {}", line)) + } + + fn parse_struct_body(&mut self, name: &BStr) -> Result { + let mut fields = vec![]; + while !self.eof() { + fields.push(self.parse_field()?); + } + Ok(Struct { + name: name.to_owned(), + fields, + needs_lt: Cell::new(None), + has_fds: Cell::new(None), + }) + } + + fn parse_field(&mut self) -> Result { + self.not_eof()?; + let line = self.tokens[self.pos].line; + let res: Result<_> = (|| { + let field = if self.tokens[self.pos].kind == TokenKind::Symbol(Symbol::At) { + self.pos += 1; + let (_, name) = self.expect_ident()?; + match name.as_bytes() { + b"pad" => Field::Pad(self.expect_num()?.1), + b"align" => Field::Align(self.expect_num()?.1), + _ => bail!("Unexpected directive {}", name), + } + } else { + let (_, name) = self.expect_ident()?; + self.expect_symbol(Symbol::Colon)?; + let ty = self.parse_type()?; + let mut value = None; + if !self.eof() { + if self.tokens[self.pos].kind == TokenKind::Symbol(Symbol::Equals) { + self.pos += 1; + value = Some(self.parse_expr()?); + } + } + Field::Real(RealField { + name: name.to_owned(), + ty, + value, + }) + }; + if !self.eof() { + self.expect_symbol(Symbol::Comma)?; + } + Ok(field) + })(); + res.with_context(|| format!("While parsing field starting at line {}", line)) + } + + fn expect_num(&mut self) -> Result<(u32, u32)> { + self.not_eof()?; + let token = &self.tokens[self.pos]; + self.pos += 1; + match &token.kind { + TokenKind::Num(id) => Ok((token.line, *id)), + k => bail!( + "In line {}: Expected number, found {}", + token.line, + k.name() + ), + } + } + + fn expect_ident(&mut self) -> Result<(u32, &'a BStr)> { + self.not_eof()?; + let token = &self.tokens[self.pos]; + self.pos += 1; + match &token.kind { + TokenKind::Ident(id) => Ok((token.line, *id)), + k => bail!( + "In line {}: Expected identifier, found {}", + token.line, + k.name() + ), + } + } + + fn expect_string(&mut self) -> Result<(u32, &'a BString)> { + self.not_eof()?; + let token = &self.tokens[self.pos]; + self.pos += 1; + match &token.kind { + TokenKind::String(id) => Ok((token.line, id)), + k => bail!( + "In line {}: Expected identifier, found {}", + token.line, + k.name() + ), + } + } + + fn expect_symbol(&mut self, symbol: Symbol) -> Result<()> { + self.not_eof()?; + let token = &self.tokens[self.pos]; + self.pos += 1; + match &token.kind { + TokenKind::Symbol(s) if *s == symbol => Ok(()), + k => bail!( + "In line {}: Expected {}, found {}", + token.line, + symbol.name(), + k.name() + ), + } + } + + fn expect_tree_(&mut self) -> Result<(u32, TreeDelim, &'a [Token<'a>])> { + self.not_eof()?; + let token = &self.tokens[self.pos]; + self.pos += 1; + match &token.kind { + TokenKind::Tree { delim, body } => Ok((token.line, *delim, body)), + k => bail!("In line {}: Expected tree, found {}", token.line, k.name()), + } + } + + fn expect_tree(&mut self, exp_delim: TreeDelim) -> Result<(u32, &'a [Token<'a>])> { + let (line, delim, tokens) = self.expect_tree_()?; + if delim == exp_delim { + Ok((line, tokens)) + } else { + bail!( + "In line {}: Expected {:?}-delimited tree, found {:?}-delimited tree", + line, + exp_delim, + delim.opening() + ) + } + } + + fn parse_type(&mut self) -> Result { + let (_, ty) = self.expect_ident()?; + let ty = match ty.as_bytes() { + b"i8" => Type::I8, + b"u8" => Type::U8, + b"i16" => Type::I16, + b"u16" => Type::U16, + b"i32" => Type::I32, + b"u32" => Type::U32, + b"i64" => Type::I64, + b"u64" => Type::U64, + b"fd" => Type::Fd, + b"str" => { + let (line, body) = self.expect_tree(TreeDelim::Paren)?; + let mut parser = Parser::new(body); + let ty: Result<_> = (|| { + let len = parser.parse_expr()?; + if !parser.eof() { + bail!("Trailing tokens in str length"); + } + Ok(Type::String(len)) + })(); + let ty = + ty.with_context(|| format!("While parsing string starting in line {}", line))?; + ty + } + b"list" => { + let (line, body) = self.expect_tree(TreeDelim::Paren)?; + let mut parser = Parser::new(body); + let ty: Result<_> = (|| { + let ty = parser.parse_type()?; + let mut len = None; + if !parser.eof() { + parser.expect_symbol(Symbol::Comma)?; + len = Some(parser.parse_expr()?); + if !parser.eof() { + bail!("Trailing tokens in list type"); + } + } + Ok(Type::List(Box::new(ty), len)) + })(); + let ty = + ty.with_context(|| format!("While parsing list starting in line {}", line))?; + ty + } + b"bitmask" => { + let (line, body) = self.expect_tree(TreeDelim::Paren)?; + let mut parser = Parser::new(body); + let ty: Result<_> = (|| { + let (_, name) = parser.expect_ident()?; + parser.expect_symbol(Symbol::Comma)?; + let len = parser.parse_expr()?; + if !parser.eof() { + bail!("Trailing tokens in bitmask type"); + } + Ok(Type::Bitmask(name.to_owned(), len)) + })(); + let ty = + ty.with_context(|| format!("While parsing bitmask starting in line {}", line))?; + ty + } + b"enum" => { + let (line, body) = self.expect_tree(TreeDelim::Paren)?; + let mut parser = Parser::new(body); + let ty: Result<_> = (|| { + let ty = parser.parse_type()?; + parser.expect_symbol(Symbol::Comma)?; + let len = parser.parse_expr()?; + if !parser.eof() { + bail!("Trailing tokens in enum type"); + } + Ok(Type::Enum(Box::new(ty), len)) + })(); + let ty = + ty.with_context(|| format!("While parsing enum starting in line {}", line))?; + ty + } + _ => Type::Named(ty.to_owned()), + }; + Ok(ty) + } + + fn parse_expr(&mut self) -> Result { + let (line, expr) = self.expect_ident()?; + let res: Result<_> = (|| { + let expr = match expr.as_bytes() { + b"len" => Expr::Len(self.parse_field_name_expr()?), + b"field" => Expr::Field(self.parse_field_name_expr()?), + b"literal" => Expr::Literal(self.parse_literal_expr()?), + b"bitmask" => Expr::Bitmask(self.parse_field_name_expr()?), + b"variant" => Expr::Variant(self.parse_field_name_expr()?), + b"sum" => Expr::Sum(self.parse_sum_expr()?), + b"iter" => Expr::Iter(self.parse_iter_expr()?), + b"it" => Expr::It, + b"popcount" => Expr::Popcount(self.parse_popcount_expr()?), + b"div" => { + let (left, right) = self.parse_div_expr()?; + Expr::Div(left, right) + } + b"plus" => { + let (left, right) = self.parse_plus_expr()?; + Expr::Plus(left, right) + } + b"map" => { + let (iter, fun) = self.parse_map_expr()?; + Expr::Map(iter, fun) + } + b"mul" => { + let (left, right) = self.parse_mul_expr()?; + Expr::Mul(left, right) + } + _ => bail!("Unknown expression {}", expr), + }; + Ok(expr) + })(); + res.with_context(|| format!("While parsing expression starting in line {}", line)) + } + + fn parse_literal_expr(&mut self) -> Result { + let (_, tree) = self.expect_tree(TreeDelim::Paren)?; + let mut parser = Parser::new(tree); + let (_, name) = parser.expect_num()?; + if !parser.eof() { + bail!("Trailing tokens in literal"); + } + Ok(name) + } + + fn parse_field_name_expr(&mut self) -> Result { + let (_, tree) = self.expect_tree(TreeDelim::Paren)?; + let mut parser = Parser::new(tree); + let (_, name) = parser.expect_ident()?; + if !parser.eof() { + bail!("Trailing tokens in field name"); + } + Ok(name.to_owned()) + } + + fn parse_sum_expr(&mut self) -> Result> { + let (_, tree) = self.expect_tree(TreeDelim::Paren)?; + let mut parser = Parser::new(tree); + let expr = parser.parse_expr()?; + if !parser.eof() { + bail!("Trailing tokens in sum body"); + } + Ok(Box::new(expr)) + } + + fn parse_iter_expr(&mut self) -> Result> { + let (_, tree) = self.expect_tree(TreeDelim::Paren)?; + let mut parser = Parser::new(tree); + let expr = parser.parse_expr()?; + if !parser.eof() { + bail!("Trailing tokens in iter body"); + } + Ok(Box::new(expr)) + } + + fn parse_popcount_expr(&mut self) -> Result> { + let (_, tree) = self.expect_tree(TreeDelim::Paren)?; + let mut parser = Parser::new(tree); + let expr = parser.parse_expr()?; + if !parser.eof() { + bail!("Trailing tokens in popcount body"); + } + Ok(Box::new(expr)) + } + + fn parse_mul_expr(&mut self) -> Result<(Box, Box)> { + let (_, tree) = self.expect_tree(TreeDelim::Paren)?; + let mut parser = Parser::new(tree); + let left = parser.parse_expr()?; + parser.expect_symbol(Symbol::Comma)?; + let right = parser.parse_expr()?; + if !parser.eof() { + bail!("Trailing tokens in mul body"); + } + Ok((Box::new(left), Box::new(right))) + } + + fn parse_map_expr(&mut self) -> Result<(Box, Box)> { + let (_, tree) = self.expect_tree(TreeDelim::Paren)?; + let mut parser = Parser::new(tree); + let iter = parser.parse_expr()?; + parser.expect_symbol(Symbol::Comma)?; + let fun = parser.parse_expr()?; + if !parser.eof() { + bail!("Trailing tokens in map body"); + } + Ok((Box::new(iter), Box::new(fun))) + } + + fn parse_div_expr(&mut self) -> Result<(Box, Box)> { + let (_, tree) = self.expect_tree(TreeDelim::Paren)?; + let mut parser = Parser::new(tree); + let iter = parser.parse_expr()?; + parser.expect_symbol(Symbol::Comma)?; + let fun = parser.parse_expr()?; + if !parser.eof() { + bail!("Trailing tokens in div body"); + } + Ok((Box::new(iter), Box::new(fun))) + } + + fn parse_plus_expr(&mut self) -> Result<(Box, Box)> { + let (_, tree) = self.expect_tree(TreeDelim::Paren)?; + let mut parser = Parser::new(tree); + let iter = parser.parse_expr()?; + parser.expect_symbol(Symbol::Comma)?; + let fun = parser.parse_expr()?; + if !parser.eof() { + bail!("Trailing tokens in plus body"); + } + Ok((Box::new(iter), Box::new(fun))) + } +} + +fn needs_lifetime(ty: &Type, protocols: &Protocols) -> Result { + let res = match ty { + Type::I8 => false, + Type::U8 => false, + Type::I16 => false, + Type::U16 => false, + Type::I32 => false, + Type::U32 => false, + Type::I64 => false, + Type::U64 => false, + Type::Fd => false, + Type::List(_, _) => true, + Type::Named(n) => named_needs_lt(n.as_bstr(), protocols)?, + Type::String(_) => true, + Type::Bitmask(_, _) => false, + Type::Enum(n, _) => needs_lifetime(n, protocols)?, + }; + Ok(res) +} + +fn named_needs_lt(name: &BStr, protocols: &Protocols) -> Result { + let s = match protocols.types_by_name.get(name) { + Some(s) => s, + _ => bail!("Struct {} referenced but not defined", name), + }; + match s { + NamedType::Struct(s) => struct_needs_lt(s, protocols), + NamedType::Bitmask(_) => Ok(false), + NamedType::Enum(s) => enum_needs_lt(s, protocols), + } +} + +fn enum_needs_lt(s: &Enum, protocols: &Protocols) -> Result { + for v in &s.variants { + if needs_lifetime(&v.ty, protocols)? { + return Ok(true); + } + } + Ok(false) +} + +fn struct_needs_lt(s: &Struct, protocols: &Protocols) -> Result { + if let Some(lt) = s.needs_lt.get() { + return Ok(lt); + } + let mut needs_lt = false; + for field in &s.fields { + if let Field::Real(f) = field { + if f.value.is_none() { + if needs_lifetime(&f.ty, protocols)? { + needs_lt = true; + break; + } + } + } + } + s.needs_lt.set(Some(needs_lt)); + Ok(needs_lt) +} + +fn type_is_pod(ty: &Type) -> bool { + match ty { + Type::I8 => true, + Type::U8 => true, + Type::I16 => true, + Type::U16 => true, + Type::I32 => true, + Type::U32 => true, + Type::I64 => true, + Type::U64 => true, + Type::String(_) => false, + Type::Fd => false, + Type::List(..) => false, + Type::Named(..) => false, + Type::Bitmask(..) => false, + Type::Enum(..) => false, + } +} + +fn type_has_fds(ty: &Type, protocols: &Protocols) -> Result { + let res = match ty { + Type::I8 => false, + Type::U8 => false, + Type::I16 => false, + Type::U16 => false, + Type::I32 => false, + Type::U32 => false, + Type::I64 => false, + Type::U64 => false, + Type::String(_) => false, + Type::Fd => true, + Type::List(ty, _) => return type_has_fds(ty, protocols), + Type::Named(n) => return named_has_fds(n.as_bstr(), protocols), + Type::Bitmask(_, _) => false, + Type::Enum(n, _) => return type_has_fds(n, protocols), + }; + Ok(res) +} + +fn named_has_fds(s: &BStr, protocols: &Protocols) -> Result { + let s = match protocols.types_by_name.get(s) { + Some(s) => s, + _ => bail!("Struct {} referenced but not defined", s), + }; + match s { + NamedType::Struct(s) => struct_has_fds(s, protocols), + NamedType::Bitmask(_) => Ok(false), + NamedType::Enum(e) => enum_has_fds(e, protocols), + } +} + +fn enum_has_fds(s: &Enum, protocols: &Protocols) -> Result { + for v in &s.variants { + if type_has_fds(&v.ty, protocols)? { + return Ok(true); + } + } + Ok(false) +} + +fn struct_has_fds(s: &Struct, protocols: &Protocols) -> Result { + if let Some(lt) = s.has_fds.get() { + return Ok(lt); + } + let mut has_fds = false; + for field in &s.fields { + if let Field::Real(f) = field { + if type_has_fds(&f.ty, protocols)? { + has_fds = true; + break; + } + } + } + s.has_fds.set(Some(has_fds)); + Ok(has_fds) +} + +#[derive(Debug, Clone)] +enum Type { + I8, + U8, + I16, + U16, + I32, + U32, + I64, + U64, + Fd, + String(Expr), + List(Box, Option), + Bitmask(BString, Expr), + Enum(Box, Expr), + Named(BString), +} + +#[derive(Debug, Clone)] +enum Expr { + It, + Field(BString), + Len(BString), + Literal(u32), + Bitmask(BString), + Variant(BString), + Sum(Box), + Mul(Box, Box), + Map(Box, Box), + Div(Box, Box), + Plus(Box, Box), + Iter(Box), + Popcount(Box), +} + +#[derive(Debug, Clone)] +struct RealField { + name: BString, + ty: Type, + value: Option, +} + +#[derive(Debug, Clone)] +enum Field { + Pad(u32), + Align(u32), + Real(RealField), + Opcode(u32), + ExtMajor, +} + +#[derive(Debug)] +struct Struct { + name: BString, + fields: Vec, + needs_lt: Cell>, + has_fds: Cell>, +} + +#[derive(Debug)] +struct BitmaskVariant { + name: BString, + ty: Type, + bit: u32, +} + +#[derive(Debug)] +struct Bitmask { + name: BString, + variants: Vec, +} + +#[derive(Debug)] +struct EnumVariant { + name: BString, + ty: Type, + value: u32, +} + +#[derive(Debug)] +struct Enum { + name: BString, + variants: Vec, +} + +#[derive(Debug)] +struct Event { + opcode: u32, + xge: bool, + data: Struct, +} + +#[derive(Debug)] +struct EventCopy { + name: BString, + opcode: u32, + original: BString, +} + +#[derive(Debug)] +struct Request { + opcode: u32, + ext_idx: Option, + request: Struct, + reply: Option, +} + +#[derive(Debug)] +struct Protocol { + extension: Option, + structs: Vec, + requests: Vec, + bitmasks: Vec, + enums: Vec, + events: Vec, + eventcopies: Vec, +} + +#[derive(Debug)] +struct Extension { + name: BString, + ident: BString, +} + +#[derive(Debug)] +enum NamedType { + Struct(Rc), + Bitmask(Rc), + Enum(Rc), +} + +#[derive(Debug)] +struct Protocols { + extensions: Vec, + structs: Vec>, + bitmasks: Vec>, + enums: Vec>, + requests: Vec, + types_by_name: HashMap, + events_by_name: HashMap>, + events: Vec>, + eventcopies: Vec, +} + +fn parse_protocol(path: &str, ext_idx: &mut usize) -> Result { + let s = std::fs::read_to_string(path)?; + let tokens = tokenize(s.as_bytes())?; + Parser::new(&tokens).parse(ext_idx) +} + +fn write_type(f: &mut F, ty: &Type, protocols: &Protocols) -> Result<()> { + let ty = match ty { + Type::I8 => "i8", + Type::U8 => "u8", + Type::I16 => "i16", + Type::U16 => "u16", + Type::I32 => "i32", + Type::U32 => "u32", + Type::I64 => "i64", + Type::U64 => "u64", + Type::String(_) => "&'a BStr", + Type::Fd => "Rc", + Type::Bitmask(n, _) => { + write!(f, "{}", n)?; + return Ok(()); + } + Type::Enum(n, _) => { + write_type(f, n, protocols)?; + return Ok(()); + } + Type::List(ty, _) => { + if type_is_pod(ty) { + write!(f, "&'a [")?; + write_type(f, ty, protocols)?; + write!(f, "]")?; + } else { + write!(f, "Cow<'a, [")?; + write_type(f, ty, protocols)?; + write!(f, "]>")?; + } + return Ok(()); + } + Type::Named(n) => { + let lt = if named_needs_lt(n.as_bstr(), protocols)? { + "<'a>" + } else { + "" + }; + write!(f, "{n}{lt}")?; + return Ok(()); + } + }; + write!(f, "{}", ty)?; + Ok(()) +} + +fn write_expr(f: &mut F, e: &Expr, prefix: &str) -> Result<()> { + match e { + Expr::Field(n) => write!(f, "{prefix}{}", n)?, + Expr::Len(n) => write!(f, "{prefix}{}.len()", n)?, + Expr::Literal(n) => write!(f, "{}", n)?, + Expr::Bitmask(n) => write!(f, "{prefix}{}.bitmask()", n)?, + Expr::Variant(n) => write!(f, "{prefix}{}.variant()", n)?, + Expr::Sum(e) => { + write_expr(f, e, prefix)?; + write!(f, ".reduce(|a, b| a + b).unwrap_or(0)")?; + } + Expr::Map(iter, map) => { + write_expr(f, iter, prefix)?; + write!(f, ".map(|val| ")?; + write_expr(f, map, "val.")?; + write!(f, ")")?; + } + Expr::Iter(ex) => { + write_expr(f, ex, prefix)?; + write!(f, ".iter()")?; + } + Expr::It => write!(f, "val")?, + Expr::Popcount(ex) => { + write_expr(f, ex, prefix)?; + write!(f, ".count_ones()")?; + } + Expr::Div(l, r) | Expr::Plus(l, r) | Expr::Mul(l, r) => { + write!(f, "(")?; + write_expr(f, l, prefix)?; + let c = match e { + Expr::Div(..) => "/", + Expr::Plus(..) => "+", + Expr::Mul(..) => "*", + _ => unreachable!(), + }; + write!(f, " as u32 {} ", c)?; + write_expr(f, r, prefix)?; + write!(f, " as u32)")?; + } + } + Ok(()) +} + +#[derive(Copy, Clone)] +enum StructUsecase<'a> { + None, + Request { + request: &'a Request, + }, + Reply, + Event { + xge: bool, + }, + EventCopy { + copy: &'a EventCopy, + original: &'a Struct, + }, +} + +fn format_xevent( + f: &mut F, + name: &BStr, + s: &Struct, + opcode: u32, + protocols: &Protocols, +) -> Result<()> { + let lt_a = if struct_needs_lt(s, protocols)? { + "<'a>" + } else { + "" + }; + writeln!(f)?; + writeln!(f, "impl<'a> XEvent<'a> for {}{lt_a} {{", name)?; + writeln!(f, " const OPCODE: u16 = {:?};", opcode)?; + writeln!(f, "}}")?; + Ok(()) +} + +fn format_event(f: &mut F, s: &Event, protocols: &Protocols) -> Result<()> { + format_struct(f, &s.data, protocols, &StructUsecase::Event { xge: s.xge })?; + format_xevent(f, s.data.name.as_bstr(), &s.data, s.opcode, protocols) +} + +fn format_eventcopy(f: &mut F, s: &EventCopy, protocols: &Protocols) -> Result<()> { + let original = match protocols.events_by_name.get(s.original.as_bstr()) { + Some(o) => o, + _ => bail!("Event {} referenced but not defined", s.original), + }; + format_struct( + f, + &original.data, + protocols, + &StructUsecase::EventCopy { + copy: s, + original: &original.data, + }, + )?; + format_xevent(f, s.name.as_bstr(), &original.data, s.opcode, protocols) +} + +fn format_request(f: &mut F, s: &Request, protocols: &Protocols) -> Result<()> { + format_struct( + f, + &s.request, + protocols, + &StructUsecase::Request { request: s }, + )?; + let mut reply_has_lt = false; + if let Some(reply) = &s.reply { + reply_has_lt = struct_needs_lt(reply, protocols)?; + format_struct(f, reply, protocols, &StructUsecase::Reply)?; + } + let lt_a = if struct_needs_lt(&s.request, protocols)? { + "<'a>" + } else { + "" + }; + writeln!(f)?; + writeln!(f, "impl<'a> Request<'a> for {}{lt_a} {{", s.request.name)?; + write!(f, " type Reply = ")?; + if s.reply.is_some() { + let lt_static = if reply_has_lt { "<'static>" } else { "" }; + writeln!(f, "{}Reply{};", s.request.name, lt_static)?; + } else { + writeln!(f, "();")?; + } + writeln!(f, " const EXTENSION: Option = {:?};", s.ext_idx)?; + writeln!(f, " const IS_VOID: bool = {};", s.reply.is_none())?; + writeln!(f, "}}")?; + Ok(()) +} + +enum FieldGroup { + Pods { len: u32, fields: Vec }, + Single(Field), +} + +fn create_groups(s: &Struct, usecase: &StructUsecase) -> Vec { + let mut fields = &s.fields[..]; + let mut res = vec![]; + let mut current_len = 0; + let mut current = vec![]; + let flush_current = + |res: &mut Vec, current: &mut Vec, current_len: &mut u32| { + if !current.is_empty() { + res.push(FieldGroup::Pods { + len: mem::take(current_len), + fields: mem::take(current), + }); + } + }; + match usecase { + StructUsecase::None => {} + StructUsecase::Request { request } => { + if request.ext_idx.is_some() { + current.push(Field::ExtMajor); + current.push(Field::Opcode(request.opcode)); + current.push(Field::Pad(2)); + } else { + current.push(Field::Opcode(request.opcode)); + if let Some((first, rest)) = fields.split_first() { + fields = rest; + current.push(first.clone()); + current.push(Field::Pad(2)); + } else { + current.push(Field::Pad(3)); + } + } + current_len = 4; + } + StructUsecase::Reply => { + if let Some((first, rest)) = fields.split_first() { + fields = rest; + current.push(Field::Pad(1)); + current.push(first.clone()); + current.push(Field::Pad(6)); + } else { + current.push(Field::Pad(8)); + } + current_len = 8; + } + StructUsecase::Event { xge } => { + current_len = 4; + if *xge { + current.push(Field::Pad(10)); + current_len = 10; + } else if let Some((first, rest)) = fields.split_first() { + fields = rest; + current.push(Field::Pad(1)); + current.push(first.clone()); + current.push(Field::Pad(2)); + } else { + current.push(Field::Pad(4)); + } + } + StructUsecase::EventCopy { .. } => unreachable!(), + } + for field in fields { + match field { + Field::Pad(n) => { + current.push(field.clone()); + current_len += n; + } + Field::Align(_) => { + flush_current(&mut res, &mut current, &mut current_len); + res.push(FieldGroup::Single(field.clone())); + } + Field::Real(rf) => match rf.ty { + Type::I8 | Type::U8 => { + current.push(field.clone()); + current_len += 1; + } + Type::I16 | Type::U16 => { + current.push(field.clone()); + current_len += 2; + } + Type::I32 | Type::U32 => { + current.push(field.clone()); + current_len += 4; + } + Type::I64 | Type::U64 => { + current.push(field.clone()); + current_len += 8; + } + _ => { + flush_current(&mut res, &mut current, &mut current_len); + res.push(FieldGroup::Single(field.clone())); + } + }, + Field::Opcode(_) => unreachable!(), + Field::ExtMajor => unreachable!(), + } + } + flush_current(&mut res, &mut current, &mut current_len); + res +} + +fn format_enum(f: &mut F, s: &Enum, protocols: &Protocols) -> Result<()> { + let needs_lt = enum_needs_lt(s, protocols)?; + let lt_a = if needs_lt { "<'a>" } else { "" }; + writeln!(f)?; + writeln!(f, "#[derive(Debug, Clone)]")?; + writeln!(f, "pub enum {}{lt_a} {{", s.name)?; + for field in &s.variants { + write!(f, " {}(", field.name)?; + write_type(f, &field.ty, protocols)?; + writeln!(f, "),")?; + } + writeln!(f, "}}")?; + writeln!(f)?; + writeln!(f, "impl{lt_a} {}{lt_a} {{", s.name)?; + writeln!(f, " pub fn variant(&self) -> u32 {{")?; + writeln!(f, " match self {{")?; + for field in &s.variants { + writeln!( + f, + " Self::{}(..) => {},", + field.name, field.value + )?; + } + writeln!(f, " }}")?; + writeln!(f, " }}")?; + writeln!(f)?; + writeln!( + f, + " pub fn serialize(&self, formatter: &mut Formatter) {{" + )?; + writeln!(f, " match self {{")?; + for field in &s.variants { + writeln!( + f, + " Self::{}(v) => v.serialize(formatter),", + field.name + )?; + } + writeln!(f, " }}")?; + writeln!(f, " }}")?; + writeln!(f)?; + writeln!(f, " pub fn deserialize(parser: &mut Parser{lt_a}, value: u32) -> Result {{")?; + writeln!(f, " let res = match value {{")?; + for field in &s.variants { + writeln!( + f, + " {} => Self::{}(parser.unmarshal()?),", + field.value, field.name + )?; + } + writeln!( + f, + " _ => return Err(XconError::UnknownEnumVariant)," + )?; + writeln!(f, " }};")?; + writeln!(f, " Ok(res)")?; + writeln!(f, " }}")?; + writeln!(f, "}}")?; + Ok(()) +} + +fn format_bitmask(f: &mut F, s: &Bitmask, protocols: &Protocols) -> Result<()> { + writeln!(f)?; + writeln!(f, "#[derive(Debug, Clone, Default)]")?; + writeln!(f, "pub struct {} {{", s.name)?; + for field in &s.variants { + write!(f, " pub {}: Option<", field.name)?; + write_type(f, &field.ty, protocols)?; + writeln!(f, ">,")?; + } + writeln!(f, "}}")?; + writeln!(f)?; + writeln!(f, "impl {} {{", s.name)?; + writeln!(f, " pub fn bitmask(&self) -> u32 {{")?; + writeln!(f, " let mut res = 0;")?; + for field in &s.variants { + writeln!( + f, + " res |= (self.{}.is_some() as u32) << {};", + field.name, field.bit + )?; + } + writeln!(f, " res")?; + writeln!(f, " }}")?; + writeln!(f)?; + writeln!( + f, + " pub fn serialize(&self, formatter: &mut Formatter) {{" + )?; + writeln!(f, " let mut bytes = [0; {}];", s.variants.len() * 4)?; + writeln!(f, " let mut pos = 0;")?; + for field in &s.variants { + writeln!(f, " if let Some(val) = self.{} {{", field.name)?; + writeln!( + f, + " bytes[pos..pos+4].copy_from_slice(&(val as u32).to_ne_bytes());" + )?; + writeln!(f, " pos += 4;")?; + writeln!(f, " }}")?; + } + writeln!(f, " formatter.write_bytes(&bytes[..pos]);")?; + writeln!(f, " }}")?; + writeln!(f)?; + writeln!(f, " pub fn deserialize(&self, parser: &mut Parser, bitmask: u32) -> Result {{")?; + writeln!( + f, + " let b = parser.read_slice(bitmask.count_ones() as usize * 4)?;" + )?; + writeln!(f, " let mut p = 0;")?; + writeln!(f, " Ok(Self {{")?; + for field in &s.variants { + writeln!( + f, + " {}: if bitmask & (1 << {}) != 0 {{", + field.name, field.bit + )?; + writeln!( + f, + " let v = u32::from_ne_bytes([b[p], b[p+1], b[p+2], b[p+3]]);" + )?; + writeln!(f, " p += 4;")?; + writeln!(f, " Some(v as _)")?; + writeln!(f, " }} else {{")?; + writeln!(f, " None")?; + writeln!(f, " }},")?; + } + writeln!(f, " }})")?; + writeln!(f, " }}")?; + writeln!(f, "}}")?; + Ok(()) +} + +fn format_struct( + f: &mut F, + s: &Struct, + protocols: &Protocols, + usecase: &StructUsecase, +) -> Result<()> { + let has_fds = struct_has_fds(s, protocols)?; + let groups = match usecase { + StructUsecase::EventCopy { .. } => vec![], + _ => create_groups(s, usecase), + }; + let struct_name = match usecase { + StructUsecase::EventCopy { copy, .. } => format!("{}", copy.name), + StructUsecase::Reply => format!("{}Reply", s.name), + _ => s.name.to_string(), + }; + let needs_lt = struct_needs_lt(s, protocols)?; + let (lt_a, lt_b) = if needs_lt { ("<'a>", "<'b>") } else { ("", "") }; + writeln!(f)?; + writeln!(f, "#[derive(Debug, Clone)]")?; + writeln!(f, "pub struct {}{lt_a} {{", struct_name)?; + if let StructUsecase::EventCopy { original, .. } = usecase { + writeln!(f, " data: {}{lt_a},", original.name)?; + } else { + for field in &s.fields { + if let Field::Real(rf) = field { + if rf.value.is_none() { + write!(f, " pub {}: ", rf.name)?; + write_type(f, &rf.ty, protocols)?; + writeln!(f, ",")?; + } + } + } + } + writeln!(f, "}}")?; + if let StructUsecase::EventCopy { original, .. } = usecase { + writeln!(f)?; + writeln!(f, "impl{lt_a} std::ops::Deref for {}{lt_a} {{", struct_name)?; + writeln!(f, " type Target = {}{lt_a};", original.name)?; + writeln!(f)?; + writeln!(f, " fn deref(&self) -> &Self::Target {{")?; + writeln!(f, " &self.data")?; + writeln!(f, " }}")?; + writeln!(f, "}}")?; + } + writeln!(f)?; + writeln!( + f, + "unsafe impl<'a> Message<'a> for {}{lt_a} {{", + struct_name + )?; + writeln!(f, " type Generic<'b> = {}{lt_b};", struct_name)?; + writeln!(f, " const IS_POD: bool = false;")?; + writeln!(f, " const HAS_FDS: bool = {has_fds};")?; + if !matches!( + usecase, + StructUsecase::Event { .. } | StructUsecase::EventCopy { .. } | StructUsecase::Reply + ) { + writeln!(f)?; + writeln!(f, " fn serialize(&self, formatter: &mut Formatter) {{")?; + for group in &groups { + match group { + FieldGroup::Pods { fields, .. } => { + writeln!(f, " {{")?; + for field in fields { + if let Field::Real(rf) = field { + write!(f, " let {}_bytes = ", rf.name)?; + match &rf.value { + Some(e) => { + writeln!(f, "{{")?; + write!(f, " let tmp: ")?; + write_type(f, &rf.ty, protocols)?; + write!(f, " = (")?; + write_expr(f, e, "self.")?; + writeln!(f, ") as _;")?; + writeln!(f, " tmp.to_ne_bytes()")?; + writeln!(f, " }};")?; + } + _ => writeln!(f, "self.{}.to_ne_bytes();", rf.name)?, + } + } + } + writeln!(f, " formatter.write_bytes(&[")?; + for field in fields { + match field { + Field::Pad(n) => { + write!(f, " ")?; + for _ in 0..*n { + write!(f, " 0,")?; + } + writeln!(f)?; + } + Field::Real(rf) => { + let num_bytes = match rf.ty { + Type::I8 | Type::U8 => 1, + Type::I16 | Type::U16 => 2, + Type::I32 | Type::U32 => 4, + Type::I64 | Type::U64 => 8, + _ => unreachable!(), + }; + write!(f, " ")?; + for i in 0..num_bytes { + write!(f, " {}_bytes[{}],", rf.name, i)?; + } + writeln!(f)?; + } + Field::Opcode(n) => { + writeln!(f, " {},", n)?; + } + Field::ExtMajor => { + writeln!(f, " formatter.ext_opcode(),")?; + } + _ => unreachable!(), + } + } + writeln!(f, " ]);")?; + writeln!(f, " }}")?; + } + FieldGroup::Single(field) => match field { + Field::Align(n) => writeln!(f, " formatter.align({n});")?, + Field::Real(rf) => match &rf.value { + Some(v) => { + writeln!(f, " {{")?; + write!(f, " let tmp: ")?; + write_type(f, &rf.ty, protocols)?; + write!(f, " = ")?; + write_expr(f, v, "self.")?; + writeln!(f, " as _;")?; + writeln!(f, " tmp.serialize(formatter);")?; + writeln!(f, " }}")?; + } + _ => writeln!(f, " self.{}.serialize(formatter);", rf.name)?, + }, + _ => unreachable!(), + }, + } + } + writeln!(f, " }}")?; + } + if !matches!(usecase, StructUsecase::Request { .. }) { + writeln!(f)?; + writeln!( + f, + " fn deserialize(parser: &mut Parser<'a>) -> Result {{" + )?; + if let StructUsecase::EventCopy { original, .. } = usecase { + writeln!(f, " Ok(Self {{")?; + writeln!( + f, + " data: {}::deserialize(parser)?,", + original.name + )?; + writeln!(f, " }})")?; + } else { + for group in &groups { + match group { + FieldGroup::Pods { len, fields } => { + writeln!(f, " let bytes_ = parser.read_bytes::<{}>()?;", len)?; + let mut pos = 0; + for field in fields { + match field { + Field::Pad(n) => pos += n, + Field::Real(rf) => { + write!(f, " let {} = ", rf.name)?; + write_type(f, &rf.ty, protocols)?; + write!(f, "::from_ne_bytes([")?; + let num_bytes = match rf.ty { + Type::I8 | Type::U8 => 1, + Type::I16 | Type::U16 => 2, + Type::I32 | Type::U32 => 4, + Type::I64 | Type::U64 => 8, + _ => unreachable!(), + }; + for i in 0..num_bytes { + if i != 0 { + write!(f, ", ")?; + } + write!(f, "bytes_[{}]", pos + i)?; + } + writeln!(f, "]);")?; + pos += num_bytes; + } + Field::Opcode(_) => pos += 1, + Field::ExtMajor => pos += 1, + _ => unreachable!(), + } + } + } + FieldGroup::Single(field) => match field { + Field::Align(n) => writeln!(f, " parser.align({n})?;")?, + Field::Real(rf) => { + write!(f, " let {}: ", rf.name)?; + write_type(f, &rf.ty, protocols)?; + write!(f, " = ")?; + match &rf.ty { + Type::List(el, len) => { + writeln!(f, "{{")?; + write!(f, " let len: Option = ")?; + if let Some(len) = len { + write!(f, "Some(")?; + write_expr(f, len, "")?; + writeln!(f, " as _);")?; + } else { + writeln!(f, "None;")?; + } + if type_is_pod(el) { + writeln!(f, " parser.read_list_slice(len)?")?; + } else { + writeln!(f, " parser.read_list(len)?")?; + } + writeln!(f, " }};")?; + } + Type::String(len) => { + writeln!(f, "{{")?; + write!(f, " let len: usize = ")?; + write_expr(f, len, "")?; + writeln!(f, " as _;")?; + writeln!(f, " parser.read_string(len)?")?; + writeln!(f, " }};")?; + } + Type::Bitmask(n, bm) => { + write!(f, "{}::deserialize(parser, ", n)?; + write_expr(f, bm, "")?; + writeln!(f, " as _)?;")?; + } + Type::Enum(n, bm) => { + write!(f, "<")?; + write_type(f, n, protocols)?; + write!(f, ">::deserialize(parser, ")?; + write_expr(f, bm, "")?; + writeln!(f, " as _)?;")?; + } + _ => writeln!(f, "parser.unmarshal()?;")?, + } + } + _ => unreachable!(), + }, + } + } + writeln!(f, " Ok(Self {{")?; + for field in &s.fields { + if let Field::Real(rf) = field { + if rf.value.is_none() { + writeln!(f, " {},", rf.name)?; + } + } + } + writeln!(f, " }})")?; + } + writeln!(f, " }}")?; + } + writeln!(f, "}}")?; + Ok(()) +} + +pub fn main() -> Result<()> { + println!("cargo:rerun-if-changed=wire-xcon"); + + let mut f = open("wire_xcon.rs")?; + + let mut files = vec![]; + for file in std::fs::read_dir("wire-xcon")? { + files.push(file?.file_name()); + } + files.sort(); + + let mut protocols = Protocols { + extensions: vec![], + structs: vec![], + bitmasks: vec![], + enums: vec![], + requests: vec![], + types_by_name: Default::default(), + events_by_name: Default::default(), + events: vec![], + eventcopies: vec![], + }; + let mut ext_idx = 0; + + for file in files { + let path = format!("wire-xcon/{}", file.as_bytes().as_bstr()); + let protocol = parse_protocol(&path, &mut ext_idx) + .with_context(|| format!("While parsing {}", path))?; + for s in protocol.structs { + let s = Rc::new(s); + protocols.structs.push(s.clone()); + protocols + .types_by_name + .insert(s.name.clone(), NamedType::Struct(s)); + } + for s in protocol.bitmasks { + let s = Rc::new(s); + protocols.bitmasks.push(s.clone()); + protocols + .types_by_name + .insert(s.name.clone(), NamedType::Bitmask(s)); + } + for s in protocol.enums { + let s = Rc::new(s); + protocols.enums.push(s.clone()); + protocols + .types_by_name + .insert(s.name.clone(), NamedType::Enum(s)); + } + protocols.requests.extend(protocol.requests); + for s in protocol.events { + let s = Rc::new(s); + protocols.events.push(s.clone()); + protocols.events_by_name.insert(s.data.name.clone(), s); + } + protocols.eventcopies.extend(protocol.eventcopies); + if let Some(ext) = protocol.extension { + let mut ident = vec![]; + for c in ext.as_bytes() { + if matches!(*c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9') { + ident.push(*c); + } + } + protocols.extensions.push(Extension { + name: ext, + ident: ident.into(), + }); + } + } + + writeln!(f, "#[derive(Copy, Clone, Debug, Eq, PartialEq)]")?; + writeln!(f, "pub enum Extension {{")?; + for ext in &protocols.extensions { + writeln!(f, r#" {},"#, ext.ident)?; + } + writeln!(f, "}}")?; + writeln!(f)?; + writeln!(f, "impl Extension {{")?; + writeln!(f, " pub fn name(self) -> &'static str {{")?; + writeln!(f, " match self {{")?; + for ext in &protocols.extensions { + writeln!(f, r#" Self::{} => "{}","#, ext.ident, ext.name)?; + } + writeln!(f, " }}")?; + writeln!(f, " }}")?; + writeln!(f, "}}")?; + writeln!(f)?; + writeln!(f, "pub const EXTENSIONS: &[Extension] = &[")?; + for ext in &protocols.extensions { + writeln!(f, r#" Extension::{},"#, ext.ident)?; + } + writeln!(f, "];")?; + + for s in &protocols.bitmasks { + format_bitmask(&mut f, s, &protocols)?; + } + + for s in &protocols.structs { + format_struct(&mut f, s, &protocols, &StructUsecase::None)?; + } + + for s in &protocols.enums { + format_enum(&mut f, s, &protocols)?; + } + + for s in &protocols.requests { + format_request(&mut f, s, &protocols)?; + } + + for s in &protocols.events { + format_event(&mut f, s, &protocols)?; + } + + for s in &protocols.eventcopies { + format_eventcopy(&mut f, s, &protocols)?; + } + + Ok(()) +} diff --git a/default-config/src/lib.rs b/default-config/src/lib.rs index 3e54ddf4..cc53c5ff 100644 --- a/default-config/src/lib.rs +++ b/default-config/src/lib.rs @@ -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; diff --git a/src/async_engine.rs b/src/async_engine.rs index f5dde889..964f2523 100644 --- a/src/async_engine.rs +++ b/src/async_engine.rs @@ -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, diff --git a/src/backends/metal.rs b/src/backends/metal.rs index 57382d90..487fe2cc 100644 --- a/src/backends/metal.rs +++ b/src/backends/metal.rs @@ -237,7 +237,7 @@ impl InputDevice for MetalInputDevice { } fn grab(&self, _grab: bool) { - log::warn!("Metal backend does not support grabbing devices"); + // nothing } } diff --git a/src/backends/mod.rs b/src/backends/mod.rs index 2b5ff444..a175399c 100644 --- a/src/backends/mod.rs +++ b/src/backends/mod.rs @@ -1,2 +1,3 @@ pub mod metal; pub mod xorg; +pub mod xorgng; diff --git a/src/backends/xorgng.rs b/src/backends/xorgng.rs new file mode 100644 index 00000000..c3a3c6a3 --- /dev/null +++ b/src/backends/xorgng.rs @@ -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, + _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, + c: Rc, + outputs: CopyHashMap>, + seats: CopyHashMap>, + mouse_seats: CopyHashMap>, + ctx: Rc, + gbm: GbmDevice, + cursor: u32, + root: u32, + scheduled_present: AsyncQueue>, + grab_requests: AsyncQueue<(Rc, bool)>, +} + +impl XorgngBackend { + pub async fn run(state: &Rc) -> Result, 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) { + 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) { + loop { + let output = self.scheduled_present.pop().await; + self.present(&output).await; + } + } + + async fn grab_handler(self: Rc) { + 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) -> 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, 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, 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, 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, + 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, 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, 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, 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, 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) { + self.scheduled_present.push(output.clone()); + } + + async fn present(&self, output: &Rc) { + 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, 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, + 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, + 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, + 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, + window: u32, + removed: Cell, + width: Cell, + height: Cell, + serial: NumCell, + next_msc: Cell, + next_image: NumCell, + images: [XorgImage; 2], + cb: CloneCell>>, +} + +struct XorgImage { + pixmap: Cell, + fb: CloneCell>, + idle: Cell, + render_on_idle: Cell, + last_serial: Cell, +} + +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) { + self.cb.set(Some(cb)); + } +} + +struct XorgSeat { + kb_id: InputDeviceId, + mouse_id: InputDeviceId, + backend: Rc, + kb: u16, + mouse: u16, + removed: Cell, + kb_cb: CloneCell>>, + mouse_cb: CloneCell>>, + kb_events: RefCell>, + mouse_events: RefCell>, + button_map: CopyHashMap, +} + +struct XorgSeatKeyboard(Rc); + +struct XorgSeatMouse(Rc); + +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 { + self.0.kb_events.borrow_mut().pop_front() + } + + fn on_change(&self, cb: Rc) { + 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 { + self.0.mouse_events.borrow_mut().pop_front() + } + + fn on_change(&self, cb: Rc) { + self.0.mouse_cb.set(Some(cb)); + } + + fn grab(&self, _grab: bool) { + log::error!("Cannot grab xorg mouse"); + } +} diff --git a/src/dbus.rs b/src/dbus.rs index 46025301..e559d82e 100644 --- a/src/dbus.rs +++ b/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), } efrom!(DbusError, AsyncError); @@ -145,11 +147,10 @@ unsafe trait ReplyHandler { pub struct DbusSocket { bus_name: &'static str, fd: AsyncFd, + bufio: Rc, eng: Rc, next_serial: NumCell, - bufs: Stack>, unique_name: CloneCell>, - outgoing: AsyncQueue, reply_handlers: CopyHashMap>, incoming: Cell>>, outgoing_: Cell>>, @@ -237,11 +238,6 @@ impl Drop for DbusHolder { } } -struct DbusMessage { - fds: Vec>, - buf: Vec, -} - #[derive(Clone, Debug)] pub enum DynamicType { U8, @@ -354,7 +350,7 @@ impl> Reply { impl> Drop for Reply { fn drop(&mut self) { - self.socket.bufs.push(mem::take(&mut self.buf)); + self.socket.bufio.add_buf(mem::take(&mut self.buf)); } } diff --git a/src/dbus/holder.rs b/src/dbus/holder.rs index d04e9bcf..70004324 100644 --- a/src/dbus/holder.rs +++ b/src/dbus/holder.rs @@ -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(), diff --git a/src/dbus/incoming.rs b/src/dbus/incoming.rs index 1019aa62..6333248d 100644 --- a/src/dbus/incoming.rs +++ b/src/dbus/incoming.rs @@ -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) { 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, - - buf: Box<[MaybeUninit; 4096]>, - buf_start: usize, - buf_end: usize, - fds: VecDeque>, - cmsg: Box<[MaybeUninit; 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) -> 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(()) - } } diff --git a/src/dbus/outgoing.rs b/src/dbus/outgoing.rs index 17e2dcb3..b4910c96 100644 --- a/src/dbus/outgoing.rs +++ b/src/dbus/outgoing.rs @@ -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) { - 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, - - msgs: VecDeque, - cmsg: Vec>, - fds: Vec, - iovecs: VecStorage>, -} - -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::()); - 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(); } diff --git a/src/dbus/socket.rs b/src/dbus/socket.rs index c9650bd1..3d594ca4 100644 --- a/src/dbus/socket.rs +++ b/src/dbus/socket.rs @@ -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 = as Message>::unmarshal(parser)?; (self.0)(Ok(&msg)); - socket.bufs.push(buf); + socket.bufio.add_buf(buf); Ok(()) } } struct AsyncReplyHandler>(Rc>); +impl> AsyncReplyHandler { + fn complete(self: Box, res: Result, DbusError>) { + self.0.data.set(Some(res)); + if let Some(waker) = self.0.waker.take() { + waker.wake(); + } + } +} + unsafe impl ReplyHandler for AsyncReplyHandler where T: Message<'static>, @@ -376,16 +385,21 @@ where ) -> Result<(), DbusError> { let msg = 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(()) } } diff --git a/src/event_loop.rs b/src/event_loop.rs index bc113ca7..52fe2277 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -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) { diff --git a/src/logind.rs b/src/logind.rs index d571936f..b0f6263a 100644 --- a/src/logind.rs +++ b/src/logind.rs @@ -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::(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(&self, vtnr: u32, f: F) - where F: FnOnce(Result<&SwitchToReply, DbusError>) + 'static + where + F: FnOnce(Result<&SwitchToReply, DbusError>) + 'static, { self.socket.call( LOGIND_NAME, diff --git a/src/main.rs b/src/main.rs index 44acd41a..4564d9c2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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")] diff --git a/src/state.rs b/src/state.rs index a18cc2f0..890d7498 100644 --- a/src/state.rs +++ b/src/state.rs @@ -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; diff --git a/src/tasks/start_backend.rs b/src/tasks/start_backend.rs index bcc3c5c3..b0d701f7 100644 --- a/src/tasks/start_backend.rs +++ b/src/tasks/start_backend.rs @@ -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) { 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, diff --git a/src/utils/bufio.rs b/src/utils/bufio.rs new file mode 100644 index 00000000..628c693b --- /dev/null +++ b/src/utils/bufio.rs @@ -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>, + pub buf: Vec, +} + +struct MessageOffset { + msg: BufIoMessage, + offset: usize, +} + +pub struct BufIo { + fd: AsyncFd, + bufs: Stack>, + outgoing: AsyncQueue, +} + +pub struct BufIoIncoming { + bufio: Rc, + + buf: Box<[MaybeUninit; 4096]>, + buf_start: usize, + buf_end: usize, + pub fds: VecDeque>, + cmsg: Box<[MaybeUninit; 256]>, +} + +struct Outgoing { + bufio: Rc, + + msgs: VecDeque, + cmsg: Vec>, + fds: Vec, + iovecs: VecStorage>, +} + +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 { + let mut buf = self.bufs.pop().unwrap_or_default(); + buf.clear(); + buf + } + + pub fn add_buf(&self, buf: Vec) { + self.bufs.push(buf); + } + + pub fn send(&self, msg: BufIoMessage) { + self.outgoing.push(msg); + } + + pub async fn outgoing(self: Rc) -> 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) -> 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, + ) -> 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::()); + 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); + } + } + } +} diff --git a/src/utils/clonecell.rs b/src/utils/clonecell.rs index ffad3ea0..442175b9 100644 --- a/src/utils/clonecell.rs +++ b/src/utils/clonecell.rs @@ -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 { data: UnsafeCell, @@ -60,6 +60,7 @@ pub unsafe trait UnsafeCellCloneSafe: Clone {} unsafe impl UnsafeCellCloneSafe for Option {} unsafe impl UnsafeCellCloneSafe for Rc {} +unsafe impl UnsafeCellCloneSafe for Weak {} unsafe impl UnsafeCellCloneSafe for NodeRef {} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 6974af74..49db84ee 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -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; diff --git a/src/wire_xcon.rs b/src/wire_xcon.rs new file mode 100644 index 00000000..974b25d9 --- /dev/null +++ b/src/wire_xcon.rs @@ -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")); diff --git a/src/xcon.rs b/src/xcon.rs new file mode 100644 index 00000000..1c1f83cc --- /dev/null +++ b/src/xcon.rs @@ -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), + #[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), + #[error("All available xids have been used")] + XidExhausted, + #[error("Enum contains an unknown variant")] + UnknownEnumVariant, +} + +#[derive(Debug)] +struct ExtensionIdRange { + name: BString, + extension: Option, + first: u8, +} + +#[derive(Default, Debug)] +struct ExtensionData { + opcodes: [Option; EXTENSIONS.len()], + ext_by_opcode: AHashMap, + events: Vec, + errors: Vec, +} + +pub struct Xcon { + data: Rc, + outgoing: Cell>>, + incoming: Cell>>, + setup: Reply>, + extensions: Rc, + + xid_next: Cell, + xid_inc: u32, + xid_max: u32, +} + +struct XconData { + bufio: Rc, + next_serial: NumCell, + last_recv_serial: Cell, + reply_handlers: RefCell>>, + dead: Cell, + need_sync: Cell, + extensions: CloneCell>>, + xorg: CloneCell>, + events: AsyncQueue, +} + +pub struct Reply> { + bufio: Rc, + buf: Vec, + t: T::Generic<'static>, +} + +pub struct Event { + bufio: Rc, + ext: Option, + code: u16, + buf: Vec, +} + +impl Deref for Event { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + &self.buf + } +} + +impl Event { + pub fn ext(&self) -> Option { + self.ext + } + + pub fn code(&self) -> u16 { + self.code + } + + pub fn parse<'a, M: Message<'a>>(&'a self) -> Result { + 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> Reply { + pub fn get<'a>(&'a self) -> &'a T::Generic<'a> { + unsafe { mem::transmute(&self.t) } + } +} + +impl> Drop for Reply { + 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, + bufio: &Rc, + parser: &mut Parser<'static>, + buf: Vec, + ) -> Result<(), XconError>; + fn handle_noreply(self: Box, bufio: &Rc) -> Result<(), XconError>; + fn handle_error(self: Box, error: XconError); +} + +struct AsyncReplyHandler> { + serial: u64, + slot: Weak>, +} + +impl> AsyncReplyHandler { + fn done(self: Box, res: Result, XconError>) { + if let Some(slot) = self.slot.upgrade() { + slot.data.set(Some(res)); + if let Some(waker) = slot.waker.take() { + waker.wake(); + } + } + } +} + +unsafe impl> ReplyHandler for AsyncReplyHandler { + fn has_fds(&self) -> bool { + T::HAS_FDS + } + + fn serial(&self) -> u64 { + self.serial + } + + fn handle_result( + self: Box, + bufio: &Rc, + parser: &mut Parser<'static>, + buf: Vec, + ) -> Result<(), XconError> { + let msg = 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, bufio: &Rc) -> Result<(), XconError> { + if TypeId::of::>() == 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, error: XconError) { + self.done(Err(error)) + } +} + +struct AsyncReplySlot> { + data: Cell, XconError>>>, + waker: Cell>, +} + +pub struct AsyncReply> { + slot: Rc>, + xorg: Rc, +} + +impl> Future for AsyncReply { + type Output = Result, XconError>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + 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 { + 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) -> Result, 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::::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, + fd: &Rc, + auth_method: &[u8], + auth_value: &[u8], + ) -> Result, 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, t: &T) -> AsyncReply { + 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, + t: &T, + extensions: &ExtensionData, + ) -> AsyncReply { + 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) -> Result, 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 { + 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) +} diff --git a/src/xcon/consts.rs b/src/xcon/consts.rs new file mode 100644 index 00000000..e80b9f52 --- /dev/null +++ b/src/xcon/consts.rs @@ -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; diff --git a/src/xcon/formatter.rs b/src/xcon/formatter.rs new file mode 100644 index 00000000..db35787e --- /dev/null +++ b/src/xcon/formatter.rs @@ -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>, + buf: &'a mut Vec, + ext_opcode: u8, +} + +impl<'a> Formatter<'a> { + pub fn new(fds: &'a mut Vec>, buf: &'a mut Vec, 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(&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) { + self.fds.push(fd.clone()); + } +} diff --git a/src/xcon/incoming.rs b/src/xcon/incoming.rs new file mode 100644 index 00000000..0a6f4ee0 --- /dev/null +++ b/src/xcon/incoming.rs @@ -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, incoming: BufIoIncoming) { + let mut incoming = Incoming { + incoming, + socket: xorg, + ed: None, + }; + incoming.run().await; +} + +pub struct Incoming { + socket: Rc, + incoming: BufIoIncoming, + ed: Option>, +} + +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]) +} diff --git a/src/xcon/outgoing.rs b/src/xcon/outgoing.rs new file mode 100644 index 00000000..8452a940 --- /dev/null +++ b/src/xcon/outgoing.rs @@ -0,0 +1,10 @@ +use crate::xcon::XconData; +use crate::ErrorFmt; +use std::rc::Rc; + +pub(super) async fn handle_outgoing(socket: Rc) { + if let Err(e) = socket.bufio.clone().outgoing().await { + log::error!("{}", ErrorFmt(e)); + } + socket.kill(); +} diff --git a/src/xcon/parser.rs b/src/xcon/parser.rs new file mode 100644 index 00000000..78588b09 --- /dev/null +++ b/src/xcon/parser.rs @@ -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>, +} + +impl<'a> Parser<'a> { + pub fn new(buf: &'a [u8], fds: Vec>) -> 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>(&mut self) -> Result { + 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, 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 { + match uapi::pod_read_init(&self.buf[self.pos..]) { + Ok(v) => { + self.pos += mem::size_of::(); + Ok(v) + } + _ => Err(XconError::UnexpectedEof), + } + } + + pub fn read_list_slice + Pod>( + &mut self, + n: Option, + ) -> Result<&'a [T], XconError> { + let n = match n { + Some(n) => n, + _ => self.rem() / mem::size_of::(), + }; + let len = mem::size_of::() * n; + if len > self.rem() { + return Err(XconError::UnexpectedEof); + } + if self.buf[self.pos..].as_ptr() as usize & (mem::align_of::() - 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 + Clone>( + &mut self, + n: Option, + ) -> Result, 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(&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()) + } +} diff --git a/src/xcon/wire_type.rs b/src/xcon/wire_type.rs new file mode 100644 index 00000000..fac7e1ae --- /dev/null +++ b/src/xcon/wire_type.rs @@ -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 { + let _ = parser; + unimplemented() + } +} + +pub trait Request<'a>: Message<'a> { + type Reply: Message<'static>; + const EXTENSION: Option; + 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 { + 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 { + type Generic<'b> = Rc; + 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 { + parser.read_fd() + } +} diff --git a/src/xcon/xauthority.rs b/src/xcon/xauthority.rs new file mode 100644 index 00000000..3bf6116b --- /dev/null +++ b/src/xcon/xauthority.rs @@ -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, 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, 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 { + 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 { + 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 { + 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) + } +} diff --git a/wire-xcon/dri3.txt b/wire-xcon/dri3.txt new file mode 100644 index 00000000..5ebb1abc --- /dev/null +++ b/wire-xcon/dri3.txt @@ -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, +); diff --git a/wire-xcon/present.txt b/wire-xcon/present.txt new file mode 100644 index 00000000..f8ef38c5 --- /dev/null +++ b/wire-xcon/present.txt @@ -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, +); diff --git a/wire-xcon/xinput.txt b/wire-xcon/xinput.txt new file mode 100644 index 00000000..d3c6b86f --- /dev/null +++ b/wire-xcon/xinput.txt @@ -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, +); diff --git a/wire-xcon/xkb.txt b/wire-xcon/xkb.txt new file mode 100644 index 00000000..f87aab62 --- /dev/null +++ b/wire-xcon/xkb.txt @@ -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, +} diff --git a/wire-xcon/xproto.txt b/wire-xcon/xproto.txt new file mode 100644 index 00000000..39edb829 --- /dev/null +++ b/wire-xcon/xproto.txt @@ -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)))), +);