ei: add support for libei
This commit is contained in:
parent
084fe50259
commit
40e87f8f91
69 changed files with 4340 additions and 72 deletions
|
|
@ -30,6 +30,7 @@ mod tokens;
|
||||||
mod vulkan;
|
mod vulkan;
|
||||||
mod wire;
|
mod wire;
|
||||||
mod wire_dbus;
|
mod wire_dbus;
|
||||||
|
mod wire_ei;
|
||||||
mod wire_xcon;
|
mod wire_xcon;
|
||||||
|
|
||||||
fn open(s: &str) -> io::Result<BufWriter<File>> {
|
fn open(s: &str) -> io::Result<BufWriter<File>> {
|
||||||
|
|
@ -46,6 +47,7 @@ fn open(s: &str) -> io::Result<BufWriter<File>> {
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
wire::main()?;
|
wire::main()?;
|
||||||
|
wire_ei::main()?;
|
||||||
wire_dbus::main()?;
|
wire_dbus::main()?;
|
||||||
wire_xcon::main()?;
|
wire_xcon::main()?;
|
||||||
enums::main()?;
|
enums::main()?;
|
||||||
|
|
|
||||||
679
build/wire_ei.rs
Normal file
679
build/wire_ei.rs
Normal file
|
|
@ -0,0 +1,679 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
open,
|
||||||
|
tokens::{tokenize, Symbol, Token, TokenKind, TreeDelim},
|
||||||
|
},
|
||||||
|
anyhow::{bail, Context, Result},
|
||||||
|
std::{fs::DirEntry, io::Write, os::unix::ffi::OsStrExt},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Lined<T> {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
line: u32,
|
||||||
|
val: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum Type {
|
||||||
|
Id(String),
|
||||||
|
U32,
|
||||||
|
I32,
|
||||||
|
U64,
|
||||||
|
I64,
|
||||||
|
F32,
|
||||||
|
Str,
|
||||||
|
OptStr,
|
||||||
|
Fd,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Field {
|
||||||
|
name: String,
|
||||||
|
ty: Lined<Type>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Message {
|
||||||
|
name: String,
|
||||||
|
camel_name: String,
|
||||||
|
safe_name: String,
|
||||||
|
id: u32,
|
||||||
|
fields: Vec<Lined<Field>>,
|
||||||
|
attribs: MessageAttribs,
|
||||||
|
has_reference_type: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct MessageAttribs {
|
||||||
|
since: Option<u32>,
|
||||||
|
context: Option<&'static str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Parser<'a> {
|
||||||
|
pos: usize,
|
||||||
|
tokens: &'a [Token<'a>],
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ParseResult {
|
||||||
|
requests: Vec<Lined<Message>>,
|
||||||
|
events: Vec<Lined<Message>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Parser<'a> {
|
||||||
|
fn parse(&mut self) -> Result<ParseResult> {
|
||||||
|
let mut requests = vec![];
|
||||||
|
let mut events = vec![];
|
||||||
|
while !self.eof() {
|
||||||
|
let (line, ty) = self.expect_ident()?;
|
||||||
|
let res = match ty.as_bytes() {
|
||||||
|
b"request" => &mut requests,
|
||||||
|
b"event" => &mut events,
|
||||||
|
_ => bail!("In line {}: Unexpected entry {:?}", line, ty),
|
||||||
|
};
|
||||||
|
res.push(self.parse_message(res.len() as _)?);
|
||||||
|
}
|
||||||
|
Ok(ParseResult { requests, events })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eof(&self) -> bool {
|
||||||
|
self.pos == self.tokens.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn not_eof(&self) -> Result<()> {
|
||||||
|
if self.eof() {
|
||||||
|
bail!("Unexpected eof");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn yes_eof(&self) -> Result<()> {
|
||||||
|
if !self.eof() {
|
||||||
|
bail!(
|
||||||
|
"Unexpected trailing tokens in line {}",
|
||||||
|
self.tokens[self.pos].line
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_message_attribs(&mut self, attribs: &mut MessageAttribs) -> Result<()> {
|
||||||
|
let (_, tokens) = self.expect_tree(TreeDelim::Paren)?;
|
||||||
|
let mut parser = Parser { pos: 0, tokens };
|
||||||
|
while !parser.eof() {
|
||||||
|
let (line, name) = parser.expect_ident()?;
|
||||||
|
match name {
|
||||||
|
"since" => {
|
||||||
|
parser.expect_symbol(Symbol::Equals)?;
|
||||||
|
attribs.since = Some(parser.expect_number()?.1)
|
||||||
|
}
|
||||||
|
"receiver" => attribs.context = Some("Receiver"),
|
||||||
|
"sender" => attribs.context = Some("Sender"),
|
||||||
|
_ => bail!("In line {}: Unexpected attribute {}", line, name),
|
||||||
|
}
|
||||||
|
if !parser.eof() {
|
||||||
|
parser.expect_symbol(Symbol::Comma)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_message(&mut self, id: u32) -> Result<Lined<Message>> {
|
||||||
|
let (line, name) = self.expect_ident()?;
|
||||||
|
let res: Result<_> = (|| {
|
||||||
|
self.not_eof()?;
|
||||||
|
let mut attribs = MessageAttribs::default();
|
||||||
|
if let TokenKind::Tree {
|
||||||
|
delim: TreeDelim::Paren,
|
||||||
|
..
|
||||||
|
} = self.tokens[self.pos].kind
|
||||||
|
{
|
||||||
|
self.parse_message_attribs(&mut attribs)?;
|
||||||
|
}
|
||||||
|
let (_, body) = self.expect_tree(TreeDelim::Brace)?;
|
||||||
|
let mut parser = Parser {
|
||||||
|
pos: 0,
|
||||||
|
tokens: body,
|
||||||
|
};
|
||||||
|
let mut fields = vec![];
|
||||||
|
while !parser.eof() {
|
||||||
|
fields.push(parser.parse_field()?);
|
||||||
|
}
|
||||||
|
let has_reference_type = fields.iter().any(|f| match &f.val.ty.val {
|
||||||
|
Type::OptStr | Type::Str => true,
|
||||||
|
_ => false,
|
||||||
|
});
|
||||||
|
let safe_name = match name {
|
||||||
|
"move" => "move_",
|
||||||
|
_ => name,
|
||||||
|
};
|
||||||
|
Ok(Lined {
|
||||||
|
line,
|
||||||
|
val: Message {
|
||||||
|
name: name.to_owned(),
|
||||||
|
camel_name: to_camel(name),
|
||||||
|
safe_name: safe_name.to_string(),
|
||||||
|
id,
|
||||||
|
fields,
|
||||||
|
attribs,
|
||||||
|
has_reference_type,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})();
|
||||||
|
res.with_context(|| format!("While parsing message starting at line {}", line))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_field(&mut self) -> Result<Lined<Field>> {
|
||||||
|
let (line, name) = self.expect_ident()?;
|
||||||
|
let res: Result<_> = (|| {
|
||||||
|
self.expect_symbol(Symbol::Colon)?;
|
||||||
|
let ty = self.parse_type()?;
|
||||||
|
if !self.eof() {
|
||||||
|
self.expect_symbol(Symbol::Comma)?;
|
||||||
|
}
|
||||||
|
Ok(Lined {
|
||||||
|
line,
|
||||||
|
val: Field {
|
||||||
|
name: name.to_owned(),
|
||||||
|
ty,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})();
|
||||||
|
res.with_context(|| format!("While parsing field starting at line {}", line))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expect_ident(&mut self) -> Result<(u32, &'a str)> {
|
||||||
|
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_number(&mut self) -> Result<(u32, u32)> {
|
||||||
|
self.not_eof()?;
|
||||||
|
let token = &self.tokens[self.pos];
|
||||||
|
self.pos += 1;
|
||||||
|
match &token.kind {
|
||||||
|
TokenKind::Num(n) => Ok((token.line, *n)),
|
||||||
|
k => bail!(
|
||||||
|
"In line {}: Expected number, 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<Lined<Type>> {
|
||||||
|
self.not_eof()?;
|
||||||
|
let (line, ty) = self.expect_ident()?;
|
||||||
|
let ty = match ty.as_bytes() {
|
||||||
|
b"u32" => Type::U32,
|
||||||
|
b"i32" => Type::I32,
|
||||||
|
b"u64" => Type::U64,
|
||||||
|
b"i64" => Type::I64,
|
||||||
|
b"f32" => Type::F32,
|
||||||
|
b"str" => Type::Str,
|
||||||
|
b"optstr" => Type::OptStr,
|
||||||
|
b"fd" => Type::Fd,
|
||||||
|
b"id" => {
|
||||||
|
let (_, body) = self.expect_tree(TreeDelim::Paren)?;
|
||||||
|
let ident: Result<_> = (|| {
|
||||||
|
let mut parser = Parser {
|
||||||
|
pos: 0,
|
||||||
|
tokens: body,
|
||||||
|
};
|
||||||
|
let id = parser.expect_ident()?;
|
||||||
|
parser.yes_eof()?;
|
||||||
|
Ok(id)
|
||||||
|
})();
|
||||||
|
let (_, ident) = ident.with_context(|| {
|
||||||
|
format!("While parsing identifier starting in line {}", line)
|
||||||
|
})?;
|
||||||
|
Type::Id(to_camel(ident))
|
||||||
|
}
|
||||||
|
_ => bail!("Unknown type {}", ty),
|
||||||
|
};
|
||||||
|
Ok(Lined { line, val: ty })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_messages(s: &[u8]) -> Result<ParseResult> {
|
||||||
|
let tokens = tokenize(s)?;
|
||||||
|
let mut parser = Parser {
|
||||||
|
pos: 0,
|
||||||
|
tokens: &tokens,
|
||||||
|
};
|
||||||
|
parser.parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_camel(s: &str) -> String {
|
||||||
|
let mut last_was_underscore = true;
|
||||||
|
let mut res = String::new();
|
||||||
|
for mut b in s.as_bytes().iter().copied() {
|
||||||
|
if b == b'_' {
|
||||||
|
last_was_underscore = true;
|
||||||
|
} else {
|
||||||
|
if last_was_underscore {
|
||||||
|
b = b.to_ascii_uppercase()
|
||||||
|
}
|
||||||
|
res.push(b as char);
|
||||||
|
last_was_underscore = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_type<W: Write>(f: &mut W, ty: &Type) -> Result<()> {
|
||||||
|
let ty = match ty {
|
||||||
|
Type::Id(id) => {
|
||||||
|
write!(f, "{}Id", id)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Type::U32 => "u32",
|
||||||
|
Type::I32 => "i32",
|
||||||
|
Type::U64 => "u64",
|
||||||
|
Type::I64 => "i64",
|
||||||
|
Type::F32 => "f32",
|
||||||
|
Type::Str => "&'a str",
|
||||||
|
Type::OptStr => "Option<&'a str>",
|
||||||
|
Type::Fd => "Rc<OwnedFd>",
|
||||||
|
};
|
||||||
|
write!(f, "{}", ty)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_field<W: Write>(f: &mut W, field: &Field) -> Result<()> {
|
||||||
|
write!(f, " pub {}: ", field.name)?;
|
||||||
|
write_type(f, &field.ty.val)?;
|
||||||
|
writeln!(f, ",")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_message_type<W: Write>(
|
||||||
|
f: &mut W,
|
||||||
|
obj: &str,
|
||||||
|
message: &Message,
|
||||||
|
needs_lifetime: bool,
|
||||||
|
) -> Result<()> {
|
||||||
|
let lifetime = if needs_lifetime { "<'a>" } else { "" };
|
||||||
|
writeln!(f, " pub struct {}{} {{", message.camel_name, lifetime)?;
|
||||||
|
writeln!(f, " pub self_id: {}Id,", obj)?;
|
||||||
|
for field in &message.fields {
|
||||||
|
write_field(f, &field.val)?;
|
||||||
|
}
|
||||||
|
writeln!(f, " }}")?;
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
" impl{} std::fmt::Debug for {}{} {{",
|
||||||
|
lifetime, message.camel_name, lifetime
|
||||||
|
)?;
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
" fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {{"
|
||||||
|
)?;
|
||||||
|
write!(f, r#" write!(fmt, "{}("#, message.name)?;
|
||||||
|
for (i, field) in message.fields.iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
write!(f, ", ")?;
|
||||||
|
}
|
||||||
|
let formatter = match &field.val.ty.val {
|
||||||
|
Type::OptStr | Type::Str | Type::Fd => "{:?}",
|
||||||
|
Type::Id(_) => "{:x}",
|
||||||
|
_ => "{}",
|
||||||
|
};
|
||||||
|
write!(f, "{}: {}", field.val.name, formatter)?;
|
||||||
|
}
|
||||||
|
write!(f, r#")""#)?;
|
||||||
|
for field in &message.fields {
|
||||||
|
write!(f, ", self.{}", field.val.name)?;
|
||||||
|
}
|
||||||
|
writeln!(f, r")")?;
|
||||||
|
writeln!(f, " }}")?;
|
||||||
|
writeln!(f, " }}")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_message<W: Write>(f: &mut W, obj: &str, message: &Message) -> Result<()> {
|
||||||
|
let has_reference_type = message.has_reference_type;
|
||||||
|
let uppercase = message.name.to_ascii_uppercase();
|
||||||
|
writeln!(f)?;
|
||||||
|
writeln!(f, " pub const {}: u32 = {};", uppercase, message.id)?;
|
||||||
|
write_message_type(f, obj, message, has_reference_type)?;
|
||||||
|
let lifetime = if has_reference_type { "<'a>" } else { "" };
|
||||||
|
let lifetime_b = if has_reference_type { "<'b>" } else { "" };
|
||||||
|
let parser = if message.fields.len() > 0 {
|
||||||
|
"parser"
|
||||||
|
} else {
|
||||||
|
"_parser"
|
||||||
|
};
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
" impl<'a> EiRequestParser<'a> for {}{} {{",
|
||||||
|
message.camel_name, lifetime
|
||||||
|
)?;
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
" type Generic<'b> = {}{};",
|
||||||
|
message.camel_name, lifetime_b,
|
||||||
|
)?;
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
" fn parse({}: &mut EiMsgParser<'_, 'a>) -> Result<Self, EiMsgParserError> {{",
|
||||||
|
parser
|
||||||
|
)?;
|
||||||
|
writeln!(f, " Ok(Self {{")?;
|
||||||
|
writeln!(f, " self_id: {}Id::NONE,", obj)?;
|
||||||
|
for field in &message.fields {
|
||||||
|
let p = match &field.val.ty.val {
|
||||||
|
Type::Id(_) => "object",
|
||||||
|
Type::U32 => "uint",
|
||||||
|
Type::I32 => "int",
|
||||||
|
Type::U64 => "ulong",
|
||||||
|
Type::I64 => "long",
|
||||||
|
Type::F32 => "float",
|
||||||
|
Type::OptStr => "optstr",
|
||||||
|
Type::Str => "str",
|
||||||
|
Type::Fd => "fd",
|
||||||
|
};
|
||||||
|
writeln!(f, " {}: parser.{}()?,", field.val.name, p)?;
|
||||||
|
}
|
||||||
|
writeln!(f, " }})")?;
|
||||||
|
writeln!(f, " }}")?;
|
||||||
|
writeln!(f, " }}")?;
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
" impl{} EiEventFormatter for {}{} {{",
|
||||||
|
lifetime, message.camel_name, lifetime
|
||||||
|
)?;
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
" fn format(self, fmt: &mut EiMsgFormatter<'_>) {{"
|
||||||
|
)?;
|
||||||
|
writeln!(f, " fmt.header(self.self_id, {});", uppercase)?;
|
||||||
|
fn write_fmt_expr<W: Write>(f: &mut W, prefix: &str, ty: &Type, access: &str) -> Result<()> {
|
||||||
|
let p = match ty {
|
||||||
|
Type::Id(_) => "object",
|
||||||
|
Type::U32 => "uint",
|
||||||
|
Type::I32 => "int",
|
||||||
|
Type::U64 => "ulong",
|
||||||
|
Type::I64 => "long",
|
||||||
|
Type::F32 => "float",
|
||||||
|
Type::OptStr => "optstr",
|
||||||
|
Type::Str => "string",
|
||||||
|
Type::Fd => "fd",
|
||||||
|
};
|
||||||
|
writeln!(f, " {prefix}fmt.{p}({access});")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
for field in &message.fields {
|
||||||
|
write_fmt_expr(
|
||||||
|
f,
|
||||||
|
"",
|
||||||
|
&field.val.ty.val,
|
||||||
|
&format!("self.{}", field.val.name),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
writeln!(f, " }}")?;
|
||||||
|
writeln!(f, " fn id(&self) -> EiObjectId {{")?;
|
||||||
|
writeln!(f, " self.self_id.into()")?;
|
||||||
|
writeln!(f, " }}")?;
|
||||||
|
writeln!(f, " fn interface(&self) -> EiInterface {{")?;
|
||||||
|
writeln!(f, " {}", obj)?;
|
||||||
|
writeln!(f, " }}")?;
|
||||||
|
writeln!(f, " }}")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_request_handler<W: Write>(
|
||||||
|
f: &mut W,
|
||||||
|
camel_obj_name: &str,
|
||||||
|
messages: &ParseResult,
|
||||||
|
) -> Result<()> {
|
||||||
|
writeln!(f)?;
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
" pub trait {camel_obj_name}RequestHandler: crate::ei::ei_object::EiObject + Sized {{"
|
||||||
|
)?;
|
||||||
|
writeln!(f, " type Error: std::error::Error;")?;
|
||||||
|
for message in &messages.requests {
|
||||||
|
let msg = &message.val;
|
||||||
|
let lt = match msg.has_reference_type {
|
||||||
|
true => "<'_>",
|
||||||
|
false => "",
|
||||||
|
};
|
||||||
|
writeln!(f)?;
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
" fn {}(&self, req: {}{lt}, _slf: &Rc<Self>) -> Result<(), Self::Error>;",
|
||||||
|
msg.safe_name, msg.camel_name
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
writeln!(f)?;
|
||||||
|
writeln!(f, " #[inline(always)]")?;
|
||||||
|
writeln!(f, " fn handle_request_impl(")?;
|
||||||
|
writeln!(f, " self: Rc<Self>,")?;
|
||||||
|
writeln!(f, " client: &crate::ei::ei_client::EiClient,")?;
|
||||||
|
writeln!(f, " req: u32,")?;
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
" parser: crate::utils::buffd::EiMsgParser<'_, '_>,"
|
||||||
|
)?;
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
" ) -> Result<(), crate::ei::ei_client::EiClientError> {{"
|
||||||
|
)?;
|
||||||
|
if messages.requests.is_empty() {
|
||||||
|
writeln!(f, " #![allow(unused_variables)]")?;
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
" Err(crate::ei::ei_client::EiClientError::InvalidMethod)"
|
||||||
|
)?;
|
||||||
|
} else {
|
||||||
|
writeln!(f, " let method;")?;
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
" let error: Box<dyn std::error::Error> = match req {{"
|
||||||
|
)?;
|
||||||
|
for message in &messages.requests {
|
||||||
|
let msg = &message.val;
|
||||||
|
write!(f, " {} ", msg.id)?;
|
||||||
|
let mut have_cond = false;
|
||||||
|
if let Some(since) = msg.attribs.since {
|
||||||
|
write!(f, "if self.version() >= {since} ")?;
|
||||||
|
have_cond = true;
|
||||||
|
}
|
||||||
|
if let Some(context) = msg.attribs.context {
|
||||||
|
if have_cond {
|
||||||
|
write!(f, "&&")?;
|
||||||
|
} else {
|
||||||
|
write!(f, "if")?;
|
||||||
|
}
|
||||||
|
write!(f, " self.context() == EiContext::{context} ")?;
|
||||||
|
}
|
||||||
|
writeln!(f, "=> {{")?;
|
||||||
|
writeln!(f, " method = \"{}\";", msg.name)?;
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
" match client.parse(&*self, parser) {{"
|
||||||
|
)?;
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
" Ok(req) => match self.{}(req, &self) {{",
|
||||||
|
msg.safe_name
|
||||||
|
)?;
|
||||||
|
writeln!(f, " Ok(()) => return Ok(()),")?;
|
||||||
|
writeln!(f, " Err(e) => Box::new(e),")?;
|
||||||
|
writeln!(f, " }},")?;
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
" Err(e) => Box::new(crate::ei::ei_client::EiParserError(e)),"
|
||||||
|
)?;
|
||||||
|
writeln!(f, " }}")?;
|
||||||
|
writeln!(f, " }},")?;
|
||||||
|
}
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
" _ => return Err(crate::ei::ei_client::EiClientError::InvalidMethod),"
|
||||||
|
)?;
|
||||||
|
writeln!(f, " }};")?;
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
" Err(crate::ei::ei_client::EiClientError::MethodError {{"
|
||||||
|
)?;
|
||||||
|
writeln!(f, " interface: {camel_obj_name},")?;
|
||||||
|
writeln!(f, " method,")?;
|
||||||
|
writeln!(f, " error,")?;
|
||||||
|
writeln!(f, " }})")?;
|
||||||
|
}
|
||||||
|
writeln!(f, " }}")?;
|
||||||
|
writeln!(f, " }}")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_file<W: Write>(f: &mut W, file: &DirEntry, obj_names: &mut Vec<String>) -> Result<()> {
|
||||||
|
let file_name = file.file_name();
|
||||||
|
let file_name = std::str::from_utf8(file_name.as_bytes())?;
|
||||||
|
println!("cargo:rerun-if-changed=wire-ei/{}", file_name);
|
||||||
|
let obj_name = file_name.split(".").next().unwrap();
|
||||||
|
obj_names.push(obj_name.to_string());
|
||||||
|
let camel_obj_name = to_camel(obj_name);
|
||||||
|
writeln!(f)?;
|
||||||
|
writeln!(f, "ei_id!({}Id);", camel_obj_name)?;
|
||||||
|
writeln!(f)?;
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
"pub const {}: EiInterface = EiInterface(\"{}\");",
|
||||||
|
camel_obj_name, obj_name
|
||||||
|
)?;
|
||||||
|
let contents = std::fs::read(file.path())?;
|
||||||
|
let messages = parse_messages(&contents)?;
|
||||||
|
writeln!(f)?;
|
||||||
|
writeln!(f, "pub mod {} {{", obj_name)?;
|
||||||
|
writeln!(f, " use super::*;")?;
|
||||||
|
for message in messages.requests.iter().chain(messages.events.iter()) {
|
||||||
|
write_message(f, &camel_obj_name, &message.val)?;
|
||||||
|
}
|
||||||
|
write_request_handler(f, &camel_obj_name, &messages)?;
|
||||||
|
writeln!(f, "}}")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_interface_versions<W: Write>(f: &mut W, obj_names: &[String]) -> Result<()> {
|
||||||
|
writeln!(f)?;
|
||||||
|
writeln!(f, "pub struct EiInterfaceVersions {{")?;
|
||||||
|
for obj_name in obj_names {
|
||||||
|
writeln!(f, " pub {obj_name}: EiInterfaceVersion,")?;
|
||||||
|
}
|
||||||
|
writeln!(f, "}}")?;
|
||||||
|
writeln!(f)?;
|
||||||
|
writeln!(f, "impl EiInterfaceVersions {{")?;
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
" pub fn for_each(&self, mut f: impl FnMut(EiInterface, &EiInterfaceVersion)) {{"
|
||||||
|
)?;
|
||||||
|
for obj_name in obj_names {
|
||||||
|
let camel = to_camel(obj_name);
|
||||||
|
writeln!(f, " f(crate::wire_ei::{camel}, &self.{obj_name});")?;
|
||||||
|
}
|
||||||
|
writeln!(f, " }}")?;
|
||||||
|
writeln!(f)?;
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
" pub fn match_(&self, name: &str, f: impl FnOnce(&EiInterfaceVersion)) -> bool {{"
|
||||||
|
)?;
|
||||||
|
writeln!(f, " match name {{")?;
|
||||||
|
for obj_name in obj_names {
|
||||||
|
writeln!(f, " \"{obj_name}\" => f(&self.{obj_name}),")?;
|
||||||
|
}
|
||||||
|
writeln!(f, " _ => return false,")?;
|
||||||
|
writeln!(f, " }}")?;
|
||||||
|
writeln!(f, " true")?;
|
||||||
|
writeln!(f, " }}")?;
|
||||||
|
for obj_name in obj_names {
|
||||||
|
writeln!(f)?;
|
||||||
|
writeln!(f, " #[allow(dead_code)]")?;
|
||||||
|
writeln!(f, " pub fn {obj_name}(&self) -> EiVersion {{")?;
|
||||||
|
writeln!(f, " self.{obj_name}.version.get()")?;
|
||||||
|
writeln!(f, " }}")?;
|
||||||
|
}
|
||||||
|
writeln!(f, "}}")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() -> Result<()> {
|
||||||
|
let mut f = open("wire_ei.rs")?;
|
||||||
|
writeln!(f, "use std::rc::Rc;")?;
|
||||||
|
writeln!(f, "use uapi::OwnedFd;")?;
|
||||||
|
writeln!(f, "use crate::ei::{{EiContext, EiInterfaceVersion}};")?;
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
"use crate::ei::ei_client::{{EiEventFormatter, EiRequestParser}};"
|
||||||
|
)?;
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
"use crate::ei::ei_object::{{EiObjectId, EiInterface, EiVersion}};"
|
||||||
|
)?;
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
"use crate::utils::buffd::{{EiMsgFormatter, EiMsgParser, EiMsgParserError}};"
|
||||||
|
)?;
|
||||||
|
println!("cargo:rerun-if-changed=wire-ei");
|
||||||
|
let mut files = vec![];
|
||||||
|
for file in std::fs::read_dir("wire-ei")? {
|
||||||
|
files.push(file?);
|
||||||
|
}
|
||||||
|
files.sort_by_key(|f| f.file_name());
|
||||||
|
let mut obj_names = vec![];
|
||||||
|
for file in files {
|
||||||
|
write_file(&mut f, &file, &mut obj_names)
|
||||||
|
.with_context(|| format!("While processing {}", file.path().display()))?;
|
||||||
|
}
|
||||||
|
write_interface_versions(&mut f, &obj_names).context("Could not write interface versions")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
@ -992,6 +992,10 @@ impl Client {
|
||||||
keymap
|
keymap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_ei_socket_enabled(&self, enabled: bool) {
|
||||||
|
self.send(&ClientMessage::SetEiSocketEnabled { enabled })
|
||||||
|
}
|
||||||
|
|
||||||
pub fn latch<F: FnOnce() + 'static>(&self, seat: Seat, f: F) {
|
pub fn latch<F: FnOnce() + 'static>(&self, seat: Seat, f: F) {
|
||||||
if !self.feat_mod_mask.get() {
|
if !self.feat_mod_mask.get() {
|
||||||
log::error!("compositor does not support latching");
|
log::error!("compositor does not support latching");
|
||||||
|
|
|
||||||
|
|
@ -506,6 +506,9 @@ pub enum ClientMessage<'a> {
|
||||||
device: InputDevice,
|
device: InputDevice,
|
||||||
matrix: [[f32; 3]; 2],
|
matrix: [[f32; 3]; 2],
|
||||||
},
|
},
|
||||||
|
SetEiSocketEnabled {
|
||||||
|
enabled: bool,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -564,3 +564,12 @@ pub enum SwitchEvent {
|
||||||
/// event of this kind is generated.
|
/// event of this kind is generated.
|
||||||
ConvertedToTablet,
|
ConvertedToTablet,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enables or disables the unauthenticated libei socket.
|
||||||
|
///
|
||||||
|
/// Even if the socket is disabled, application can still request access via the portal.
|
||||||
|
///
|
||||||
|
/// The default is `false`.
|
||||||
|
pub fn set_libei_socket_enabled(enabled: bool) {
|
||||||
|
get!().set_ei_socket_enabled(enabled);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
- Add support for adaptive sync.
|
- Add support for adaptive sync.
|
||||||
- Add support for tearing.
|
- Add support for tearing.
|
||||||
- Add support for touch input.
|
- Add support for touch input.
|
||||||
|
- Add support for libei.
|
||||||
|
|
||||||
# 1.4.0 (2024-07-07)
|
# 1.4.0 (2024-07-07)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -418,7 +418,9 @@ pub enum InputEvent {
|
||||||
time_usec: u64,
|
time_usec: u64,
|
||||||
id: i32,
|
id: i32,
|
||||||
},
|
},
|
||||||
TouchFrame,
|
TouchFrame {
|
||||||
|
time_usec: u64,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum DrmEvent {
|
pub enum DrmEvent {
|
||||||
|
|
|
||||||
|
|
@ -582,7 +582,9 @@ impl MetalBackend {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_touch_frame(self: &Rc<Self>, event: LibInputEvent) {
|
fn handle_touch_frame(self: &Rc<Self>, event: LibInputEvent) {
|
||||||
let (_, dev) = unpack!(self, event, touch_event);
|
let (event, dev) = unpack!(self, event, touch_event);
|
||||||
dev.event(InputEvent::TouchFrame)
|
dev.event(InputEvent::TouchFrame {
|
||||||
|
time_usec: event.time_usec(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,11 @@ use {
|
||||||
errorfmt::ErrorFmt,
|
errorfmt::ErrorFmt,
|
||||||
numcell::NumCell,
|
numcell::NumCell,
|
||||||
pending_serial::PendingSerial,
|
pending_serial::PendingSerial,
|
||||||
trim::AsciiTrim,
|
pid_info::{get_pid_info, get_socket_creds, PidInfo},
|
||||||
},
|
},
|
||||||
wire::WlRegistryId,
|
wire::WlRegistryId,
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
bstr::ByteSlice,
|
|
||||||
std::{
|
std::{
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
collections::VecDeque,
|
collections::VecDeque,
|
||||||
|
|
@ -123,22 +122,8 @@ impl Clients {
|
||||||
effective_caps: ClientCaps,
|
effective_caps: ClientCaps,
|
||||||
bounding_caps: ClientCaps,
|
bounding_caps: ClientCaps,
|
||||||
) -> Result<(), ClientError> {
|
) -> Result<(), ClientError> {
|
||||||
let (uid, pid) = {
|
let Some((uid, pid)) = get_socket_creds(&socket) else {
|
||||||
let mut cred = c::ucred {
|
return Ok(());
|
||||||
pid: 0,
|
|
||||||
uid: 0,
|
|
||||||
gid: 0,
|
|
||||||
};
|
|
||||||
match uapi::getsockopt(socket.raw(), c::SOL_SOCKET, c::SO_PEERCRED, &mut cred) {
|
|
||||||
Ok(_) => (cred.uid, cred.pid),
|
|
||||||
Err(e) => {
|
|
||||||
log::error!(
|
|
||||||
"Cannot determine peer credentials of new connection: {:?}",
|
|
||||||
crate::utils::oserror::OsError::from(e)
|
|
||||||
);
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
self.spawn2(
|
self.spawn2(
|
||||||
id,
|
id,
|
||||||
|
|
@ -274,12 +259,6 @@ pub trait RequestParser<'a>: Debug + Sized {
|
||||||
fn parse(parser: &mut MsgParser<'_, 'a>) -> Result<Self, MsgParserError>;
|
fn parse(parser: &mut MsgParser<'_, 'a>) -> Result<Self, MsgParserError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PidInfo {
|
|
||||||
pub _uid: c::uid_t,
|
|
||||||
pub pid: c::pid_t,
|
|
||||||
pub comm: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Client {
|
pub struct Client {
|
||||||
pub id: ClientId,
|
pub id: ClientId,
|
||||||
pub state: Rc<State>,
|
pub state: Rc<State>,
|
||||||
|
|
@ -516,18 +495,3 @@ pub trait WaylandObjectLookup: Copy + Into<ObjectId> {
|
||||||
|
|
||||||
fn lookup(client: &Client, id: Self) -> Option<Rc<Self::Object>>;
|
fn lookup(client: &Client, id: Self) -> Option<Rc<Self::Object>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_pid_info(uid: c::uid_t, pid: c::pid_t) -> PidInfo {
|
|
||||||
let comm = match std::fs::read(format!("/proc/{}/comm", pid)) {
|
|
||||||
Ok(name) => name.trim().as_bstr().to_string(),
|
|
||||||
Err(e) => {
|
|
||||||
log::warn!("Could not read `comm` of pid {}: {}", pid, ErrorFmt(e));
|
|
||||||
"Unknown".to_string()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
PidInfo {
|
|
||||||
_uid: uid,
|
|
||||||
pid,
|
|
||||||
comm,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ use {
|
||||||
config::ConfigProxy,
|
config::ConfigProxy,
|
||||||
damage::{visualize_damage, DamageVisualizer},
|
damage::{visualize_damage, DamageVisualizer},
|
||||||
dbus::Dbus,
|
dbus::Dbus,
|
||||||
|
ei::ei_client::EiClients,
|
||||||
forker,
|
forker,
|
||||||
globals::Globals,
|
globals::Globals,
|
||||||
ifs::{
|
ifs::{
|
||||||
|
|
@ -109,6 +110,7 @@ pub enum CompositorError {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const WAYLAND_DISPLAY: &str = "WAYLAND_DISPLAY";
|
pub const WAYLAND_DISPLAY: &str = "WAYLAND_DISPLAY";
|
||||||
|
pub const LIBEI_SOCKET: &str = "LIBEI_SOCKET";
|
||||||
pub const DISPLAY: &str = "DISPLAY";
|
pub const DISPLAY: &str = "DISPLAY";
|
||||||
|
|
||||||
const STATIC_VARS: &[(&str, &str)] = &[
|
const STATIC_VARS: &[(&str, &str)] = &[
|
||||||
|
|
@ -251,6 +253,11 @@ fn start_compositor2(
|
||||||
default_vrr_mode: Cell::new(VrrMode::NEVER),
|
default_vrr_mode: Cell::new(VrrMode::NEVER),
|
||||||
default_vrr_cursor_hz: Cell::new(None),
|
default_vrr_cursor_hz: Cell::new(None),
|
||||||
default_tearing_mode: Cell::new(TearingMode::VARIANT_3),
|
default_tearing_mode: Cell::new(TearingMode::VARIANT_3),
|
||||||
|
ei_acceptor: Default::default(),
|
||||||
|
ei_acceptor_future: Default::default(),
|
||||||
|
enable_ei_acceptor: Default::default(),
|
||||||
|
ei_clients: EiClients::new(),
|
||||||
|
slow_ei_clients: Default::default(),
|
||||||
});
|
});
|
||||||
state.tracker.register(ClientId::from_raw(0));
|
state.tracker.register(ClientId::from_raw(0));
|
||||||
create_dummy_output(&state);
|
create_dummy_output(&state);
|
||||||
|
|
@ -305,6 +312,7 @@ async fn start_compositor3(state: Rc<State>, test_future: Option<TestFuture>) {
|
||||||
if state.create_default_seat.get() && state.globals.seats.is_empty() {
|
if state.create_default_seat.get() && state.globals.seats.is_empty() {
|
||||||
state.create_seat(DEFAULT_SEAT_NAME);
|
state.create_seat(DEFAULT_SEAT_NAME);
|
||||||
}
|
}
|
||||||
|
state.update_ei_acceptor();
|
||||||
|
|
||||||
let _geh = start_global_event_handlers(&state, &backend);
|
let _geh = start_global_event_handlers(&state, &backend);
|
||||||
state.start_xwayland();
|
state.start_xwayland();
|
||||||
|
|
@ -351,6 +359,7 @@ fn start_global_event_handlers(
|
||||||
eng.spawn2(Phase::Present, perform_toplevel_screencasts(state.clone())),
|
eng.spawn2(Phase::Present, perform_toplevel_screencasts(state.clone())),
|
||||||
eng.spawn2(Phase::PostLayout, perform_screencast_realloc(state.clone())),
|
eng.spawn2(Phase::PostLayout, perform_screencast_realloc(state.clone())),
|
||||||
eng.spawn2(Phase::PostLayout, visualize_damage(state.clone())),
|
eng.spawn2(Phase::PostLayout, visualize_damage(state.clone())),
|
||||||
|
eng.spawn(tasks::handle_slow_ei_clients(state.clone())),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -699,6 +699,11 @@ impl ConfigProxyHandler {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_set_ei_socket_enabled(&self, enabled: bool) {
|
||||||
|
self.state.enable_ei_acceptor.set(enabled);
|
||||||
|
self.state.update_ei_acceptor();
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_get_workspace(&self, name: &str) {
|
fn handle_get_workspace(&self, name: &str) {
|
||||||
let name = Rc::new(name.to_owned());
|
let name = Rc::new(name.to_owned());
|
||||||
let ws = match self.workspaces_by_name.get(&name) {
|
let ws = match self.workspaces_by_name.get(&name) {
|
||||||
|
|
@ -1910,6 +1915,9 @@ impl ConfigProxyHandler {
|
||||||
ClientMessage::SetCalibrationMatrix { device, matrix } => self
|
ClientMessage::SetCalibrationMatrix { device, matrix } => self
|
||||||
.handle_set_calibration_matrix(device, matrix)
|
.handle_set_calibration_matrix(device, matrix)
|
||||||
.wrn("set_calibration_matrix")?,
|
.wrn("set_calibration_matrix")?,
|
||||||
|
ClientMessage::SetEiSocketEnabled { enabled } => {
|
||||||
|
self.handle_set_ei_socket_enabled(enabled)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
33
src/ei.rs
Normal file
33
src/ei.rs
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
use {crate::ei::ei_object::EiVersion, std::cell::Cell};
|
||||||
|
|
||||||
|
pub mod ei_acceptor;
|
||||||
|
pub mod ei_client;
|
||||||
|
pub mod ei_ifs;
|
||||||
|
pub mod ei_object;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub enum EiContext {
|
||||||
|
Sender,
|
||||||
|
Receiver,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EiInterfaceVersion {
|
||||||
|
pub server_max_version: EiVersion,
|
||||||
|
pub client_max_version: Cell<EiVersion>,
|
||||||
|
pub version: Cell<EiVersion>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiInterfaceVersion {
|
||||||
|
pub fn new(server_max_version: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
server_max_version: EiVersion(server_max_version),
|
||||||
|
client_max_version: Cell::new(EiVersion(0)),
|
||||||
|
version: Cell::new(EiVersion(0)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_client_version(&self, version: EiVersion) {
|
||||||
|
self.client_max_version.set(version);
|
||||||
|
self.version.set(self.server_max_version.min(version));
|
||||||
|
}
|
||||||
|
}
|
||||||
156
src/ei/ei_acceptor.rs
Normal file
156
src/ei/ei_acceptor.rs
Normal file
|
|
@ -0,0 +1,156 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
async_engine::SpawnedFuture,
|
||||||
|
state::State,
|
||||||
|
utils::{errorfmt::ErrorFmt, oserror::OsError, xrd::xrd},
|
||||||
|
},
|
||||||
|
std::rc::Rc,
|
||||||
|
thiserror::Error,
|
||||||
|
uapi::{c, format_ustr, Errno, OwnedFd, Ustring},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum EiAcceptorError {
|
||||||
|
#[error("XDG_RUNTIME_DIR is not set")]
|
||||||
|
XrdNotSet,
|
||||||
|
#[error("XDG_RUNTIME_DIR ({0:?}) is too long to form a unix socket address")]
|
||||||
|
XrdTooLong(String),
|
||||||
|
#[error("Could not create a libei socket")]
|
||||||
|
SocketFailed(#[source] OsError),
|
||||||
|
#[error("Could not stat the existing socket")]
|
||||||
|
SocketStat(#[source] OsError),
|
||||||
|
#[error("Could not start listening for incoming connections")]
|
||||||
|
ListenFailed(#[source] OsError),
|
||||||
|
#[error("Could not open the lock file")]
|
||||||
|
OpenLockFile(#[source] OsError),
|
||||||
|
#[error("Could not lock the lock file")]
|
||||||
|
LockLockFile(#[source] OsError),
|
||||||
|
#[error("Could not bind the socket to an address")]
|
||||||
|
BindFailed(#[source] OsError),
|
||||||
|
#[error("All libei addresses in the range 0..1000 are already in use")]
|
||||||
|
AddressesInUse,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EiAcceptor {
|
||||||
|
socket: EiAllocatedSocket,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct EiAllocatedSocket {
|
||||||
|
// eis-x
|
||||||
|
name: String,
|
||||||
|
// /run/user/1000/eis-x
|
||||||
|
path: Ustring,
|
||||||
|
insecure: Rc<OwnedFd>,
|
||||||
|
// /run/user/1000/eis-x.lock
|
||||||
|
lock_path: Ustring,
|
||||||
|
_lock_fd: OwnedFd,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for EiAllocatedSocket {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let _ = uapi::unlink(&self.path);
|
||||||
|
let _ = uapi::unlink(&self.lock_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bind_socket(
|
||||||
|
insecure: &Rc<OwnedFd>,
|
||||||
|
xrd: &str,
|
||||||
|
id: u32,
|
||||||
|
) -> Result<EiAllocatedSocket, EiAcceptorError> {
|
||||||
|
let mut addr: c::sockaddr_un = uapi::pod_zeroed();
|
||||||
|
addr.sun_family = c::AF_UNIX as _;
|
||||||
|
let name = format!("eis-{}", id);
|
||||||
|
let path = format_ustr!("{}/{}", xrd, name);
|
||||||
|
let lock_path = format_ustr!("{}.lock", path.display());
|
||||||
|
if path.len() + 1 > addr.sun_path.len() {
|
||||||
|
return Err(EiAcceptorError::XrdTooLong(xrd.to_string()));
|
||||||
|
}
|
||||||
|
let lock_fd = match uapi::open(&*lock_path, c::O_CREAT | c::O_CLOEXEC | c::O_RDWR, 0o644) {
|
||||||
|
Ok(l) => l,
|
||||||
|
Err(e) => return Err(EiAcceptorError::OpenLockFile(e.into())),
|
||||||
|
};
|
||||||
|
if let Err(e) = uapi::flock(lock_fd.raw(), c::LOCK_EX | c::LOCK_NB) {
|
||||||
|
return Err(EiAcceptorError::LockLockFile(e.into()));
|
||||||
|
}
|
||||||
|
match uapi::lstat(&path) {
|
||||||
|
Ok(_) => {
|
||||||
|
log::info!("Unlinking {}", path.display());
|
||||||
|
let _ = uapi::unlink(&path);
|
||||||
|
}
|
||||||
|
Err(Errno(c::ENOENT)) => {}
|
||||||
|
Err(e) => return Err(EiAcceptorError::SocketStat(e.into())),
|
||||||
|
}
|
||||||
|
let sun_path = uapi::as_bytes_mut(&mut addr.sun_path[..]);
|
||||||
|
sun_path[..path.len()].copy_from_slice(path.as_bytes());
|
||||||
|
sun_path[path.len()] = 0;
|
||||||
|
if let Err(e) = uapi::bind(insecure.raw(), &addr) {
|
||||||
|
return Err(EiAcceptorError::BindFailed(e.into()));
|
||||||
|
}
|
||||||
|
Ok(EiAllocatedSocket {
|
||||||
|
name,
|
||||||
|
path,
|
||||||
|
insecure: insecure.clone(),
|
||||||
|
lock_path,
|
||||||
|
_lock_fd: lock_fd,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn allocate_socket() -> Result<EiAllocatedSocket, EiAcceptorError> {
|
||||||
|
let xrd = match xrd() {
|
||||||
|
Some(d) => d,
|
||||||
|
_ => return Err(EiAcceptorError::XrdNotSet),
|
||||||
|
};
|
||||||
|
let socket = match uapi::socket(c::AF_UNIX, c::SOCK_STREAM | c::SOCK_CLOEXEC, 0) {
|
||||||
|
Ok(f) => Rc::new(f),
|
||||||
|
Err(e) => return Err(EiAcceptorError::SocketFailed(e.into())),
|
||||||
|
};
|
||||||
|
for i in 1..1000 {
|
||||||
|
match bind_socket(&socket, &xrd, i) {
|
||||||
|
Ok(s) => return Ok(s),
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("Cannot use the eis-{} socket: {}", i, ErrorFmt(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(EiAcceptorError::AddressesInUse)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiAcceptor {
|
||||||
|
pub fn spawn(
|
||||||
|
state: &Rc<State>,
|
||||||
|
) -> Result<(Rc<EiAcceptor>, SpawnedFuture<()>), EiAcceptorError> {
|
||||||
|
let socket = allocate_socket()?;
|
||||||
|
log::info!("bound to socket {}", socket.path.display());
|
||||||
|
if let Err(e) = uapi::listen(socket.insecure.raw(), 4096) {
|
||||||
|
return Err(EiAcceptorError::ListenFailed(e.into()));
|
||||||
|
}
|
||||||
|
let acc = Rc::new(EiAcceptor { socket });
|
||||||
|
let future = state
|
||||||
|
.eng
|
||||||
|
.spawn(accept(acc.socket.insecure.clone(), state.clone()));
|
||||||
|
Ok((acc, future))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn socket_name(&self) -> &str {
|
||||||
|
&self.socket.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn accept(fd: Rc<OwnedFd>, state: Rc<State>) {
|
||||||
|
loop {
|
||||||
|
let fd = match state.ring.accept(&fd, c::SOCK_CLOEXEC).await {
|
||||||
|
Ok(fd) => fd,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Could not accept a client: {}", ErrorFmt(e));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let id = state.clients.id();
|
||||||
|
if let Err(e) = state.ei_clients.spawn(id, &state, fd) {
|
||||||
|
log::error!("Could not spawn a client: {}", ErrorFmt(e));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.ring.stop();
|
||||||
|
}
|
||||||
301
src/ei/ei_client.rs
Normal file
301
src/ei/ei_client.rs
Normal file
|
|
@ -0,0 +1,301 @@
|
||||||
|
pub use crate::ei::ei_client::ei_error::{EiClientError, EiParserError};
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
async_engine::SpawnedFuture,
|
||||||
|
client::ClientId,
|
||||||
|
ei::{
|
||||||
|
ei_client::ei_objects::EiObjects,
|
||||||
|
ei_ifs::{ei_connection::EiConnection, ei_handshake::EiHandshake},
|
||||||
|
ei_object::{EiInterface, EiObject, EiObjectId},
|
||||||
|
EiContext, EiInterfaceVersion,
|
||||||
|
},
|
||||||
|
ifs::wl_seat::WlSeatGlobal,
|
||||||
|
leaks::Tracker,
|
||||||
|
state::State,
|
||||||
|
utils::{
|
||||||
|
asyncevent::AsyncEvent,
|
||||||
|
buffd::{EiMsgFormatter, EiMsgParser, EiMsgParserError, OutBufferSwapchain},
|
||||||
|
clonecell::CloneCell,
|
||||||
|
errorfmt::ErrorFmt,
|
||||||
|
numcell::NumCell,
|
||||||
|
pid_info::{get_pid_info, get_socket_creds, PidInfo},
|
||||||
|
},
|
||||||
|
wire_ei::EiInterfaceVersions,
|
||||||
|
},
|
||||||
|
ahash::AHashMap,
|
||||||
|
std::{
|
||||||
|
cell::{Cell, RefCell},
|
||||||
|
error::Error,
|
||||||
|
fmt::Debug,
|
||||||
|
mem,
|
||||||
|
ops::DerefMut,
|
||||||
|
rc::Rc,
|
||||||
|
},
|
||||||
|
uapi::{c, OwnedFd},
|
||||||
|
};
|
||||||
|
|
||||||
|
mod ei_error;
|
||||||
|
mod ei_objects;
|
||||||
|
mod ei_tasks;
|
||||||
|
|
||||||
|
pub struct EiClients {
|
||||||
|
pub clients: RefCell<AHashMap<ClientId, EiClientHolder>>,
|
||||||
|
shutdown_clients: RefCell<AHashMap<ClientId, EiClientHolder>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiClients {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
clients: Default::default(),
|
||||||
|
shutdown_clients: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn announce_seat(&self, seat: &Rc<WlSeatGlobal>) {
|
||||||
|
for ei_client in self.clients.borrow().values() {
|
||||||
|
if let Some(connection) = ei_client.data.connection.get() {
|
||||||
|
connection.announce_seat(&seat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&self) {
|
||||||
|
mem::take(self.clients.borrow_mut().deref_mut());
|
||||||
|
mem::take(self.shutdown_clients.borrow_mut().deref_mut());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spawn(
|
||||||
|
&self,
|
||||||
|
id: ClientId,
|
||||||
|
global: &Rc<State>,
|
||||||
|
socket: Rc<OwnedFd>,
|
||||||
|
) -> Result<(), EiClientError> {
|
||||||
|
let Some((uid, pid)) = get_socket_creds(&socket) else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
self.spawn2(id, global, socket, uid, pid)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spawn2(
|
||||||
|
&self,
|
||||||
|
id: ClientId,
|
||||||
|
global: &Rc<State>,
|
||||||
|
socket: Rc<OwnedFd>,
|
||||||
|
uid: c::uid_t,
|
||||||
|
pid: c::pid_t,
|
||||||
|
) -> Result<Rc<EiClient>, EiClientError> {
|
||||||
|
let versions = EiInterfaceVersions {
|
||||||
|
ei_button: EiInterfaceVersion::new(1),
|
||||||
|
ei_callback: EiInterfaceVersion::new(1),
|
||||||
|
ei_connection: EiInterfaceVersion::new(1),
|
||||||
|
ei_device: EiInterfaceVersion::new(2),
|
||||||
|
ei_handshake: EiInterfaceVersion::new(1),
|
||||||
|
ei_keyboard: EiInterfaceVersion::new(1),
|
||||||
|
ei_pingpong: EiInterfaceVersion::new(1),
|
||||||
|
ei_pointer: EiInterfaceVersion::new(1),
|
||||||
|
ei_pointer_absolute: EiInterfaceVersion::new(1),
|
||||||
|
ei_scroll: EiInterfaceVersion::new(1),
|
||||||
|
ei_seat: EiInterfaceVersion::new(1),
|
||||||
|
ei_touchscreen: EiInterfaceVersion::new(1),
|
||||||
|
};
|
||||||
|
let data = Rc::new(EiClient {
|
||||||
|
id,
|
||||||
|
state: global.clone(),
|
||||||
|
context: Cell::new(EiContext::Receiver),
|
||||||
|
connection: Default::default(),
|
||||||
|
checking_queue_size: Cell::new(false),
|
||||||
|
socket,
|
||||||
|
objects: EiObjects::new(),
|
||||||
|
swapchain: Default::default(),
|
||||||
|
flush_request: Default::default(),
|
||||||
|
shutdown: Default::default(),
|
||||||
|
tracker: Default::default(),
|
||||||
|
pid_info: get_pid_info(uid, pid),
|
||||||
|
disconnect_announced: Cell::new(false),
|
||||||
|
versions,
|
||||||
|
name: Default::default(),
|
||||||
|
last_serial: Default::default(),
|
||||||
|
});
|
||||||
|
track!(data, data);
|
||||||
|
let handshake = Rc::new(EiHandshake::new(&data));
|
||||||
|
track!(data, handshake);
|
||||||
|
handshake.send_handshake_version();
|
||||||
|
data.objects.add_handshake(&handshake);
|
||||||
|
let client = EiClientHolder {
|
||||||
|
_handler: global.eng.spawn(ei_tasks::ei_client(data.clone())),
|
||||||
|
data: data.clone(),
|
||||||
|
};
|
||||||
|
log::info!(
|
||||||
|
"Client {} connected, pid: {}, uid: {}, fd: {}, comm: {:?}",
|
||||||
|
id,
|
||||||
|
pid,
|
||||||
|
uid,
|
||||||
|
client.data.socket.raw(),
|
||||||
|
data.pid_info.comm,
|
||||||
|
);
|
||||||
|
self.clients.borrow_mut().insert(client.data.id, client);
|
||||||
|
Ok(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn kill(&self, client: ClientId) {
|
||||||
|
log::info!("Removing client {}", client);
|
||||||
|
if self.clients.borrow_mut().remove(&client).is_none() {
|
||||||
|
self.shutdown_clients.borrow_mut().remove(&client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shutdown(&self, client_id: ClientId) {
|
||||||
|
if let Some(client) = self.clients.borrow_mut().remove(&client_id) {
|
||||||
|
log::info!("Shutting down client {}", client.data.id);
|
||||||
|
client.data.shutdown.trigger();
|
||||||
|
client.data.flush_request.trigger();
|
||||||
|
self.shutdown_clients.borrow_mut().insert(client_id, client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for EiClients {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let _clients1 = mem::take(&mut *self.clients.borrow_mut());
|
||||||
|
let _clients2 = mem::take(&mut *self.shutdown_clients.borrow_mut());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EiClientHolder {
|
||||||
|
pub data: Rc<EiClient>,
|
||||||
|
_handler: SpawnedFuture<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for EiClientHolder {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.data.objects.destroy();
|
||||||
|
self.data.flush_request.clear();
|
||||||
|
self.data.shutdown.clear();
|
||||||
|
self.data.connection.take();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait EiEventFormatter: Debug {
|
||||||
|
fn format(self, fmt: &mut EiMsgFormatter<'_>);
|
||||||
|
fn id(&self) -> EiObjectId;
|
||||||
|
fn interface(&self) -> EiInterface;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait EiRequestParser<'a>: Debug + Sized {
|
||||||
|
type Generic<'b>: EiRequestParser<'b>;
|
||||||
|
fn parse(parser: &mut EiMsgParser<'_, 'a>) -> Result<Self, EiMsgParserError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EiClient {
|
||||||
|
pub id: ClientId,
|
||||||
|
pub state: Rc<State>,
|
||||||
|
pub context: Cell<EiContext>,
|
||||||
|
pub connection: CloneCell<Option<Rc<EiConnection>>>,
|
||||||
|
checking_queue_size: Cell<bool>,
|
||||||
|
socket: Rc<OwnedFd>,
|
||||||
|
pub objects: EiObjects,
|
||||||
|
swapchain: Rc<RefCell<OutBufferSwapchain>>,
|
||||||
|
flush_request: AsyncEvent,
|
||||||
|
shutdown: AsyncEvent,
|
||||||
|
pub tracker: Tracker<EiClient>,
|
||||||
|
pub pid_info: PidInfo,
|
||||||
|
pub disconnect_announced: Cell<bool>,
|
||||||
|
pub versions: EiInterfaceVersions,
|
||||||
|
pub name: RefCell<Option<String>>,
|
||||||
|
pub last_serial: NumCell<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiClient {
|
||||||
|
pub fn new_id<T: From<EiObjectId>>(&self) -> T {
|
||||||
|
self.objects.id()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serial(&self) -> u32 {
|
||||||
|
(self.last_serial.fetch_add(1) + 1) as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn last_serial(&self) -> u32 {
|
||||||
|
self.last_serial.get() as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn error(&self, message: impl Error) {
|
||||||
|
let msg = ErrorFmt(message).to_string();
|
||||||
|
log::error!("Client {}: A fatal error occurred: {}", self.id, msg);
|
||||||
|
match self.connection.get() {
|
||||||
|
Some(d) => {
|
||||||
|
d.send_disconnected(Some(&msg));
|
||||||
|
self.state.clients.shutdown(self.id);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
self.state.clients.kill(self.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse<'a, R: EiRequestParser<'a>>(
|
||||||
|
&self,
|
||||||
|
obj: &impl EiObject,
|
||||||
|
mut parser: EiMsgParser<'_, 'a>,
|
||||||
|
) -> Result<R, EiMsgParserError> {
|
||||||
|
let res = R::parse(&mut parser)?;
|
||||||
|
parser.eof()?;
|
||||||
|
log::trace!(
|
||||||
|
"Client {} -> {}@{:x}.{:?}",
|
||||||
|
self.id,
|
||||||
|
obj.interface().name(),
|
||||||
|
obj.id(),
|
||||||
|
res
|
||||||
|
);
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn event<T: EiEventFormatter>(self: &Rc<Self>, event: T) {
|
||||||
|
log::trace!(
|
||||||
|
"Client {} <= {}@{:x}.{:?}",
|
||||||
|
self.id,
|
||||||
|
event.interface().name(),
|
||||||
|
event.id(),
|
||||||
|
event,
|
||||||
|
);
|
||||||
|
let mut fds = vec![];
|
||||||
|
let mut swapchain = self.swapchain.borrow_mut();
|
||||||
|
let mut fmt = EiMsgFormatter::new(&mut swapchain.cur, &mut fds);
|
||||||
|
event.format(&mut fmt);
|
||||||
|
fmt.write_len();
|
||||||
|
if swapchain.cur.is_full() {
|
||||||
|
swapchain.commit();
|
||||||
|
if swapchain.exceeds_limit() {
|
||||||
|
if !self.checking_queue_size.replace(true) {
|
||||||
|
self.state.slow_ei_clients.push(self.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.flush_request.trigger();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn check_queue_size(&self) {
|
||||||
|
if self.swapchain.borrow_mut().exceeds_limit() {
|
||||||
|
self.state.eng.yield_now().await;
|
||||||
|
if self.swapchain.borrow_mut().exceeds_limit() {
|
||||||
|
log::error!("Client {} is too slow at fetching events", self.id);
|
||||||
|
self.state.ei_clients.kill(self.id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.checking_queue_size.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_client_obj<T: EiObject>(&self, obj: &Rc<T>) -> Result<(), EiClientError> {
|
||||||
|
self.objects.add_client_object(obj.clone())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_server_obj<T: EiObject>(&self, obj: &Rc<T>) {
|
||||||
|
self.objects.add_server_object(obj.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_obj<T: EiObject>(self: &Rc<Self>, obj: &T) -> Result<(), EiClientError> {
|
||||||
|
self.objects.remove_obj(obj.id())
|
||||||
|
}
|
||||||
|
}
|
||||||
45
src/ei/ei_client/ei_error.rs
Normal file
45
src/ei/ei_client/ei_error.rs
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
ei::ei_object::{EiInterface, EiObjectId},
|
||||||
|
utils::buffd::{BufFdError, EiMsgParserError},
|
||||||
|
},
|
||||||
|
std::error::Error,
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum EiClientError {
|
||||||
|
#[error("An error occurred reading from/writing to the client")]
|
||||||
|
Io(#[from] BufFdError),
|
||||||
|
#[error("An error occurred while processing a request")]
|
||||||
|
RequestError(#[source] Box<EiClientError>),
|
||||||
|
#[error("Client tried to invoke a non-existent method")]
|
||||||
|
InvalidMethod,
|
||||||
|
#[error("The message size is < 16")]
|
||||||
|
MessageSizeTooSmall,
|
||||||
|
#[error("The message size is > 2^16")]
|
||||||
|
MessageSizeTooLarge,
|
||||||
|
#[error("The size of the message is not a multiple of 4")]
|
||||||
|
UnalignedMessage,
|
||||||
|
#[error("The object id is unknown")]
|
||||||
|
UnknownId,
|
||||||
|
#[error("Client tried to access non-existent object {0}")]
|
||||||
|
InvalidObject(EiObjectId),
|
||||||
|
#[error("The id is already in use")]
|
||||||
|
IdAlreadyInUse,
|
||||||
|
#[error("The client object id is out of bounds")]
|
||||||
|
ClientIdOutOfBounds,
|
||||||
|
#[error("Could not process a `{}.{}` request", .interface.name(), .method)]
|
||||||
|
MethodError {
|
||||||
|
interface: EiInterface,
|
||||||
|
method: &'static str,
|
||||||
|
#[source]
|
||||||
|
error: Box<dyn Error + 'static>,
|
||||||
|
},
|
||||||
|
#[error("Could not add object {0} to the client")]
|
||||||
|
AddObjectError(EiObjectId, #[source] Box<EiClientError>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
#[error("Parsing failed")]
|
||||||
|
pub struct EiParserError(#[source] pub EiMsgParserError);
|
||||||
83
src/ei/ei_client/ei_objects.rs
Normal file
83
src/ei/ei_client/ei_objects.rs
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
ei::{
|
||||||
|
ei_client::ei_error::EiClientError,
|
||||||
|
ei_ifs::ei_handshake::EiHandshake,
|
||||||
|
ei_object::{EiObject, EiObjectId},
|
||||||
|
},
|
||||||
|
utils::{copyhashmap::CopyHashMap, numcell::NumCell},
|
||||||
|
},
|
||||||
|
std::rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct EiObjects {
|
||||||
|
registry: CopyHashMap<EiObjectId, Rc<dyn EiObject>>,
|
||||||
|
next_sever_id: NumCell<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const MIN_SERVER_ID: u64 = 0xff00_0000_0000_0000;
|
||||||
|
|
||||||
|
impl EiObjects {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
registry: Default::default(),
|
||||||
|
next_sever_id: NumCell::new(MIN_SERVER_ID),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(&self) {
|
||||||
|
for obj in self.registry.lock().values_mut() {
|
||||||
|
obj.break_loops();
|
||||||
|
}
|
||||||
|
self.registry.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id<T>(&self) -> T
|
||||||
|
where
|
||||||
|
EiObjectId: Into<T>,
|
||||||
|
{
|
||||||
|
EiObjectId::from_raw(self.next_sever_id.fetch_add(1)).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_obj(&self, id: EiObjectId) -> Option<Rc<dyn EiObject>> {
|
||||||
|
self.registry.get(&id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_server_object(&self, obj: Rc<dyn EiObject>) {
|
||||||
|
let id = obj.id();
|
||||||
|
assert!(id.raw() >= MIN_SERVER_ID);
|
||||||
|
assert!(!self.registry.contains(&id));
|
||||||
|
self.registry.set(id, obj.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_handshake(&self, obj: &Rc<EiHandshake>) {
|
||||||
|
assert_eq!(obj.id.raw(), 0);
|
||||||
|
assert!(self.registry.is_empty());
|
||||||
|
self.registry.set(obj.id.into(), obj.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_client_object(&self, obj: Rc<dyn EiObject>) -> Result<(), EiClientError> {
|
||||||
|
let id = obj.id();
|
||||||
|
let res = (|| {
|
||||||
|
if id.raw() == 0 || id.raw() >= MIN_SERVER_ID {
|
||||||
|
return Err(EiClientError::ClientIdOutOfBounds);
|
||||||
|
}
|
||||||
|
if self.registry.contains(&id) {
|
||||||
|
return Err(EiClientError::IdAlreadyInUse);
|
||||||
|
}
|
||||||
|
self.registry.set(id, obj.clone());
|
||||||
|
Ok(())
|
||||||
|
})();
|
||||||
|
if let Err(e) = res {
|
||||||
|
return Err(EiClientError::AddObjectError(id, Box::new(e)));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_obj(&self, id: EiObjectId) -> Result<(), EiClientError> {
|
||||||
|
match self.registry.remove(&id) {
|
||||||
|
Some(_) => Ok(()),
|
||||||
|
_ => Err(EiClientError::UnknownId),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
141
src/ei/ei_client/ei_tasks.rs
Normal file
141
src/ei/ei_client/ei_tasks.rs
Normal file
|
|
@ -0,0 +1,141 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
async_engine::Phase,
|
||||||
|
ei::{
|
||||||
|
ei_client::{ei_error::EiClientError, EiClient},
|
||||||
|
ei_object::EiObjectId,
|
||||||
|
},
|
||||||
|
utils::{
|
||||||
|
buffd::{BufFdIn, BufFdOut, EiMsgParser},
|
||||||
|
errorfmt::ErrorFmt,
|
||||||
|
vec_ext::VecExt,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
futures_util::{select, FutureExt},
|
||||||
|
std::{collections::VecDeque, mem, rc::Rc, time::Duration},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub async fn ei_client(data: Rc<EiClient>) {
|
||||||
|
let mut recv = data.state.eng.spawn(receive(data.clone())).fuse();
|
||||||
|
let mut shutdown = data.shutdown.triggered().fuse();
|
||||||
|
let _send = data.state.eng.spawn2(Phase::PostLayout, send(data.clone()));
|
||||||
|
select! {
|
||||||
|
_ = recv => { },
|
||||||
|
_ = shutdown => { },
|
||||||
|
}
|
||||||
|
drop(recv);
|
||||||
|
data.flush_request.trigger();
|
||||||
|
match data.state.wheel.timeout(5000).await {
|
||||||
|
Ok(_) => {
|
||||||
|
log::error!("Could not shut down client {} within 5 seconds", data.id);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Could not create a timeout: {}", ErrorFmt(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data.state.ei_clients.kill(data.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn receive(data: Rc<EiClient>) {
|
||||||
|
let recv = async {
|
||||||
|
let mut buf = BufFdIn::new(&data.socket, &data.state.ring);
|
||||||
|
let mut data_buf = Vec::<u32>::new();
|
||||||
|
loop {
|
||||||
|
let mut hdr = [0u32; 4];
|
||||||
|
buf.read_full(&mut hdr[..]).await?;
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
let obj_id = (hdr[0] as u64) | ((hdr[1] as u64) << 32);
|
||||||
|
#[cfg(target_endian = "big")]
|
||||||
|
let obj_id = (hdr[1] as u64) | ((hdr[0] as u64) << 32);
|
||||||
|
let obj_id = EiObjectId::from_raw(obj_id);
|
||||||
|
let len = hdr[2] as usize;
|
||||||
|
let request = hdr[3];
|
||||||
|
if len < 16 {
|
||||||
|
return Err(EiClientError::MessageSizeTooSmall);
|
||||||
|
}
|
||||||
|
if len > (u16::MAX as usize) {
|
||||||
|
return Err(EiClientError::MessageSizeTooLarge);
|
||||||
|
}
|
||||||
|
if len % 4 != 0 {
|
||||||
|
return Err(EiClientError::UnalignedMessage);
|
||||||
|
}
|
||||||
|
let len = (len - 16) / 4;
|
||||||
|
data_buf.clear();
|
||||||
|
data_buf.reserve(len);
|
||||||
|
let unused = data_buf.split_at_spare_mut_ext().1;
|
||||||
|
buf.read_full(&mut unused[..len]).await?;
|
||||||
|
unsafe {
|
||||||
|
data_buf.set_len(len);
|
||||||
|
}
|
||||||
|
let obj = match data.objects.get_obj(obj_id) {
|
||||||
|
Some(obj) => obj,
|
||||||
|
_ => match data.connection.get() {
|
||||||
|
None => {
|
||||||
|
return Err(EiClientError::InvalidObject(obj_id));
|
||||||
|
}
|
||||||
|
Some(c) => {
|
||||||
|
c.send_invalid_object(obj_id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let parser = EiMsgParser::new(&mut buf, &data_buf[..]);
|
||||||
|
if let Err(e) = obj.handle_request(&data, request, parser) {
|
||||||
|
return Err(EiClientError::RequestError(Box::new(e)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let res: Result<(), EiClientError> = recv.await;
|
||||||
|
if let Err(e) = res {
|
||||||
|
if data.disconnect_announced.get() {
|
||||||
|
log::info!("Client {} terminated the connection", data.id);
|
||||||
|
data.state.ei_clients.kill(data.id);
|
||||||
|
} else {
|
||||||
|
let e = ErrorFmt(e);
|
||||||
|
log::error!(
|
||||||
|
"An error occurred while trying to handle a message from client {}: {}",
|
||||||
|
data.id,
|
||||||
|
e
|
||||||
|
);
|
||||||
|
if let Some(c) = data.connection.get() {
|
||||||
|
c.send_disconnected(Some(&e.to_string()));
|
||||||
|
data.state.ei_clients.shutdown(data.id);
|
||||||
|
} else {
|
||||||
|
data.state.ei_clients.kill(data.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn send(data: Rc<EiClient>) {
|
||||||
|
let send = async {
|
||||||
|
let mut out = BufFdOut::new(&data.socket, &data.state.ring);
|
||||||
|
let mut buffers = VecDeque::new();
|
||||||
|
loop {
|
||||||
|
data.flush_request.triggered().await;
|
||||||
|
{
|
||||||
|
let mut swapchain = data.swapchain.borrow_mut();
|
||||||
|
swapchain.commit();
|
||||||
|
mem::swap(&mut swapchain.pending, &mut buffers);
|
||||||
|
}
|
||||||
|
let timeout = data.state.now() + Duration::from_millis(5000);
|
||||||
|
while let Some(mut cur) = buffers.pop_front() {
|
||||||
|
out.flush(&mut cur, timeout).await?;
|
||||||
|
data.swapchain.borrow_mut().free.push(cur);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let res: Result<(), EiClientError> = send.await;
|
||||||
|
if let Err(e) = res {
|
||||||
|
if data.disconnect_announced.get() {
|
||||||
|
log::info!("Client {} terminated the connection", data.id);
|
||||||
|
} else {
|
||||||
|
log::error!(
|
||||||
|
"An error occurred while sending data to client {}: {}",
|
||||||
|
data.id,
|
||||||
|
ErrorFmt(e)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data.state.ei_clients.kill(data.id);
|
||||||
|
}
|
||||||
44
src/ei/ei_ifs.rs
Normal file
44
src/ei/ei_ifs.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
macro_rules! ei_device_interface {
|
||||||
|
($camel:ident, $snake:ident, $field:ident) => {
|
||||||
|
impl EiDeviceInterface for $camel {
|
||||||
|
fn new(device: &Rc<EiDevice>, version: EiVersion) -> Rc<Self> {
|
||||||
|
let v = Rc::new(Self {
|
||||||
|
id: device.client.new_id(),
|
||||||
|
client: device.client.clone(),
|
||||||
|
tracker: Default::default(),
|
||||||
|
version,
|
||||||
|
device: device.clone(),
|
||||||
|
});
|
||||||
|
track!(v.client, v);
|
||||||
|
v
|
||||||
|
}
|
||||||
|
|
||||||
|
fn destroy(&self) -> Result<(), EiClientError> {
|
||||||
|
self.send_destroyed(self.client.serial());
|
||||||
|
self.client.remove_obj(self)?;
|
||||||
|
self.device.seat.$field.take();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_destroyed(&self, serial: u32) {
|
||||||
|
self.client.event(crate::wire_ei::$snake::Destroyed {
|
||||||
|
self_id: self.id,
|
||||||
|
serial,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod ei_button;
|
||||||
|
pub mod ei_callback;
|
||||||
|
pub mod ei_connection;
|
||||||
|
pub mod ei_device;
|
||||||
|
pub mod ei_handshake;
|
||||||
|
pub mod ei_keyboard;
|
||||||
|
pub mod ei_pingpong;
|
||||||
|
pub mod ei_pointer;
|
||||||
|
pub mod ei_pointer_absolute;
|
||||||
|
pub mod ei_scroll;
|
||||||
|
pub mod ei_seat;
|
||||||
|
pub mod ei_touchscreen;
|
||||||
72
src/ei/ei_ifs/ei_button.rs
Normal file
72
src/ei/ei_ifs/ei_button.rs
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
backend::KeyState,
|
||||||
|
ei::{
|
||||||
|
ei_client::{EiClient, EiClientError},
|
||||||
|
ei_ifs::ei_device::{EiDevice, EiDeviceInterface},
|
||||||
|
ei_object::{EiObject, EiVersion},
|
||||||
|
},
|
||||||
|
leaks::Tracker,
|
||||||
|
wire_ei::{
|
||||||
|
ei_button::{ClientButton, EiButtonRequestHandler, Release, ServerButton},
|
||||||
|
EiButtonId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
std::rc::Rc,
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct EiButton {
|
||||||
|
pub id: EiButtonId,
|
||||||
|
pub client: Rc<EiClient>,
|
||||||
|
pub tracker: Tracker<Self>,
|
||||||
|
pub version: EiVersion,
|
||||||
|
pub device: Rc<EiDevice>,
|
||||||
|
}
|
||||||
|
|
||||||
|
ei_device_interface!(EiButton, ei_button, button);
|
||||||
|
|
||||||
|
impl EiButton {
|
||||||
|
pub fn send_button(&self, button: u32, state: KeyState) {
|
||||||
|
self.client.event(ServerButton {
|
||||||
|
self_id: self.id,
|
||||||
|
button,
|
||||||
|
state: state as _,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiButtonRequestHandler for EiButton {
|
||||||
|
type Error = EiButtonError;
|
||||||
|
|
||||||
|
fn release(&self, _req: Release, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
self.destroy()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn client_button(&self, req: ClientButton, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
let pressed = match req.state {
|
||||||
|
0 => KeyState::Released,
|
||||||
|
1 => KeyState::Pressed,
|
||||||
|
_ => return Err(EiButtonError::InvalidButtonState(req.state)),
|
||||||
|
};
|
||||||
|
self.device.button_changes.push((req.button, pressed));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ei_object_base! {
|
||||||
|
self = EiButton;
|
||||||
|
version = self.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiObject for EiButton {}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum EiButtonError {
|
||||||
|
#[error(transparent)]
|
||||||
|
EiClientError(Box<EiClientError>),
|
||||||
|
#[error("Invalid button state {0}")]
|
||||||
|
InvalidButtonState(u32),
|
||||||
|
}
|
||||||
|
efrom!(EiButtonError, EiClientError);
|
||||||
49
src/ei/ei_ifs/ei_callback.rs
Normal file
49
src/ei/ei_ifs/ei_callback.rs
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
ei::{
|
||||||
|
ei_client::{EiClient, EiClientError},
|
||||||
|
ei_object::{EiObject, EiVersion},
|
||||||
|
},
|
||||||
|
leaks::Tracker,
|
||||||
|
wire_ei::{
|
||||||
|
ei_callback::{Done, EiCallbackRequestHandler},
|
||||||
|
EiCallbackId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
std::rc::Rc,
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct EiCallback {
|
||||||
|
pub id: EiCallbackId,
|
||||||
|
pub client: Rc<EiClient>,
|
||||||
|
pub tracker: Tracker<Self>,
|
||||||
|
pub version: EiVersion,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiCallback {
|
||||||
|
pub fn send_done(&self, callback_data: u64) {
|
||||||
|
self.client.event(Done {
|
||||||
|
self_id: self.id,
|
||||||
|
callback_data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiCallbackRequestHandler for EiCallback {
|
||||||
|
type Error = EiCallbackError;
|
||||||
|
}
|
||||||
|
|
||||||
|
ei_object_base! {
|
||||||
|
self = EiCallback;
|
||||||
|
version = self.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiObject for EiCallback {}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum EiCallbackError {
|
||||||
|
#[error(transparent)]
|
||||||
|
EiClientError(Box<EiClientError>),
|
||||||
|
}
|
||||||
|
efrom!(EiCallbackError, EiClientError);
|
||||||
160
src/ei/ei_ifs/ei_connection.rs
Normal file
160
src/ei/ei_ifs/ei_connection.rs
Normal file
|
|
@ -0,0 +1,160 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
ei::{
|
||||||
|
ei_client::{EiClient, EiClientError},
|
||||||
|
ei_ifs::{
|
||||||
|
ei_callback::EiCallback,
|
||||||
|
ei_seat::{
|
||||||
|
EiSeat, EI_CAP_BUTTON, EI_CAP_KEYBOARD, EI_CAP_POINTER,
|
||||||
|
EI_CAP_POINTER_ABSOLUTE, EI_CAP_SCROLL, EI_CAP_TOUCHSCREEN,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ei_object::{EiObject, EiObjectId, EiVersion},
|
||||||
|
EiContext,
|
||||||
|
},
|
||||||
|
ifs::wl_seat::WlSeatGlobal,
|
||||||
|
leaks::Tracker,
|
||||||
|
wire_ei::{
|
||||||
|
ei_connection::{
|
||||||
|
Disconnect, Disconnected, EiConnectionRequestHandler, InvalidObject, Seat,
|
||||||
|
},
|
||||||
|
EiButton, EiConnectionId, EiKeyboard, EiPointer, EiPointerAbsolute, EiScroll,
|
||||||
|
EiTouchscreen,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
std::{cell::Cell, rc::Rc},
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct EiConnection {
|
||||||
|
pub id: EiConnectionId,
|
||||||
|
pub client: Rc<EiClient>,
|
||||||
|
pub tracker: Tracker<Self>,
|
||||||
|
pub version: EiVersion,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiConnection {
|
||||||
|
pub fn send_invalid_object(&self, id: EiObjectId) {
|
||||||
|
self.client.event(InvalidObject {
|
||||||
|
self_id: self.id,
|
||||||
|
last_serial: self.client.last_serial(),
|
||||||
|
invalid_id: id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_disconnected(&self, error: Option<&str>) {
|
||||||
|
self.client.event(Disconnected {
|
||||||
|
self_id: self.id,
|
||||||
|
last_serial: self.client.last_serial(),
|
||||||
|
reason: error.is_some() as _,
|
||||||
|
explanation: error,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_seat(&self, seat: &EiSeat) {
|
||||||
|
self.client.event(Seat {
|
||||||
|
self_id: self.id,
|
||||||
|
seat: seat.id,
|
||||||
|
version: seat.version.0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn announce_seat(&self, seat: &Rc<WlSeatGlobal>) {
|
||||||
|
let version = self.client.versions.ei_seat.version.get();
|
||||||
|
if version == EiVersion(0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let xkb_state_id = match self.context() {
|
||||||
|
EiContext::Sender => seat.seat_xkb_state().borrow().id,
|
||||||
|
EiContext::Receiver => seat.latest_xkb_state().borrow().id,
|
||||||
|
};
|
||||||
|
let seat = Rc::new(EiSeat {
|
||||||
|
id: self.client.new_id(),
|
||||||
|
client: self.client.clone(),
|
||||||
|
tracker: Default::default(),
|
||||||
|
version,
|
||||||
|
seat: seat.clone(),
|
||||||
|
capabilities: Cell::new(0),
|
||||||
|
kb_state_id: Cell::new(xkb_state_id),
|
||||||
|
device: Default::default(),
|
||||||
|
pointer: Default::default(),
|
||||||
|
pointer_absolute: Default::default(),
|
||||||
|
keyboard: Default::default(),
|
||||||
|
button: Default::default(),
|
||||||
|
scroll: Default::default(),
|
||||||
|
touchscreen: Default::default(),
|
||||||
|
});
|
||||||
|
track!(self.client, seat);
|
||||||
|
self.client.add_server_obj(&seat);
|
||||||
|
self.send_seat(&seat);
|
||||||
|
let v = &self.client.versions;
|
||||||
|
let caps = [
|
||||||
|
(EI_CAP_POINTER, EiPointer, &v.ei_pointer),
|
||||||
|
(
|
||||||
|
EI_CAP_POINTER_ABSOLUTE,
|
||||||
|
EiPointerAbsolute,
|
||||||
|
&v.ei_pointer_absolute,
|
||||||
|
),
|
||||||
|
(EI_CAP_SCROLL, EiScroll, &v.ei_scroll),
|
||||||
|
(EI_CAP_BUTTON, EiButton, &v.ei_button),
|
||||||
|
(EI_CAP_KEYBOARD, EiKeyboard, &v.ei_keyboard),
|
||||||
|
(EI_CAP_TOUCHSCREEN, EiTouchscreen, &v.ei_touchscreen),
|
||||||
|
];
|
||||||
|
for (mask, interface, version) in caps {
|
||||||
|
if version.version.get() > EiVersion(0) {
|
||||||
|
seat.send_capability(interface, mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
seat.send_name(&seat.seat.seat_name());
|
||||||
|
seat.send_done();
|
||||||
|
seat.seat.add_ei_seat(&seat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiConnectionRequestHandler for EiConnection {
|
||||||
|
type Error = EiConnectionError;
|
||||||
|
|
||||||
|
fn sync(
|
||||||
|
&self,
|
||||||
|
req: crate::wire_ei::ei_connection::Sync,
|
||||||
|
_slf: &Rc<Self>,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
let version = EiVersion(req.version);
|
||||||
|
if version > self.client.versions.ei_callback.version.get() {
|
||||||
|
return Err(EiConnectionError::CallbackVersion(req.version));
|
||||||
|
}
|
||||||
|
let cb = Rc::new(EiCallback {
|
||||||
|
id: req.callback,
|
||||||
|
client: self.client.clone(),
|
||||||
|
tracker: Default::default(),
|
||||||
|
version,
|
||||||
|
});
|
||||||
|
track!(self.client, cb);
|
||||||
|
self.client.add_client_obj(&cb)?;
|
||||||
|
cb.send_done(0);
|
||||||
|
self.client.remove_obj(&*cb)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn disconnect(&self, _req: Disconnect, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
self.client.disconnect_announced.set(true);
|
||||||
|
self.client.state.ei_clients.shutdown(self.client.id);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ei_object_base! {
|
||||||
|
self = EiConnection;
|
||||||
|
version = self.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiObject for EiConnection {}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum EiConnectionError {
|
||||||
|
#[error(transparent)]
|
||||||
|
EiClientError(Box<EiClientError>),
|
||||||
|
#[error("The callback version is too large: {0}")]
|
||||||
|
CallbackVersion(u32),
|
||||||
|
}
|
||||||
|
efrom!(EiConnectionError, EiClientError);
|
||||||
249
src/ei/ei_ifs/ei_device.rs
Normal file
249
src/ei/ei_ifs/ei_device.rs
Normal file
|
|
@ -0,0 +1,249 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
backend::{KeyState, ScrollAxis},
|
||||||
|
ei::{
|
||||||
|
ei_client::{EiClient, EiClientError},
|
||||||
|
ei_ifs::{ei_seat::EiSeat, ei_touchscreen::TouchChange},
|
||||||
|
ei_object::{EiObject, EiVersion},
|
||||||
|
},
|
||||||
|
fixed::Fixed,
|
||||||
|
ifs::wl_seat::PX_PER_SCROLL,
|
||||||
|
leaks::Tracker,
|
||||||
|
rect::Rect,
|
||||||
|
scale::Scale,
|
||||||
|
utils::syncqueue::SyncQueue,
|
||||||
|
wire_ei::{
|
||||||
|
ei_device::{
|
||||||
|
ClientFrame, ClientStartEmulating, ClientStopEmulating, Destroyed, DeviceType,
|
||||||
|
Done, EiDeviceRequestHandler, Interface, Paused, Region, Release, Resumed,
|
||||||
|
ServerFrame, ServerStartEmulating,
|
||||||
|
},
|
||||||
|
EiDeviceId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
std::{cell::Cell, rc::Rc},
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const EI_DEVICE_TYPE_VIRTUAL: u32 = 1;
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub const EI_DEVICE_TYPE_PHYSICAL: u32 = 2;
|
||||||
|
|
||||||
|
pub struct EiDevice {
|
||||||
|
pub id: EiDeviceId,
|
||||||
|
pub client: Rc<EiClient>,
|
||||||
|
pub tracker: Tracker<Self>,
|
||||||
|
pub version: EiVersion,
|
||||||
|
pub seat: Rc<EiSeat>,
|
||||||
|
|
||||||
|
pub button_changes: SyncQueue<(u32, KeyState)>,
|
||||||
|
pub touch_changes: SyncQueue<(u32, TouchChange)>,
|
||||||
|
pub scroll_px: [Cell<Option<f32>>; 2],
|
||||||
|
pub scroll_v120: [Cell<Option<i32>>; 2],
|
||||||
|
pub scroll_stop: [Cell<Option<bool>>; 2],
|
||||||
|
pub absolute_motion: Cell<Option<(f32, f32)>>,
|
||||||
|
pub relative_motion: Cell<Option<(f32, f32)>>,
|
||||||
|
pub key_changes: SyncQueue<(u32, KeyState)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait EiDeviceInterface: EiObject {
|
||||||
|
fn new(device: &Rc<EiDevice>, version: EiVersion) -> Rc<Self>;
|
||||||
|
|
||||||
|
fn destroy(&self) -> Result<(), EiClientError>;
|
||||||
|
|
||||||
|
fn send_destroyed(&self, serial: u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiDevice {
|
||||||
|
pub fn send_interface<T>(&self, interface: &T)
|
||||||
|
where
|
||||||
|
T: EiDeviceInterface,
|
||||||
|
{
|
||||||
|
self.client.event(Interface {
|
||||||
|
self_id: self.id,
|
||||||
|
object: interface.id(),
|
||||||
|
interface_name: interface.interface().name(),
|
||||||
|
version: interface.version().0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_device_type(&self, ty: u32) {
|
||||||
|
self.client.event(DeviceType {
|
||||||
|
self_id: self.id,
|
||||||
|
device_type: ty,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_resumed(&self, serial: u32) {
|
||||||
|
self.client.event(Resumed {
|
||||||
|
self_id: self.id,
|
||||||
|
serial,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_start_emulating(&self, serial: u32, sequence: u32) {
|
||||||
|
self.client.event(ServerStartEmulating {
|
||||||
|
self_id: self.id,
|
||||||
|
serial,
|
||||||
|
sequence,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_region(&self, rect: Rect, scale: Scale) {
|
||||||
|
self.client.event(Region {
|
||||||
|
self_id: self.id,
|
||||||
|
offset_x: rect.x1() as u32,
|
||||||
|
offset_y: rect.y1() as u32,
|
||||||
|
width: rect.width() as u32,
|
||||||
|
hight: rect.height() as u32,
|
||||||
|
scale: scale.to_f64() as f32,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn send_paused(&self, serial: u32) {
|
||||||
|
self.client.event(Paused {
|
||||||
|
self_id: self.id,
|
||||||
|
serial,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_done(&self) {
|
||||||
|
self.client.event(Done { self_id: self.id });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_frame(&self, serial: u32, timestamp: u64) {
|
||||||
|
self.client.event(ServerFrame {
|
||||||
|
self_id: self.id,
|
||||||
|
serial,
|
||||||
|
timestamp,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_destroyed(&self, serial: u32) {
|
||||||
|
self.client.event(Destroyed {
|
||||||
|
self_id: self.id,
|
||||||
|
serial,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(&self) -> Result<(), EiClientError> {
|
||||||
|
macro_rules! destroy_interface {
|
||||||
|
($name:ident) => {
|
||||||
|
if let Some(interface) = self.seat.$name.take() {
|
||||||
|
interface.destroy()?;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
destroy_interface!(pointer);
|
||||||
|
destroy_interface!(pointer_absolute);
|
||||||
|
destroy_interface!(scroll);
|
||||||
|
destroy_interface!(button);
|
||||||
|
destroy_interface!(keyboard);
|
||||||
|
destroy_interface!(touchscreen);
|
||||||
|
self.send_destroyed(self.client.serial());
|
||||||
|
self.client.remove_obj(self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiDeviceRequestHandler for EiDevice {
|
||||||
|
type Error = EiDeviceError;
|
||||||
|
|
||||||
|
fn release(&self, _req: Release, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
self.destroy()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn client_start_emulating(
|
||||||
|
&self,
|
||||||
|
_req: ClientStartEmulating,
|
||||||
|
_slf: &Rc<Self>,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn client_stop_emulating(
|
||||||
|
&self,
|
||||||
|
_req: ClientStopEmulating,
|
||||||
|
_slf: &Rc<Self>,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn client_frame(&self, req: ClientFrame, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
let seat = &self.seat.seat;
|
||||||
|
let time = req.timestamp;
|
||||||
|
while let Some((button, pressed)) = self.button_changes.pop() {
|
||||||
|
seat.button_event(time, button, pressed);
|
||||||
|
}
|
||||||
|
while let Some((button, pressed)) = self.key_changes.pop() {
|
||||||
|
seat.key_event_with_seat_state(time, button, pressed);
|
||||||
|
}
|
||||||
|
if let Some((x, y)) = self.relative_motion.take() {
|
||||||
|
let x = Fixed::from_f32(x);
|
||||||
|
let y = Fixed::from_f32(y);
|
||||||
|
seat.motion_event(time, x, y, x, y);
|
||||||
|
}
|
||||||
|
if let Some((x, y)) = self.absolute_motion.take() {
|
||||||
|
let x = Fixed::from_f32(x);
|
||||||
|
let y = Fixed::from_f32(y);
|
||||||
|
seat.motion_event_abs(time, x, y);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut need_frame = false;
|
||||||
|
for axis in [ScrollAxis::Horizontal, ScrollAxis::Vertical] {
|
||||||
|
let idx = axis as usize;
|
||||||
|
if let Some(v120) = self.scroll_v120[idx].take() {
|
||||||
|
need_frame = true;
|
||||||
|
seat.axis_120(v120, axis, false);
|
||||||
|
}
|
||||||
|
if let Some(px) = self.scroll_px[idx].take() {
|
||||||
|
need_frame = true;
|
||||||
|
seat.axis_px(Fixed::from_f32(px), axis, false);
|
||||||
|
}
|
||||||
|
if self.scroll_stop[idx].take() == Some(true) {
|
||||||
|
need_frame = true;
|
||||||
|
seat.axis_stop(axis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if need_frame {
|
||||||
|
seat.axis_frame(PX_PER_SCROLL, time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.touch_changes.is_not_empty() {
|
||||||
|
while let Some((touch_id, change)) = self.touch_changes.pop() {
|
||||||
|
let id = touch_id as i32;
|
||||||
|
match change {
|
||||||
|
TouchChange::Down(x, y) => {
|
||||||
|
let x = Fixed::from_f32(x);
|
||||||
|
let y = Fixed::from_f32(y);
|
||||||
|
seat.touch_down_at(time, id, x, y);
|
||||||
|
}
|
||||||
|
TouchChange::Motion(x, y) => {
|
||||||
|
let x = Fixed::from_f32(x);
|
||||||
|
let y = Fixed::from_f32(y);
|
||||||
|
seat.touch_motion_at(time, id, x, y);
|
||||||
|
}
|
||||||
|
TouchChange::Up => seat.touch_up(time, id),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
seat.touch_frame(time);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ei_object_base! {
|
||||||
|
self = EiDevice;
|
||||||
|
version = self.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiObject for EiDevice {}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum EiDeviceError {
|
||||||
|
#[error(transparent)]
|
||||||
|
EiClientError(Box<EiClientError>),
|
||||||
|
}
|
||||||
|
efrom!(EiDeviceError, EiClientError);
|
||||||
189
src/ei/ei_ifs/ei_handshake.rs
Normal file
189
src/ei/ei_ifs/ei_handshake.rs
Normal file
|
|
@ -0,0 +1,189 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
ei::{
|
||||||
|
ei_client::{EiClient, EiClientError},
|
||||||
|
ei_ifs::ei_connection::EiConnection,
|
||||||
|
ei_object::{EiInterface, EiObject, EiVersion, EI_HANDSHAKE_ID},
|
||||||
|
EiContext,
|
||||||
|
},
|
||||||
|
leaks::Tracker,
|
||||||
|
wire_ei::{
|
||||||
|
ei_handshake::{
|
||||||
|
ClientHandshakeVersion, ClientInterfaceVersion, Connection, ContextType,
|
||||||
|
EiHandshakeRequestHandler, Finish, Name, ServerHandshakeVersion,
|
||||||
|
ServerInterfaceVersion,
|
||||||
|
},
|
||||||
|
EiHandshake, EiHandshakeId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
std::{cell::Cell, rc::Rc},
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct EiHandshake {
|
||||||
|
pub id: EiHandshakeId,
|
||||||
|
client: Rc<EiClient>,
|
||||||
|
version: Cell<EiVersion>,
|
||||||
|
pub tracker: Tracker<Self>,
|
||||||
|
have_context_type: Cell<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiHandshake {
|
||||||
|
pub fn new(client: &Rc<EiClient>) -> Self {
|
||||||
|
Self {
|
||||||
|
id: EI_HANDSHAKE_ID,
|
||||||
|
client: client.clone(),
|
||||||
|
version: Cell::new(EiVersion(1)),
|
||||||
|
tracker: Default::default(),
|
||||||
|
have_context_type: Cell::new(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_handshake_version(&self) {
|
||||||
|
self.client.event(ServerHandshakeVersion {
|
||||||
|
self_id: self.id,
|
||||||
|
version: 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_interface_version(&self, interface: EiInterface, version: EiVersion) {
|
||||||
|
self.client.event(ServerInterfaceVersion {
|
||||||
|
self_id: self.id,
|
||||||
|
name: interface.0,
|
||||||
|
version: version.0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_connection(&self, serial: u32, connection: &EiConnection) {
|
||||||
|
self.client.event(Connection {
|
||||||
|
self_id: self.id,
|
||||||
|
serial,
|
||||||
|
connection: connection.id,
|
||||||
|
version: connection.version.0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiHandshakeRequestHandler for EiHandshake {
|
||||||
|
type Error = EiHandshakeError;
|
||||||
|
|
||||||
|
fn client_handshake_version(
|
||||||
|
&self,
|
||||||
|
req: ClientHandshakeVersion,
|
||||||
|
_slf: &Rc<Self>,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
let version = EiVersion(req.version);
|
||||||
|
if version > self.client.versions.ei_handshake.server_max_version {
|
||||||
|
return Err(EiHandshakeError::UnknownHandshakeVersion);
|
||||||
|
}
|
||||||
|
self.client
|
||||||
|
.versions
|
||||||
|
.ei_handshake
|
||||||
|
.set_client_version(version);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish(&self, _req: Finish, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
if !self.have_context_type.get() {
|
||||||
|
return Err(EiHandshakeError::NoContextType);
|
||||||
|
}
|
||||||
|
if self.client.name.borrow().is_none() {
|
||||||
|
return Err(EiHandshakeError::NoName);
|
||||||
|
}
|
||||||
|
if self.client.versions.ei_connection.version.get() == EiVersion(0) {
|
||||||
|
return Err(EiHandshakeError::NoConnectionVersion);
|
||||||
|
}
|
||||||
|
if self.client.versions.ei_callback.version.get() == EiVersion(0) {
|
||||||
|
return Err(EiHandshakeError::NoCallbackVersion);
|
||||||
|
}
|
||||||
|
self.client.versions.for_each(|interface, version| {
|
||||||
|
let version = version.version.get();
|
||||||
|
if version > EiVersion(0) && interface != EiHandshake {
|
||||||
|
self.send_interface_version(interface, version);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let connection = Rc::new(EiConnection {
|
||||||
|
id: self.client.new_id(),
|
||||||
|
client: self.client.clone(),
|
||||||
|
tracker: Default::default(),
|
||||||
|
version: self.client.versions.ei_connection.version.get(),
|
||||||
|
});
|
||||||
|
self.client.add_server_obj(&connection);
|
||||||
|
track!(self.client, connection);
|
||||||
|
self.client.connection.set(Some(connection.clone()));
|
||||||
|
self.send_connection(self.client.serial(), &connection);
|
||||||
|
self.client.remove_obj(self)?;
|
||||||
|
for seat in self.client.state.globals.seats.lock().values() {
|
||||||
|
connection.announce_seat(seat);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn context_type(&self, req: ContextType, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
if self.have_context_type.replace(true) {
|
||||||
|
return Err(EiHandshakeError::ContextTypeSet);
|
||||||
|
}
|
||||||
|
let ty = match req.context_type {
|
||||||
|
1 => EiContext::Receiver,
|
||||||
|
2 => EiContext::Sender,
|
||||||
|
_ => return Err(EiHandshakeError::UnknownContextType(req.context_type)),
|
||||||
|
};
|
||||||
|
self.client.context.set(ty);
|
||||||
|
self.have_context_type.set(true);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self, req: Name<'_>, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
let name = &mut *self.client.name.borrow_mut();
|
||||||
|
if name.is_some() {
|
||||||
|
return Err(EiHandshakeError::NameSet);
|
||||||
|
}
|
||||||
|
*name = Some(req.name.to_string());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn client_interface_version(
|
||||||
|
&self,
|
||||||
|
req: ClientInterfaceVersion<'_>,
|
||||||
|
_slf: &Rc<Self>,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
self.client.versions.match_(req.name, |v| {
|
||||||
|
v.set_client_version(EiVersion(req.version));
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ei_object_base! {
|
||||||
|
self = EiHandshake;
|
||||||
|
version = self.version.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiObject for EiHandshake {
|
||||||
|
fn context(&self) -> EiContext {
|
||||||
|
panic!("context requested for EiHandshake")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum EiHandshakeError {
|
||||||
|
#[error(transparent)]
|
||||||
|
EiClientError(Box<EiClientError>),
|
||||||
|
#[error("ei_handshake version is too large")]
|
||||||
|
UnknownHandshakeVersion,
|
||||||
|
#[error("Name is already set")]
|
||||||
|
NameSet,
|
||||||
|
#[error("Unknown context type {0}")]
|
||||||
|
UnknownContextType(u32),
|
||||||
|
#[error("Context type is already set")]
|
||||||
|
ContextTypeSet,
|
||||||
|
#[error("Client did not set connection version")]
|
||||||
|
NoConnectionVersion,
|
||||||
|
#[error("Client did not set callback version")]
|
||||||
|
NoCallbackVersion,
|
||||||
|
#[error("Client did not set context type")]
|
||||||
|
NoContextType,
|
||||||
|
#[error("Client did not set name")]
|
||||||
|
NoName,
|
||||||
|
}
|
||||||
|
efrom!(EiHandshakeError, EiClientError);
|
||||||
97
src/ei/ei_ifs/ei_keyboard.rs
Normal file
97
src/ei/ei_ifs/ei_keyboard.rs
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
backend::KeyState,
|
||||||
|
ei::{
|
||||||
|
ei_client::{EiClient, EiClientError},
|
||||||
|
ei_ifs::ei_device::{EiDevice, EiDeviceInterface},
|
||||||
|
ei_object::{EiObject, EiVersion},
|
||||||
|
},
|
||||||
|
leaks::Tracker,
|
||||||
|
wire_ei::{
|
||||||
|
ei_keyboard::{
|
||||||
|
ClientKey, EiKeyboardRequestHandler, Keymap, Modifiers, Release, ServerKey,
|
||||||
|
},
|
||||||
|
EiKeyboardId,
|
||||||
|
},
|
||||||
|
xkbcommon::KeyboardState,
|
||||||
|
},
|
||||||
|
std::rc::Rc,
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct EiKeyboard {
|
||||||
|
pub id: EiKeyboardId,
|
||||||
|
pub client: Rc<EiClient>,
|
||||||
|
pub tracker: Tracker<Self>,
|
||||||
|
pub version: EiVersion,
|
||||||
|
pub device: Rc<EiDevice>,
|
||||||
|
}
|
||||||
|
|
||||||
|
ei_device_interface!(EiKeyboard, ei_keyboard, keyboard);
|
||||||
|
|
||||||
|
const KEYMAP_TYPE_XKB: u32 = 1;
|
||||||
|
|
||||||
|
impl EiKeyboard {
|
||||||
|
pub fn send_keymap(&self, state: &KeyboardState) {
|
||||||
|
self.client.event(Keymap {
|
||||||
|
self_id: self.id,
|
||||||
|
keymap_type: KEYMAP_TYPE_XKB,
|
||||||
|
size: state.map_len as _,
|
||||||
|
keymap: state.map.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_modifiers(&self, state: &KeyboardState) {
|
||||||
|
self.client.event(Modifiers {
|
||||||
|
self_id: self.id,
|
||||||
|
serial: self.client.serial(),
|
||||||
|
depressed: state.mods.mods_depressed,
|
||||||
|
locked: state.mods.mods_locked,
|
||||||
|
latched: state.mods.mods_latched,
|
||||||
|
group: state.mods.group,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_key(&self, key: u32, state: u32) {
|
||||||
|
self.client.event(ServerKey {
|
||||||
|
self_id: self.id,
|
||||||
|
key,
|
||||||
|
state,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiKeyboardRequestHandler for EiKeyboard {
|
||||||
|
type Error = EiKeyboardError;
|
||||||
|
|
||||||
|
fn release(&self, _req: Release, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
self.destroy()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn client_key(&self, req: ClientKey, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
let pressed = match req.state {
|
||||||
|
0 => KeyState::Released,
|
||||||
|
1 => KeyState::Pressed,
|
||||||
|
_ => return Err(EiKeyboardError::InvalidKeyState(req.state)),
|
||||||
|
};
|
||||||
|
self.device.key_changes.push((req.key, pressed));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ei_object_base! {
|
||||||
|
self = EiKeyboard;
|
||||||
|
version = self.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiObject for EiKeyboard {}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum EiKeyboardError {
|
||||||
|
#[error(transparent)]
|
||||||
|
EiClientError(Box<EiClientError>),
|
||||||
|
#[error("Invalid key state {0}")]
|
||||||
|
InvalidKeyState(u32),
|
||||||
|
}
|
||||||
|
efrom!(EiKeyboardError, EiClientError);
|
||||||
45
src/ei/ei_ifs/ei_pingpong.rs
Normal file
45
src/ei/ei_ifs/ei_pingpong.rs
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
ei::{
|
||||||
|
ei_client::{EiClient, EiClientError},
|
||||||
|
ei_object::{EiObject, EiVersion},
|
||||||
|
},
|
||||||
|
leaks::Tracker,
|
||||||
|
wire_ei::{
|
||||||
|
ei_pingpong::{Done, EiPingpongRequestHandler},
|
||||||
|
EiPingpongId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
std::rc::Rc,
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub struct EiPingpong {
|
||||||
|
pub id: EiPingpongId,
|
||||||
|
pub client: Rc<EiClient>,
|
||||||
|
pub tracker: Tracker<Self>,
|
||||||
|
pub version: EiVersion,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiPingpongRequestHandler for EiPingpong {
|
||||||
|
type Error = EiPingpongError;
|
||||||
|
|
||||||
|
fn done(&self, _req: Done, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ei_object_base! {
|
||||||
|
self = EiPingpong;
|
||||||
|
version = self.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiObject for EiPingpong {}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum EiPingpongError {
|
||||||
|
#[error(transparent)]
|
||||||
|
EiClientError(Box<EiClientError>),
|
||||||
|
}
|
||||||
|
efrom!(EiPingpongError, EiClientError);
|
||||||
71
src/ei/ei_ifs/ei_pointer.rs
Normal file
71
src/ei/ei_ifs/ei_pointer.rs
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
ei::{
|
||||||
|
ei_client::{EiClient, EiClientError},
|
||||||
|
ei_ifs::ei_device::{EiDevice, EiDeviceInterface},
|
||||||
|
ei_object::{EiObject, EiVersion},
|
||||||
|
},
|
||||||
|
fixed::Fixed,
|
||||||
|
leaks::Tracker,
|
||||||
|
wire_ei::{
|
||||||
|
ei_pointer::{
|
||||||
|
ClientMotionRelative, EiPointerRequestHandler, Release, ServerMotionRelative,
|
||||||
|
},
|
||||||
|
EiPointerId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
std::rc::Rc,
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct EiPointer {
|
||||||
|
pub id: EiPointerId,
|
||||||
|
pub client: Rc<EiClient>,
|
||||||
|
pub tracker: Tracker<Self>,
|
||||||
|
pub version: EiVersion,
|
||||||
|
pub device: Rc<EiDevice>,
|
||||||
|
}
|
||||||
|
|
||||||
|
ei_device_interface!(EiPointer, ei_pointer, pointer);
|
||||||
|
|
||||||
|
impl EiPointer {
|
||||||
|
pub fn send_motion(&self, dx: Fixed, dy: Fixed) {
|
||||||
|
self.client.event(ServerMotionRelative {
|
||||||
|
self_id: self.id,
|
||||||
|
x: dx.to_f32(),
|
||||||
|
y: dy.to_f32(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiPointerRequestHandler for EiPointer {
|
||||||
|
type Error = EiPointerError;
|
||||||
|
|
||||||
|
fn release(&self, _req: Release, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
self.destroy()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn client_motion_relative(
|
||||||
|
&self,
|
||||||
|
req: ClientMotionRelative,
|
||||||
|
_slf: &Rc<Self>,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
self.device.relative_motion.set(Some((req.x, req.y)));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ei_object_base! {
|
||||||
|
self = EiPointer;
|
||||||
|
version = self.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiObject for EiPointer {}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum EiPointerError {
|
||||||
|
#[error(transparent)]
|
||||||
|
EiClientError(Box<EiClientError>),
|
||||||
|
}
|
||||||
|
efrom!(EiPointerError, EiClientError);
|
||||||
72
src/ei/ei_ifs/ei_pointer_absolute.rs
Normal file
72
src/ei/ei_ifs/ei_pointer_absolute.rs
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
ei::{
|
||||||
|
ei_client::{EiClient, EiClientError},
|
||||||
|
ei_ifs::ei_device::{EiDevice, EiDeviceInterface},
|
||||||
|
ei_object::{EiObject, EiVersion},
|
||||||
|
},
|
||||||
|
fixed::Fixed,
|
||||||
|
leaks::Tracker,
|
||||||
|
wire_ei::{
|
||||||
|
ei_pointer_absolute::{
|
||||||
|
ClientMotionAbsolute, EiPointerAbsoluteRequestHandler, Release,
|
||||||
|
ServerMotionAbsolute,
|
||||||
|
},
|
||||||
|
EiPointerAbsoluteId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
std::rc::Rc,
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct EiPointerAbsolute {
|
||||||
|
pub id: EiPointerAbsoluteId,
|
||||||
|
pub client: Rc<EiClient>,
|
||||||
|
pub tracker: Tracker<Self>,
|
||||||
|
pub version: EiVersion,
|
||||||
|
pub device: Rc<EiDevice>,
|
||||||
|
}
|
||||||
|
|
||||||
|
ei_device_interface!(EiPointerAbsolute, ei_pointer_absolute, pointer_absolute);
|
||||||
|
|
||||||
|
impl EiPointerAbsolute {
|
||||||
|
pub fn send_motion_absolute(&self, x: Fixed, y: Fixed) {
|
||||||
|
self.client.event(ServerMotionAbsolute {
|
||||||
|
self_id: self.id,
|
||||||
|
x: x.to_f32(),
|
||||||
|
y: y.to_f32(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiPointerAbsoluteRequestHandler for EiPointerAbsolute {
|
||||||
|
type Error = EiCallbackError;
|
||||||
|
|
||||||
|
fn release(&self, _req: Release, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
self.destroy()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn client_motion_absolute(
|
||||||
|
&self,
|
||||||
|
req: ClientMotionAbsolute,
|
||||||
|
_slf: &Rc<Self>,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
self.device.absolute_motion.set(Some((req.x, req.y)));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ei_object_base! {
|
||||||
|
self = EiPointerAbsolute;
|
||||||
|
version = self.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiObject for EiPointerAbsolute {}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum EiCallbackError {
|
||||||
|
#[error(transparent)]
|
||||||
|
EiClientError(Box<EiClientError>),
|
||||||
|
}
|
||||||
|
efrom!(EiCallbackError, EiClientError);
|
||||||
106
src/ei/ei_ifs/ei_scroll.rs
Normal file
106
src/ei/ei_ifs/ei_scroll.rs
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
ei::{
|
||||||
|
ei_client::{EiClient, EiClientError},
|
||||||
|
ei_ifs::ei_device::{EiDevice, EiDeviceInterface},
|
||||||
|
ei_object::{EiObject, EiVersion},
|
||||||
|
},
|
||||||
|
fixed::Fixed,
|
||||||
|
leaks::Tracker,
|
||||||
|
wire_ei::{
|
||||||
|
ei_scroll::{
|
||||||
|
ClientScroll, ClientScrollDiscrete, ClientScrollStop, EiScrollRequestHandler,
|
||||||
|
Release, ServerScroll, ServerScrollDiscrete, ServerScrollStop,
|
||||||
|
},
|
||||||
|
EiScrollId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
std::rc::Rc,
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct EiScroll {
|
||||||
|
pub id: EiScrollId,
|
||||||
|
pub client: Rc<EiClient>,
|
||||||
|
pub tracker: Tracker<Self>,
|
||||||
|
pub version: EiVersion,
|
||||||
|
pub device: Rc<EiDevice>,
|
||||||
|
}
|
||||||
|
|
||||||
|
ei_device_interface!(EiScroll, ei_scroll, scroll);
|
||||||
|
|
||||||
|
impl EiScroll {
|
||||||
|
pub fn send_scroll(&self, x: Fixed, y: Fixed) {
|
||||||
|
self.client.event(ServerScroll {
|
||||||
|
self_id: self.id,
|
||||||
|
x: x.to_f32(),
|
||||||
|
y: y.to_f32(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_scroll_discrete(&self, x: i32, y: i32) {
|
||||||
|
self.client.event(ServerScrollDiscrete {
|
||||||
|
self_id: self.id,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_scroll_stop(&self, x: bool, y: bool) {
|
||||||
|
self.client.event(ServerScrollStop {
|
||||||
|
self_id: self.id,
|
||||||
|
x: x as _,
|
||||||
|
y: y as _,
|
||||||
|
is_cancel: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiScrollRequestHandler for EiScroll {
|
||||||
|
type Error = EiScrollError;
|
||||||
|
|
||||||
|
fn release(&self, _req: Release, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
self.destroy()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn client_scroll(&self, req: ClientScroll, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
self.device.scroll_px[0].set(Some(req.x));
|
||||||
|
self.device.scroll_px[1].set(Some(req.y));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn client_scroll_discrete(
|
||||||
|
&self,
|
||||||
|
req: ClientScrollDiscrete,
|
||||||
|
_slf: &Rc<Self>,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
self.device.scroll_v120[0].set(Some(req.x));
|
||||||
|
self.device.scroll_v120[1].set(Some(req.y));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn client_scroll_stop(
|
||||||
|
&self,
|
||||||
|
req: ClientScrollStop,
|
||||||
|
_slf: &Rc<Self>,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
self.device.scroll_stop[0].set(Some(req.x != 0));
|
||||||
|
self.device.scroll_stop[1].set(Some(req.y != 0));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ei_object_base! {
|
||||||
|
self = EiScroll;
|
||||||
|
version = self.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiObject for EiScroll {}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum EiScrollError {
|
||||||
|
#[error(transparent)]
|
||||||
|
EiClientError(Box<EiClientError>),
|
||||||
|
}
|
||||||
|
efrom!(EiScrollError, EiClientError);
|
||||||
425
src/ei/ei_ifs/ei_seat.rs
Normal file
425
src/ei/ei_ifs/ei_seat.rs
Normal file
|
|
@ -0,0 +1,425 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
backend::KeyState,
|
||||||
|
ei::{
|
||||||
|
ei_client::{EiClient, EiClientError},
|
||||||
|
ei_ifs::{
|
||||||
|
ei_button::EiButton,
|
||||||
|
ei_device::{EiDevice, EiDeviceInterface, EI_DEVICE_TYPE_VIRTUAL},
|
||||||
|
ei_keyboard::EiKeyboard,
|
||||||
|
ei_pointer::EiPointer,
|
||||||
|
ei_pointer_absolute::EiPointerAbsolute,
|
||||||
|
ei_scroll::EiScroll,
|
||||||
|
ei_touchscreen::EiTouchscreen,
|
||||||
|
},
|
||||||
|
ei_object::{EiInterface, EiObject, EiVersion},
|
||||||
|
EiContext,
|
||||||
|
},
|
||||||
|
fixed::Fixed,
|
||||||
|
ifs::wl_seat::{wl_pointer::PendingScroll, WlSeatGlobal},
|
||||||
|
leaks::Tracker,
|
||||||
|
tree::Node,
|
||||||
|
utils::{array, bitflags::BitflagsExt, clonecell::CloneCell},
|
||||||
|
wire_ei::{
|
||||||
|
ei_seat::{
|
||||||
|
Bind, Capability, Destroyed, Device, Done, EiSeatRequestHandler, Name, Release,
|
||||||
|
},
|
||||||
|
EiSeatId,
|
||||||
|
},
|
||||||
|
xkbcommon::{DynKeyboardState, KeyboardState, KeyboardStateId},
|
||||||
|
},
|
||||||
|
std::{cell::Cell, rc::Rc},
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const EI_CAP_POINTER: u64 = 1 << 0;
|
||||||
|
pub const EI_CAP_POINTER_ABSOLUTE: u64 = 1 << 1;
|
||||||
|
pub const EI_CAP_SCROLL: u64 = 1 << 2;
|
||||||
|
pub const EI_CAP_BUTTON: u64 = 1 << 3;
|
||||||
|
pub const EI_CAP_KEYBOARD: u64 = 1 << 4;
|
||||||
|
pub const EI_CAP_TOUCHSCREEN: u64 = 1 << 5;
|
||||||
|
|
||||||
|
pub const EI_CAP_ALL: u64 = (1 << 6) - 1;
|
||||||
|
|
||||||
|
pub struct EiSeat {
|
||||||
|
pub id: EiSeatId,
|
||||||
|
pub client: Rc<EiClient>,
|
||||||
|
pub tracker: Tracker<Self>,
|
||||||
|
pub version: EiVersion,
|
||||||
|
pub seat: Rc<WlSeatGlobal>,
|
||||||
|
pub capabilities: Cell<u64>,
|
||||||
|
pub kb_state_id: Cell<KeyboardStateId>,
|
||||||
|
|
||||||
|
pub device: CloneCell<Option<Rc<EiDevice>>>,
|
||||||
|
pub pointer: CloneCell<Option<Rc<EiPointer>>>,
|
||||||
|
pub pointer_absolute: CloneCell<Option<Rc<EiPointerAbsolute>>>,
|
||||||
|
pub keyboard: CloneCell<Option<Rc<EiKeyboard>>>,
|
||||||
|
pub button: CloneCell<Option<Rc<EiButton>>>,
|
||||||
|
pub scroll: CloneCell<Option<Rc<EiScroll>>>,
|
||||||
|
pub touchscreen: CloneCell<Option<Rc<EiTouchscreen>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiSeat {
|
||||||
|
fn is_sender(&self) -> bool {
|
||||||
|
self.context() == EiContext::Sender
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn regions_changed(self: &Rc<Self>) {
|
||||||
|
if self.touchscreen.is_none() && self.pointer_absolute.is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let kb_state = self.get_kb_state();
|
||||||
|
let kb_state = kb_state.borrow();
|
||||||
|
if let Err(e) = self.recreate_all(false, &kb_state) {
|
||||||
|
self.client.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_xkb_state_change(self: &Rc<Self>, old_id: KeyboardStateId, new: &KeyboardState) {
|
||||||
|
if self.keyboard.is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if self.kb_state_id.get() != old_id {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.kb_state_id.set(new.id);
|
||||||
|
if let Err(e) = self.recreate_all(false, new) {
|
||||||
|
self.client.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_modifiers_changed(self: &Rc<Self>, state: &KeyboardState) {
|
||||||
|
let old_id = self.kb_state_id.get();
|
||||||
|
if old_id != state.id {
|
||||||
|
if self.is_sender() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.handle_xkb_state_change(old_id, state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Some(kb) = self.keyboard.get() {
|
||||||
|
kb.send_modifiers(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_key(
|
||||||
|
self: &Rc<Self>,
|
||||||
|
time_usec: u64,
|
||||||
|
key: u32,
|
||||||
|
state: u32,
|
||||||
|
kb_state: &KeyboardState,
|
||||||
|
) {
|
||||||
|
if self.is_sender() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let old_id = self.kb_state_id.get();
|
||||||
|
if old_id != kb_state.id {
|
||||||
|
self.handle_xkb_state_change(old_id, kb_state);
|
||||||
|
}
|
||||||
|
if let Some(kb) = self.keyboard.get() {
|
||||||
|
kb.send_key(key, state);
|
||||||
|
kb.device.send_frame(self.client.serial(), time_usec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_motion_abs(&self, time_usec: u64, x: Fixed, y: Fixed) {
|
||||||
|
if self.is_sender() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Some(v) = self.pointer_absolute.get() {
|
||||||
|
v.send_motion_absolute(x, y);
|
||||||
|
v.device.send_frame(self.client.serial(), time_usec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_motion(&self, time_usec: u64, dx: Fixed, dy: Fixed) {
|
||||||
|
if self.is_sender() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Some(v) = self.pointer.get() {
|
||||||
|
v.send_motion(dx, dy);
|
||||||
|
v.device.send_frame(self.client.serial(), time_usec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_button(&self, time_usec: u64, button: u32, state: KeyState) {
|
||||||
|
if self.is_sender() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Some(b) = self.button.get() {
|
||||||
|
b.send_button(button, state);
|
||||||
|
b.device.send_frame(self.client.serial(), time_usec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_pending_scroll(&self, time_usec: u64, ps: &PendingScroll) {
|
||||||
|
if self.is_sender() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Some(b) = self.scroll.get() {
|
||||||
|
b.send_scroll(
|
||||||
|
ps.px[0].get().unwrap_or_default(),
|
||||||
|
ps.px[1].get().unwrap_or_default(),
|
||||||
|
);
|
||||||
|
b.send_scroll_discrete(
|
||||||
|
ps.v120[0].get().unwrap_or_default(),
|
||||||
|
ps.v120[1].get().unwrap_or_default(),
|
||||||
|
);
|
||||||
|
b.send_scroll_stop(ps.stop[0].get(), ps.stop[1].get());
|
||||||
|
b.device.send_frame(self.client.serial(), time_usec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_touch_down(&self, id: u32, x: Fixed, y: Fixed) {
|
||||||
|
if self.is_sender() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Some(b) = self.touchscreen.get() {
|
||||||
|
b.send_down(id, x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_touch_motion(&self, id: u32, x: Fixed, y: Fixed) {
|
||||||
|
if self.is_sender() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Some(b) = self.touchscreen.get() {
|
||||||
|
b.send_motion(id, x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_touch_up(&self, id: u32) {
|
||||||
|
if self.is_sender() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Some(b) = self.touchscreen.get() {
|
||||||
|
b.send_up(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_touch_frame(&self, time_usec: u64) {
|
||||||
|
if self.is_sender() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Some(b) = self.touchscreen.get() {
|
||||||
|
b.device.send_frame(self.client.serial(), time_usec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_capability(&self, interface: EiInterface, mask: u64) {
|
||||||
|
self.client.event(Capability {
|
||||||
|
self_id: self.id,
|
||||||
|
mask,
|
||||||
|
interface: interface.0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_done(&self) {
|
||||||
|
self.client.event(Done { self_id: self.id });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_name(&self, name: &str) {
|
||||||
|
self.client.event(Name {
|
||||||
|
self_id: self.id,
|
||||||
|
name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_device(&self, device: &EiDevice) {
|
||||||
|
self.client.event(Device {
|
||||||
|
self_id: self.id,
|
||||||
|
device: device.id,
|
||||||
|
version: device.version.0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_destroyed(&self) {
|
||||||
|
self.client.event(Destroyed {
|
||||||
|
self_id: self.id,
|
||||||
|
serial: self.client.serial(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_interface<T>(self: &Rc<Self>, field: &CloneCell<Option<Rc<T>>>, version: EiVersion)
|
||||||
|
where
|
||||||
|
T: EiDeviceInterface,
|
||||||
|
{
|
||||||
|
if version == EiVersion(0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let Some(device) = self.device.get() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let interface = T::new(&device, version);
|
||||||
|
self.client.add_server_obj(&interface);
|
||||||
|
device.send_interface(&*interface);
|
||||||
|
field.set(Some(interface.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_pointer(self: &Rc<Self>) {
|
||||||
|
self.create_interface(&self.pointer, self.client.versions.ei_pointer());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_button(self: &Rc<Self>) {
|
||||||
|
self.create_interface(&self.button, self.client.versions.ei_button());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_keyboard(self: &Rc<Self>) {
|
||||||
|
self.create_interface(&self.keyboard, self.client.versions.ei_keyboard());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_scroll(self: &Rc<Self>) {
|
||||||
|
self.create_interface(&self.scroll, self.client.versions.ei_scroll());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_pointer_absolute(self: &Rc<Self>) {
|
||||||
|
self.create_interface(
|
||||||
|
&self.pointer_absolute,
|
||||||
|
self.client.versions.ei_pointer_absolute(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_touchscreen(self: &Rc<Self>) {
|
||||||
|
self.create_interface(&self.touchscreen, self.client.versions.ei_touchscreen());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_kb_state(&self) -> Rc<dyn DynKeyboardState> {
|
||||||
|
match self.context() {
|
||||||
|
EiContext::Sender => self.seat.seat_xkb_state(),
|
||||||
|
EiContext::Receiver => self.seat.latest_xkb_state(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn recreate_all(
|
||||||
|
self: &Rc<Self>,
|
||||||
|
create_all: bool,
|
||||||
|
kb_state: &KeyboardState,
|
||||||
|
) -> Result<(), EiClientError> {
|
||||||
|
self.kb_state_id.set(kb_state.id);
|
||||||
|
let have_outputs = self.client.state.root.outputs.is_not_empty();
|
||||||
|
let create_pointer = create_all || self.pointer.is_some();
|
||||||
|
let create_pointer_absolute =
|
||||||
|
have_outputs && (create_all || self.pointer_absolute.is_some());
|
||||||
|
let create_scroll = create_all || self.scroll.is_some();
|
||||||
|
let create_button = create_all || self.button.is_some();
|
||||||
|
let create_keyboard = create_all || self.keyboard.is_some();
|
||||||
|
let create_touchscreen = have_outputs && (create_all || self.touchscreen.is_some());
|
||||||
|
if let Some(device) = self.device.take() {
|
||||||
|
device.destroy()?;
|
||||||
|
}
|
||||||
|
let version = self.client.versions.ei_device();
|
||||||
|
if version == EiVersion(0) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let device = Rc::new(EiDevice {
|
||||||
|
id: self.client.new_id(),
|
||||||
|
client: self.client.clone(),
|
||||||
|
tracker: Default::default(),
|
||||||
|
version,
|
||||||
|
seat: self.clone(),
|
||||||
|
button_changes: Default::default(),
|
||||||
|
touch_changes: Default::default(),
|
||||||
|
scroll_px: array::from_fn(|_| Default::default()),
|
||||||
|
scroll_v120: array::from_fn(|_| Default::default()),
|
||||||
|
scroll_stop: array::from_fn(|_| Default::default()),
|
||||||
|
absolute_motion: Default::default(),
|
||||||
|
relative_motion: Default::default(),
|
||||||
|
key_changes: Default::default(),
|
||||||
|
});
|
||||||
|
track!(self.client, device);
|
||||||
|
self.device.set(Some(device.clone()));
|
||||||
|
self.client.add_server_obj(&device);
|
||||||
|
self.send_device(&device);
|
||||||
|
device.send_device_type(EI_DEVICE_TYPE_VIRTUAL);
|
||||||
|
let caps = self.capabilities.get();
|
||||||
|
macro_rules! apply {
|
||||||
|
($cap:expr, $create:ident) => {
|
||||||
|
if $create && caps.contains($cap) {
|
||||||
|
self.$create();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
apply!(EI_CAP_POINTER, create_pointer);
|
||||||
|
apply!(EI_CAP_POINTER_ABSOLUTE, create_pointer_absolute);
|
||||||
|
apply!(EI_CAP_SCROLL, create_scroll);
|
||||||
|
apply!(EI_CAP_BUTTON, create_button);
|
||||||
|
apply!(EI_CAP_KEYBOARD, create_keyboard);
|
||||||
|
apply!(EI_CAP_TOUCHSCREEN, create_touchscreen);
|
||||||
|
for output in self.client.state.root.outputs.lock().values() {
|
||||||
|
device.send_region(
|
||||||
|
output.node_absolute_position(),
|
||||||
|
output.global.persistent.scale.get(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(kb) = self.keyboard.get() {
|
||||||
|
kb.send_keymap(kb_state);
|
||||||
|
}
|
||||||
|
device.send_done();
|
||||||
|
device.send_resumed(self.client.serial());
|
||||||
|
if self.context() == EiContext::Receiver {
|
||||||
|
device.send_start_emulating(self.client.serial(), 1);
|
||||||
|
}
|
||||||
|
if let Some(kb) = self.keyboard.get() {
|
||||||
|
kb.send_modifiers(kb_state);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_touch_input(&self) -> bool {
|
||||||
|
self.capabilities.get().contains(EI_CAP_TOUCHSCREEN) && self.context() == EiContext::Sender
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiSeatRequestHandler for EiSeat {
|
||||||
|
type Error = EiSeatError;
|
||||||
|
|
||||||
|
fn release(&self, _req: Release, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
self.seat.remove_ei_seat(self);
|
||||||
|
self.send_destroyed();
|
||||||
|
if let Some(device) = self.device.take() {
|
||||||
|
device.destroy()?;
|
||||||
|
}
|
||||||
|
self.client.remove_obj(self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bind(&self, req: Bind, slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
let caps = req.capabilities;
|
||||||
|
let unknown = caps & !EI_CAP_ALL;
|
||||||
|
if unknown != 0 {
|
||||||
|
return Err(EiSeatError::UnknownCapabilities(unknown));
|
||||||
|
}
|
||||||
|
self.capabilities.set(caps);
|
||||||
|
let kb_state = self.get_kb_state();
|
||||||
|
slf.recreate_all(true, &kb_state.borrow())?;
|
||||||
|
self.seat.update_capabilities();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ei_object_base! {
|
||||||
|
self = EiSeat;
|
||||||
|
version = self.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiObject for EiSeat {
|
||||||
|
fn break_loops(&self) {
|
||||||
|
self.seat.remove_ei_seat(self);
|
||||||
|
self.device.take();
|
||||||
|
self.pointer.take();
|
||||||
|
self.pointer_absolute.take();
|
||||||
|
self.keyboard.take();
|
||||||
|
self.button.take();
|
||||||
|
self.scroll.take();
|
||||||
|
self.touchscreen.take();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum EiSeatError {
|
||||||
|
#[error(transparent)]
|
||||||
|
EiClientError(Box<EiClientError>),
|
||||||
|
#[error("Capabilities {0} are unknown")]
|
||||||
|
UnknownCapabilities(u64),
|
||||||
|
}
|
||||||
|
efrom!(EiSeatError, EiClientError);
|
||||||
108
src/ei/ei_ifs/ei_touchscreen.rs
Normal file
108
src/ei/ei_ifs/ei_touchscreen.rs
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
ei::{
|
||||||
|
ei_client::{EiClient, EiClientError},
|
||||||
|
ei_ifs::ei_device::{EiDevice, EiDeviceInterface},
|
||||||
|
ei_object::{EiObject, EiVersion},
|
||||||
|
},
|
||||||
|
fixed::Fixed,
|
||||||
|
leaks::Tracker,
|
||||||
|
wire_ei::{
|
||||||
|
ei_touchscreen::{
|
||||||
|
ClientDown, ClientMotion, ClientUp, EiTouchscreenRequestHandler, Release,
|
||||||
|
ServerDown, ServerMotion, ServerUp,
|
||||||
|
},
|
||||||
|
EiTouchscreenId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
std::rc::Rc,
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct EiTouchscreen {
|
||||||
|
pub id: EiTouchscreenId,
|
||||||
|
pub client: Rc<EiClient>,
|
||||||
|
pub tracker: Tracker<Self>,
|
||||||
|
pub version: EiVersion,
|
||||||
|
pub device: Rc<EiDevice>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub enum TouchChange {
|
||||||
|
Down(f32, f32),
|
||||||
|
Motion(f32, f32),
|
||||||
|
Up,
|
||||||
|
}
|
||||||
|
|
||||||
|
ei_device_interface!(EiTouchscreen, ei_touchscreen, touchscreen);
|
||||||
|
|
||||||
|
impl EiTouchscreen {
|
||||||
|
pub fn send_down(&self, touchid: u32, x: Fixed, y: Fixed) {
|
||||||
|
self.client.event(ServerDown {
|
||||||
|
self_id: self.id,
|
||||||
|
touchid,
|
||||||
|
x: x.to_f32(),
|
||||||
|
y: y.to_f32(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_motion(&self, touchid: u32, x: Fixed, y: Fixed) {
|
||||||
|
self.client.event(ServerMotion {
|
||||||
|
self_id: self.id,
|
||||||
|
touchid,
|
||||||
|
x: x.to_f32(),
|
||||||
|
y: y.to_f32(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_up(&self, touchid: u32) {
|
||||||
|
self.client.event(ServerUp {
|
||||||
|
self_id: self.id,
|
||||||
|
touchid,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiTouchscreenRequestHandler for EiTouchscreen {
|
||||||
|
type Error = EiTouchscreenError;
|
||||||
|
|
||||||
|
fn release(&self, _req: Release, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
self.destroy()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn client_down(&self, req: ClientDown, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
self.device
|
||||||
|
.touch_changes
|
||||||
|
.push((req.touchid, TouchChange::Down(req.x, req.y)));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn client_motion(&self, req: ClientMotion, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
self.device
|
||||||
|
.touch_changes
|
||||||
|
.push((req.touchid, TouchChange::Motion(req.x, req.y)));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn client_up(&self, req: ClientUp, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
self.device
|
||||||
|
.touch_changes
|
||||||
|
.push((req.touchid, TouchChange::Up));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ei_object_base! {
|
||||||
|
self = EiTouchscreen;
|
||||||
|
version = self.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EiObject for EiTouchscreen {}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum EiTouchscreenError {
|
||||||
|
#[error(transparent)]
|
||||||
|
EiClientError(Box<EiClientError>),
|
||||||
|
}
|
||||||
|
efrom!(EiTouchscreenError, EiClientError);
|
||||||
94
src/ei/ei_object.rs
Normal file
94
src/ei/ei_object.rs
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
ei::{
|
||||||
|
ei_client::{EiClient, EiClientError},
|
||||||
|
EiContext,
|
||||||
|
},
|
||||||
|
utils::buffd::EiMsgParser,
|
||||||
|
wire_ei::EiHandshakeId,
|
||||||
|
},
|
||||||
|
std::{
|
||||||
|
cmp::Ordering,
|
||||||
|
fmt::{Display, Formatter, LowerHex},
|
||||||
|
rc::Rc,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const EI_HANDSHAKE_ID: EiHandshakeId = EiHandshakeId::from_raw(0);
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
|
||||||
|
pub struct EiObjectId(u64);
|
||||||
|
|
||||||
|
impl EiObjectId {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub const NONE: Self = EiObjectId(0);
|
||||||
|
|
||||||
|
pub fn from_raw(raw: u64) -> Self {
|
||||||
|
Self(raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn raw(self) -> u64 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for EiObjectId {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
Display::fmt(&self.0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LowerHex for EiObjectId {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
LowerHex::fmt(&self.0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait EiObjectBase {
|
||||||
|
fn id(&self) -> EiObjectId;
|
||||||
|
fn version(&self) -> EiVersion;
|
||||||
|
fn client(&self) -> &EiClient;
|
||||||
|
fn handle_request(
|
||||||
|
self: Rc<Self>,
|
||||||
|
client: &EiClient,
|
||||||
|
request: u32,
|
||||||
|
parser: EiMsgParser<'_, '_>,
|
||||||
|
) -> Result<(), EiClientError>;
|
||||||
|
fn interface(&self) -> EiInterface;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait EiObject: EiObjectBase + 'static {
|
||||||
|
fn break_loops(&self) {}
|
||||||
|
|
||||||
|
fn context(&self) -> EiContext {
|
||||||
|
self.client().context.get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct EiInterface(pub &'static str);
|
||||||
|
|
||||||
|
impl EiInterface {
|
||||||
|
pub fn name(self) -> &'static str {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
|
pub struct EiVersion(pub u32);
|
||||||
|
|
||||||
|
impl EiVersion {
|
||||||
|
// pub const ALL: EiVersion = EiVersion(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<u32> for EiVersion {
|
||||||
|
fn eq(&self, other: &u32) -> bool {
|
||||||
|
self.0 == *other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd<u32> for EiVersion {
|
||||||
|
fn partial_cmp(&self, other: &u32) -> Option<Ordering> {
|
||||||
|
self.0.partial_cmp(other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -19,10 +19,18 @@ impl Fixed {
|
||||||
Self((f * 256.0) as i32)
|
Self((f * 256.0) as i32)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_f32(f: f32) -> Self {
|
||||||
|
Self::from_f64(f as f64)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn to_f64(self) -> f64 {
|
pub fn to_f64(self) -> f64 {
|
||||||
self.0 as f64 / 256.0
|
self.0 as f64 / 256.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_f32(self) -> f32 {
|
||||||
|
self.0 as f32 / 256.0
|
||||||
|
}
|
||||||
|
|
||||||
pub fn from_1616(i: i32) -> Self {
|
pub fn from_1616(i: i32) -> Self {
|
||||||
Self(i >> 8)
|
Self(i >> 8)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ use {
|
||||||
async_engine::SpawnedFuture,
|
async_engine::SpawnedFuture,
|
||||||
client::{Client, ClientError, ClientId},
|
client::{Client, ClientError, ClientId},
|
||||||
cursor_user::{CursorUser, CursorUserGroup, CursorUserOwner},
|
cursor_user::{CursorUser, CursorUserGroup, CursorUserOwner},
|
||||||
|
ei::ei_ifs::ei_seat::EiSeat,
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
globals::{Global, GlobalName},
|
globals::{Global, GlobalName},
|
||||||
ifs::{
|
ifs::{
|
||||||
|
|
@ -84,6 +85,7 @@ use {
|
||||||
WlSeatId, WlTouchId, ZwlrDataControlDeviceV1Id, ZwpPrimarySelectionDeviceV1Id,
|
WlSeatId, WlTouchId, ZwlrDataControlDeviceV1Id, ZwpPrimarySelectionDeviceV1Id,
|
||||||
ZwpRelativePointerV1Id, ZwpTextInputV3Id,
|
ZwpRelativePointerV1Id, ZwpTextInputV3Id,
|
||||||
},
|
},
|
||||||
|
wire_ei::EiSeatId,
|
||||||
xkbcommon::{DynKeyboardState, KeyboardState, KeymapId, XkbKeymap, XkbState},
|
xkbcommon::{DynKeyboardState, KeyboardState, KeymapId, XkbKeymap, XkbState},
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
|
|
@ -195,6 +197,7 @@ pub struct WlSeatGlobal {
|
||||||
pinch_bindings: PerClientBindings<ZwpPointerGesturePinchV1>,
|
pinch_bindings: PerClientBindings<ZwpPointerGesturePinchV1>,
|
||||||
hold_bindings: PerClientBindings<ZwpPointerGestureHoldV1>,
|
hold_bindings: PerClientBindings<ZwpPointerGestureHoldV1>,
|
||||||
tablet: TabletSeatData,
|
tablet: TabletSeatData,
|
||||||
|
ei_seats: CopyHashMap<(ClientId, EiSeatId), Rc<EiSeat>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const CHANGE_CURSOR_MOVED: u32 = 1 << 0;
|
const CHANGE_CURSOR_MOVED: u32 = 1 << 0;
|
||||||
|
|
@ -263,6 +266,7 @@ impl WlSeatGlobal {
|
||||||
pinch_bindings: Default::default(),
|
pinch_bindings: Default::default(),
|
||||||
hold_bindings: Default::default(),
|
hold_bindings: Default::default(),
|
||||||
tablet: Default::default(),
|
tablet: Default::default(),
|
||||||
|
ei_seats: Default::default(),
|
||||||
});
|
});
|
||||||
slf.pointer_cursor.set_owner(slf.clone());
|
slf.pointer_cursor.set_owner(slf.clone());
|
||||||
let seat = slf.clone();
|
let seat = slf.clone();
|
||||||
|
|
@ -280,10 +284,14 @@ impl WlSeatGlobal {
|
||||||
slf
|
slf
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_capabilities(&self) {
|
pub fn update_capabilities(&self) {
|
||||||
let mut caps = POINTER | KEYBOARD;
|
let mut caps = POINTER | KEYBOARD;
|
||||||
if self.num_touch_devices.get() > 0 {
|
if self.num_touch_devices.get() > 0 {
|
||||||
caps |= TOUCH;
|
caps |= TOUCH;
|
||||||
|
} else {
|
||||||
|
if self.ei_seats.lock().values().any(|s| s.is_touch_input()) {
|
||||||
|
caps |= TOUCH;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if self.capabilities.replace(caps) != caps {
|
if self.capabilities.replace(caps) != caps {
|
||||||
for client in self.bindings.borrow().values() {
|
for client in self.bindings.borrow().values() {
|
||||||
|
|
@ -481,6 +489,9 @@ impl WlSeatGlobal {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_xkb_state_change(&self, old: &XkbState, new: &XkbState) {
|
fn handle_xkb_state_change(&self, old: &XkbState, new: &XkbState) {
|
||||||
|
self.for_each_ei_seat(|ei_seat| {
|
||||||
|
ei_seat.handle_xkb_state_change(old.kb_state.id, &new.kb_state);
|
||||||
|
});
|
||||||
let Some(surface) = self.keyboard_node.get().node_into_surface() else {
|
let Some(surface) = self.keyboard_node.get().node_into_surface() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
@ -888,6 +899,7 @@ impl WlSeatGlobal {
|
||||||
self.hold_bindings.clear();
|
self.hold_bindings.clear();
|
||||||
self.cursor_user_group.detach();
|
self.cursor_user_group.detach();
|
||||||
self.tablet_clear();
|
self.tablet_clear();
|
||||||
|
self.ei_seats.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn id(&self) -> SeatId {
|
pub fn id(&self) -> SeatId {
|
||||||
|
|
@ -982,6 +994,30 @@ impl WlSeatGlobal {
|
||||||
self.pointer_owner
|
self.pointer_owner
|
||||||
.set_window_management_enabled(self, enabled);
|
.set_window_management_enabled(self, enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_ei_seat(&self, ei: &Rc<EiSeat>) {
|
||||||
|
self.ei_seats.set((ei.client.id, ei.id), ei.clone());
|
||||||
|
self.update_capabilities();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_ei_seat(&self, ei: &EiSeat) {
|
||||||
|
self.ei_seats.remove(&(ei.client.id, ei.id));
|
||||||
|
self.update_capabilities();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn seat_xkb_state(&self) -> Rc<dyn DynKeyboardState> {
|
||||||
|
self.seat_xkb_state.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn latest_xkb_state(&self) -> Rc<dyn DynKeyboardState> {
|
||||||
|
self.latest_kb_state.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn output_extents_changed(&self) {
|
||||||
|
self.for_each_ei_seat(|ei_seat| {
|
||||||
|
ei_seat.regions_changed();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CursorUserOwner for WlSeatGlobal {
|
impl CursorUserOwner for WlSeatGlobal {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
backend::{ConnectorId, InputDeviceId, InputEvent, KeyState, AXIS_120},
|
backend::{
|
||||||
|
AxisSource, ConnectorId, InputDeviceId, InputEvent, KeyState, ScrollAxis, AXIS_120,
|
||||||
|
},
|
||||||
client::ClientId,
|
client::ClientId,
|
||||||
config::InvokedShortcut,
|
config::InvokedShortcut,
|
||||||
|
ei::ei_ifs::ei_seat::EiSeat,
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
ifs::{
|
ifs::{
|
||||||
ipc::{
|
ipc::{
|
||||||
|
|
@ -245,10 +248,7 @@ impl WlSeatGlobal {
|
||||||
| InputEvent::TabletPadModeSwitch { time_usec, .. }
|
| InputEvent::TabletPadModeSwitch { time_usec, .. }
|
||||||
| InputEvent::TabletPadRing { time_usec, .. }
|
| InputEvent::TabletPadRing { time_usec, .. }
|
||||||
| InputEvent::TabletPadStrip { time_usec, .. }
|
| InputEvent::TabletPadStrip { time_usec, .. }
|
||||||
| InputEvent::TouchDown { time_usec, .. }
|
| InputEvent::TouchFrame { time_usec, .. } => {
|
||||||
| InputEvent::TouchUp { time_usec, .. }
|
|
||||||
| InputEvent::TouchMotion { time_usec, .. }
|
|
||||||
| InputEvent::TouchCancel { time_usec, .. } => {
|
|
||||||
self.last_input_usec.set(time_usec);
|
self.last_input_usec.set(time_usec);
|
||||||
if self.idle_notifications.is_not_empty() {
|
if self.idle_notifications.is_not_empty() {
|
||||||
for notification in self.idle_notifications.lock().drain_values() {
|
for notification in self.idle_notifications.lock().drain_values() {
|
||||||
|
|
@ -262,7 +262,10 @@ impl WlSeatGlobal {
|
||||||
| InputEvent::Axis120 { .. }
|
| InputEvent::Axis120 { .. }
|
||||||
| InputEvent::TabletToolAdded { .. }
|
| InputEvent::TabletToolAdded { .. }
|
||||||
| InputEvent::TabletToolRemoved { .. }
|
| InputEvent::TabletToolRemoved { .. }
|
||||||
| InputEvent::TouchFrame => {}
|
| InputEvent::TouchDown { .. }
|
||||||
|
| InputEvent::TouchUp { .. }
|
||||||
|
| InputEvent::TouchMotion { .. }
|
||||||
|
| InputEvent::TouchCancel { .. } => {}
|
||||||
}
|
}
|
||||||
match event {
|
match event {
|
||||||
InputEvent::ConnectorPosition { .. }
|
InputEvent::ConnectorPosition { .. }
|
||||||
|
|
@ -297,7 +300,7 @@ impl WlSeatGlobal {
|
||||||
InputEvent::TouchUp { .. } => {}
|
InputEvent::TouchUp { .. } => {}
|
||||||
InputEvent::TouchMotion { .. } => {}
|
InputEvent::TouchMotion { .. } => {}
|
||||||
InputEvent::TouchCancel { .. } => {}
|
InputEvent::TouchCancel { .. } => {}
|
||||||
InputEvent::TouchFrame => {}
|
InputEvent::TouchFrame { .. } => {}
|
||||||
}
|
}
|
||||||
match event {
|
match event {
|
||||||
InputEvent::Key {
|
InputEvent::Key {
|
||||||
|
|
@ -324,19 +327,21 @@ impl WlSeatGlobal {
|
||||||
state,
|
state,
|
||||||
} => self.button_event(time_usec, button, state),
|
} => self.button_event(time_usec, button, state),
|
||||||
|
|
||||||
InputEvent::AxisSource { source } => self.pointer_owner.axis_source(source),
|
InputEvent::AxisSource { source } => self.axis_source(source),
|
||||||
InputEvent::Axis120 {
|
InputEvent::Axis120 {
|
||||||
dist,
|
dist,
|
||||||
axis,
|
axis,
|
||||||
inverted,
|
inverted,
|
||||||
} => self.pointer_owner.axis_120(dist, axis, inverted),
|
} => self.axis_120(dist, axis, inverted),
|
||||||
InputEvent::AxisPx {
|
InputEvent::AxisPx {
|
||||||
dist,
|
dist,
|
||||||
axis,
|
axis,
|
||||||
inverted,
|
inverted,
|
||||||
} => self.pointer_owner.axis_px(dist, axis, inverted),
|
} => self.axis_px(dist, axis, inverted),
|
||||||
InputEvent::AxisStop { axis } => self.pointer_owner.axis_stop(axis),
|
InputEvent::AxisStop { axis } => self.axis_stop(axis),
|
||||||
InputEvent::AxisFrame { time_usec } => self.pointer_owner.frame(dev, self, time_usec),
|
InputEvent::AxisFrame { time_usec } => {
|
||||||
|
self.axis_frame(dev.px_per_scroll_wheel.get(), time_usec)
|
||||||
|
}
|
||||||
InputEvent::SwipeBegin {
|
InputEvent::SwipeBegin {
|
||||||
time_usec,
|
time_usec,
|
||||||
finger_count,
|
finger_count,
|
||||||
|
|
@ -445,7 +450,7 @@ impl WlSeatGlobal {
|
||||||
y_normed,
|
y_normed,
|
||||||
} => self.touch_motion(time_usec, id, dev.get_rect(&self.state), x_normed, y_normed),
|
} => self.touch_motion(time_usec, id, dev.get_rect(&self.state), x_normed, y_normed),
|
||||||
InputEvent::TouchCancel { time_usec, id } => self.touch_cancel(time_usec, id),
|
InputEvent::TouchCancel { time_usec, id } => self.touch_cancel(time_usec, id),
|
||||||
InputEvent::TouchFrame => self.touch_frame(),
|
InputEvent::TouchFrame { time_usec } => self.touch_frame(time_usec),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -481,7 +486,14 @@ impl WlSeatGlobal {
|
||||||
let pos = output.global.pos.get();
|
let pos = output.global.pos.get();
|
||||||
x += Fixed::from_int(pos.x1());
|
x += Fixed::from_int(pos.x1());
|
||||||
y += Fixed::from_int(pos.y1());
|
y += Fixed::from_int(pos.y1());
|
||||||
(x, y) = self.set_pointer_cursor_position(x, y);
|
self.motion_event_abs(time_usec, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn motion_event_abs(self: &Rc<Self>, time_usec: u64, x: Fixed, y: Fixed) {
|
||||||
|
self.for_each_ei_seat(|ei_seat| {
|
||||||
|
ei_seat.handle_motion_abs(time_usec, x, y);
|
||||||
|
});
|
||||||
|
let (x, y) = self.set_pointer_cursor_position(x, y);
|
||||||
if let Some(c) = self.constraint.get() {
|
if let Some(c) = self.constraint.get() {
|
||||||
if c.ty == ConstraintType::Lock || !c.contains(x.round_down(), y.round_down()) {
|
if c.ty == ConstraintType::Lock || !c.contains(x.round_down(), y.round_down()) {
|
||||||
c.deactivate();
|
c.deactivate();
|
||||||
|
|
@ -493,7 +505,7 @@ impl WlSeatGlobal {
|
||||||
self.cursor_moved(time_usec);
|
self.cursor_moved(time_usec);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn motion_event(
|
pub fn motion_event(
|
||||||
self: &Rc<Self>,
|
self: &Rc<Self>,
|
||||||
time_usec: u64,
|
time_usec: u64,
|
||||||
dx: Fixed,
|
dx: Fixed,
|
||||||
|
|
@ -501,6 +513,9 @@ impl WlSeatGlobal {
|
||||||
dx_unaccelerated: Fixed,
|
dx_unaccelerated: Fixed,
|
||||||
dy_unaccelerated: Fixed,
|
dy_unaccelerated: Fixed,
|
||||||
) {
|
) {
|
||||||
|
self.for_each_ei_seat(|ei_seat| {
|
||||||
|
ei_seat.handle_motion(time_usec, dx, dy);
|
||||||
|
});
|
||||||
self.pointer_owner.relative_motion(
|
self.pointer_owner.relative_motion(
|
||||||
self,
|
self,
|
||||||
time_usec,
|
time_usec,
|
||||||
|
|
@ -545,13 +560,37 @@ impl WlSeatGlobal {
|
||||||
self.cursor_moved(time_usec);
|
self.cursor_moved(time_usec);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn button_event(self: &Rc<Self>, time_usec: u64, button: u32, state: KeyState) {
|
pub fn button_event(self: &Rc<Self>, time_usec: u64, button: u32, state: KeyState) {
|
||||||
|
self.for_each_ei_seat(|ei_seat| {
|
||||||
|
ei_seat.handle_button(time_usec, button, state);
|
||||||
|
});
|
||||||
self.state.for_each_seat_tester(|t| {
|
self.state.for_each_seat_tester(|t| {
|
||||||
t.send_button(self.id, time_usec, button, state);
|
t.send_button(self.id, time_usec, button, state);
|
||||||
});
|
});
|
||||||
self.pointer_owner.button(self, time_usec, button, state);
|
self.pointer_owner.button(self, time_usec, button, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn axis_source(&self, axis_source: AxisSource) {
|
||||||
|
self.pointer_owner.axis_source(axis_source);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn axis_120(&self, delta: i32, axis: ScrollAxis, inverted: bool) {
|
||||||
|
self.pointer_owner.axis_120(delta, axis, inverted);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn axis_px(&self, delta: Fixed, axis: ScrollAxis, inverted: bool) {
|
||||||
|
self.pointer_owner.axis_px(delta, axis, inverted);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn axis_stop(&self, axis: ScrollAxis) {
|
||||||
|
self.pointer_owner.axis_stop(axis);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn axis_frame(self: &Rc<Self>, px_per_scroll_wheel: f64, time_usec: u64) {
|
||||||
|
self.pointer_owner
|
||||||
|
.frame(px_per_scroll_wheel, self, time_usec);
|
||||||
|
}
|
||||||
|
|
||||||
fn swipe_begin(self: &Rc<Self>, time_usec: u64, finger_count: u32) {
|
fn swipe_begin(self: &Rc<Self>, time_usec: u64, finger_count: u32) {
|
||||||
self.state.for_each_seat_tester(|t| {
|
self.state.for_each_seat_tester(|t| {
|
||||||
t.send_swipe_begin(self.id, time_usec, finger_count);
|
t.send_swipe_begin(self.id, time_usec, finger_count);
|
||||||
|
|
@ -660,16 +699,26 @@ impl WlSeatGlobal {
|
||||||
x_normed: Fixed,
|
x_normed: Fixed,
|
||||||
y_normed: Fixed,
|
y_normed: Fixed,
|
||||||
) {
|
) {
|
||||||
self.cursor_group().deactivate();
|
|
||||||
let x = Fixed::from_f64(rect.x1() as f64 + rect.width() as f64 * x_normed.to_f64());
|
let x = Fixed::from_f64(rect.x1() as f64 + rect.width() as f64 * x_normed.to_f64());
|
||||||
let y = Fixed::from_f64(rect.y1() as f64 + rect.height() as f64 * y_normed.to_f64());
|
let y = Fixed::from_f64(rect.y1() as f64 + rect.height() as f64 * y_normed.to_f64());
|
||||||
|
self.touch_down_at(time_usec, id, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn touch_down_at(self: &Rc<Self>, time_usec: u64, id: i32, x: Fixed, y: Fixed) {
|
||||||
|
self.for_each_ei_seat(|ei_seat| {
|
||||||
|
ei_seat.handle_touch_down(id as _, x, y);
|
||||||
|
});
|
||||||
|
self.cursor_group().deactivate();
|
||||||
self.state.for_each_seat_tester(|t| {
|
self.state.for_each_seat_tester(|t| {
|
||||||
t.send_touch_down(self.id, time_usec, id, x, y);
|
t.send_touch_down(self.id, time_usec, id, x, y);
|
||||||
});
|
});
|
||||||
self.touch_owner.down(self, time_usec, id, x, y);
|
self.touch_owner.down(self, time_usec, id, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn touch_up(self: &Rc<Self>, time_usec: u64, id: i32) {
|
pub fn touch_up(self: &Rc<Self>, time_usec: u64, id: i32) {
|
||||||
|
self.for_each_ei_seat(|ei_seat| {
|
||||||
|
ei_seat.handle_touch_up(id as _);
|
||||||
|
});
|
||||||
self.state.for_each_seat_tester(|t| {
|
self.state.for_each_seat_tester(|t| {
|
||||||
t.send_touch_up(self.id, time_usec, id);
|
t.send_touch_up(self.id, time_usec, id);
|
||||||
});
|
});
|
||||||
|
|
@ -684,9 +733,16 @@ impl WlSeatGlobal {
|
||||||
x_normed: Fixed,
|
x_normed: Fixed,
|
||||||
y_normed: Fixed,
|
y_normed: Fixed,
|
||||||
) {
|
) {
|
||||||
self.cursor_group().deactivate();
|
|
||||||
let x = Fixed::from_f64(rect.x1() as f64 + rect.width() as f64 * x_normed.to_f64());
|
let x = Fixed::from_f64(rect.x1() as f64 + rect.width() as f64 * x_normed.to_f64());
|
||||||
let y = Fixed::from_f64(rect.y1() as f64 + rect.height() as f64 * y_normed.to_f64());
|
let y = Fixed::from_f64(rect.y1() as f64 + rect.height() as f64 * y_normed.to_f64());
|
||||||
|
self.touch_motion_at(time_usec, id, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn touch_motion_at(self: &Rc<Self>, time_usec: u64, id: i32, x: Fixed, y: Fixed) {
|
||||||
|
self.for_each_ei_seat(|ei_seat| {
|
||||||
|
ei_seat.handle_touch_motion(id as _, x, y);
|
||||||
|
});
|
||||||
|
self.cursor_group().deactivate();
|
||||||
self.state.for_each_seat_tester(|t| {
|
self.state.for_each_seat_tester(|t| {
|
||||||
t.send_touch_motion(self.id, time_usec, id, x, y);
|
t.send_touch_motion(self.id, time_usec, id, x, y);
|
||||||
});
|
});
|
||||||
|
|
@ -694,16 +750,31 @@ impl WlSeatGlobal {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn touch_cancel(self: &Rc<Self>, time_usec: u64, id: i32) {
|
fn touch_cancel(self: &Rc<Self>, time_usec: u64, id: i32) {
|
||||||
|
self.for_each_ei_seat(|ei_seat| {
|
||||||
|
ei_seat.handle_touch_up(id as _);
|
||||||
|
});
|
||||||
self.state.for_each_seat_tester(|t| {
|
self.state.for_each_seat_tester(|t| {
|
||||||
t.send_touch_cancel(self.id, time_usec, id);
|
t.send_touch_cancel(self.id, time_usec, id);
|
||||||
});
|
});
|
||||||
self.touch_owner.cancel(self);
|
self.touch_owner.cancel(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn touch_frame(self: &Rc<Self>) {
|
pub fn touch_frame(self: &Rc<Self>, time_usec: u64) {
|
||||||
|
self.for_each_ei_seat(|ei_seat| {
|
||||||
|
ei_seat.handle_touch_frame(time_usec);
|
||||||
|
});
|
||||||
self.touch_owner.frame(self);
|
self.touch_owner.frame(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn key_event_with_seat_state(
|
||||||
|
self: &Rc<Self>,
|
||||||
|
time_usec: u64,
|
||||||
|
key: u32,
|
||||||
|
key_state: KeyState,
|
||||||
|
) {
|
||||||
|
self.key_event(time_usec, key, key_state, || self.seat_xkb_state.get());
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn key_event<F>(
|
pub(super) fn key_event<F>(
|
||||||
self: &Rc<Self>,
|
self: &Rc<Self>,
|
||||||
time_usec: u64,
|
time_usec: u64,
|
||||||
|
|
@ -787,8 +858,14 @@ impl WlSeatGlobal {
|
||||||
Some(g) => g.on_key(time_usec, key, state, &xkb_state.kb_state),
|
Some(g) => g.on_key(time_usec, key, state, &xkb_state.kb_state),
|
||||||
_ => node.node_on_key(self, time_usec, key, state, &xkb_state.kb_state),
|
_ => node.node_on_key(self, time_usec, key, state, &xkb_state.kb_state),
|
||||||
}
|
}
|
||||||
|
self.for_each_ei_seat(|ei_seat| {
|
||||||
|
ei_seat.handle_key(time_usec, key, state, &xkb_state.kb_state);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if new_mods {
|
if new_mods {
|
||||||
|
self.for_each_ei_seat(|ei_seat| {
|
||||||
|
ei_seat.handle_modifiers_changed(&xkb_state.kb_state);
|
||||||
|
});
|
||||||
self.state.for_each_seat_tester(|t| {
|
self.state.for_each_seat_tester(|t| {
|
||||||
t.send_modifiers(self.id, &xkb_state.kb_state.mods);
|
t.send_modifiers(self.id, &xkb_state.kb_state.mods);
|
||||||
});
|
});
|
||||||
|
|
@ -808,6 +885,14 @@ impl WlSeatGlobal {
|
||||||
drop(xkb_state);
|
drop(xkb_state);
|
||||||
self.latest_kb_state.set(xkb_state_rc);
|
self.latest_kb_state.set(xkb_state_rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn for_each_ei_seat(&self, mut f: impl FnMut(&Rc<EiSeat>)) {
|
||||||
|
if self.ei_seats.is_not_empty() {
|
||||||
|
for ei_seat in self.ei_seats.lock().values() {
|
||||||
|
f(ei_seat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WlSeatGlobal {
|
impl WlSeatGlobal {
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ use {
|
||||||
wl_surface::{dnd_icon::DndIcon, WlSurface},
|
wl_surface::{dnd_icon::DndIcon, WlSurface},
|
||||||
xdg_toplevel_drag_v1::XdgToplevelDragV1,
|
xdg_toplevel_drag_v1::XdgToplevelDragV1,
|
||||||
},
|
},
|
||||||
state::DeviceHandlerData,
|
|
||||||
tree::{ContainingNode, FindTreeUsecase, FoundNode, Node, ToplevelNode, WorkspaceNode},
|
tree::{ContainingNode, FindTreeUsecase, FoundNode, Node, ToplevelNode, WorkspaceNode},
|
||||||
utils::{clonecell::CloneCell, smallmap::SmallMap},
|
utils::{clonecell::CloneCell, smallmap::SmallMap},
|
||||||
},
|
},
|
||||||
|
|
@ -73,15 +72,18 @@ impl PointerOwnerHolder {
|
||||||
self.pending_scroll.stop[axis as usize].set(true);
|
self.pending_scroll.stop[axis as usize].set(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn frame(&self, dev: &DeviceHandlerData, seat: &Rc<WlSeatGlobal>, time_usec: u64) {
|
pub fn frame(&self, px_per_scroll_wheel: f64, seat: &Rc<WlSeatGlobal>, time_usec: u64) {
|
||||||
self.pending_scroll.time_usec.set(time_usec);
|
self.pending_scroll.time_usec.set(time_usec);
|
||||||
let pending = self.pending_scroll.take();
|
let pending = self.pending_scroll.take();
|
||||||
for axis in 0..2 {
|
for axis in 0..2 {
|
||||||
if let Some(dist) = pending.v120[axis].get() {
|
if let Some(dist) = pending.v120[axis].get() {
|
||||||
let px = (dist as f64 / AXIS_120 as f64) * dev.px_per_scroll_wheel.get();
|
let px = (dist as f64 / AXIS_120 as f64) * px_per_scroll_wheel;
|
||||||
pending.px[axis].set(Some(Fixed::from_f64(px)));
|
pending.px[axis].set(Some(Fixed::from_f64(px)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
seat.for_each_ei_seat(|ei_seat| {
|
||||||
|
ei_seat.handle_pending_scroll(time_usec, &pending);
|
||||||
|
});
|
||||||
seat.state.for_each_seat_tester(|t| {
|
seat.state.for_each_seat_tester(|t| {
|
||||||
t.send_axis(seat.id, time_usec, &pending);
|
t.send_axis(seat.id, time_usec, &pending);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -655,3 +655,86 @@ macro_rules! pw_object_base {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! ei_id {
|
||||||
|
($name:ident) => {
|
||||||
|
#[derive(Debug, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
|
||||||
|
pub struct $name(u64);
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
impl $name {
|
||||||
|
pub const NONE: Self = $name(0);
|
||||||
|
|
||||||
|
pub const fn from_raw(raw: u64) -> Self {
|
||||||
|
Self(raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn raw(self) -> u64 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_some(self) -> bool {
|
||||||
|
self.0 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_none(self) -> bool {
|
||||||
|
self.0 == 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::ei::ei_object::EiObjectId> for $name {
|
||||||
|
fn from(f: crate::ei::ei_object::EiObjectId) -> Self {
|
||||||
|
Self(f.raw())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<$name> for crate::ei::ei_object::EiObjectId {
|
||||||
|
fn from(f: $name) -> Self {
|
||||||
|
crate::ei::ei_object::EiObjectId::from_raw(f.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for $name {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
std::fmt::Display::fmt(&self.0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::LowerHex for $name {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
std::fmt::LowerHex::fmt(&self.0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! ei_object_base {
|
||||||
|
($self:ident = $oname:ident; version = $version:expr;) => {
|
||||||
|
impl crate::ei::ei_object::EiObjectBase for $oname {
|
||||||
|
fn id(&$self) -> crate::ei::ei_object::EiObjectId {
|
||||||
|
$self.id.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn version(&$self) -> crate::ei::ei_object::EiVersion {
|
||||||
|
$version
|
||||||
|
}
|
||||||
|
|
||||||
|
fn client(&$self) -> &crate::ei::ei_client::EiClient {
|
||||||
|
&$self.client
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_request(
|
||||||
|
$self: std::rc::Rc<Self>,
|
||||||
|
client: &crate::ei::ei_client::EiClient,
|
||||||
|
request: u32,
|
||||||
|
parser: crate::utils::buffd::EiMsgParser<'_, '_>,
|
||||||
|
) -> Result<(), crate::ei::ei_client::EiClientError> {
|
||||||
|
$self.handle_request_impl(client, request, parser)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn interface(&$self) -> crate::ei::ei_object::EiInterface {
|
||||||
|
crate::wire_ei::$oname
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ mod damage;
|
||||||
mod dbus;
|
mod dbus;
|
||||||
mod drm_feedback;
|
mod drm_feedback;
|
||||||
mod edid;
|
mod edid;
|
||||||
|
mod ei;
|
||||||
mod fixed;
|
mod fixed;
|
||||||
mod forker;
|
mod forker;
|
||||||
mod format;
|
mod format;
|
||||||
|
|
@ -97,6 +98,7 @@ mod video;
|
||||||
mod wheel;
|
mod wheel;
|
||||||
mod wire;
|
mod wire;
|
||||||
mod wire_dbus;
|
mod wire_dbus;
|
||||||
|
mod wire_ei;
|
||||||
mod wire_xcon;
|
mod wire_xcon;
|
||||||
mod wl_usr;
|
mod wl_usr;
|
||||||
mod xcon;
|
mod xcon;
|
||||||
|
|
|
||||||
57
src/state.rs
57
src/state.rs
|
|
@ -11,12 +11,17 @@ use {
|
||||||
cli::RunArgs,
|
cli::RunArgs,
|
||||||
client::{Client, ClientId, Clients, SerialRange, NUM_CACHED_SERIAL_RANGES},
|
client::{Client, ClientId, Clients, SerialRange, NUM_CACHED_SERIAL_RANGES},
|
||||||
clientmem::ClientMemOffset,
|
clientmem::ClientMemOffset,
|
||||||
|
compositor::LIBEI_SOCKET,
|
||||||
config::ConfigProxy,
|
config::ConfigProxy,
|
||||||
cursor::{Cursor, ServerCursors},
|
cursor::{Cursor, ServerCursors},
|
||||||
cursor_user::{CursorUserGroup, CursorUserGroupId, CursorUserGroupIds, CursorUserIds},
|
cursor_user::{CursorUserGroup, CursorUserGroupId, CursorUserGroupIds, CursorUserIds},
|
||||||
damage::DamageVisualizer,
|
damage::DamageVisualizer,
|
||||||
dbus::Dbus,
|
dbus::Dbus,
|
||||||
drm_feedback::{DrmFeedback, DrmFeedbackIds},
|
drm_feedback::{DrmFeedback, DrmFeedbackIds},
|
||||||
|
ei::{
|
||||||
|
ei_acceptor::EiAcceptor,
|
||||||
|
ei_client::{EiClient, EiClients},
|
||||||
|
},
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
forker::ForkerProxy,
|
forker::ForkerProxy,
|
||||||
format::Format,
|
format::Format,
|
||||||
|
|
@ -204,6 +209,11 @@ pub struct State {
|
||||||
pub default_vrr_mode: Cell<&'static VrrMode>,
|
pub default_vrr_mode: Cell<&'static VrrMode>,
|
||||||
pub default_vrr_cursor_hz: Cell<Option<f64>>,
|
pub default_vrr_cursor_hz: Cell<Option<f64>>,
|
||||||
pub default_tearing_mode: Cell<&'static TearingMode>,
|
pub default_tearing_mode: Cell<&'static TearingMode>,
|
||||||
|
pub ei_acceptor: CloneCell<Option<Rc<EiAcceptor>>>,
|
||||||
|
pub ei_acceptor_future: CloneCell<Option<SpawnedFuture<()>>>,
|
||||||
|
pub enable_ei_acceptor: Cell<bool>,
|
||||||
|
pub ei_clients: EiClients,
|
||||||
|
pub slow_ei_clients: AsyncQueue<Rc<EiClient>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl Drop for State {
|
// impl Drop for State {
|
||||||
|
|
@ -827,6 +837,10 @@ impl State {
|
||||||
}
|
}
|
||||||
self.wheel.clear();
|
self.wheel.clear();
|
||||||
self.eng.clear();
|
self.eng.clear();
|
||||||
|
self.ei_acceptor.take();
|
||||||
|
self.ei_acceptor_future.take();
|
||||||
|
self.ei_clients.clear();
|
||||||
|
self.slow_ei_clients.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disable_hardware_cursors(&self) {
|
pub fn disable_hardware_cursors(&self) {
|
||||||
|
|
@ -1019,6 +1033,7 @@ impl State {
|
||||||
let global_name = self.globals.name();
|
let global_name = self.globals.name();
|
||||||
let seat = WlSeatGlobal::new(global_name, name, self);
|
let seat = WlSeatGlobal::new(global_name, name, self);
|
||||||
self.globals.add_global(self, &seat);
|
self.globals.add_global(self, &seat);
|
||||||
|
self.ei_clients.announce_seat(&seat);
|
||||||
seat
|
seat
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1095,6 +1110,48 @@ impl State {
|
||||||
pub fn now_msec(&self) -> u64 {
|
pub fn now_msec(&self) -> u64 {
|
||||||
self.eng.now().msec()
|
self.eng.now().msec()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn output_extents_changed(&self) {
|
||||||
|
self.root.update_extents();
|
||||||
|
for seat in self.globals.seats.lock().values() {
|
||||||
|
seat.output_extents_changed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_ei_acceptor(self: &Rc<Self>) {
|
||||||
|
self.update_ei_acceptor2();
|
||||||
|
if let Some(forker) = self.forker.get() {
|
||||||
|
match self.ei_acceptor.get() {
|
||||||
|
None => {
|
||||||
|
forker.unsetenv(LIBEI_SOCKET.as_bytes());
|
||||||
|
}
|
||||||
|
Some(s) => {
|
||||||
|
forker.setenv(LIBEI_SOCKET.as_bytes(), s.socket_name().as_bytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_ei_acceptor2(self: &Rc<Self>) {
|
||||||
|
if self.ei_acceptor.is_some() == self.enable_ei_acceptor.get() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if self.enable_ei_acceptor.get() {
|
||||||
|
let (acceptor, future) = match EiAcceptor::spawn(self) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Could not create libei socket: {}", ErrorFmt(e));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.ei_acceptor.set(Some(acceptor));
|
||||||
|
self.ei_acceptor_future.set(Some(future));
|
||||||
|
} else {
|
||||||
|
log::info!("Disabling libei socket");
|
||||||
|
self.ei_acceptor.take();
|
||||||
|
self.ei_acceptor_future.take();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
|
|
|
||||||
10
src/tasks.rs
10
src/tasks.rs
|
|
@ -10,7 +10,10 @@ mod udev_utils;
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
state::State,
|
state::State,
|
||||||
tasks::{backend::BackendEventHandler, slow_clients::SlowClientHandler},
|
tasks::{
|
||||||
|
backend::BackendEventHandler,
|
||||||
|
slow_clients::{SlowClientHandler, SlowEiClientHandler},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
std::rc::Rc,
|
std::rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
@ -25,3 +28,8 @@ pub async fn handle_slow_clients(state: Rc<State>) {
|
||||||
let mut sch = SlowClientHandler { state };
|
let mut sch = SlowClientHandler { state };
|
||||||
sch.handle_events().await;
|
sch.handle_events().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn handle_slow_ei_clients(state: Rc<State>) {
|
||||||
|
let mut sch = SlowEiClientHandler { state };
|
||||||
|
sch.handle_events().await;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -199,7 +199,7 @@ impl ConnectorHandler {
|
||||||
self.state.outputs.set(self.id, output_data);
|
self.state.outputs.set(self.id, output_data);
|
||||||
on.schedule_update_render_data();
|
on.schedule_update_render_data();
|
||||||
self.state.root.outputs.set(self.id, on.clone());
|
self.state.root.outputs.set(self.id, on.clone());
|
||||||
self.state.root.update_extents();
|
self.state.output_extents_changed();
|
||||||
global.opt.node.set(Some(on.clone()));
|
global.opt.node.set(Some(on.clone()));
|
||||||
global.opt.global.set(Some(global.clone()));
|
global.opt.global.set(Some(global.clone()));
|
||||||
let mut ws_to_move = VecDeque::new();
|
let mut ws_to_move = VecDeque::new();
|
||||||
|
|
@ -280,7 +280,7 @@ impl ConnectorHandler {
|
||||||
}
|
}
|
||||||
global.destroyed.set(true);
|
global.destroyed.set(true);
|
||||||
self.state.root.outputs.remove(&self.id);
|
self.state.root.outputs.remove(&self.id);
|
||||||
self.state.root.update_extents();
|
self.state.output_extents_changed();
|
||||||
self.state.outputs.remove(&self.id);
|
self.state.outputs.remove(&self.id);
|
||||||
on.lock_surface.take();
|
on.lock_surface.take();
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -12,3 +12,16 @@ impl SlowClientHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct SlowEiClientHandler {
|
||||||
|
pub state: Rc<State>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SlowEiClientHandler {
|
||||||
|
pub async fn handle_events(&mut self) {
|
||||||
|
loop {
|
||||||
|
let client = self.state.slow_ei_clients.pop().await;
|
||||||
|
client.check_queue_size().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -610,7 +610,7 @@ impl OutputNode {
|
||||||
}
|
}
|
||||||
self.global.persistent.pos.set((rect.x1(), rect.y1()));
|
self.global.persistent.pos.set((rect.x1(), rect.y1()));
|
||||||
self.global.pos.set(*rect);
|
self.global.pos.set(*rect);
|
||||||
self.state.root.update_extents();
|
self.state.output_extents_changed();
|
||||||
self.update_rects();
|
self.update_rects();
|
||||||
if let Some(ls) = self.lock_surface.get() {
|
if let Some(ls) = self.lock_surface.get() {
|
||||||
ls.change_extents(*rect);
|
ls.change_extents(*rect);
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ pub mod option_ext;
|
||||||
pub mod oserror;
|
pub mod oserror;
|
||||||
pub mod page_size;
|
pub mod page_size;
|
||||||
pub mod pending_serial;
|
pub mod pending_serial;
|
||||||
|
pub mod pid_info;
|
||||||
pub mod process_name;
|
pub mod process_name;
|
||||||
pub mod ptr_ext;
|
pub mod ptr_ext;
|
||||||
pub mod queue;
|
pub mod queue;
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,16 @@ use {crate::io_uring::IoUringError, thiserror::Error};
|
||||||
pub use {
|
pub use {
|
||||||
buf_in::BufFdIn,
|
buf_in::BufFdIn,
|
||||||
buf_out::{BufFdOut, OutBuffer, OutBufferSwapchain},
|
buf_out::{BufFdOut, OutBuffer, OutBufferSwapchain},
|
||||||
|
ei_formatter::EiMsgFormatter,
|
||||||
|
ei_parser::{EiMsgParser, EiMsgParserError},
|
||||||
formatter::MsgFormatter,
|
formatter::MsgFormatter,
|
||||||
parser::{MsgParser, MsgParserError},
|
parser::{MsgParser, MsgParserError},
|
||||||
};
|
};
|
||||||
|
|
||||||
mod buf_in;
|
mod buf_in;
|
||||||
mod buf_out;
|
mod buf_out;
|
||||||
|
mod ei_formatter;
|
||||||
|
mod ei_parser;
|
||||||
mod formatter;
|
mod formatter;
|
||||||
mod parser;
|
mod parser;
|
||||||
|
|
||||||
|
|
|
||||||
106
src/utils/buffd/ei_formatter.rs
Normal file
106
src/utils/buffd/ei_formatter.rs
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
ei::ei_object::EiObjectId,
|
||||||
|
utils::buffd::buf_out::{MsgFds, OutBuffer, OutBufferMeta, OUT_BUF_SIZE},
|
||||||
|
},
|
||||||
|
std::{mem, rc::Rc},
|
||||||
|
uapi::OwnedFd,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct EiMsgFormatter<'a> {
|
||||||
|
buf: &'a mut [u8],
|
||||||
|
meta: &'a mut OutBufferMeta,
|
||||||
|
pos: usize,
|
||||||
|
fds: &'a mut Vec<Rc<OwnedFd>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> EiMsgFormatter<'a> {
|
||||||
|
pub fn new(buf: &'a mut OutBuffer, fds: &'a mut Vec<Rc<OwnedFd>>) -> Self {
|
||||||
|
Self {
|
||||||
|
pos: buf.meta.write_pos,
|
||||||
|
buf: &mut buf.buf[..],
|
||||||
|
fds,
|
||||||
|
meta: &mut buf.meta,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, bytes: &[u8]) {
|
||||||
|
if bytes.len() > OUT_BUF_SIZE - self.meta.write_pos {
|
||||||
|
panic!("Out buffer overflow");
|
||||||
|
}
|
||||||
|
self.buf[self.meta.write_pos..self.meta.write_pos + bytes.len()].copy_from_slice(bytes);
|
||||||
|
self.meta.write_pos += bytes.len();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn int(&mut self, int: i32) -> &mut Self {
|
||||||
|
self.write(uapi::as_bytes(&int));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uint(&mut self, int: u32) -> &mut Self {
|
||||||
|
self.write(uapi::as_bytes(&int));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn long(&mut self, int: i64) -> &mut Self {
|
||||||
|
self.write(uapi::as_bytes(&int));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ulong(&mut self, int: u64) -> &mut Self {
|
||||||
|
self.write(uapi::as_bytes(&int));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float(&mut self, f: f32) -> &mut Self {
|
||||||
|
self.write(uapi::as_bytes(&f));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn optstr<S: AsRef<[u8]> + ?Sized>(&mut self, s: Option<&S>) -> &mut Self {
|
||||||
|
match s {
|
||||||
|
Some(s) => self.string(s),
|
||||||
|
_ => self.uint(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn string<S: AsRef<[u8]> + ?Sized>(&mut self, s: &S) -> &mut Self {
|
||||||
|
let s = s.as_ref();
|
||||||
|
let len = s.len() + 1;
|
||||||
|
let cap = (len + 3) & !3;
|
||||||
|
self.uint(len as u32);
|
||||||
|
self.write(uapi::as_bytes(s));
|
||||||
|
let none = [0; 4];
|
||||||
|
self.write(&none[..cap - len + 1]);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fd(&mut self, fd: Rc<OwnedFd>) -> &mut Self {
|
||||||
|
self.fds.push(fd);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn object<T: Into<EiObjectId>>(&mut self, obj: T) -> &mut Self {
|
||||||
|
self.ulong(obj.into().raw())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn header<T: Into<EiObjectId>>(&mut self, obj: T, event: u32) -> &mut Self {
|
||||||
|
self.object(obj).uint(0).uint(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_len(self) {
|
||||||
|
assert!(self.meta.write_pos - self.pos >= 16);
|
||||||
|
assert_eq!(self.pos % 4, 0);
|
||||||
|
unsafe {
|
||||||
|
let second_ptr = self.buf.as_ptr().add(self.pos + 8) as *mut u32;
|
||||||
|
*second_ptr = (self.meta.write_pos - self.pos) as u32;
|
||||||
|
}
|
||||||
|
if self.fds.len() > 0 {
|
||||||
|
self.meta.fds.push_back(MsgFds {
|
||||||
|
pos: self.pos,
|
||||||
|
fds: mem::take(self.fds),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
112
src/utils/buffd/ei_parser.rs
Normal file
112
src/utils/buffd/ei_parser.rs
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
use {
|
||||||
|
crate::{ei::ei_object::EiObjectId, utils::buffd::BufFdIn},
|
||||||
|
std::{ptr, rc::Rc},
|
||||||
|
thiserror::Error,
|
||||||
|
uapi::OwnedFd,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum EiMsgParserError {
|
||||||
|
#[error("The message ended unexpectedly")]
|
||||||
|
UnexpectedEof,
|
||||||
|
#[error("The message contained a string of size 0")]
|
||||||
|
EmptyString,
|
||||||
|
#[error("Message is missing a required file descriptor")]
|
||||||
|
MissingFd,
|
||||||
|
#[error("There is trailing data after the message")]
|
||||||
|
TrailingData,
|
||||||
|
#[error("String is not UTF-8")]
|
||||||
|
NonUtf8,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EiMsgParser<'a, 'b> {
|
||||||
|
buf: &'a mut BufFdIn,
|
||||||
|
pos: usize,
|
||||||
|
data: &'b [u8],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> EiMsgParser<'a, 'b> {
|
||||||
|
pub fn new(buf: &'a mut BufFdIn, data: &'b [u32]) -> Self {
|
||||||
|
Self {
|
||||||
|
buf,
|
||||||
|
pos: 0,
|
||||||
|
data: uapi::as_bytes(data),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn int(&mut self) -> Result<i32, EiMsgParserError> {
|
||||||
|
if self.data.len() - self.pos < 4 {
|
||||||
|
return Err(EiMsgParserError::UnexpectedEof);
|
||||||
|
}
|
||||||
|
let res = unsafe { *(self.data.as_ptr().add(self.pos) as *const i32) };
|
||||||
|
self.pos += 4;
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uint(&mut self) -> Result<u32, EiMsgParserError> {
|
||||||
|
self.int().map(|i| i as u32)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn long(&mut self) -> Result<i64, EiMsgParserError> {
|
||||||
|
if self.data.len() - self.pos < 8 {
|
||||||
|
return Err(EiMsgParserError::UnexpectedEof);
|
||||||
|
}
|
||||||
|
let res = unsafe { ptr::read_unaligned(self.data.as_ptr().add(self.pos) as *const i64) };
|
||||||
|
self.pos += 8;
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ulong(&mut self) -> Result<u64, EiMsgParserError> {
|
||||||
|
self.long().map(|i| i as u64)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn object<T>(&mut self) -> Result<T, EiMsgParserError>
|
||||||
|
where
|
||||||
|
EiObjectId: Into<T>,
|
||||||
|
{
|
||||||
|
self.ulong().map(|i| EiObjectId::from_raw(i).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float(&mut self) -> Result<f32, EiMsgParserError> {
|
||||||
|
Ok(f32::from_bits(self.uint()?))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn optstr(&mut self) -> Result<Option<&'b str>, EiMsgParserError> {
|
||||||
|
let len = self.uint()? as usize;
|
||||||
|
if len == 0 {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
let cap = (len + 3) & !3;
|
||||||
|
if cap > self.data.len() - self.pos {
|
||||||
|
return Err(EiMsgParserError::UnexpectedEof);
|
||||||
|
}
|
||||||
|
let pos = self.pos;
|
||||||
|
self.pos += cap;
|
||||||
|
match std::str::from_utf8(&self.data[pos..pos + len - 1]) {
|
||||||
|
Ok(s) => Ok(Some(s)),
|
||||||
|
Err(_) => Err(EiMsgParserError::NonUtf8),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn str(&mut self) -> Result<&'b str, EiMsgParserError> {
|
||||||
|
match self.optstr()? {
|
||||||
|
Some(s) => Ok(s),
|
||||||
|
_ => Err(EiMsgParserError::EmptyString),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fd(&mut self) -> Result<Rc<OwnedFd>, EiMsgParserError> {
|
||||||
|
match self.buf.get_fd() {
|
||||||
|
Ok(fd) => Ok(fd),
|
||||||
|
_ => Err(EiMsgParserError::MissingFd),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eof(&self) -> Result<(), EiMsgParserError> {
|
||||||
|
if self.pos == self.data.len() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(EiMsgParserError::TrailingData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
44
src/utils/pid_info.rs
Normal file
44
src/utils/pid_info.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
use {
|
||||||
|
crate::utils::{errorfmt::ErrorFmt, oserror::OsError, trim::AsciiTrim},
|
||||||
|
bstr::ByteSlice,
|
||||||
|
uapi::{c, OwnedFd},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct PidInfo {
|
||||||
|
pub _uid: c::uid_t,
|
||||||
|
pub pid: c::pid_t,
|
||||||
|
pub comm: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_pid_info(uid: c::uid_t, pid: c::pid_t) -> PidInfo {
|
||||||
|
let comm = match std::fs::read(format!("/proc/{}/comm", pid)) {
|
||||||
|
Ok(name) => name.trim().as_bstr().to_string(),
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("Could not read `comm` of pid {}: {}", pid, ErrorFmt(e));
|
||||||
|
"Unknown".to_string()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
PidInfo {
|
||||||
|
_uid: uid,
|
||||||
|
pid,
|
||||||
|
comm,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_socket_creds(socket: &OwnedFd) -> Option<(c::uid_t, c::pid_t)> {
|
||||||
|
let mut cred = c::ucred {
|
||||||
|
pid: 0,
|
||||||
|
uid: 0,
|
||||||
|
gid: 0,
|
||||||
|
};
|
||||||
|
match uapi::getsockopt(socket.raw(), c::SOL_SOCKET, c::SO_PEERCRED, &mut cred) {
|
||||||
|
Ok(_) => Some((cred.uid, cred.pid)),
|
||||||
|
Err(e) => {
|
||||||
|
log::error!(
|
||||||
|
"Cannot determine peer credentials of new connection: {:?}",
|
||||||
|
OsError::from(e)
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -37,6 +37,10 @@ impl<T> SyncQueue<T> {
|
||||||
unsafe { self.el.get().deref_mut().is_empty() }
|
unsafe { self.el.get().deref_mut().is_empty() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_not_empty(&self) -> bool {
|
||||||
|
!self.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn swap(&self, queue: &mut VecDeque<T>) {
|
pub fn swap(&self, queue: &mut VecDeque<T>) {
|
||||||
unsafe {
|
unsafe {
|
||||||
mem::swap(self.el.get().deref_mut(), queue);
|
mem::swap(self.el.get().deref_mut(), queue);
|
||||||
|
|
|
||||||
3
src/wire_ei.rs
Normal file
3
src/wire_ei.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#![allow(non_upper_case_globals)]
|
||||||
|
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/wire_ei.rs"));
|
||||||
|
|
@ -299,6 +299,11 @@ pub struct Tearing {
|
||||||
pub mode: Option<TearingMode>,
|
pub mode: Option<TearingMode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct Libei {
|
||||||
|
pub enable_socket: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Shortcut {
|
pub struct Shortcut {
|
||||||
pub mask: Modifiers,
|
pub mask: Modifiers,
|
||||||
|
|
@ -334,6 +339,7 @@ pub struct Config {
|
||||||
pub window_management_key: Option<ModifiedKeySym>,
|
pub window_management_key: Option<ModifiedKeySym>,
|
||||||
pub vrr: Option<Vrr>,
|
pub vrr: Option<Vrr>,
|
||||||
pub tearing: Option<Tearing>,
|
pub tearing: Option<Tearing>,
|
||||||
|
pub libei: Libei,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ mod idle;
|
||||||
mod input;
|
mod input;
|
||||||
mod input_match;
|
mod input_match;
|
||||||
pub mod keymap;
|
pub mod keymap;
|
||||||
|
mod libei;
|
||||||
mod log_level;
|
mod log_level;
|
||||||
mod mode;
|
mod mode;
|
||||||
pub mod modified_keysym;
|
pub mod modified_keysym;
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ use {
|
||||||
idle::IdleParser,
|
idle::IdleParser,
|
||||||
input::InputsParser,
|
input::InputsParser,
|
||||||
keymap::KeymapParser,
|
keymap::KeymapParser,
|
||||||
|
libei::LibeiParser,
|
||||||
log_level::LogLevelParser,
|
log_level::LogLevelParser,
|
||||||
output::OutputsParser,
|
output::OutputsParser,
|
||||||
repeat_rate::RepeatRateParser,
|
repeat_rate::RepeatRateParser,
|
||||||
|
|
@ -27,7 +28,7 @@ use {
|
||||||
vrr::VrrParser,
|
vrr::VrrParser,
|
||||||
},
|
},
|
||||||
spanned::SpannedErrorExt,
|
spanned::SpannedErrorExt,
|
||||||
Action, Config, Theme,
|
Action, Config, Libei, Theme,
|
||||||
},
|
},
|
||||||
toml::{
|
toml::{
|
||||||
toml_span::{DespanExt, Span, Spanned},
|
toml_span::{DespanExt, Span, Spanned},
|
||||||
|
|
@ -110,6 +111,7 @@ impl Parser for ConfigParser<'_> {
|
||||||
window_management_key_val,
|
window_management_key_val,
|
||||||
vrr_val,
|
vrr_val,
|
||||||
tearing_val,
|
tearing_val,
|
||||||
|
libei_val,
|
||||||
),
|
),
|
||||||
) = ext.extract((
|
) = ext.extract((
|
||||||
(
|
(
|
||||||
|
|
@ -144,6 +146,7 @@ impl Parser for ConfigParser<'_> {
|
||||||
recover(opt(str("window-management-key"))),
|
recover(opt(str("window-management-key"))),
|
||||||
opt(val("vrr")),
|
opt(val("vrr")),
|
||||||
opt(val("tearing")),
|
opt(val("tearing")),
|
||||||
|
opt(val("libei")),
|
||||||
),
|
),
|
||||||
))?;
|
))?;
|
||||||
let mut keymap = None;
|
let mut keymap = None;
|
||||||
|
|
@ -326,6 +329,15 @@ impl Parser for ConfigParser<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let mut libei = Libei::default();
|
||||||
|
if let Some(value) = libei_val {
|
||||||
|
match value.parse(&mut LibeiParser(self.0)) {
|
||||||
|
Ok(v) => libei = v,
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("Could not parse libei setting: {}", self.0.error(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(Config {
|
Ok(Config {
|
||||||
keymap,
|
keymap,
|
||||||
repeat_rate,
|
repeat_rate,
|
||||||
|
|
@ -352,6 +364,7 @@ impl Parser for ConfigParser<'_> {
|
||||||
window_management_key,
|
window_management_key,
|
||||||
vrr,
|
vrr,
|
||||||
tearing,
|
tearing,
|
||||||
|
libei,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
44
toml-config/src/config/parsers/libei.rs
Normal file
44
toml-config/src/config/parsers/libei.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
config::{
|
||||||
|
context::Context,
|
||||||
|
extractor::{bol, opt, recover, Extractor, ExtractorError},
|
||||||
|
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
|
||||||
|
Libei,
|
||||||
|
},
|
||||||
|
toml::{
|
||||||
|
toml_span::{DespanExt, Span, Spanned},
|
||||||
|
toml_value::Value,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
indexmap::IndexMap,
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum LibeiParserError {
|
||||||
|
#[error(transparent)]
|
||||||
|
Expected(#[from] UnexpectedDataType),
|
||||||
|
#[error(transparent)]
|
||||||
|
Extract(#[from] ExtractorError),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LibeiParser<'a>(pub &'a Context<'a>);
|
||||||
|
|
||||||
|
impl Parser for LibeiParser<'_> {
|
||||||
|
type Value = Libei;
|
||||||
|
type Error = LibeiParserError;
|
||||||
|
const EXPECTED: &'static [DataType] = &[DataType::Table];
|
||||||
|
|
||||||
|
fn parse_table(
|
||||||
|
&mut self,
|
||||||
|
span: Span,
|
||||||
|
table: &IndexMap<Spanned<String>, Spanned<Value>>,
|
||||||
|
) -> ParseResult<Self> {
|
||||||
|
let mut ext = Extractor::new(self.0, span, table);
|
||||||
|
let enable_socket = ext.extract(recover(opt(bol("enable-socket"))))?;
|
||||||
|
Ok(Libei {
|
||||||
|
enable_socket: enable_socket.despan(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -17,7 +17,8 @@ use {
|
||||||
get_workspace,
|
get_workspace,
|
||||||
input::{
|
input::{
|
||||||
capability::CAP_SWITCH, get_seat, input_devices, on_input_device_removed,
|
capability::CAP_SWITCH, get_seat, input_devices, on_input_device_removed,
|
||||||
on_new_input_device, FocusFollowsMouseMode, InputDevice, Seat, SwitchEvent,
|
on_new_input_device, set_libei_socket_enabled, FocusFollowsMouseMode, InputDevice,
|
||||||
|
Seat, SwitchEvent,
|
||||||
},
|
},
|
||||||
is_reload,
|
is_reload,
|
||||||
keyboard::{Keymap, ModifiedKeySym},
|
keyboard::{Keymap, ModifiedKeySym},
|
||||||
|
|
@ -1047,6 +1048,7 @@ fn load_config(initial_load: bool, persistent: &Rc<PersistentState>) {
|
||||||
set_tearing_mode(mode);
|
set_tearing_mode(mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
set_libei_socket_enabled(config.libei.enable_socket.unwrap_or(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_command(exec: &Exec) -> Command {
|
fn create_command(exec: &Exec) -> Command {
|
||||||
|
|
|
||||||
|
|
@ -585,6 +585,10 @@
|
||||||
"tearing": {
|
"tearing": {
|
||||||
"description": "Configures the default tearing settings.\n\nThis can be overwritten for individual outputs.\n\nBy default, the tearing mode is `variant3`.\n\n- Example:\n\n ```toml\n tearing.mode = \"never\"\n ```\n",
|
"description": "Configures the default tearing settings.\n\nThis can be overwritten for individual outputs.\n\nBy default, the tearing mode is `variant3`.\n\n- Example:\n\n ```toml\n tearing.mode = \"never\"\n ```\n",
|
||||||
"$ref": "#/$defs/Tearing"
|
"$ref": "#/$defs/Tearing"
|
||||||
|
},
|
||||||
|
"libei": {
|
||||||
|
"description": "Configures the libei settings.\n\n- Example:\n\n ```toml\n libei.enable-socket = true\n ```\n",
|
||||||
|
"$ref": "#/$defs/Libei"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": []
|
"required": []
|
||||||
|
|
@ -967,6 +971,17 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"Libei": {
|
||||||
|
"description": "Describes libei settings.\n\n- Example:\n\n ```toml\n libei.enable-socket = \"true\"\n ```\n",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"enable-socket": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Enables or disables the unauthenticated libei socket.\n\nEven if the socket is disabled, application can still request access via the portal.\n\nThe default is `false`.\n"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": []
|
||||||
|
},
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "A log level.",
|
"description": "A log level.",
|
||||||
|
|
|
||||||
|
|
@ -1142,6 +1142,18 @@ The table has the following fields:
|
||||||
|
|
||||||
The value of this field should be a [Tearing](#types-Tearing).
|
The value of this field should be a [Tearing](#types-Tearing).
|
||||||
|
|
||||||
|
- `libei` (optional):
|
||||||
|
|
||||||
|
Configures the libei settings.
|
||||||
|
|
||||||
|
- Example:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
libei.enable-socket = true
|
||||||
|
```
|
||||||
|
|
||||||
|
The value of this field should be a [Libei](#types-Libei).
|
||||||
|
|
||||||
|
|
||||||
<a name="types-Connector"></a>
|
<a name="types-Connector"></a>
|
||||||
### `Connector`
|
### `Connector`
|
||||||
|
|
@ -2045,6 +2057,32 @@ The table has the following fields:
|
||||||
The value of this field should be a string.
|
The value of this field should be a string.
|
||||||
|
|
||||||
|
|
||||||
|
<a name="types-Libei"></a>
|
||||||
|
### `Libei`
|
||||||
|
|
||||||
|
Describes libei settings.
|
||||||
|
|
||||||
|
- Example:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
libei.enable-socket = "true"
|
||||||
|
```
|
||||||
|
|
||||||
|
Values of this type should be tables.
|
||||||
|
|
||||||
|
The table has the following fields:
|
||||||
|
|
||||||
|
- `enable-socket` (optional):
|
||||||
|
|
||||||
|
Enables or disables the unauthenticated libei socket.
|
||||||
|
|
||||||
|
Even if the socket is disabled, application can still request access via the portal.
|
||||||
|
|
||||||
|
The default is `false`.
|
||||||
|
|
||||||
|
The value of this field should be a boolean.
|
||||||
|
|
||||||
|
|
||||||
<a name="types-LogLevel"></a>
|
<a name="types-LogLevel"></a>
|
||||||
### `LogLevel`
|
### `LogLevel`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2228,6 +2228,17 @@ Config:
|
||||||
```toml
|
```toml
|
||||||
tearing.mode = "never"
|
tearing.mode = "never"
|
||||||
```
|
```
|
||||||
|
libei:
|
||||||
|
ref: Libei
|
||||||
|
required: false
|
||||||
|
description: |
|
||||||
|
Configures the libei settings.
|
||||||
|
|
||||||
|
- Example:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
libei.enable-socket = true
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Idle:
|
Idle:
|
||||||
|
|
@ -2457,3 +2468,25 @@ TearingMode:
|
||||||
description: |
|
description: |
|
||||||
Tearing is enabled when a single application is displayed and the application has
|
Tearing is enabled when a single application is displayed and the application has
|
||||||
requested tearing.
|
requested tearing.
|
||||||
|
|
||||||
|
|
||||||
|
Libei:
|
||||||
|
kind: table
|
||||||
|
description: |
|
||||||
|
Describes libei settings.
|
||||||
|
|
||||||
|
- Example:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
libei.enable-socket = "true"
|
||||||
|
```
|
||||||
|
fields:
|
||||||
|
enable-socket:
|
||||||
|
kind: boolean
|
||||||
|
required: false
|
||||||
|
description: |
|
||||||
|
Enables or disables the unauthenticated libei socket.
|
||||||
|
|
||||||
|
Even if the socket is disabled, application can still request access via the portal.
|
||||||
|
|
||||||
|
The default is `false`.
|
||||||
|
|
|
||||||
16
wire-ei/ei_button.txt
Normal file
16
wire-ei/ei_button.txt
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
request release {
|
||||||
|
}
|
||||||
|
|
||||||
|
request client_button (sender) {
|
||||||
|
button: u32,
|
||||||
|
state: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event destroyed {
|
||||||
|
serial: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event server_button (receiver) {
|
||||||
|
button: u32,
|
||||||
|
state: u32,
|
||||||
|
}
|
||||||
3
wire-ei/ei_callback.txt
Normal file
3
wire-ei/ei_callback.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
event done {
|
||||||
|
callback_data: u64,
|
||||||
|
}
|
||||||
28
wire-ei/ei_connection.txt
Normal file
28
wire-ei/ei_connection.txt
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
request sync {
|
||||||
|
callback: id(ei_callback),
|
||||||
|
version: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
request disconnect {
|
||||||
|
}
|
||||||
|
|
||||||
|
event disconnected {
|
||||||
|
last_serial: u32,
|
||||||
|
reason: u32,
|
||||||
|
explanation: optstr,
|
||||||
|
}
|
||||||
|
|
||||||
|
event seat {
|
||||||
|
seat: id(ei_seat),
|
||||||
|
version: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event invalid_object {
|
||||||
|
last_serial: u32,
|
||||||
|
invalid_id: id(ei_object),
|
||||||
|
}
|
||||||
|
|
||||||
|
event ping {
|
||||||
|
ping: id(ei_pingpong),
|
||||||
|
version: u32,
|
||||||
|
}
|
||||||
76
wire-ei/ei_device.txt
Normal file
76
wire-ei/ei_device.txt
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
request release {
|
||||||
|
}
|
||||||
|
|
||||||
|
request client_start_emulating (sender) {
|
||||||
|
last_serial: u32,
|
||||||
|
sequence: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
request client_stop_emulating (sender) {
|
||||||
|
last_serial: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
request client_frame (sender) {
|
||||||
|
last_serial: u32,
|
||||||
|
timestamp: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
event destroyed {
|
||||||
|
serial: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event name {
|
||||||
|
name: str,
|
||||||
|
}
|
||||||
|
|
||||||
|
event device_type {
|
||||||
|
device_type: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event dimensions {
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event region {
|
||||||
|
offset_x: u32,
|
||||||
|
offset_y: u32,
|
||||||
|
width: u32,
|
||||||
|
hight: u32,
|
||||||
|
scale: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event interface {
|
||||||
|
object: id(ei_object),
|
||||||
|
interface_name: str,
|
||||||
|
version: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event done {
|
||||||
|
}
|
||||||
|
|
||||||
|
event resumed {
|
||||||
|
serial: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event paused {
|
||||||
|
serial: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event server_start_emulating (receiver) {
|
||||||
|
serial: u32,
|
||||||
|
sequence: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event server_stop_emulating (receiver) {
|
||||||
|
serial: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event server_frame (receiver) {
|
||||||
|
serial: u32,
|
||||||
|
timestamp: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
event region_mapping_id (since = 2) {
|
||||||
|
mapping_id: str,
|
||||||
|
}
|
||||||
34
wire-ei/ei_handshake.txt
Normal file
34
wire-ei/ei_handshake.txt
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
request client_handshake_version {
|
||||||
|
version: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
request finish {
|
||||||
|
}
|
||||||
|
|
||||||
|
request context_type {
|
||||||
|
context_type: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
request name {
|
||||||
|
name: str,
|
||||||
|
}
|
||||||
|
|
||||||
|
request client_interface_version {
|
||||||
|
name: str,
|
||||||
|
version: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event server_handshake_version {
|
||||||
|
version: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event server_interface_version {
|
||||||
|
name: str,
|
||||||
|
version: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event connection {
|
||||||
|
serial: u32,
|
||||||
|
connection: id(ei_connection),
|
||||||
|
version: u32,
|
||||||
|
}
|
||||||
30
wire-ei/ei_keyboard.txt
Normal file
30
wire-ei/ei_keyboard.txt
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
request release {
|
||||||
|
}
|
||||||
|
|
||||||
|
request client_key (sender) {
|
||||||
|
key: u32,
|
||||||
|
state: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event destroyed {
|
||||||
|
serial: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event keymap {
|
||||||
|
keymap_type: u32,
|
||||||
|
size: u32,
|
||||||
|
keymap: fd,
|
||||||
|
}
|
||||||
|
|
||||||
|
event server_key (receiver) {
|
||||||
|
key: u32,
|
||||||
|
state: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event modifiers {
|
||||||
|
serial: u32,
|
||||||
|
depressed: u32,
|
||||||
|
locked: u32,
|
||||||
|
latched: u32,
|
||||||
|
group: u32,
|
||||||
|
}
|
||||||
3
wire-ei/ei_pingpong.txt
Normal file
3
wire-ei/ei_pingpong.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
request done {
|
||||||
|
callback_data: u64,
|
||||||
|
}
|
||||||
16
wire-ei/ei_pointer.txt
Normal file
16
wire-ei/ei_pointer.txt
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
request release {
|
||||||
|
}
|
||||||
|
|
||||||
|
request client_motion_relative (sender) {
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event destroyed {
|
||||||
|
serial: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event server_motion_relative (receiver) {
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
}
|
||||||
16
wire-ei/ei_pointer_absolute.txt
Normal file
16
wire-ei/ei_pointer_absolute.txt
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
request release {
|
||||||
|
}
|
||||||
|
|
||||||
|
request client_motion_absolute (sender) {
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event destroyed {
|
||||||
|
serial: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event server_motion_absolute (receiver) {
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
}
|
||||||
38
wire-ei/ei_scroll.txt
Normal file
38
wire-ei/ei_scroll.txt
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
request release {
|
||||||
|
}
|
||||||
|
|
||||||
|
request client_scroll (sender) {
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
request client_scroll_discrete (sender) {
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
request client_scroll_stop (sender) {
|
||||||
|
x: u32,
|
||||||
|
y: u32,
|
||||||
|
is_cancel: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event destroyed {
|
||||||
|
serial: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event server_scroll (receiver) {
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event server_scroll_discrete (receiver) {
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event server_scroll_stop (receiver) {
|
||||||
|
x: u32,
|
||||||
|
y: u32,
|
||||||
|
is_cancel: u32,
|
||||||
|
}
|
||||||
27
wire-ei/ei_seat.txt
Normal file
27
wire-ei/ei_seat.txt
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
request release {
|
||||||
|
}
|
||||||
|
|
||||||
|
request bind {
|
||||||
|
capabilities: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
event destroyed {
|
||||||
|
serial: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event name {
|
||||||
|
name: str,
|
||||||
|
}
|
||||||
|
|
||||||
|
event capability {
|
||||||
|
mask: u64,
|
||||||
|
interface: str,
|
||||||
|
}
|
||||||
|
|
||||||
|
event done {
|
||||||
|
}
|
||||||
|
|
||||||
|
event device {
|
||||||
|
device: id(ei_device),
|
||||||
|
version: u32,
|
||||||
|
}
|
||||||
38
wire-ei/ei_touchscreen.txt
Normal file
38
wire-ei/ei_touchscreen.txt
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
request release {
|
||||||
|
}
|
||||||
|
|
||||||
|
request client_down (sender) {
|
||||||
|
touchid: u32,
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
request client_motion (sender) {
|
||||||
|
touchid: u32,
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
request client_up (sender) {
|
||||||
|
touchid: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event destroyed {
|
||||||
|
serial: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event server_down (receiver) {
|
||||||
|
touchid: u32,
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event server_motion (receiver) {
|
||||||
|
touchid: u32,
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event server_up (receiver) {
|
||||||
|
touchid: u32,
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue