From 4a939477a24165cff9784e76b872e846e14f1322 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Thu, 6 Jan 2022 19:08:32 +0100 Subject: [PATCH] autocommit 2022-01-06 19:08:32 CET --- Cargo.lock | 79 +- Cargo.toml | 11 +- build.rs | 82 ++ c/bridge.c | 19 + src/acceptor.rs | 5 +- src/async_engine.rs | 4 +- src/backend.rs | 30 + src/backends/mod.rs | 1 + src/backends/xorg/mod.rs | 787 ++++++++++++++++++ src/client/mod.rs | 46 +- src/client/objects.rs | 11 +- src/client/tasks.rs | 31 +- src/clientmem.rs | 49 +- src/event_loop.rs | 9 +- src/fixed.rs | 35 + src/format.rs | 4 + src/globals.rs | 14 +- src/ifs/wl_buffer/mod.rs | 43 +- src/ifs/wl_buffer/types.rs | 12 +- src/ifs/wl_output/mod.rs | 222 +++++ src/ifs/wl_output/types.rs | 136 +++ src/ifs/wl_registry/types.rs | 3 +- src/ifs/wl_seat/mod.rs | 120 +++ src/ifs/wl_seat/types.rs | 35 + src/ifs/wl_surface/mod.rs | 229 ++++- src/ifs/wl_surface/types.rs | 9 + src/ifs/wl_surface/wl_subsurface/mod.rs | 1 + src/ifs/wl_surface/xdg_surface/mod.rs | 28 +- .../xdg_surface/xdg_toplevel/mod.rs | 2 +- .../xdg_surface/xdg_toplevel/types.rs | 5 +- src/macros.rs | 68 ++ src/main.rs | 32 +- src/object.rs | 4 + src/pixman.rs | 95 --- src/pixman/consts.rs | 192 +++++ src/pixman/mod.rs | 291 +++++++ src/servermem.rs | 93 +++ src/sighand.rs | 9 +- src/state.rs | 15 +- src/tasks.rs | 138 +++ src/tree.rs | 171 ++++ src/utils/asyncevent.rs | 41 + src/utils/buffd/formatter.rs | 11 +- src/utils/buffd/parser.rs | 15 +- src/utils/mod.rs | 1 + src/utils/numcell.rs | 4 + src/utils/ptr_ext.rs | 10 +- src/utils/queue.rs | 5 + src/wheel.rs | 96 ++- src/xkbcommon/consts.rs | 43 + src/xkbcommon/mod.rs | 249 ++++++ 51 files changed, 3438 insertions(+), 207 deletions(-) create mode 100644 build.rs create mode 100644 c/bridge.c create mode 100644 src/backend.rs create mode 100644 src/backends/mod.rs create mode 100644 src/backends/xorg/mod.rs create mode 100644 src/fixed.rs create mode 100644 src/ifs/wl_output/types.rs create mode 100644 src/ifs/wl_seat/types.rs delete mode 100644 src/pixman.rs create mode 100644 src/pixman/consts.rs create mode 100644 src/pixman/mod.rs create mode 100644 src/servermem.rs create mode 100644 src/tasks.rs create mode 100644 src/tree.rs create mode 100644 src/utils/asyncevent.rs create mode 100644 src/xkbcommon/consts.rs create mode 100644 src/xkbcommon/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 491f4442..e87a5900 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -51,6 +51,23 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + [[package]] name = "cc" version = "1.0.72" @@ -204,17 +221,27 @@ dependencies = [ "ahash", "anyhow", "bitflags", + "bstr", "env_logger", "futures", + "isnt", + "libloading", "log", "num-derive", "num-traits", + "repc", "thiserror", "uapi", - "waker-fn", "xcb-dl", + "xcb-dl-util", ] +[[package]] +name = "isnt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a7558cc96ddcaf0b4144d7149984ace2899bb29d4ee2999979d429efc305200" + [[package]] name = "lazy_static" version = "1.4.0" @@ -319,18 +346,45 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" + [[package]] name = "regex-syntax" version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +[[package]] +name = "repc" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd5497e1d1e81bb330f7a48f4093b01b95da3b5b7ac07e7cd9a17d4f5cfe98e6" +dependencies = [ + "repc-impl", +] + +[[package]] +name = "repc-impl" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e110b7d5a1335c2e801176c42a626a905c23eecdee104d9bdfbd6ea5f0b8368" + [[package]] name = "slab" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" +[[package]] +name = "smallvec" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" + [[package]] name = "syn" version = "1.0.84" @@ -409,12 +463,6 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "waker-fn" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" - [[package]] name = "wasi" version = "0.10.2+wasi-snapshot-preview1" @@ -461,3 +509,20 @@ dependencies = [ "libc", "libloading", ] + +[[package]] +name = "xcb-dl-util" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac09222229087f380311b876bf8651de4cfe78cf793ef8702932a81385c0f400" +dependencies = [ + "bitflags", + "bstr", + "byteorder", + "isnt", + "libc", + "log", + "smallvec", + "thiserror", + "xcb-dl", +] diff --git a/Cargo.toml b/Cargo.toml index 336f0cbe..2766e011 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,8 +3,6 @@ name = "i4" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [profile.release] panic = "abort" @@ -19,8 +17,15 @@ ahash = "0.7.6" log = "0.4.14" env_logger = "0.9.0" futures = "0.3.19" -waker-fn = "1.1.0" num-traits = "0.2.14" num-derive = "0.3.3" bitflags = "1.3.2" xcb-dl = "0.2.0" +xcb-dl-util = { version = "0.2.0", features = ["xcb_shm", "xcb_xinput", "xcb_xkb"]} +libloading = "0.7.2" +bstr = "0.2.17" +isnt = "0.1.0" + +[build-dependencies] +repc = "0.1.1" +anyhow = "1.0.52" diff --git a/build.rs b/build.rs new file mode 100644 index 00000000..2e3d6730 --- /dev/null +++ b/build.rs @@ -0,0 +1,82 @@ +use repc::layout::{Type, TypeVariant}; +use std::fs::{File, OpenOptions}; +use std::io::BufWriter; +use std::io::Write; +use std::path::PathBuf; +use std::{env, io}; + +#[allow(unused_macros)] +#[macro_use] +#[path = "src/macros.rs"] +mod macros; + +#[path = "src/pixman/consts.rs"] +mod pixman; + +#[path = "src/xkbcommon/consts.rs"] +mod xkbcommon; + +fn open(s: &str) -> io::Result> { + let mut path = PathBuf::from(env::var("OUT_DIR").unwrap()); + path.push(s); + Ok(BufWriter::new( + OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open(path)?, + )) +} + +fn get_target() -> repc::Target { + let rustc_target = env::var("TARGET").unwrap(); + repc::TARGET_MAP + .iter() + .cloned() + .find(|t| t.0 == &rustc_target) + .unwrap() + .1 +} + +fn get_enum_ty(variants: Vec) -> anyhow::Result { + let target = get_target(); + let ty = Type { + layout: (), + annotations: vec![], + variant: TypeVariant::Enum(variants), + }; + let ty = repc::compute_layout(target, &ty)?; + assert!(ty.layout.pointer_alignment_bits <= ty.layout.size_bits); + Ok(ty.layout.size_bits) +} + +fn write_ty(f: &mut W, vals: &[u32], ty: &str) -> anyhow::Result<()> { + let variants: Vec<_> = vals.iter().cloned().map(|v| v as i128).collect(); + let size = get_enum_ty(variants)?; + writeln!(f, "pub type {} = u{};", ty, size)?; + Ok(()) +} + +fn main() -> anyhow::Result<()> { + let mut f = open("pixman_tys.rs")?; + write_ty(&mut f, pixman::FORMATS, "PixmanFormat")?; + write_ty(&mut f, pixman::OPS, "PixmanOp")?; + + let mut f = open("xkbcommon_tys.rs")?; + write_ty( + &mut f, + xkbcommon::XKB_X11_SETUP_XKB_EXTENSION_FLAGS, + "xkb_x11_setup_xkb_extension_flags", + )?; + write_ty(&mut f, xkbcommon::XKB_LOG_LEVEL, "xkb_log_level")?; + write_ty(&mut f, xkbcommon::XKB_CONTEXT_FLAGS, "xkb_context_flags")?; + write_ty( + &mut f, + xkbcommon::XKB_KEYMAP_COMPILE_FLAGS, + "xkb_keymap_compile_flags", + )?; + write_ty(&mut f, xkbcommon::XKB_KEYMAP_FORMAT, "xkb_keymap_format")?; + + println!("cargo:rerun-if-changed=build.rs"); + Ok(()) +} diff --git a/c/bridge.c b/c/bridge.c new file mode 100644 index 00000000..daf254da --- /dev/null +++ b/c/bridge.c @@ -0,0 +1,19 @@ +#define _GNU_SOURCE +#include +#include +#include + +extern void i4_xkbcommon_log_fn(enum xkb_log_level level, unsigned char *bytes, size_t len); + +void i4_xkbcommon_log_fn_bridge( + struct xkb_context *context, + enum xkb_log_level level, + const char *format, va_list args) +{ + char *buf; + int len = vasprintf(&buf, format, args); + if (len < 0) { + abort(); + } + i4_xkbcommon_log_fn(level, (unsigned char *)buf, len); +} diff --git a/src/acceptor.rs b/src/acceptor.rs index 5ff35eff..e3bbafcf 100644 --- a/src/acceptor.rs +++ b/src/acceptor.rs @@ -104,7 +104,10 @@ impl Acceptor { } impl EventLoopDispatcher for Acceptor { - fn dispatch(&self, events: i32) -> Result<(), Box> { + fn dispatch( + self: Rc, + events: i32, + ) -> Result<(), Box> { if events & (c::EPOLLERR | c::EPOLLHUP) != 0 { return Err(Box::new(AcceptorError::ErrorEvent)); } diff --git a/src/async_engine.rs b/src/async_engine.rs index 272b87c1..1ca9b4a5 100644 --- a/src/async_engine.rs +++ b/src/async_engine.rs @@ -435,7 +435,7 @@ mod queue { } impl EventLoopDispatcher for Dispatcher { - fn dispatch(&self, _events: i32) -> Result<(), Box> { + fn dispatch(self: Rc, _events: i32) -> Result<(), Box> { loop { self.queue.iteration.fetch_add(1); let mut stash = self.stash.borrow_mut(); @@ -549,7 +549,7 @@ mod fd { } impl EventLoopDispatcher for AsyncFdData { - fn dispatch(&self, events: i32) -> Result<(), Box> { + fn dispatch(self: Rc, events: i32) -> Result<(), Box> { if events & (c::EPOLLERR | c::EPOLLHUP) != 0 { self.erroneous.set(true); if let Err(e) = self.el.remove(self.id) { diff --git a/src/backend.rs b/src/backend.rs new file mode 100644 index 00000000..904a1554 --- /dev/null +++ b/src/backend.rs @@ -0,0 +1,30 @@ +use std::rc::Rc; +use crate::fixed::Fixed; + +linear_ids!(OutputIds, OutputId); +linear_ids!(SeatIds, SeatId); + +pub trait Output { + fn id(&self) -> OutputId; + fn removed(&self) -> bool; + fn width(&self) -> u32; + fn height(&self) -> u32; + fn on_change(&self, cb: Rc); +} + +pub trait Seat { + fn id(&self) -> SeatId; + fn removed(&self) -> bool; + fn event(&self) -> Option; + fn on_change(&self, cb: Rc); +} + +pub enum BackendEvent { + NewOutput(Rc), + NewSeat(Rc), +} + +#[derive(Debug)] +pub enum SeatEvent { + Motion(OutputId, Fixed, Fixed), +} diff --git a/src/backends/mod.rs b/src/backends/mod.rs new file mode 100644 index 00000000..68b939e6 --- /dev/null +++ b/src/backends/mod.rs @@ -0,0 +1 @@ +pub mod xorg; diff --git a/src/backends/xorg/mod.rs b/src/backends/xorg/mod.rs new file mode 100644 index 00000000..8a344ced --- /dev/null +++ b/src/backends/xorg/mod.rs @@ -0,0 +1,787 @@ +use crate::backend::{BackendEvent, Output, OutputId, Seat, SeatEvent, SeatId}; +use crate::event_loop::{EventLoopDispatcher, EventLoopId}; +use crate::ifs::wl_buffer::WlBuffer; +use crate::ifs::wl_surface::WlSurface; +use crate::pixman::{Image, PixmanError}; +use crate::servermem::{ServerMem, ServerMemError}; +use crate::tree::{Node, NodeKind, ToplevelNode}; +use crate::utils::copyhashmap::CopyHashMap; +use crate::utils::ptr_ext::PtrExt; +use crate::wheel::{WheelDispatcher, WheelId}; +use crate::xkbcommon::{ + XkbCommonError, XkbCommonX11, XkbContext, XkbState, XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS, +}; +use crate::{pixman, EventLoopError, State, WheelError}; +use isnt::std_1::primitive::IsntConstPtrExt; +use std::cell::{Cell, RefCell}; +use std::collections::VecDeque; +use std::error::Error; +use std::ops::Deref; +use std::ptr; +use std::rc::Rc; +use thiserror::Error; +use uapi::c; +use xcb_dl::{ffi, Xcb, XcbShm, XcbXinput, XcbXkb}; +use xcb_dl_util::error::{XcbError, XcbErrorParser}; +use xcb_dl_util::xcb_box::XcbBox; +use crate::fixed::Fixed; + +#[derive(Debug, Error)] +pub enum XorgBackendError { + #[error("The xcb connection is in an error state")] + ErrorEvent, + #[error("Could not select input events")] + CannotSelectInputEvents(#[source] XcbError), + #[error("libloading returned an error")] + Libloading(#[from] libloading::Error), + #[error("xcb returned an error")] + XcbError(#[from] XcbError), + #[error("The event loop caused an error")] + EventLoopError(#[source] Box), + #[error("The timer wheel caused an error")] + WheelError(#[source] Box), + #[error("Could not allocate and map image memory")] + ServerMemError(#[source] Box), + #[error("Pixman returned an error")] + PixmanError(#[source] Box), + #[error("dupfd failed")] + DupfdFailed(#[source] std::io::Error), + #[error("Could not create a window")] + CreateWindow(#[source] XcbError), + #[error("Could not set WM_CLASS")] + WmClass(#[source] XcbError), + #[error("Could not select window events")] + WindowEvents(#[source] XcbError), + #[error("Could not map a window")] + MapWindow(#[source] XcbError), + #[error("Could not query device")] + QueryDevice(#[source] XcbError), + #[error("xkbcommon error")] + XkbCommon(#[from] Box), +} +efrom!(XorgBackendError, EventLoopError, EventLoopError); +efrom!(XorgBackendError, ServerMemError, ServerMemError); +efrom!(XorgBackendError, PixmanError, PixmanError); +efrom!(XorgBackendError, WheelError, WheelError); +efrom!(XorgBackendError, XkbCommon, XkbCommonError); + +struct XcbCon { + xcb: Box, + shm: Box, + input: Box, + input_opcode: u8, + xkb: Box, + c: *mut ffi::xcb_connection_t, + errors: XcbErrorParser, +} + +impl XcbCon { + fn new() -> Result { + unsafe { + let xcb = Box::new(Xcb::load_loose()?); + let shm = Box::new(XcbShm::load_loose()?); + let input = Box::new(XcbXinput::load_loose()?); + let xkb = Box::new(XcbXkb::load_loose()?); + + let c = xcb.xcb_connect(ptr::null(), ptr::null_mut()); + let errors = XcbErrorParser::new(&xcb, c); + + let mut con = Self { + xcb, + shm, + input, + input_opcode: 0, + xkb, + c, + errors, + }; + + con.errors.check_connection(&con.xcb)?; + + let mut err = ptr::null_mut(); + + let res = + con.shm + .xcb_shm_query_version_reply(c, con.shm.xcb_shm_query_version(c), &mut err); + con.errors.check(&con.xcb, res, err)?; + + let res = con.input.xcb_input_xi_query_version_reply( + c, + con.input.xcb_input_xi_query_version(c, 2, 2), + &mut err, + ); + con.errors.check(&con.xcb, res, err)?; + + let input_ex = con + .xcb + .xcb_get_extension_data(con.c, con.input.xcb_input_id()); + assert!(input_ex.is_not_null()); + con.input_opcode = input_ex.deref().major_opcode; + + let res = con.xkb.xcb_xkb_use_extension_reply( + c, + con.xkb.xcb_xkb_use_extension(c, 1, 0), + &mut err, + ); + con.errors.check(&con.xcb, res, err)?; + + Ok(con) + } + } + + fn check_cookie(&self, cookie: ffi::xcb_void_cookie_t) -> Result<(), XcbError> { + unsafe { self.errors.check_cookie(&self.xcb, cookie) } + } + + fn check( + &self, + reply: *mut T, + err: *mut ffi::xcb_generic_error_t, + ) -> Result, XcbError> { + unsafe { self.errors.check(&self.xcb, reply, err) } + } + + fn screen(&self) -> &ffi::xcb_screen_t { + unsafe { + self.xcb + .xcb_setup_roots_iterator(self.xcb.xcb_get_setup(self.c)) + .data + .deref() + } + } +} + +impl Drop for XcbCon { + fn drop(&mut self) { + unsafe { + self.xcb.xcb_disconnect(self.c); + } + } +} + +pub struct XorgBackend { + id: EventLoopId, + wheel_id: WheelId, + state: Rc, + con: XcbCon, + xkbcommon: XkbContext, + xkbcommonx11: XkbCommonX11, + outputs: CopyHashMap>, + seats: CopyHashMap>, + mouse_seats: CopyHashMap>, +} + +impl XorgBackend { + pub fn new(state: &Rc) -> Result, XorgBackendError> { + unsafe { + let con = XcbCon::new()?; + + let fd = con.xcb.xcb_get_file_descriptor(con.c); + + let wheel_id = state.wheel.id(); + + let slf = Rc::new(Self { + id: state.el.id(), + wheel_id, + state: state.clone(), + con, + xkbcommon: XkbContext::new()?, + xkbcommonx11: XkbCommonX11::load()?, + outputs: Default::default(), + seats: Default::default(), + mouse_seats: Default::default(), + }); + + { + let cookie = xcb_dl_util::input::select_events_checked( + &slf.con.input, + slf.con.c, + slf.con.screen().root, + ffi::XCB_INPUT_DEVICE_ALL as _, + [ffi::XCB_INPUT_XI_EVENT_MASK_HIERARCHY], + ); + if let Err(e) = slf.con.check_cookie(cookie) { + return Err(XorgBackendError::CannotSelectInputEvents(e)); + } + } + + state.wheel.periodic(wheel_id, 16_667, slf.clone())?; + state.el.insert(slf.id, Some(fd), c::EPOLLIN, slf.clone())?; + + slf.add_output()?; + slf.query_devices(ffi::XCB_INPUT_DEVICE_ALL_MASTER as _)?; + slf.handle_events()?; + + Ok(slf) + } + } + + fn add_output(self: &Rc) -> Result<(), XorgBackendError> { + unsafe { + let con = &self.con; + let screen = con + .xcb + .xcb_setup_roots_iterator(con.xcb.xcb_get_setup(con.c)) + .data + .deref(); + let window_id = con.xcb.xcb_generate_id(con.c); + { + let cookie = con.xcb.xcb_create_window_checked( + con.c, + 0, + window_id, + screen.root, + 0, + 0, + 800, + 600, + 0, + ffi::XCB_WINDOW_CLASS_INPUT_OUTPUT as _, + 0, + 0, + ptr::null(), + ); + if let Err(e) = con.check_cookie(cookie) { + return Err(XorgBackendError::CreateWindow(e)); + } + } + let output = Rc::new(XorgOutput { + id: self.state.output_ids.next(), + backend: self.clone(), + window: window_id, + removed: Cell::new(false), + width: Cell::new(0), + height: Cell::new(0), + image: RefCell::new(None), + cb: RefCell::new(None), + }); + { + let class = "i4\0i4\0"; + let cookie = con.xcb.xcb_change_property_checked( + con.c, + ffi::XCB_PROP_MODE_REPLACE as _, + window_id, + ffi::XCB_ATOM_WM_CLASS, + ffi::XCB_ATOM_STRING, + 8, + class.len() as _, + class.as_ptr() as _, + ); + if let Err(e) = con.check_cookie(cookie) { + return Err(XorgBackendError::WmClass(e)); + } + } + { + let event_mask = ffi::XCB_EVENT_MASK_EXPOSURE + | ffi::XCB_EVENT_MASK_STRUCTURE_NOTIFY + | ffi::XCB_EVENT_MASK_VISIBILITY_CHANGE; + let cookie = con.xcb.xcb_change_window_attributes_checked( + con.c, + window_id, + ffi::XCB_CW_EVENT_MASK, + &event_mask as *const _ as _, + ); + if let Err(e) = con.check_cookie(cookie) { + return Err(XorgBackendError::WindowEvents(e)); + } + } + { + let cookie = con.xcb.xcb_map_window_checked(con.c, window_id); + if let Err(e) = con.check_cookie(cookie) { + return Err(XorgBackendError::MapWindow(e)); + } + } + { + let mask = ffi::XCB_INPUT_XI_EVENT_MASK_MOTION + | ffi::XCB_INPUT_XI_EVENT_MASK_BUTTON_PRESS + | ffi::XCB_INPUT_XI_EVENT_MASK_BUTTON_RELEASE + | ffi::XCB_INPUT_XI_EVENT_MASK_KEY_PRESS + | ffi::XCB_INPUT_XI_EVENT_MASK_KEY_RELEASE + | ffi::XCB_INPUT_XI_EVENT_MASK_ENTER + | ffi::XCB_INPUT_XI_EVENT_MASK_LEAVE + | ffi::XCB_INPUT_XI_EVENT_MASK_FOCUS_IN + | ffi::XCB_INPUT_XI_EVENT_MASK_FOCUS_OUT + | ffi::XCB_INPUT_XI_EVENT_MASK_TOUCH_BEGIN + | ffi::XCB_INPUT_XI_EVENT_MASK_TOUCH_UPDATE + | ffi::XCB_INPUT_XI_EVENT_MASK_TOUCH_END; + let cookie = xcb_dl_util::input::select_events_checked( + &con.input, + con.c, + window_id, + ffi::XCB_INPUT_DEVICE_ALL_MASTER as _, + [mask], + ); + if let Err(e) = con.check_cookie(cookie) { + return Err(XorgBackendError::CannotSelectInputEvents(e)); + } + } + self.outputs.set(window_id, output.clone()); + self.state + .backend_events + .push(BackendEvent::NewOutput(output.clone())); + } + Ok(()) + } + + fn query_devices( + self: &Rc, + device_id: ffi::xcb_input_device_id_t, + ) -> Result<(), XorgBackendError> { + unsafe { + let con = &self.con; + let mut err = ptr::null_mut(); + let reply = con.input.xcb_input_xi_query_device_reply( + con.c, + con.input.xcb_input_xi_query_device(con.c, device_id), + &mut err, + ); + let reply = match con.check(reply, err) { + Ok(i) => i, + Err(e) => return Err(XorgBackendError::QueryDevice(e)), + }; + let mut iter = con.input.xcb_input_xi_query_device_infos_iterator(&*reply); + while iter.rem > 0 { + self.handle_input_device(iter.data.deref()); + con.input.xcb_input_xi_device_info_next(&mut iter); + } + } + Ok(()) + } + + fn handle_input_device(self: &Rc, info: &ffi::xcb_input_xi_device_info_t) { + if info.type_ != ffi::XCB_INPUT_DEVICE_TYPE_MASTER_KEYBOARD as _ { + return; + } + let con = &self.con; + self.mouse_seats.remove(&info.attachment); + if let Some(kb) = self.seats.remove(&info.deviceid) { + kb.removed.set(true); + kb.changed(); + } + unsafe { + let mut err = ptr::null_mut(); + let cookie = con.xkb.xcb_xkb_per_client_flags( + con.c, + info.deviceid, + ffi::XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT, + ffi::XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT, + 0, + 0, + 0, + ); + let reply = con + .xkb + .xcb_xkb_per_client_flags_reply(con.c, cookie, &mut err); + if let Err(e) = con.check(reply, err) { + log::warn!( + "Could not make auto repeat detectable for keyboard {}: {:#}", + info.deviceid, + e + ); + } + let res: Result<_, XkbCommonError> = (|| { + let keymap = self.xkbcommonx11.keymap_from_device( + &self.xkbcommon, + con.c, + info.deviceid as _, + XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS, + )?; + let state = + self.xkbcommonx11 + .state_from_device(&keymap, con.c, info.deviceid as _)?; + Ok(state) + })(); + let state = match res { + Ok(c) => c, + Err(e) => { + log::error!( + "Could not create an xkb context for device {}: {:#}", + info.deviceid, + e + ); + return; + } + }; + let seat = Rc::new(XorgSeat { + id: self.state.seat_ids.next(), + backend: self.clone(), + kb: info.deviceid, + mouse: info.attachment, + removed: Cell::new(false), + cb: RefCell::new(None), + events: RefCell::new(Default::default()), + state, + }); + self.seats.set(info.deviceid, seat.clone()); + self.mouse_seats.set(info.attachment, seat.clone()); + self.state + .backend_events + .push(BackendEvent::NewSeat(seat.clone())); + } + } + + fn handle_events(self: &Rc) -> Result<(), XorgBackendError> { + unsafe { + loop { + let event = self.con.xcb.xcb_poll_for_event(self.con.c); + if event.is_null() { + self.con.errors.check_connection(&self.con.xcb)?; + return Ok(()); + } + let event = XcbBox::new(event); + self.handle_event(&event)?; + } + } + } + + fn handle_event( + self: &Rc, + event: &ffi::xcb_generic_event_t, + ) -> Result<(), XorgBackendError> { + let event_type = event.response_type & 0x7f; + match event_type { + ffi::XCB_CONFIGURE_NOTIFY => self.handle_configure(event)?, + ffi::XCB_DESTROY_NOTIFY => self.handle_destroy(event)?, + ffi::XCB_GE_GENERIC => self.handle_generic(event)?, + _ => {} + } + Ok(()) + } + + fn handle_generic( + self: &Rc, + event: &ffi::xcb_generic_event_t, + ) -> Result<(), XorgBackendError> { + let event = unsafe { (event as *const _ as *const ffi::xcb_ge_generic_event_t).deref() }; + if event.extension == self.con.input_opcode { + self.handle_input_event(event)?; + } + Ok(()) + } + + fn handle_input_event( + self: &Rc, + event: &ffi::xcb_ge_generic_event_t, + ) -> Result<(), XorgBackendError> { + match event.event_type { + ffi::XCB_INPUT_MOTION => self.handle_input_motion(event)?, + ffi::XCB_INPUT_HIERARCHY => self.handle_input_hierarchy(event)?, + _ => {} + } + Ok(()) + } + + fn handle_input_hierarchy( + self: &Rc, + event: &ffi::xcb_ge_generic_event_t, + ) -> Result<(), XorgBackendError> { + let event = + unsafe { (event as *const _ as *const ffi::xcb_input_hierarchy_event_t).deref() }; + let infos = unsafe { + std::slice::from_raw_parts( + self.con.input.xcb_input_hierarchy_infos(event), + event.num_infos as _, + ) + }; + for info in infos { + if info.flags & ffi::XCB_INPUT_HIERARCHY_MASK_MASTER_ADDED != 0 { + self.query_devices(info.deviceid); + } else if info.flags & ffi::XCB_INPUT_HIERARCHY_MASK_MASTER_REMOVED != 0 { + self.mouse_seats.remove(&info.attachment); + if let Some(seat) = self.seats.remove(&info.deviceid) { + seat.removed.set(true); + seat.changed(); + } + } + } + Ok(()) + } + + fn handle_input_motion( + &self, + event: &ffi::xcb_ge_generic_event_t, + ) -> Result<(), XorgBackendError> { + let event = unsafe { (event as *const _ as *const ffi::xcb_input_motion_event_t).deref() }; + let win = match self.outputs.get(&event.event) { + Some(w) => w, + _ => return Ok(()), + }; + let seat = match self.mouse_seats.get(&event.deviceid) { + Some(s) => s, + _ => return Ok(()), + }; + seat.event(SeatEvent::Motion( + win.id, + Fixed::from_1616(event.event_x), + Fixed::from_1616(event.event_y), + )); + Ok(()) + } + + fn handle_destroy(&self, event: &ffi::xcb_generic_event_t) -> Result<(), XorgBackendError> { + let event = + unsafe { (event as *const _ as *const ffi::xcb_destroy_notify_event_t).deref() }; + let output = match self.outputs.remove(&event.event) { + Some(o) => o, + _ => return Ok(()), + }; + output.removed.set(true); + *output.image.borrow_mut() = None; + output.changed(); + Ok(()) + } + + fn handle_configure(&self, event: &ffi::xcb_generic_event_t) -> Result<(), XorgBackendError> { + let event = + unsafe { (event as *const _ as *const ffi::xcb_configure_notify_event_t).deref() }; + let output = match self.outputs.get(&event.event) { + Some(o) => o, + _ => return Ok(()), + }; + let width = event.width as u32; + let height = event.height as u32; + let mut changed = false; + changed |= output.width.replace(width) != width; + changed |= output.height.replace(height) != height; + if changed { + let shm = Rc::new(ServerMem::new((width * height * 4) as usize)?); + let fd = shm.fd(); + let image = Image::new(shm, pixman::X8R8G8B8, width, height, width * 4)?; + *output.image.borrow_mut() = Some(image); + unsafe { + let fd = match uapi::fcntl_dupfd_cloexec(fd, 0) { + Ok(fd) => fd, + Err(e) => return Err(XorgBackendError::DupfdFailed(e.into())), + }; + let shmseg = self.con.xcb.xcb_generate_id(self.con.c); + let cookie = + self.con + .shm + .xcb_shm_attach_fd_checked(self.con.c, shmseg, fd.unwrap(), 0); + self.con.check_cookie(cookie)?; + let pixmap = self.con.xcb.xcb_generate_id(self.con.c); + let cookie = self.con.shm.xcb_shm_create_pixmap( + self.con.c, + pixmap, + output.window, + width as _, + height as _, + 24, + shmseg, + 0, + ); + self.con.check_cookie(cookie)?; + let cookie = self.con.xcb.xcb_change_window_attributes_checked( + self.con.c, + output.window, + ffi::XCB_CW_BACK_PIXMAP, + &pixmap as *const _ as _, + ); + self.con.check_cookie(cookie)?; + self.con.xcb.xcb_free_pixmap(self.con.c, pixmap); + self.con.shm.xcb_shm_detach(self.con.c, shmseg); + } + output.changed(); + self.render()?; + } + Ok(()) + } + + fn render_node(&self, image: &Image>, node: Rc) { + match node.into_kind() { + NodeKind::Display(_) => {} + NodeKind::Output(_) => {} + NodeKind::Toplevel(tl) => self.render_toplevel(image, &tl), + NodeKind::Container(_) => {} + } + } + + fn render_toplevel(&self, image: &Image>, tl: &Rc) { + let surface = &tl.surface.surface.surface; + let extents = surface.extents.get(); + self.render_surface(image, surface, -extents.x1, -extents.y1); + } + + fn render_surface( + &self, + image: &Image>, + surface: &Rc, + x: i32, + y: i32, + ) { + let children = surface.children.borrow(); + if let Some(children) = &*children { + for child in children.below.iter() { + if let Some((sx, sy)) = child.surface.subsurface_position() { + self.render_surface(image, &child.surface, x + sx, y + sy); + } + } + } + let buffer = surface.buffer.borrow(); + if let Some(buffer) = &*buffer { + self.render_buffer(image, buffer, x, y); + let mut fr = surface.frame_requests.borrow_mut(); + for cb in fr.drain(..) { + surface.client.dispatch_frame_requests.push(cb); + } + } + if let Some(children) = &*children { + for child in children.above.iter() { + if let Some((sx, sy)) = child.surface.subsurface_position() { + self.render_surface(image, &child.surface, x + sx, y + sy); + } + } + } + } + + fn render_buffer(&self, image: &Image>, buffer: &Rc, x: i32, y: i32) { + image.add_image(&buffer.image, x, y); + } + + fn render(&self) -> Result<(), XorgBackendError> { + let outputs = self.outputs.lock(); + for output in outputs.values() { + let image = output.image.borrow(); + let image = match image.deref() { + Some(i) => i, + None => continue, + }; + image.fill(0, 0, 0, 255); + let node = match self.state.root.outputs.get(&output.id) { + Some(n) => n, + _ => continue, + }; + for floating in node.floating.iter() { + self.render_node(image, floating.clone()); + } + unsafe { + let cookie = + self.con + .xcb + .xcb_clear_area_checked(self.con.c, 0, output.window, 0, 0, 0, 0); + self.con.check_cookie(cookie)?; + } + } + Ok(()) + } +} + +impl EventLoopDispatcher for XorgBackend { + fn dispatch(self: Rc, events: i32) -> Result<(), Box> { + if events & (c::EPOLLERR | c::EPOLLHUP) != 0 { + return Err(Box::new(XorgBackendError::ErrorEvent)); + } + self.handle_events()?; + Ok(()) + } +} + +impl WheelDispatcher for XorgBackend { + fn dispatch(self: Rc) -> Result<(), Box> { + self.render()?; + Ok(()) + } +} + +impl Drop for XorgBackend { + fn drop(&mut self) { + let _ = self.state.el.remove(self.id); + let _ = self.state.wheel.remove(self.wheel_id); + } +} + +struct XorgOutput { + id: OutputId, + backend: Rc, + window: ffi::xcb_window_t, + removed: Cell, + width: Cell, + height: Cell, + image: RefCell>>>, + cb: RefCell>>, +} + +impl Drop for XorgOutput { + fn drop(&mut self) { + unsafe { + let con = &self.backend.con; + con.xcb.xcb_destroy_window(con.c, self.window); + } + } +} + +impl XorgOutput { + fn changed(&self) { + if let Some(cb) = self.cb.borrow().clone() { + cb(); + } + } +} + +impl Output for XorgOutput { + fn id(&self) -> OutputId { + self.id + } + + fn removed(&self) -> bool { + self.removed.get() + } + + fn width(&self) -> u32 { + self.width.get() + } + + fn height(&self) -> u32 { + self.height.get() + } + + fn on_change(&self, cb: Rc) { + *self.cb.borrow_mut() = Some(cb); + } +} + +struct XorgSeat { + id: SeatId, + backend: Rc, + kb: ffi::xcb_input_device_id_t, + mouse: ffi::xcb_input_device_id_t, + removed: Cell, + cb: RefCell>>, + events: RefCell>, + state: XkbState, +} + +impl XorgSeat { + fn changed(&self) { + if let Some(cb) = self.cb.borrow().clone() { + cb(); + } + } + + fn event(&self, event: SeatEvent) { + self.events.borrow_mut().push_back(event); + self.changed(); + } +} + +impl Seat for XorgSeat { + fn id(&self) -> SeatId { + self.id + } + + fn removed(&self) -> bool { + self.removed.get() + } + + fn event(&self) -> Option { + self.events.borrow_mut().pop_front() + } + + fn on_change(&self, cb: Rc) { + *self.cb.borrow_mut() = Some(cb); + } +} + +fn fp1616_to_f64(i: ffi::xcb_input_fp1616_t) -> f64 { + i as f64 / (1 << 16) as f64 +} diff --git a/src/client/mod.rs b/src/client/mod.rs index bc2e5226..f0b3b9af 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -1,10 +1,13 @@ use crate::async_engine::{AsyncError, AsyncFd, SpawnedFuture}; use crate::client::objects::Objects; +use crate::ifs::wl_buffer::{WlBuffer, WlBufferError, WlBufferId}; use crate::ifs::wl_callback::WlCallback; use crate::ifs::wl_compositor::{WlCompositorError, WlCompositorObj}; use crate::ifs::wl_display::{WlDisplay, WlDisplayError}; +use crate::ifs::wl_output::{WlOutputError, WlOutputObj}; use crate::ifs::wl_region::{WlRegion, WlRegionError, WlRegionId}; use crate::ifs::wl_registry::{WlRegistry, WlRegistryError, WlRegistryId}; +use crate::ifs::wl_seat::{WlSeatError, WlSeatObj}; use crate::ifs::wl_shm::{WlShmError, WlShmObj}; use crate::ifs::wl_shm_pool::{WlShmPool, WlShmPoolError}; use crate::ifs::wl_subcompositor::{WlSubcompositorError, WlSubcompositorObj}; @@ -30,7 +33,6 @@ use std::mem; use std::rc::Rc; use thiserror::Error; use uapi::OwnedFd; -use crate::ifs::wl_buffer::{WlBuffer, WlBufferError}; mod objects; mod tasks; @@ -57,6 +59,8 @@ pub enum ClientError { ClientDoesNotExist(ClientId), #[error("There is no wl_region with id {0}")] RegionDoesNotExist(WlRegionId), + #[error("There is no wl_buffer with id {0}")] + BufferDoesNotExist(WlBufferId), #[error("There is no wl_surface with id {0}")] SurfaceDoesNotExist(WlSurfaceId), #[error("There is no xdg_surface with id {0}")] @@ -105,6 +109,10 @@ pub enum ClientError { XdgWmBaseError(#[source] Box), #[error("An error occurred in a `wl_buffer`")] WlBufferError(#[source] Box), + #[error("An error occurred in a `wl_output`")] + WlOutputError(#[source] Box), + #[error("An error occurred in a `wl_seat`")] + WlSeatError(#[source] Box), #[error("Object {0} is not a display")] NotADisplay(ObjectId), } @@ -125,6 +133,8 @@ efrom!(ClientError, XdgWmBaseError, XdgWmBaseError); efrom!(ClientError, XdgToplevelError, XdgToplevelError); efrom!(ClientError, XdgPopupError, XdgPopupError); efrom!(ClientError, WlBufferError, WlBufferError); +efrom!(ClientError, WlOutputError, WlOutputError); +efrom!(ClientError, WlSeatError, WlSeatError); impl ClientError { fn peer_closed(&self) -> bool { @@ -146,7 +156,7 @@ impl Display for ClientId { pub struct Clients { next_client_id: NumCell, - clients: RefCell>, + pub clients: RefCell>, shutdown_clients: RefCell>, } @@ -186,6 +196,7 @@ impl Clients { events: AsyncQueue::new(), shutdown: Cell::new(Some(send)), shutdown_sent: Cell::new(false), + dispatch_frame_requests: AsyncQueue::new(), }); let display = Rc::new(WlDisplay::new(&data)); *data.objects.display.borrow_mut() = Some(display.clone()); @@ -234,14 +245,16 @@ impl Drop for Clients { } } -struct ClientHolder { - data: Rc, +pub struct ClientHolder { + pub data: Rc, _handler: SpawnedFuture<()>, } impl Drop for ClientHolder { fn drop(&mut self) { self.data.objects.destroy(); + self.data.events.clear(); + self.data.dispatch_frame_requests.clear(); } } @@ -256,7 +269,7 @@ pub trait RequestParser<'a>: Debug + Sized { fn parse(parser: &mut MsgParser<'_, 'a>) -> Result; } -enum WlEvent { +pub enum WlEvent { Flush, Shutdown, Event(Box), @@ -266,10 +279,11 @@ pub struct Client { pub id: ClientId, pub state: Rc, socket: AsyncFd, - objects: Objects, + pub objects: Objects, events: AsyncQueue, shutdown: Cell>>, shutdown_sent: Cell, + pub dispatch_frame_requests: AsyncQueue>, } const MAX_PENDING_EVENTS: usize = 100; @@ -342,11 +356,20 @@ impl Client { self.event2(WlEvent::Event(event)).await } + pub async fn flush(&self) -> Result<(), ClientError> { + self.event2(WlEvent::Flush).await + } + async fn event2(&self, event: WlEvent) -> Result<(), ClientError> { self.events.push(event); self.check_queue_size().await } + pub fn event2_locked(&self, event: WlEvent) -> bool { + self.events.push(event); + self.events.size() > MAX_PENDING_EVENTS + } + pub async fn check_queue_size(&self) -> Result<(), ClientError> { if self.events.size() > MAX_PENDING_EVENTS { self.state.eng.yield_now().await; @@ -359,6 +382,13 @@ impl Client { Ok(()) } + pub fn get_buffer(&self, id: WlBufferId) -> Result, ClientError> { + match self.objects.buffers.get(&id) { + Some(r) => Ok(r), + _ => Err(ClientError::BufferDoesNotExist(id)), + } + } + pub fn get_region(&self, id: WlRegionId) -> Result, ClientError> { match self.objects.regions.get(&id) { Some(r) => Ok(r), @@ -453,7 +483,8 @@ simple_add_obj!(WlSubsurface); simple_add_obj!(XdgPositioner); simple_add_obj!(XdgToplevel); simple_add_obj!(XdgPopup); -simple_add_obj!(WlBuffer); +simple_add_obj!(WlOutputObj); +simple_add_obj!(WlSeatObj); macro_rules! dedicated_add_obj { ($ty:ty, $field:ident) => { @@ -477,3 +508,4 @@ dedicated_add_obj!(WlRegion, regions); dedicated_add_obj!(WlSurface, surfaces); dedicated_add_obj!(XdgWmBaseObj, xdg_wm_bases); dedicated_add_obj!(XdgSurface, xdg_surfaces); +dedicated_add_obj!(WlBuffer, buffers); diff --git a/src/client/objects.rs b/src/client/objects.rs index 9e27f8a2..92df9fd1 100644 --- a/src/client/objects.rs +++ b/src/client/objects.rs @@ -1,4 +1,5 @@ use crate::client::{Client, ClientError}; +use crate::ifs::wl_buffer::{WlBuffer, WlBufferId}; use crate::ifs::wl_display::WlDisplay; use crate::ifs::wl_region::{WlRegion, WlRegionId}; use crate::ifs::wl_registry::{WlRegistry, WlRegistryId}; @@ -19,6 +20,7 @@ pub struct Objects { pub surfaces: CopyHashMap>, pub xdg_surfaces: CopyHashMap>, pub regions: CopyHashMap>, + pub buffers: CopyHashMap>, pub xdg_wm_bases: CopyHashMap>, ids: RefCell>, } @@ -35,6 +37,7 @@ impl Objects { surfaces: Default::default(), xdg_surfaces: Default::default(), regions: Default::default(), + buffers: Default::default(), xdg_wm_bases: Default::default(), ids: RefCell::new(vec![]), } @@ -54,6 +57,7 @@ impl Objects { self.surfaces.clear(); self.xdg_wm_bases.clear(); self.xdg_surfaces.clear(); + self.buffers.clear(); } fn id(&self, client_data: &Client) -> Result @@ -106,9 +110,10 @@ impl Objects { } pub async fn remove_obj(&self, client_data: &Client, id: ObjectId) -> Result<(), ClientError> { - if self.registry.remove(&id).is_none() { - return Err(ClientError::UnknownId); - } + let _obj = match self.registry.remove(&id) { + Some(o) => o, + _ => return Err(ClientError::UnknownId), + }; if id.raw() >= MIN_SERVER_ID { let offset = (id.raw() - MIN_SERVER_ID) as usize; let pos = offset / SEG_SIZE; diff --git a/src/client/tasks.rs b/src/client/tasks.rs index 84e578d0..86554e37 100644 --- a/src/client/tasks.rs +++ b/src/client/tasks.rs @@ -1,4 +1,4 @@ -use crate::client::{Client, ClientError, WlEvent}; +use crate::client::{AddObj, Client, ClientError, WlEvent}; use crate::object::ObjectId; use crate::utils::buffd::{BufFdIn, BufFdOut, MsgFormatter, MsgParser}; use crate::utils::oneshot::OneshotRx; @@ -10,12 +10,15 @@ use std::rc::Rc; pub async fn client(data: Rc, shutdown: OneshotRx<()>) { let mut recv = data.state.eng.spawn(receive(data.clone())).fuse(); + let mut dispatch_fr = data.state.eng.spawn(dispatch_fr(data.clone())).fuse(); let _send = data.state.eng.spawn(send(data.clone())); select! { _ = recv => { }, + _ = dispatch_fr => { }, _ = shutdown.fuse() => { }, } drop(recv); + drop(dispatch_fr); if !data.shutdown_sent.get() { data.events.push(WlEvent::Shutdown); } @@ -25,12 +28,36 @@ pub async fn client(data: Rc, shutdown: OneshotRx<()>) { log::error!("Could not shut down client {} within 5 seconds", data.id.0); } Err(e) => { - log::error!("Could not create a timeout: {:#}", e); + log::error!("Could not create a timeout: {:#}", anyhow!(e)); } } data.state.clients.kill(data.id); } +async fn dispatch_fr(data: Rc) { + loop { + let mut fr = data.dispatch_frame_requests.pop().await; + loop { + if let Err(e) = data.event(fr.done()).await { + log::error!("Could not dispatch frame event: {:#}", anyhow!(e)); + return; + } + if let Err(e) = data.remove_obj(&*fr).await { + log::error!("Could not remove frame object: {:#}", anyhow!(e)); + return; + } + fr = match data.dispatch_frame_requests.try_pop() { + Some(f) => f, + _ => break, + }; + } + if let Err(e) = data.event2(WlEvent::Flush).await { + log::error!("Could not dispatch frame event: {:#}", anyhow!(e)); + return; + } + } +} + async fn receive(data: Rc) { let display = data.display().unwrap(); let recv = async { diff --git a/src/clientmem.rs b/src/clientmem.rs index 2a267e4a..768ec965 100644 --- a/src/clientmem.rs +++ b/src/clientmem.rs @@ -1,5 +1,7 @@ +use crate::pixman::PixmanMemory; use std::cell::{Cell, UnsafeCell}; use std::mem::MaybeUninit; +use std::rc::Rc; use std::sync::atomic::{compiler_fence, Ordering}; use std::{mem, ptr}; use thiserror::Error; @@ -19,7 +21,13 @@ pub enum ClientMemError { pub struct ClientMem { failed: Cell, sigbus_impossible: bool, - data: *mut [Cell], + data: *const [Cell], +} + +#[derive(Clone)] +pub struct ClientMemOffset { + mem: Rc, + data: *const [Cell], } impl ClientMem { @@ -53,32 +61,42 @@ impl ClientMem { }) } + pub fn len(&self) -> usize { + unsafe { (*self.data).len() } + } + + pub fn offset(self: &Rc, offset: usize) -> ClientMemOffset { + let mem = unsafe { &*self.data }; + ClientMemOffset { + mem: self.clone(), + data: &mem[offset..], + } + } +} + +impl ClientMemOffset { pub fn access]) -> T>(&self, f: F) -> Result { unsafe { - if self.sigbus_impossible { - return Ok(f(&mut *self.data)); + if self.mem.sigbus_impossible { + return Ok(f(&*self.data)); } MEM.with(|m| { let mref = MemRef { - mem: self, + mem: &*self.mem, outer: *m.get(), }; *m.get() = &mref; compiler_fence(Ordering::SeqCst); - let res = f(&mut *self.data); + let res = f(&*self.data); *m.get() = mref.outer; compiler_fence(Ordering::SeqCst); - match self.failed.get() { + match self.mem.failed.get() { true => Err(ClientMemError::Sigbus), _ => Ok(res), } }) } } - - pub fn len(&self) -> usize { - unsafe { (*self.data).len() } - } } impl Drop for ClientMem { @@ -147,3 +165,14 @@ pub fn init() -> Result<(), ClientMemError> { } } } + +unsafe impl PixmanMemory for ClientMemOffset { + type E = ClientMemError; + + fn access(&self, f: F) -> Result + where + F: FnOnce(&[Cell]) -> T, + { + ClientMemOffset::access(self, f) + } +} diff --git a/src/event_loop.rs b/src/event_loop.rs index a582540d..102ba819 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -30,7 +30,10 @@ pub enum EventLoopError { pub struct EventLoopId(u64); pub trait EventLoopDispatcher { - fn dispatch(&self, events: i32) -> Result<(), Box>; + fn dispatch( + self: Rc, + events: i32, + ) -> Result<(), Box>; } #[derive(Clone)] @@ -155,7 +158,7 @@ impl EventLoop { break; } if let Some(entry) = self.entries.get(&id) { - if let Err(e) = entry.dispatcher.dispatch(0) { + if let Err(e) = entry.dispatcher.clone().dispatch(0) { return Err(EventLoopError::DispatcherError(e)); } } @@ -180,7 +183,7 @@ impl EventLoop { continue; } }; - if let Err(e) = entry.dispatcher.dispatch(event.events as i32) { + if let Err(e) = entry.dispatcher.clone().dispatch(event.events as i32) { return Err(EventLoopError::DispatcherError(e)); } } diff --git a/src/fixed.rs b/src/fixed.rs new file mode 100644 index 00000000..1f905de1 --- /dev/null +++ b/src/fixed.rs @@ -0,0 +1,35 @@ +use std::fmt::{Debug, Formatter}; + +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] +#[repr(transparent)] +pub struct Fixed(pub i32); + +impl Fixed { + pub fn from_1616(i: i32) -> Self { + Self(i >> 8) + } +} + +impl From for Fixed { + fn from(v: f64) -> Self { + Self((v * 256.0) as i32) + } +} + +impl From for f64 { + fn from(v: Fixed) -> Self { + v.0 as f64 / 256.0 + } +} + +impl From for i32 { + fn from(f: Fixed) -> Self { + f.0 >> 8 + } +} + +impl Debug for Fixed { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Debug::fmt(&f64::from(*self), f) + } +} diff --git a/src/format.rs b/src/format.rs index 4d586694..438a233b 100644 --- a/src/format.rs +++ b/src/format.rs @@ -1,3 +1,4 @@ +use crate::pixman; use ahash::AHashMap; #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -5,6 +6,7 @@ pub struct Format { pub id: u32, pub name: &'static str, pub bpp: u32, + pub pixman: pixman::Format, } pub fn formats() -> AHashMap { @@ -24,11 +26,13 @@ static FORMATS: &[Format] = &[ id: 0, name: "argb8888", bpp: 4, + pixman: pixman::A8R8G8B8, }, Format { id: 1, name: "xrgb8888", bpp: 4, + pixman: pixman::X8R8G8B8, }, // Format { // id: fourcc_code('C', '8', ' ', ' '), diff --git a/src/globals.rs b/src/globals.rs index 7a3b2456..efd53fb3 100644 --- a/src/globals.rs +++ b/src/globals.rs @@ -1,6 +1,8 @@ -use crate::client::{Client, ClientError, DynEventFormatter}; +use crate::client::{Client, ClientError, DynEventFormatter, WlEvent}; use crate::ifs::wl_compositor::WlCompositorError; +use crate::ifs::wl_output::WlOutputError; use crate::ifs::wl_registry::WlRegistry; +use crate::ifs::wl_seat::WlSeatError; use crate::ifs::wl_shm::WlShmError; use crate::ifs::wl_subcompositor::WlSubcompositorError; use crate::ifs::xdg_wm_base::XdgWmBaseError; @@ -28,12 +30,18 @@ pub enum GlobalError { WlSubcompositorError(#[source] Box), #[error("An error occurred in a xdg_wm_base")] XdgWmBaseError(#[source] Box), + #[error("An error occurred in a wl_output")] + WlOutputError(#[source] Box), + #[error("An error occurred in a wl_seat")] + WlSeatError(#[source] Box), } efrom!(GlobalError, WlCompositorError, WlCompositorError); efrom!(GlobalError, WlShmError, WlShmError); efrom!(GlobalError, WlSubcompositorError, WlSubcompositorError); efrom!(GlobalError, XdgWmBaseError, XdgWmBaseError); +efrom!(GlobalError, WlOutputError, WlOutputError); +efrom!(GlobalError, WlSeatError, WlSeatError); #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub struct GlobalName(u32); @@ -68,6 +76,7 @@ pub trait Global: GlobalBind { fn interface(&self) -> Interface; fn version(&self) -> u32; fn pre_remove(&self); + fn break_loops(&self) {} } pub struct Globals { @@ -142,6 +151,9 @@ impl Globals { clients_to_check.insert(c.id); } } + if c.event2_locked(WlEvent::Flush) { + clients_to_check.insert(c.id); + } }); for client in clients_to_check.drain() { if let Ok(c) = state.clients.get(client) { diff --git a/src/ifs/wl_buffer/mod.rs b/src/ifs/wl_buffer/mod.rs index e19307c4..1b79c833 100644 --- a/src/ifs/wl_buffer/mod.rs +++ b/src/ifs/wl_buffer/mod.rs @@ -1,13 +1,15 @@ mod types; -use std::cell::RefCell; -use crate::client::{AddObj, Client}; -use crate::clientmem::ClientMem; +use crate::client::{AddObj, Client, DynEventFormatter}; +use crate::clientmem::{ClientMem, ClientMemOffset}; use crate::format::Format; +use crate::ifs::wl_surface::{WlSurface, WlSurfaceId}; use crate::object::{Interface, Object, ObjectId}; +use crate::pixman; +use crate::utils::buffd::MsgParser; +use crate::utils::copyhashmap::CopyHashMap; use std::rc::Rc; pub use types::*; -use crate::utils::buffd::MsgParser; const DESTROY: u32 = 0; @@ -19,11 +21,12 @@ pub struct WlBuffer { id: WlBufferId, client: Rc, offset: usize, - width: u32, - height: u32, + pub width: u32, + pub height: u32, stride: u32, format: &'static Format, - mem: RefCell>>, + pub image: Rc>, + pub(super) surfaces: CopyHashMap>, } impl WlBuffer { @@ -42,10 +45,12 @@ impl WlBuffer { if required > mem.len() as u64 { return Err(WlBufferError::OutOfBounds); } + let mem = mem.offset(offset); let min_row_size = width as u64 * format.bpp as u64; if (stride as u64) < min_row_size { return Err(WlBufferError::StrideTooSmall); } + let image = pixman::Image::new(mem, format.pixman, width, height, stride)?; Ok(Self { id, client: client.clone(), @@ -54,24 +59,38 @@ impl WlBuffer { height, stride, format, - mem: RefCell::new(Some(mem.clone())), + image: Rc::new(image), + surfaces: Default::default(), }) } async fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), DestroyError> { let _req: Destroy = self.client.parse(self, parser)?; - *self.mem.borrow_mut() = None; + { + let surfaces = self.surfaces.lock(); + for surface in surfaces.values() { + *surface.buffer.borrow_mut() = None; + } + } self.client.remove_obj(self).await?; Ok(()) } - async fn handle_request_(&self, request: u32, parser: MsgParser<'_, '_>) -> Result<(), WlBufferError> { + async fn handle_request_( + &self, + request: u32, + parser: MsgParser<'_, '_>, + ) -> Result<(), WlBufferError> { match request { DESTROY => self.destroy(parser).await?, _ => unreachable!(), } Ok(()) } + + pub fn release(self: &Rc) -> DynEventFormatter { + Box::new(Release { obj: self.clone() }) + } } handle_request!(WlBuffer); @@ -88,4 +107,8 @@ impl Object for WlBuffer { fn num_requests(&self) -> u32 { DESTROY + 1 } + + fn break_loops(&self) { + self.surfaces.clear(); + } } diff --git a/src/ifs/wl_buffer/types.rs b/src/ifs/wl_buffer/types.rs index cf7cbf66..bd7739ee 100644 --- a/src/ifs/wl_buffer/types.rs +++ b/src/ifs/wl_buffer/types.rs @@ -1,10 +1,11 @@ +use crate::client::{ClientError, EventFormatter, RequestParser}; +use crate::ifs::wl_buffer::{WlBuffer, RELEASE}; +use crate::object::Object; +use crate::pixman::PixmanError; +use crate::utils::buffd::{MsgFormatter, MsgParser, MsgParserError}; use std::fmt::{Debug, Formatter}; use std::rc::Rc; use thiserror::Error; -use crate::client::{ClientError, EventFormatter, RequestParser}; -use crate::ifs::wl_buffer::{RELEASE, WlBuffer}; -use crate::object::Object; -use crate::utils::buffd::{MsgFormatter, MsgParser, MsgParserError}; #[derive(Debug, Error)] pub enum WlBufferError { @@ -14,7 +15,10 @@ pub enum WlBufferError { StrideTooSmall, #[error("Could not handle a `destroy` request")] DestroyError(#[from] DestroyError), + #[error("Pixman returned an error")] + PixmanError(#[source] Box), } +efrom!(WlBufferError, PixmanError, PixmanError); #[derive(Debug, Error)] pub enum DestroyError { diff --git a/src/ifs/wl_output/mod.rs b/src/ifs/wl_output/mod.rs index 65845678..ea602276 100644 --- a/src/ifs/wl_output/mod.rs +++ b/src/ifs/wl_output/mod.rs @@ -1 +1,223 @@ +mod types; + +use crate::backend::Output; +use crate::client::{AddObj, Client, ClientId, DynEventFormatter, WlEvent}; +use crate::globals::{Global, GlobalName}; +use crate::object::{Interface, Object, ObjectId}; +use crate::utils::buffd::MsgParser; +use crate::utils::copyhashmap::CopyHashMap; +use ahash::AHashMap; +use std::cell::Cell; +use std::iter; +use std::rc::Rc; +pub use types::*; + id!(WlOutputId); + +const RELEASE: u32 = 0; + +const GEOMETRY: u32 = 0; +const MODE: u32 = 1; +const DONE: u32 = 2; +const SCALE: u32 = 3; + +const SP_UNKNOWN: i32 = 0; +const SP_NONE: i32 = 1; +const SP_HORIZONTAL_RGB: i32 = 2; +const SP_HORIZONTAL_BGR: i32 = 3; +const SP_VERTICAL_RGB: i32 = 4; +const SP_VERTICAL_BGR: i32 = 5; + +const TF_NORMAL: i32 = 0; +const TF_90: i32 = 1; +const TF_180: i32 = 2; +const TF_270: i32 = 3; +const TF_FLIPPED: i32 = 4; +const TF_FLIPPED_90: i32 = 5; +const TF_FLIPPED_180: i32 = 6; +const TF_FLIPPED_270: i32 = 7; + +const MODE_CURRENT: u32 = 1; +const MODE_PREFERRED: u32 = 2; + +pub struct WlOutputGlobal { + name: GlobalName, + output: Rc, + width: Cell, + height: Cell, + bindings: CopyHashMap<(ClientId, WlOutputId), Rc>, +} + +impl WlOutputGlobal { + pub fn new(name: GlobalName, output: &Rc) -> Self { + Self { + name, + output: output.clone(), + width: Cell::new(output.width()), + height: Cell::new(output.height()), + bindings: Default::default(), + } + } + + pub async fn update_properties(&self) { + let width = self.output.width(); + let height = self.output.height(); + + let mut changed = false; + changed |= self.width.replace(width) != width; + changed |= self.height.replace(height) != height; + + if changed { + let mut clients = AHashMap::new(); + { + let bindings = self.bindings.lock(); + for binding in bindings.values() { + let events = [ + binding.geometry(), + binding.mode(), + binding.scale(), + binding.done(), + ]; + let events = events + .into_iter() + .map(|e| WlEvent::Event(e)) + .chain(iter::once(WlEvent::Flush)); + for event in events { + if binding.client.event2_locked(event) { + clients.insert(binding.client.id, binding.client.clone()); + } + } + } + } + for client in clients.values() { + let _ = client.check_queue_size().await; + } + } + } + + async fn bind_( + self: Rc, + id: WlOutputId, + client: &Rc, + version: u32, + ) -> Result<(), WlOutputError> { + let obj = Rc::new(WlOutputObj { + global: self.clone(), + id, + client: client.clone(), + }); + client.add_client_obj(&obj)?; + self.bindings.set((client.id, id), obj.clone()); + client.event(obj.geometry()).await?; + client.event(obj.mode()).await?; + client.event(obj.scale()).await?; + client.event(obj.done()).await?; + Ok(()) + } +} + +bind!(WlOutputGlobal); + +impl Global for WlOutputGlobal { + fn name(&self) -> GlobalName { + self.name + } + + fn interface(&self) -> Interface { + Interface::WlOutput + } + + fn version(&self) -> u32 { + 3 + } + + fn pre_remove(&self) { + // + } + + fn break_loops(&self) { + self.bindings.clear(); + } +} + +pub struct WlOutputObj { + global: Rc, + id: WlOutputId, + client: Rc, +} + +impl WlOutputObj { + fn geometry(self: &Rc) -> DynEventFormatter { + Box::new(Geometry { + obj: self.clone(), + x: 0, + y: 0, + physical_width: self.global.width.get() as _, + physical_height: self.global.height.get() as _, + subpixel: SP_UNKNOWN, + make: String::new(), + model: String::new(), + transform: TF_NORMAL, + }) + } + + fn mode(self: &Rc) -> DynEventFormatter { + Box::new(Mode { + obj: self.clone(), + flags: MODE_CURRENT, + width: self.global.width.get() as _, + height: self.global.height.get() as _, + refresh: 60_000_000, + }) + } + + fn scale(self: &Rc) -> DynEventFormatter { + Box::new(Scale { + obj: self.clone(), + factor: 1, + }) + } + + fn done(self: &Rc) -> DynEventFormatter { + Box::new(Done { obj: self.clone() }) + } + + async fn release(&self, parser: MsgParser<'_, '_>) -> Result<(), ReleaseError> { + let _req: Release = self.client.parse(self, parser)?; + self.global.bindings.remove(&(self.client.id, self.id)); + self.client.remove_obj(self).await?; + Ok(()) + } + + async fn handle_request_( + &self, + request: u32, + parser: MsgParser<'_, '_>, + ) -> Result<(), WlOutputError> { + match request { + RELEASE => self.release(parser).await?, + _ => unreachable!(), + } + Ok(()) + } +} + +handle_request!(WlOutputObj); + +impl Object for WlOutputObj { + fn id(&self) -> ObjectId { + self.id.into() + } + + fn interface(&self) -> Interface { + Interface::WlOutput + } + + fn num_requests(&self) -> u32 { + RELEASE + 1 + } + + fn break_loops(&self) { + self.global.bindings.remove(&(self.client.id, self.id)); + } +} diff --git a/src/ifs/wl_output/types.rs b/src/ifs/wl_output/types.rs new file mode 100644 index 00000000..ff2f5dce --- /dev/null +++ b/src/ifs/wl_output/types.rs @@ -0,0 +1,136 @@ +use crate::client::{ClientError, EventFormatter, RequestParser}; +use crate::ifs::wl_output::{WlOutputObj, DONE, GEOMETRY, MODE, SCALE}; +use crate::object::Object; +use crate::utils::buffd::{MsgFormatter, MsgParser, MsgParserError}; +use std::fmt::{Debug, Formatter}; +use std::rc::Rc; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum WlOutputError { + #[error("Could not handle `release` request")] + ReleaseError(#[from] ReleaseError), + #[error(transparent)] + ClientError(Box), +} +efrom!(WlOutputError, ClientError, ClientError); + +#[derive(Debug, Error)] +pub enum ReleaseError { + #[error("Parsing failed")] + ParseError(#[source] Box), + #[error(transparent)] + ClientError(Box), +} +efrom!(ReleaseError, ClientError, ClientError); +efrom!(ReleaseError, ParseError, MsgParserError); + +pub(super) struct Release; +impl RequestParser<'_> for Release { + fn parse(_parser: &mut MsgParser<'_, '_>) -> Result { + Ok(Self) + } +} +impl Debug for Release { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "release()") + } +} + +pub(super) struct Geometry { + pub obj: Rc, + pub x: i32, + pub y: i32, + pub physical_width: i32, + pub physical_height: i32, + pub subpixel: i32, + pub make: String, + pub model: String, + pub transform: i32, +} +impl EventFormatter for Geometry { + fn format(self: Box, fmt: &mut MsgFormatter<'_>) { + fmt.header(self.obj.id, GEOMETRY) + .int(self.x) + .int(self.y) + .int(self.physical_width) + .int(self.physical_height) + .int(self.subpixel) + .string(&self.make) + .string(&self.model) + .int(self.transform); + } + fn obj(&self) -> &dyn Object { + &*self.obj + } +} +impl Debug for Geometry { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "geometry(x: {}, y: {}, physical_width: {}, physical_height: {}, subpixel: {}, make: {}, model: {}, transform: {})", + self.x, self.y, self.physical_width, self.physical_height, self.subpixel, self.make, self.model, self.transform) + } +} + +pub(super) struct Mode { + pub obj: Rc, + pub flags: u32, + pub width: i32, + pub height: i32, + pub refresh: i32, +} +impl EventFormatter for Mode { + fn format(self: Box, fmt: &mut MsgFormatter<'_>) { + fmt.header(self.obj.id, MODE) + .uint(self.flags) + .int(self.width) + .int(self.height) + .int(self.refresh); + } + fn obj(&self) -> &dyn Object { + &*self.obj + } +} +impl Debug for Mode { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "mode(flags: 0x{:x}, width: {}, height: {}, refresh: {})", + self.flags, self.width, self.height, self.refresh + ) + } +} + +pub(super) struct Done { + pub obj: Rc, +} +impl EventFormatter for Done { + fn format(self: Box, fmt: &mut MsgFormatter<'_>) { + fmt.header(self.obj.id, DONE); + } + fn obj(&self) -> &dyn Object { + &*self.obj + } +} +impl Debug for Done { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "done()") + } +} + +pub(super) struct Scale { + pub obj: Rc, + pub factor: i32, +} +impl EventFormatter for Scale { + fn format(self: Box, fmt: &mut MsgFormatter<'_>) { + fmt.header(self.obj.id, SCALE).int(self.factor); + } + fn obj(&self) -> &dyn Object { + &*self.obj + } +} +impl Debug for Scale { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "scale(factor: {})", self.factor) + } +} diff --git a/src/ifs/wl_registry/types.rs b/src/ifs/wl_registry/types.rs index 0c6a448b..75c95398 100644 --- a/src/ifs/wl_registry/types.rs +++ b/src/ifs/wl_registry/types.rs @@ -3,6 +3,7 @@ use crate::globals::{Global, GlobalError, GlobalName}; use crate::ifs::wl_registry::{WlRegistry, GLOBAL, GLOBAL_REMOVE}; use crate::object::{Interface, Object, ObjectId}; use crate::utils::buffd::{MsgFormatter, MsgParser, MsgParserError}; +use bstr::BStr; use std::fmt::{Debug, Formatter}; use std::rc::Rc; use thiserror::Error; @@ -93,7 +94,7 @@ impl Debug for GlobalRemove { pub(super) struct Bind<'a> { pub name: GlobalName, pub id: ObjectId, - pub interface: &'a str, + pub interface: &'a BStr, pub version: u32, } impl<'a> RequestParser<'a> for Bind<'a> { diff --git a/src/ifs/wl_seat/mod.rs b/src/ifs/wl_seat/mod.rs index 5fbd737d..3a095cb4 100644 --- a/src/ifs/wl_seat/mod.rs +++ b/src/ifs/wl_seat/mod.rs @@ -1 +1,121 @@ +mod types; + +use crate::backend::{Seat, SeatEvent}; +use crate::client::{AddObj, Client, ClientId}; +use crate::globals::{Global, GlobalName}; +use crate::object::{Interface, Object, ObjectId}; +use crate::utils::buffd::MsgParser; +use crate::utils::copyhashmap::CopyHashMap; +use std::rc::Rc; +pub use types::*; + id!(WlSeatId); + +const RELEASE: u32 = 0; + +pub struct WlSeatGlobal { + name: GlobalName, + seat: Rc, + bindings: CopyHashMap<(ClientId, WlSeatId), Rc>, +} + +impl WlSeatGlobal { + pub fn new(name: GlobalName, seat: &Rc) -> Self { + Self { + name, + seat: seat.clone(), + bindings: Default::default(), + } + } + + pub async fn event(&self, event: SeatEvent) { + log::debug!("se: {:?}", event); + } + + async fn bind_( + self: Rc, + id: WlSeatId, + client: &Rc, + version: u32, + ) -> Result<(), WlSeatError> { + let obj = Rc::new(WlSeatObj { + global: self.clone(), + id, + client: client.clone(), + }); + client.add_client_obj(&obj)?; + self.bindings.set((client.id, id), obj.clone()); + Ok(()) + } +} + +bind!(WlSeatGlobal); + +impl Global for WlSeatGlobal { + fn name(&self) -> GlobalName { + self.name + } + + fn interface(&self) -> Interface { + Interface::WlSeat + } + + fn version(&self) -> u32 { + 3 + } + + fn pre_remove(&self) { + // + } + + fn break_loops(&self) { + self.bindings.clear(); + } +} + +pub struct WlSeatObj { + global: Rc, + id: WlSeatId, + client: Rc, +} + +impl WlSeatObj { + async fn release(&self, parser: MsgParser<'_, '_>) -> Result<(), ReleaseError> { + let _req: Release = self.client.parse(self, parser)?; + self.global.bindings.remove(&(self.client.id, self.id)); + self.client.remove_obj(self).await?; + Ok(()) + } + + async fn handle_request_( + &self, + request: u32, + parser: MsgParser<'_, '_>, + ) -> Result<(), WlSeatError> { + match request { + RELEASE => self.release(parser).await?, + _ => unreachable!(), + } + Ok(()) + } +} + +handle_request!(WlSeatObj); + +impl Object for WlSeatObj { + fn id(&self) -> ObjectId { + self.id.into() + } + + fn interface(&self) -> Interface { + Interface::WlSeat + } + + fn num_requests(&self) -> u32 { + RELEASE + 1 + } + + fn break_loops(&self) { + self.global.bindings.remove(&(self.client.id, self.id)); + } +} diff --git a/src/ifs/wl_seat/types.rs b/src/ifs/wl_seat/types.rs new file mode 100644 index 00000000..946f8f03 --- /dev/null +++ b/src/ifs/wl_seat/types.rs @@ -0,0 +1,35 @@ +use crate::client::{ClientError, RequestParser}; +use crate::utils::buffd::{MsgParser, MsgParserError}; +use std::fmt::{Debug, Formatter}; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum WlSeatError { + #[error("Could not handle `release` request")] + ReleaseError(#[from] ReleaseError), + #[error(transparent)] + ClientError(Box), +} +efrom!(WlSeatError, ClientError, ClientError); + +#[derive(Debug, Error)] +pub enum ReleaseError { + #[error("Parsing failed")] + ParseError(#[source] Box), + #[error(transparent)] + ClientError(Box), +} +efrom!(ReleaseError, ClientError, ClientError); +efrom!(ReleaseError, ParseError, MsgParserError); + +pub(super) struct Release; +impl RequestParser<'_> for Release { + fn parse(_parser: &mut MsgParser<'_, '_>) -> Result { + Ok(Self) + } +} +impl Debug for Release { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "release()") + } +} diff --git a/src/ifs/wl_surface/mod.rs b/src/ifs/wl_surface/mod.rs index f171829e..75b3ee85 100644 --- a/src/ifs/wl_surface/mod.rs +++ b/src/ifs/wl_surface/mod.rs @@ -3,17 +3,22 @@ pub mod wl_subsurface; pub mod xdg_surface; use crate::client::{AddObj, Client, RequestParser}; +use crate::ifs::wl_buffer::WlBuffer; +use crate::ifs::wl_callback::WlCallback; use crate::ifs::wl_surface::wl_subsurface::WlSubsurface; use crate::ifs::wl_surface::xdg_surface::xdg_popup::XdgPopup; use crate::ifs::wl_surface::xdg_surface::xdg_toplevel::XdgToplevel; use crate::ifs::wl_surface::xdg_surface::XdgSurface; use crate::object::{Interface, Object, ObjectId}; use crate::pixman::Region; +use crate::tree::{NodeBase, NodeCommon, ToplevelNode}; use crate::utils::buffd::{MsgParser, MsgParserError}; use crate::utils::copyhashmap::CopyHashMap; -use crate::utils::linkedlist::{LinkedList, Node}; +use crate::utils::linkedlist::{LinkedList, Node as LinkNode}; use ahash::AHashMap; use std::cell::{Cell, RefCell}; +use std::mem; +use std::ops::{Deref, DerefMut}; use std::rc::Rc; pub use types::*; @@ -56,11 +61,24 @@ impl SurfaceRole { pub struct WlSurface { id: WlSurfaceId, - client: Rc, + pub client: Rc, role: Cell, pending: PendingState, - children: RefCell>>, + input_region: Cell>, + opaque_region: Cell>, + pub extents: Cell, + pub buffer: RefCell>>, + pub children: RefCell>>, role_data: RefCell, + pub frame_requests: RefCell>>, +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)] +pub struct SurfaceExtents { + pub x1: i32, + pub y1: i32, + pub x2: i32, + pub y2: i32, } enum RoleData { @@ -79,11 +97,13 @@ impl RoleData { struct PendingState { opaque_region: Cell>, input_region: Cell>, + frame_request: RefCell>>, } struct XdgSurfaceData { xdg_surface: Rc, - committed: bool, + requested_serial: u32, + acked_serial: Option, role: XdgSurfaceRole, role_data: XdgSurfaceRoleData, popups: CopyHashMap>, @@ -121,6 +141,17 @@ struct XdgPopupData { struct XdgToplevelData { toplevel: Rc, + node: Option, +} + +struct ToplevelNodeHolder { + node: Rc, +} + +impl Drop for ToplevelNodeHolder { + fn drop(&mut self) { + mem::take(&mut *self.node.common.floating_outputs.borrow_mut()); + } } struct SubsurfaceData { @@ -129,27 +160,27 @@ struct SubsurfaceData { y: i32, sync_requested: bool, sync_ancestor: bool, - node: Node, + node: LinkNode, depth: u32, pending: PendingSubsurfaceData, } #[derive(Default)] struct PendingSubsurfaceData { - node: Option>, + node: Option>, position: Option<(i32, i32)>, } #[derive(Default)] -struct ParentData { +pub struct ParentData { subsurfaces: AHashMap>, - below: LinkedList, - above: LinkedList, + pub below: LinkedList, + pub above: LinkedList, } -struct StackElement { +pub struct StackElement { pending: Cell, - surface: Rc, + pub surface: Rc, } impl WlSurface { @@ -159,11 +190,60 @@ impl WlSurface { client: client.clone(), role: Cell::new(SurfaceRole::None), pending: Default::default(), + input_region: Cell::new(None), + opaque_region: Cell::new(None), + extents: Default::default(), + buffer: RefCell::new(None), children: Default::default(), role_data: RefCell::new(RoleData::None), + frame_requests: RefCell::new(vec![]), } } + pub fn subsurface_position(&self) -> Option<(i32, i32)> { + let rd = self.role_data.borrow(); + match rd.deref() { + RoleData::Subsurface(ss) => Some((ss.x, ss.y)), + _ => None, + } + } + + fn calculate_extents(&self) { + { + let mut extents = SurfaceExtents::default(); + if let Some(b) = self.buffer.borrow().deref() { + extents.x2 = b.width as i32; + extents.y2 = b.height as i32; + } + let children = self.children.borrow(); + if let Some(children) = &*children { + for surface in children.subsurfaces.values() { + let rd = surface.role_data.borrow(); + if let RoleData::Subsurface(ss) = &*rd { + let mut ss_extents = surface.extents.get(); + extents.x1 = extents.x1.min(ss_extents.x1 + ss.x); + extents.y1 = extents.y1.min(ss_extents.y1 + ss.y); + extents.x2 = extents.x2.max(ss_extents.x2 + ss.x); + extents.y2 = extents.y2.max(ss_extents.y2 + ss.y); + } + } + } + self.extents.set(extents); + } + let parent = { + let rd = self.role_data.borrow(); + match &*rd { + RoleData::Subsurface(ss) => ss.subsurface.parent.clone(), + _ => return, + } + }; + parent.calculate_extents(); + } + + pub fn is_subsurface(&self) -> bool { + self.role.get() == SurfaceRole::Subsurface + } + pub fn get_root(self: &Rc) -> Rc { let mut root = self.clone(); loop { @@ -197,11 +277,13 @@ impl WlSurface { } *children = None; } + let mut ss_parent = None; { let mut data = self.role_data.borrow_mut(); match &mut *data { RoleData::None => {} RoleData::Subsurface(ss) => { + ss_parent = Some(ss.subsurface.parent.clone()); let mut children = ss.subsurface.parent.children.borrow_mut(); if let Some(children) = &mut *children { children.subsurfaces.remove(&self.id); @@ -229,12 +311,63 @@ impl WlSurface { } *data = RoleData::None; } + { + let buffer = self.buffer.borrow(); + if let Some(buffer) = &*buffer { + buffer.surfaces.remove(&self.id); + } + } + if let Some(parent) = ss_parent { + parent.calculate_extents(); + } self.client.remove_obj(self).await?; Ok(()) } async fn attach(&self, parser: MsgParser<'_, '_>) -> Result<(), AttachError> { let req: Attach = self.parse(parser)?; + { + let mut buffer = self.buffer.borrow_mut(); + if let Some(buffer) = buffer.take() { + self.client.event(buffer.release()).await?; + buffer.surfaces.remove(&self.id); + } + let mut rd = self.role_data.borrow_mut(); + if req.buffer.is_some() { + *buffer = Some(self.client.get_buffer(req.buffer)?); + if let RoleData::XdgSurface(xdg) = &mut *rd { + if let XdgSurfaceRoleData::Toplevel(td) = &mut xdg.role_data { + if td.node.is_none() { + let outputs = self.client.state.root.outputs.lock(); + if let Some(output) = outputs.values().next() { + let node = Rc::new(ToplevelNode { + common: NodeCommon { + extents: Cell::new(Default::default()), + id: self.client.state.node_ids.next(), + parent: Some(output.clone()), + floating_outputs: RefCell::new(Default::default()), + }, + surface: td.toplevel.clone(), + }); + td.node = Some(ToplevelNodeHolder { node: node.clone() }); + let link = output.floating.append(node.clone()); + node.common + .floating_outputs + .borrow_mut() + .insert(output.id(), link); + } + } + } + } + } else { + *buffer = None; + if let RoleData::XdgSurface(xdg) = &mut *rd { + if let XdgSurfaceRoleData::Toplevel(td) = &mut xdg.role_data { + td.node = None; + } + } + } + } Ok(()) } @@ -245,6 +378,9 @@ impl WlSurface { async fn frame(&self, parser: MsgParser<'_, '_>) -> Result<(), FrameError> { let req: Frame = self.parse(parser)?; + let cb = Rc::new(WlCallback::new(req.callback)); + self.client.add_client_obj(&cb)?; + self.pending.frame_request.borrow_mut().push(cb); Ok(()) } @@ -265,8 +401,75 @@ impl WlSurface { Ok(()) } + fn do_commit(&self) { + { + let mut rd = self.role_data.borrow_mut(); + match &mut *rd { + RoleData::None => {} + RoleData::Subsurface(ss) => { + if let Some(v) = ss.pending.node.take() { + v.pending.set(false); + ss.node = v; + } + if let Some((x, y)) = ss.pending.position.take() { + ss.x = x; + ss.y = y; + } + } + RoleData::XdgSurface(xdg) => {} + } + } + { + let mut pfr = self.pending.frame_request.borrow_mut(); + self.frame_requests.borrow_mut().extend(pfr.drain(..)); + } + { + if let Some(region) = self.pending.input_region.take() { + self.input_region.set(Some(region)); + } + if let Some(region) = self.pending.opaque_region.take() { + self.opaque_region.set(Some(region)); + } + } + let mut committed_any_children = false; + { + let children = self.children.borrow(); + if let Some(children) = children.deref() { + for child in children.subsurfaces.values() { + child.do_commit(); + committed_any_children = true; + } + } + } + if !committed_any_children { + self.calculate_extents(); + } + } + async fn commit(&self, parser: MsgParser<'_, '_>) -> Result<(), CommitError> { - let req: Commit = self.parse(parser)?; + let _req: Commit = self.parse(parser)?; + { + let rd = self.role_data.borrow(); + match rd.deref() { + RoleData::Subsurface(ss) => { + if ss.sync_ancestor || ss.sync_requested { + return Ok(()); + } + } + RoleData::XdgSurface(xdg) => { + if xdg.acked_serial != Some(xdg.requested_serial) { + if xdg.acked_serial.is_none() { + self.client + .event(xdg.xdg_surface.configure(xdg.requested_serial)) + .await?; + } + return Ok(()); + } + } + _ => {} + } + } + self.do_commit(); Ok(()) } @@ -328,5 +531,7 @@ impl Object for WlSurface { fn break_loops(&self) { *self.children.borrow_mut() = None; *self.role_data.borrow_mut() = RoleData::None; + mem::take(self.frame_requests.borrow_mut().deref_mut()); + *self.buffer.borrow_mut() = None; } } diff --git a/src/ifs/wl_surface/types.rs b/src/ifs/wl_surface/types.rs index 7f6e829b..4f95d2b6 100644 --- a/src/ifs/wl_surface/types.rs +++ b/src/ifs/wl_surface/types.rs @@ -57,8 +57,11 @@ efrom!(DestroyError, ClientError, ClientError); pub enum AttachError { #[error("Parsing failed")] ParseFailed(#[source] Box), + #[error(transparent)] + ClientError(Box), } efrom!(AttachError, ParseFailed, MsgParserError); +efrom!(AttachError, ClientError, ClientError); #[derive(Debug, Error)] pub enum DamageError { @@ -71,8 +74,11 @@ efrom!(DamageError, ParseFailed, MsgParserError); pub enum FrameError { #[error("Parsing failed")] ParseFailed(#[source] Box), + #[error(transparent)] + ClientError(Box), } efrom!(FrameError, ParseFailed, MsgParserError); +efrom!(FrameError, ClientError, ClientError); #[derive(Debug, Error)] pub enum SetOpaqueRegionError { @@ -98,8 +104,11 @@ efrom!(SetInputRegionError, ClientError, ClientError); pub enum CommitError { #[error("Parsing failed")] ParseFailed(#[source] Box), + #[error(transparent)] + ClientError(Box), } efrom!(CommitError, ParseFailed, MsgParserError); +efrom!(CommitError, ClientError, ClientError); #[derive(Debug, Error)] pub enum SetBufferTransformError { diff --git a/src/ifs/wl_surface/wl_subsurface/mod.rs b/src/ifs/wl_surface/wl_subsurface/mod.rs index 5cc44c50..ca083f7e 100644 --- a/src/ifs/wl_surface/wl_subsurface/mod.rs +++ b/src/ifs/wl_surface/wl_subsurface/mod.rs @@ -142,6 +142,7 @@ impl WlSubsurface { } } self.surface.client.remove_obj(self).await?; + self.parent.calculate_extents(); Ok(()) } diff --git a/src/ifs/wl_surface/xdg_surface/mod.rs b/src/ifs/wl_surface/xdg_surface/mod.rs index b65faf65..57d6dc55 100644 --- a/src/ifs/wl_surface/xdg_surface/mod.rs +++ b/src/ifs/wl_surface/xdg_surface/mod.rs @@ -2,7 +2,7 @@ mod types; pub mod xdg_popup; pub mod xdg_toplevel; -use crate::client::AddObj; +use crate::client::{AddObj, DynEventFormatter}; use crate::ifs::wl_surface::xdg_surface::xdg_popup::XdgPopup; use crate::ifs::wl_surface::xdg_surface::xdg_toplevel::XdgToplevel; use crate::ifs::wl_surface::{ @@ -12,6 +12,7 @@ use crate::ifs::wl_surface::{ use crate::ifs::xdg_wm_base::XdgWmBaseObj; use crate::object::{Interface, Object, ObjectId}; use crate::utils::buffd::MsgParser; +use std::ops::DerefMut; use std::rc::Rc; pub use types::*; @@ -32,7 +33,7 @@ id!(XdgSurfaceId); pub struct XdgSurface { id: XdgSurfaceId, wm_base: Rc, - pub(super) surface: Rc, + pub surface: Rc, version: u32, } @@ -51,6 +52,13 @@ impl XdgSurface { } } + pub fn configure(self: &Rc, serial: u32) -> DynEventFormatter { + Box::new(Configure { + obj: self.clone(), + serial, + }) + } + pub fn install(self: &Rc) -> Result<(), XdgSurfaceError> { let old_role = self.surface.role.get(); if !matches!(old_role, SurfaceRole::None | SurfaceRole::XdgSurface) { @@ -62,7 +70,8 @@ impl XdgSurface { } *data = RoleData::XdgSurface(Box::new(XdgSurfaceData { xdg_surface: self.clone(), - committed: false, + requested_serial: 0, + acked_serial: None, role: XdgSurfaceRole::None, role_data: XdgSurfaceRoleData::None, popups: Default::default(), @@ -119,7 +128,10 @@ impl XdgSurface { data.role = XdgSurfaceRole::Toplevel; let toplevel = Rc::new(XdgToplevel::new(req.id, self, self.version)); self.surface.client.add_client_obj(&toplevel)?; - data.role_data = XdgSurfaceRoleData::Toplevel(XdgToplevelData { toplevel }); + data.role_data = XdgSurfaceRoleData::Toplevel(XdgToplevelData { + toplevel, + node: None, + }); } Ok(()) } @@ -169,7 +181,13 @@ impl XdgSurface { } async fn ack_configure(&self, parser: MsgParser<'_, '_>) -> Result<(), AckConfigureError> { - let _req: AckConfigure = self.surface.client.parse(self, parser)?; + let req: AckConfigure = self.surface.client.parse(self, parser)?; + let mut rd = self.surface.role_data.borrow_mut(); + if let RoleData::XdgSurface(xdg) = rd.deref_mut() { + if xdg.requested_serial == req.serial { + xdg.acked_serial = Some(xdg.requested_serial); + } + } Ok(()) } diff --git a/src/ifs/wl_surface/xdg_surface/xdg_toplevel/mod.rs b/src/ifs/wl_surface/xdg_surface/xdg_toplevel/mod.rs index 20806aac..dbb9b1b3 100644 --- a/src/ifs/wl_surface/xdg_surface/xdg_toplevel/mod.rs +++ b/src/ifs/wl_surface/xdg_surface/xdg_toplevel/mod.rs @@ -52,7 +52,7 @@ id!(XdgToplevelId); pub struct XdgToplevel { id: XdgToplevelId, - surface: Rc, + pub surface: Rc, version: u32, } diff --git a/src/ifs/wl_surface/xdg_surface/xdg_toplevel/types.rs b/src/ifs/wl_surface/xdg_surface/xdg_toplevel/types.rs index d9677810..a2670d00 100644 --- a/src/ifs/wl_surface/xdg_surface/xdg_toplevel/types.rs +++ b/src/ifs/wl_surface/xdg_surface/xdg_toplevel/types.rs @@ -4,6 +4,7 @@ use crate::ifs::wl_seat::WlSeatId; use crate::ifs::wl_surface::xdg_surface::xdg_toplevel::{XdgToplevel, XdgToplevelId, CLOSE}; use crate::object::Object; use crate::utils::buffd::{MsgFormatter, MsgParser, MsgParserError}; +use bstr::BStr; use std::fmt::{Debug, Formatter}; use std::rc::Rc; use thiserror::Error; @@ -209,7 +210,7 @@ impl Debug for SetParent { } pub(super) struct SetTitle<'a> { - pub title: &'a str, + pub title: &'a BStr, } impl<'a> RequestParser<'a> for SetTitle<'a> { fn parse(parser: &mut MsgParser<'_, 'a>) -> Result { @@ -225,7 +226,7 @@ impl<'a> Debug for SetTitle<'a> { } pub(super) struct SetAppId<'a> { - pub app_id: &'a str, + pub app_id: &'a BStr, } impl<'a> RequestParser<'a> for SetAppId<'a> { fn parse(parser: &mut MsgParser<'_, 'a>) -> Result { diff --git a/src/macros.rs b/src/macros.rs index cc2595f4..c91869b6 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -92,3 +92,71 @@ macro_rules! id { } }; } + +macro_rules! linear_ids { + ($ids:ident, $id:ident) => { + pub struct $ids { + next: crate::utils::numcell::NumCell, + } + + impl Default for $ids { + fn default() -> Self { + Self { + next: crate::utils::numcell::NumCell::new(1), + } + } + } + + impl $ids { + pub fn next(&self) -> $id { + $id(self.next.fetch_add(1)) + } + } + + #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] + pub struct $id(u32); + + impl $id { + pub fn raw(&self) -> u32 { + self.0 + } + } + + impl std::fmt::Display for $id { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(&self.0, f) + } + } + }; +} + +macro_rules! cenum { + ($name:ident, $uc:ident; $($name2:ident = $val:expr,)*) => { + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + pub struct $name(pub(super) u32); + + impl $name { + pub fn raw(self) -> u32 { + self.0 + } + } + + pub const $uc: &[u32] = &[$($val,)*]; + + $( + pub const $name2: $name = $name($val); + )* + } +} + +macro_rules! bitor { + ($name:ident) => { + impl std::ops::BitOr for $name { + type Output = Self; + + fn bitor(self, rhs: Self) -> Self::Output { + Self(self.0 | rhs.0) + } + } + }; +} diff --git a/src/main.rs b/src/main.rs index a0486790..e653af2c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,13 @@ -#![feature(generic_associated_types, type_alias_impl_trait)] +#![feature( + generic_associated_types, + type_alias_impl_trait, + never_type, + c_variadic +)] use crate::acceptor::AcceptorError; use crate::async_engine::AsyncError; +use crate::backends::xorg::{XorgBackend, XorgBackendError}; use crate::client::Clients; use crate::clientmem::ClientMemError; use crate::event_loop::EventLoopError; @@ -12,7 +18,9 @@ use crate::ifs::wl_subcompositor::WlSubcompositorGlobal; use crate::ifs::xdg_wm_base::XdgWmBaseGlobal; use crate::sighand::SighandError; use crate::state::State; +use crate::tree::{DisplayNode, NodeIds}; use crate::utils::numcell::NumCell; +use crate::utils::queue::AsyncQueue; use crate::wheel::WheelError; use acceptor::Acceptor; use anyhow::anyhow; @@ -27,6 +35,8 @@ use wheel::Wheel; mod macros; mod acceptor; mod async_engine; +mod backend; +mod backends; mod client; mod clientmem; mod event_loop; @@ -35,11 +45,16 @@ mod globals; mod ifs; mod object; mod pixman; +mod servermem; mod sighand; mod state; +mod tasks; mod time; +mod tree; mod utils; mod wheel; +mod xkbcommon; +mod fixed; fn main() { env_logger::builder() @@ -65,6 +80,8 @@ enum MainError { WheelError(#[from] WheelError), #[error("The async subsystem caused an error")] AsyncError(#[from] AsyncError), + #[error("The xorg backend caused an error")] + XorgBackendError(#[from] XorgBackendError), } fn main_() -> Result<(), MainError> { @@ -78,15 +95,26 @@ fn main_() -> Result<(), MainError> { globals.insert_no_broadcast(Rc::new(WlShmGlobal::new(globals.name()))); globals.insert_no_broadcast(Rc::new(WlSubcompositorGlobal::new(globals.name()))); globals.insert_no_broadcast(Rc::new(XdgWmBaseGlobal::new(globals.name()))); + let node_ids = NodeIds::default(); let state = Rc::new(State { - eng: engine, + eng: engine.clone(), el: el.clone(), + wheel, clients: Clients::new(), next_name: NumCell::new(1), globals, formats: format::formats(), + output_ids: Default::default(), + root: Rc::new(DisplayNode::new(node_ids.next())), + node_ids, + backend_events: AsyncQueue::new(), + output_handlers: Default::default(), + seat_ids: Default::default(), + seat_handlers: Default::default(), }); + let _global_event_handler = engine.spawn(tasks::handle_backend_events(state.clone())); Acceptor::install(&state)?; + let _backend = XorgBackend::new(&state)?; el.run()?; Ok(()) } diff --git a/src/object.rs b/src/object.rs index 2264a369..73af628f 100644 --- a/src/object.rs +++ b/src/object.rs @@ -48,6 +48,7 @@ pub enum Interface { WlDisplay, WlCallback, WlCompositor, + WlOutput, WlRegistry, WlShm, WlShmPool, @@ -61,6 +62,7 @@ pub enum Interface { XdgToplevel, WlRegion, WlBuffer, + WlSeat, } impl Interface { @@ -82,6 +84,8 @@ impl Interface { Interface::XdgPopup => "xdg_popup", Interface::XdgToplevel => "xdg_toplevel", Interface::WlBuffer => "wl_buffer", + Interface::WlOutput => "wl_output", + Interface::WlSeat => "wl_seat", } } } diff --git a/src/pixman.rs b/src/pixman.rs deleted file mode 100644 index e3ee26fc..00000000 --- a/src/pixman.rs +++ /dev/null @@ -1,95 +0,0 @@ -use std::ptr; -use uapi::c; - -#[link(name = "pixman-1")] -extern "C" { - fn pixman_region32_init(region: *mut Region); - fn pixman_region32_init_rect( - region: *mut Region, - x: c::c_int, - y: c::c_int, - width: c::c_uint, - height: c::c_uint, - ); - fn pixman_region32_fini(region: *mut Region); - fn pixman_region32_copy(dst: *mut Region, src: *const Region); - fn pixman_region32_union(dst: *mut Region, a: *const Region, b: *const Region); - fn pixman_region32_subtract(dst: *mut Region, a: *const Region, b: *const Region); -} - -#[repr(C)] -#[derive(Copy, Clone, Default, Debug)] -pub struct Box32 { - x1: i32, - y1: i32, - x2: i32, - y2: i32, -} - -#[repr(C)] -struct RegionData { - size: c::c_long, - num_rects: c::c_long, - // rects: [Box32; size], -} - -#[repr(C)] -pub struct Region { - extents: Box32, - data: *mut RegionData, -} - -impl Region { - pub fn new() -> Self { - let mut slf = Region { - extents: Default::default(), - data: ptr::null_mut(), - }; - unsafe { - pixman_region32_init(&mut slf); - } - slf - } - - pub fn rect(x: i32, y: i32, width: u32, height: u32) -> Self { - let mut new = Region::new(); - unsafe { - pixman_region32_init_rect(&mut new, x as _, y as _, width as _, height as _); - } - new - } - - pub fn add(&self, region: &Self) -> Self { - let mut new = Region::new(); - unsafe { - pixman_region32_union(&mut new, self, region); - } - new - } - - pub fn subtract(&self, region: &Self) -> Self { - let mut new = Region::new(); - unsafe { - pixman_region32_subtract(&mut new, self, region); - } - new - } -} - -impl Clone for Region { - fn clone(&self) -> Self { - let mut new = Region::new(); - unsafe { - pixman_region32_copy(&mut new, self); - } - new - } -} - -impl Drop for Region { - fn drop(&mut self) { - unsafe { - pixman_region32_fini(self); - } - } -} diff --git a/src/pixman/consts.rs b/src/pixman/consts.rs new file mode 100644 index 00000000..11f470be --- /dev/null +++ b/src/pixman/consts.rs @@ -0,0 +1,192 @@ +#![allow(dead_code)] + +pub const fn format(bpp: u32, ty: u32, a: u32, r: u32, g: u32, b: u32) -> u32 { + (bpp << 24) | (ty << 16) | (a << 12) | (r << 8) | (g << 4) | b +} + +pub const fn format_byte(bpp: u32, ty: u32, a: u32, r: u32, g: u32, b: u32) -> u32 { + ((bpp >> 3) << 24) + | (3 << 22) + | (ty << 16) + | ((a >> 3) << 12) + | ((r >> 3) << 8) + | ((g >> 3) << 4) + | (b >> 3) +} + +pub const fn format_reshift(val: u32, ofs: u32, num: u32) -> u32 { + ((val >> (ofs)) & ((1 << (num)) - 1)) << ((val >> 22) & 3) +} + +pub const fn format_bpp(f: u32) -> u32 { + format_reshift(f, 24, 8) +} + +pub const fn format_shift(f: u32) -> u32 { + (f >> 22) & 3 +} + +pub const fn format_type(f: u32) -> u32 { + (f >> 16) & 0x3f +} + +pub const fn format_a(f: u32) -> u32 { + format_reshift(f, 12, 4) +} + +pub const fn format_r(f: u32) -> u32 { + format_reshift(f, 8, 4) +} + +pub const fn format_g(f: u32) -> u32 { + format_reshift(f, 4, 4) +} + +pub const fn format_b(f: u32) -> u32 { + format_reshift(f, 0, 4) +} + +pub const fn format_rgb(f: u32) -> u32 { + f & 0xfff +} + +pub const fn format_vis(f: u32) -> u32 { + f & 0xffff +} + +pub const fn format_depth(f: u32) -> u32 { + format_a(f) + format_r(f) + format_g(f) + format_b(f) +} + +pub const TYPE_OTHER: u32 = 0; +pub const TYPE_A: u32 = 1; +pub const TYPE_ARGB: u32 = 2; +pub const TYPE_ABGR: u32 = 3; +pub const TYPE_COLOR: u32 = 4; +pub const TYPE_GRAY: u32 = 5; +pub const TYPE_YUY2: u32 = 6; +pub const TYPE_YV12: u32 = 7; +pub const TYPE_BGRA: u32 = 8; +pub const TYPE_RGBA: u32 = 9; +pub const TYPE_ARGB_SRGB: u32 = 10; +pub const TYPE_RGBA_FLOAT: u32 = 11; + +pub const fn format_color(f: u32) -> bool { + format_type(f) == TYPE_ARGB + || format_type(f) == TYPE_ABGR + || format_type(f) == TYPE_BGRA + || format_type(f) == TYPE_RGBA + || format_type(f) == TYPE_RGBA_FLOAT +} + +cenum! { + Format, FORMATS; + + RGBA_FLOAT = format_byte(128, TYPE_RGBA_FLOAT, 32, 32, 32, 32), + RGB_FLOAT = format_byte(96, TYPE_RGBA_FLOAT, 0, 32, 32, 32), + A8R8G8B8 = format(32, TYPE_ARGB, 8, 8, 8, 8), + X8R8G8B8 = format(32, TYPE_ARGB, 0, 8, 8, 8), + A8B8G8R8 = format(32, TYPE_ABGR, 8, 8, 8, 8), + X8B8G8R8 = format(32, TYPE_ABGR, 0, 8, 8, 8), + B8G8R8A8 = format(32, TYPE_BGRA, 8, 8, 8, 8), + B8G8R8X8 = format(32, TYPE_BGRA, 0, 8, 8, 8), + R8G8B8A8 = format(32, TYPE_RGBA, 8, 8, 8, 8), + R8G8B8X8 = format(32, TYPE_RGBA, 0, 8, 8, 8), + X14R6G6B6 = format(32, TYPE_ARGB, 0, 6, 6, 6), + X2R10G10B10 = format(32, TYPE_ARGB, 0, 10, 10, 10), + A2R10G10B10 = format(32, TYPE_ARGB, 2, 10, 10, 10), + X2B10G10R10 = format(32, TYPE_ABGR, 0, 10, 10, 10), + A2B10G10R10 = format(32, TYPE_ABGR, 2, 10, 10, 10), + A8R8G8B8_SRGB = format(32, TYPE_ARGB_SRGB, 8, 8, 8, 8), + R8G8B8 = format(24, TYPE_ARGB, 0, 8, 8, 8), + B8G8R8 = format(24, TYPE_ABGR, 0, 8, 8, 8), + R5G6B5 = format(16, TYPE_ARGB, 0, 5, 6, 5), + B5G6R5 = format(16, TYPE_ABGR, 0, 5, 6, 5), + A1R5G5B5 = format(16, TYPE_ARGB, 1, 5, 5, 5), + X1R5G5B5 = format(16, TYPE_ARGB, 0, 5, 5, 5), + A1B5G5R5 = format(16, TYPE_ABGR, 1, 5, 5, 5), + X1B5G5R5 = format(16, TYPE_ABGR, 0, 5, 5, 5), + A4R4G4B4 = format(16, TYPE_ARGB, 4, 4, 4, 4), + X4R4G4B4 = format(16, TYPE_ARGB, 0, 4, 4, 4), + A4B4G4R4 = format(16, TYPE_ABGR, 4, 4, 4, 4), + X4B4G4R4 = format(16, TYPE_ABGR, 0, 4, 4, 4), + A8 = format(8, TYPE_A, 8, 0, 0, 0), + R3G3B2 = format(8, TYPE_ARGB, 0, 3, 3, 2), + B2G3R3 = format(8, TYPE_ABGR, 0, 3, 3, 2), + A2R2G2B2 = format(8, TYPE_ARGB, 2, 2, 2, 2), + A2B2G2R2 = format(8, TYPE_ABGR, 2, 2, 2, 2), + C8 = format(8, TYPE_COLOR, 0, 0, 0, 0), + G8 = format(8, TYPE_GRAY, 0, 0, 0, 0), + X4A4 = format(8, TYPE_A, 4, 0, 0, 0), + X4C4 = format(8, TYPE_COLOR, 0, 0, 0, 0), + X4G4 = format(8, TYPE_GRAY, 0, 0, 0, 0), + A4 = format(4, TYPE_A, 4, 0, 0, 0), + R1G2B1 = format(4, TYPE_ARGB, 0, 1, 2, 1), + B1G2R1 = format(4, TYPE_ABGR, 0, 1, 2, 1), + A1R1G1B1 = format(4, TYPE_ARGB, 1, 1, 1, 1), + A1B1G1R1 = format(4, TYPE_ABGR, 1, 1, 1, 1), + C4 = format(4, TYPE_COLOR, 0, 0, 0, 0), + G4 = format(4, TYPE_GRAY, 0, 0, 0, 0), + A1 = format(1, TYPE_A, 1, 0, 0, 0), + G1 = format(1, TYPE_GRAY, 0, 0, 0, 0), + YUY2 = format(16, TYPE_YUY2, 0, 0, 0, 0), + YV12 = format(12, TYPE_YV12, 0, 0, 0, 0), +} + +cenum! { + Op, OPS; + + OP_CLEAR = 0x00, + OP_SRC = 0x01, + OP_DST = 0x02, + OP_OVER = 0x03, + OP_OVER_REVERSE = 0x04, + OP_IN = 0x05, + OP_IN_REVERSE = 0x06, + OP_OUT = 0x07, + OP_OUT_REVERSE = 0x08, + OP_ATOP = 0x09, + OP_ATOP_REVERSE = 0x0a, + OP_XOR = 0x0b, + OP_ADD = 0x0c, + OP_SATURATE = 0x0d, + OP_DISJOINT_CLEAR = 0x10, + OP_DISJOINT_SRC = 0x11, + OP_DISJOINT_DST = 0x12, + OP_DISJOINT_OVER = 0x13, + OP_DISJOINT_OVER_REVERSE = 0x14, + OP_DISJOINT_IN = 0x15, + OP_DISJOINT_IN_REVERSE = 0x16, + OP_DISJOINT_OUT = 0x17, + OP_DISJOINT_OUT_REVERSE = 0x18, + OP_DISJOINT_ATOP = 0x19, + OP_DISJOINT_ATOP_REVERSE = 0x1a, + OP_DISJOINT_XOR = 0x1b, + OP_CONJOINT_CLEAR = 0x20, + OP_CONJOINT_SRC = 0x21, + OP_CONJOINT_DST = 0x22, + OP_CONJOINT_OVER = 0x23, + OP_CONJOINT_OVER_REVERSE = 0x24, + OP_CONJOINT_IN = 0x25, + OP_CONJOINT_IN_REVERSE = 0x26, + OP_CONJOINT_OUT = 0x27, + OP_CONJOINT_OUT_REVERSE = 0x28, + OP_CONJOINT_ATOP = 0x29, + OP_CONJOINT_ATOP_REVERSE = 0x2a, + OP_CONJOINT_XOR = 0x2b, + OP_MULTIPLY = 0x30, + OP_SCREEN = 0x31, + OP_OVERLAY = 0x32, + OP_DARKEN = 0x33, + OP_LIGHTEN = 0x34, + OP_COLOR_DODGE = 0x35, + OP_COLOR_BURN = 0x36, + OP_HARD_LIGHT = 0x37, + OP_SOFT_LIGHT = 0x38, + OP_DIFFERENCE = 0x39, + OP_EXCLUSION = 0x3a, + OP_HSL_HUE = 0x3b, + OP_HSL_SATURATION = 0x3c, + OP_HSL_COLOR = 0x3d, + OP_HSL_LUMINOSITY = 0x3e, +} diff --git a/src/pixman/mod.rs b/src/pixman/mod.rs new file mode 100644 index 00000000..bf50ec62 --- /dev/null +++ b/src/pixman/mod.rs @@ -0,0 +1,291 @@ +mod consts; + +include!(concat!(env!("OUT_DIR"), "/pixman_tys.rs")); + +use crate::ClientMemError; +pub use consts::*; +use std::cell::Cell; +use std::ptr; +use thiserror::Error; +use uapi::c; + +#[link(name = "pixman-1")] +#[allow(improper_ctypes)] +extern "C" { + fn pixman_region32_init(region: *mut Region); + fn pixman_region32_init_rect( + region: *mut Region, + x: c::c_int, + y: c::c_int, + width: c::c_uint, + height: c::c_uint, + ); + fn pixman_region32_fini(region: *mut Region); + fn pixman_region32_copy(dst: *mut Region, src: *const Region); + fn pixman_region32_union(dst: *mut Region, a: *const Region, b: *const Region); + fn pixman_region32_subtract(dst: *mut Region, a: *const Region, b: *const Region); + fn pixman_image_create_bits_no_clear( + format: PixmanFormat, + width: c::c_int, + height: c::c_int, + bits: *mut u32, + stride: c::c_int, + ) -> *mut PixmanImage; + fn pixman_image_unref(image: *mut PixmanImage) -> c::c_int; + // fn pixman_image_ref(image: *mut PixmanImage) -> *mut PixmanImage; + fn pixman_image_fill_boxes( + op: PixmanOp, + dest: *mut PixmanImage, + color: *const Color, + nboxes: c::c_int, + boxes: *const Box32, + ) -> c::c_int; + fn pixman_image_composite32( + op: PixmanOp, + src: *mut PixmanImage, + mask: *mut PixmanImage, + dest: *mut PixmanImage, + src_x: i32, + src_y: i32, + mask_x: i32, + mask_y: i32, + dest_x: i32, + dest_y: i32, + width: i32, + height: i32, + ); +} + +#[repr(C)] +#[derive(Copy, Clone, Default, Debug)] +pub struct Box32 { + pub x1: i32, + pub y1: i32, + pub x2: i32, + pub y2: i32, +} + +#[repr(C)] +#[derive(Copy, Clone, Default, Debug)] +pub struct Color { + red: u16, + green: u16, + blue: u16, + alpha: u16, +} + +#[repr(C)] +struct RegionData { + size: c::c_long, + num_rects: c::c_long, + // rects: [Box32; size], +} + +#[repr(C)] +pub struct Region { + extents: Box32, + data: *mut RegionData, +} + +impl Region { + pub fn new() -> Self { + let mut slf = Region { + extents: Default::default(), + data: ptr::null_mut(), + }; + unsafe { + pixman_region32_init(&mut slf); + } + slf + } + + pub fn rect(x: i32, y: i32, width: u32, height: u32) -> Self { + let mut new = Region::new(); + unsafe { + pixman_region32_init_rect(&mut new, x as _, y as _, width as _, height as _); + } + new + } + + pub fn add(&self, region: &Self) -> Self { + let mut new = Region::new(); + unsafe { + pixman_region32_union(&mut new, self, region); + } + new + } + + pub fn subtract(&self, region: &Self) -> Self { + let mut new = Region::new(); + unsafe { + pixman_region32_subtract(&mut new, self, region); + } + new + } +} + +impl Clone for Region { + fn clone(&self) -> Self { + let mut new = Region::new(); + unsafe { + pixman_region32_copy(&mut new, self); + } + new + } +} + +impl Drop for Region { + fn drop(&mut self) { + unsafe { + pixman_region32_fini(self); + } + } +} + +pub unsafe trait PixmanMemory: Clone { + type E; + + fn access(&self, f: F) -> Result + where + F: FnOnce(&[Cell]) -> T; +} + +#[derive(Debug, Error)] +pub enum PixmanError { + #[error("The image size values cannot be represented in c_int")] + SizeOverflow, + #[error("The pixman memory does not contain enough memory to hold the image")] + InsufficientMemory, + #[error("The stride does not contain enough bytes to contain a row")] + RowOverflow, + #[error("Pixman images must be aligned at a 4 byte boundary")] + UnalignedMemory, + #[error("Internal pixman error")] + Internal, + #[error(transparent)] + ClientMemError(Box), +} +efrom!(PixmanError, ClientMemError, ClientMemError); + +impl From for PixmanError { + fn from(_: !) -> Self { + unreachable!() + } +} + +struct PixmanImage; + +pub struct Image { + data: *mut PixmanImage, + width: u32, + height: u32, + memory: T, +} + +impl Image +where + PixmanError: From<::E>, +{ + pub fn new( + memory: T, + format: Format, + width: u32, + height: u32, + stride: u32, + ) -> Result { + let data = memory.access(|m| { + if format_bpp(format.raw()) as u64 * width as u64 > stride as u64 * 8 { + return Err(PixmanError::RowOverflow); + } + if (m.len() as u64) < height as u64 * stride as u64 { + return Err(PixmanError::InsufficientMemory); + } + let (width, height, stride) = + match (width.try_into(), height.try_into(), stride.try_into()) { + (Ok(w), Ok(h), Ok(s)) => (w, h, s), + _ => return Err(PixmanError::SizeOverflow), + }; + if m.as_ptr() as usize % 4 != 0 { + return Err(PixmanError::UnalignedMemory); + } + let data = unsafe { + pixman_image_create_bits_no_clear( + format.raw() as _, + width, + height, + m.as_ptr() as _, + stride, + ) + }; + if data.is_null() { + return Err(PixmanError::Internal); + } + Ok(data) + })??; + Ok(Self { + data, + width, + height, + memory, + }) + } + + pub fn fill(&self, r: u8, g: u8, b: u8, a: u8) -> Result<(), PixmanError> { + self.memory.access(|_| { + let bx = Box32 { + x1: 0, + y1: 0, + x2: self.width as _, + y2: self.height as _, + }; + let color = Color { + red: (r as u16) << 8, + green: (g as u16) << 8, + blue: (b as u16) << 8, + alpha: (a as u16) << 8, + }; + unsafe { + pixman_image_fill_boxes(OP_SRC.raw() as PixmanOp, self.data, &color, 1, &bx); + } + })?; + Ok(()) + } + + pub fn add_image(&self, over: &Image, x: i32, y: i32) -> Result<(), PixmanError> + where + U: PixmanMemory, + PixmanError: From<::E>, + { + self.memory.access(|_| { + over.memory.access(|_| unsafe { + pixman_image_composite32( + OP_OVER.raw(), + over.data, + ptr::null_mut(), + self.data, + 0, + 0, + 0, + 0, + x, + y, + over.width as _, + over.height as _, + ); + }) + })??; + Ok(()) + } + + pub fn memory(&self) -> &T { + &self.memory + } +} + +impl Drop for Image { + fn drop(&mut self) { + unsafe { + pixman_image_unref(self.data); + } + } +} diff --git a/src/servermem.rs b/src/servermem.rs new file mode 100644 index 00000000..a631739d --- /dev/null +++ b/src/servermem.rs @@ -0,0 +1,93 @@ +use crate::pixman::PixmanMemory; +use std::cell::Cell; +use std::ptr; +use std::rc::Rc; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::Relaxed; +use thiserror::Error; +use uapi::{c, Errno, OwnedFd}; + +#[derive(Debug, Error)] +pub enum ServerMemError { + #[error("memfd_create failed")] + MemfdCreate(#[source] std::io::Error), + #[error("The provided size does not fit into off_t")] + SizeOverflow, + #[error("ftruncate failed")] + Ftruncate(#[source] std::io::Error), + #[error("mmap failed")] + MmapFailed(#[source] std::io::Error), + #[error("sealing failed")] + Seal(#[source] std::io::Error), +} + +pub struct ServerMem { + fd: OwnedFd, + mem: *const [Cell], +} + +static NEXT_ID: AtomicUsize = AtomicUsize::new(1); + +impl ServerMem { + pub fn new(size: usize) -> Result { + let name = format!("servermem-{}", NEXT_ID.fetch_add(1, Relaxed)); + let fd = match uapi::memfd_create(name, c::MFD_CLOEXEC | c::MFD_ALLOW_SEALING) { + Ok(f) => f, + Err(e) => return Err(ServerMemError::MemfdCreate(e.into())), + }; + let o_size = match size.try_into() { + Ok(s) => s, + _ => return Err(ServerMemError::SizeOverflow), + }; + if let Err(e) = uapi::ftruncate(fd.raw(), o_size) { + return Err(ServerMemError::Ftruncate(e.into())); + } + if let Err(e) = + uapi::fcntl_add_seals(fd.raw(), c::F_SEAL_SHRINK | c::F_SEAL_GROW | c::F_SEAL_SEAL) + { + return Err(ServerMemError::Seal(e.into())); + } + let mem = unsafe { + let res = c::mmap64( + ptr::null_mut(), + size, + c::PROT_READ | c::PROT_WRITE, + c::MAP_SHARED, + fd.raw(), + 0, + ); + if res == c::MAP_FAILED { + return Err(ServerMemError::MmapFailed(Errno::default().into())); + } + std::slice::from_raw_parts(res as *mut Cell, size) + }; + Ok(Self { fd, mem }) + } + + pub fn access]) -> T>(&self, f: F) -> T { + unsafe { f(&*self.mem) } + } + + pub fn fd(&self) -> i32 { + self.fd.raw() + } +} + +impl Drop for ServerMem { + fn drop(&mut self) { + unsafe { + c::munmap(self.mem as *const _ as _, (*self.mem).len()); + } + } +} + +unsafe impl PixmanMemory for Rc { + type E = !; + + fn access(&self, f: F) -> Result + where + F: FnOnce(&[Cell]) -> T, + { + Ok(ServerMem::access(self, f)) + } +} diff --git a/src/sighand.rs b/src/sighand.rs index fb7914c0..b32031be 100644 --- a/src/sighand.rs +++ b/src/sighand.rs @@ -22,6 +22,7 @@ pub enum SighandError { pub fn install(el: &Rc) -> Result<(), SighandError> { let mut set: c::sigset_t = uapi::pod_zeroed(); uapi::sigaddset(&mut set, c::SIGINT).unwrap(); + uapi::sigaddset(&mut set, c::SIGTERM).unwrap(); if let Err(e) = uapi::pthread_sigmask(c::SIG_BLOCK, Some(&set), None) { return Err(SighandError::BlockFailed(e.into())); } @@ -46,7 +47,7 @@ struct Sighand { } impl EventLoopDispatcher for Sighand { - fn dispatch(&self, events: i32) -> Result<(), Box> { + fn dispatch(self: Rc, events: i32) -> Result<(), Box> { if events & (c::EPOLLERR | c::EPOLLHUP) != 0 { return Err(Box::new(SighandError::ErrorEvent)); } @@ -59,10 +60,8 @@ impl EventLoopDispatcher for Sighand { } } log::info!("Received signal {}", sigfd.ssi_signo); - if sigfd.ssi_signo == c::SIGINT as _ { - log::info!("Exiting"); - self.el.stop(); - } + log::info!("Exiting"); + self.el.stop(); } Ok(()) } diff --git a/src/state.rs b/src/state.rs index 58347f3d..bb67c330 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,17 +1,30 @@ -use crate::async_engine::AsyncEngine; +use crate::async_engine::{AsyncEngine, SpawnedFuture}; +use crate::backend::{BackendEvent, OutputId, OutputIds, SeatId, SeatIds}; use crate::client::Clients; use crate::event_loop::EventLoop; use crate::format::Format; use crate::globals::Globals; +use crate::tree::{DisplayNode, NodeIds}; use crate::utils::numcell::NumCell; +use crate::utils::queue::AsyncQueue; +use crate::Wheel; use ahash::AHashMap; +use std::cell::RefCell; use std::rc::Rc; pub struct State { pub eng: Rc, pub el: Rc, + pub wheel: Rc, pub clients: Clients, pub next_name: NumCell, pub globals: Globals, pub formats: AHashMap, + pub output_ids: OutputIds, + pub seat_ids: SeatIds, + pub node_ids: NodeIds, + pub root: Rc, + pub backend_events: AsyncQueue, + pub output_handlers: RefCell>>, + pub seat_handlers: RefCell>>, } diff --git a/src/tasks.rs b/src/tasks.rs new file mode 100644 index 00000000..967734c3 --- /dev/null +++ b/src/tasks.rs @@ -0,0 +1,138 @@ +use crate::backend::{BackendEvent, Output, Seat}; +use crate::ifs::wl_output::WlOutputGlobal; +use crate::ifs::wl_seat::WlSeatGlobal; +use crate::tree::{NodeCommon, NodeExtents, OutputNode}; +use crate::utils::asyncevent::AsyncEvent; +use crate::State; +use std::cell::{Cell, RefCell}; +use std::rc::Rc; + +pub async fn handle_backend_events(state: Rc) { + let mut beh = BackendEventHandler { state }; + beh.handle_events().await; +} + +struct BackendEventHandler { + state: Rc, +} + +impl BackendEventHandler { + async fn handle_events(&mut self) { + loop { + let event = self.state.backend_events.pop().await; + self.handle_event(event).await; + } + } + + async fn handle_event(&mut self, event: BackendEvent) { + match event { + BackendEvent::NewOutput(output) => self.handle_new_output(output).await, + BackendEvent::NewSeat(seat) => self.handle_new_seat(seat).await, + } + } + + async fn handle_new_output(&mut self, output: Rc) { + let id = output.id(); + let oh = OutputHandler { + state: self.state.clone(), + output, + }; + let future = self.state.eng.spawn(oh.handle()); + self.state.output_handlers.borrow_mut().insert(id, future); + } + + async fn handle_new_seat(&mut self, seat: Rc) { + let id = seat.id(); + let oh = SeatHandler { + state: self.state.clone(), + seat, + }; + let future = self.state.eng.spawn(oh.handle()); + self.state.seat_handlers.borrow_mut().insert(id, future); + } +} + +struct OutputHandler { + state: Rc, + output: Rc, +} + +impl OutputHandler { + async fn handle(self) { + let ae = Rc::new(AsyncEvent::default()); + { + let ae = ae.clone(); + self.output.on_change(Rc::new(move || ae.trigger())); + } + let on = Rc::new(OutputNode { + common: NodeCommon { + extents: Cell::new(NodeExtents { + x: 0, + y: 0, + width: self.output.width(), + height: self.output.height(), + }), + id: self.state.node_ids.next(), + parent: Some(self.state.root.clone()), + floating_outputs: RefCell::new(Default::default()), + }, + backend: self.output.clone(), + child: RefCell::new(None), + floating: Default::default(), + }); + self.state.root.outputs.set(self.output.id(), on.clone()); + let name = self.state.globals.name(); + let global = Rc::new(WlOutputGlobal::new(name, &self.output)); + self.state.globals.insert(&self.state, global.clone()).await; + loop { + if self.output.removed() { + break; + } + on.common.extents.set(NodeExtents { + x: 0, + y: 0, + width: self.output.width(), + height: self.output.height(), + }); + global.update_properties().await; + ae.triggered().await; + } + self.state.globals.remove(&self.state, name).await; + self.state + .output_handlers + .borrow_mut() + .remove(&self.output.id()); + } +} + +struct SeatHandler { + state: Rc, + seat: Rc, +} + +impl SeatHandler { + async fn handle(self) { + let ae = Rc::new(AsyncEvent::default()); + { + let ae = ae.clone(); + self.seat.on_change(Rc::new(move || ae.trigger())); + } + let name = self.state.globals.name(); + let global = Rc::new(WlSeatGlobal::new(name, &self.seat)); + self.state.globals.insert(&self.state, global.clone()).await; + loop { + if self.seat.removed() { + break; + } + while let Some(event) = self.seat.event() { + global.event(event).await; + } + ae.triggered().await; + } + self.state.globals.remove(&self.state, name).await; + self.state + .seat_handlers + .borrow_mut() + .remove(&self.seat.id()); + } +} diff --git a/src/tree.rs b/src/tree.rs new file mode 100644 index 00000000..9d85e7b3 --- /dev/null +++ b/src/tree.rs @@ -0,0 +1,171 @@ +use crate::backend::{Output, OutputId}; +use crate::ifs::wl_surface::xdg_surface::xdg_toplevel::XdgToplevel; +use crate::utils::copyhashmap::CopyHashMap; +use crate::utils::linkedlist::{LinkedList, Node as LinkedNode}; +use ahash::AHashMap; +use std::cell::{Cell, RefCell}; +use std::mem; +use std::rc::Rc; + +linear_ids!(NodeIds, NodeId); + +pub trait NodeBase { + fn id(&self) -> NodeId; + fn parent(&self) -> Option>; + fn extents(&self) -> NodeExtents; +} + +macro_rules! base { + ($name:ident) => { + impl NodeBase for $name { + fn id(&self) -> NodeId { + self.common.id + } + + fn parent(&self) -> Option> { + self.common.parent.clone() + } + + fn extents(&self) -> NodeExtents { + self.common.extents.get() + } + } + }; +} + +pub trait Node: NodeBase { + fn into_kind(self: Rc) -> NodeKind; + fn clear(&self); +} + +pub enum NodeKind { + Display(Rc), + Output(Rc), + Toplevel(Rc), + Container(Rc), +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)] +pub struct NodeExtents { + pub x: i32, + pub y: i32, + pub width: u32, + pub height: u32, +} + +pub struct NodeCommon { + pub extents: Cell, + pub id: NodeId, + pub parent: Option>, + pub floating_outputs: RefCell>>>, +} + +impl NodeCommon { + fn clear(&self) { + mem::take(&mut *self.floating_outputs.borrow_mut()); + } +} + +pub struct DisplayNode { + pub common: NodeCommon, + pub outputs: CopyHashMap>, +} + +impl DisplayNode { + pub fn new(id: NodeId) -> Self { + Self { + common: NodeCommon { + extents: Default::default(), + id, + parent: None, + floating_outputs: Default::default(), + }, + outputs: Default::default(), + } + } +} + +base!(DisplayNode); + +impl Node for DisplayNode { + fn into_kind(self: Rc) -> NodeKind { + NodeKind::Display(self) + } + + fn clear(&self) { + self.common.clear(); + let mut outputs = self.outputs.lock(); + for output in outputs.values() { + output.clear(); + } + outputs.clear(); + } +} + +pub struct OutputNode { + pub common: NodeCommon, + pub backend: Rc, + pub child: RefCell>>, + pub floating: LinkedList>, +} + +base!(OutputNode); + +impl Node for OutputNode { + fn into_kind(self: Rc) -> NodeKind { + NodeKind::Output(self) + } + + fn clear(&self) { + self.common.clear(); + for floating in self.floating.iter() { + floating.clear(); + } + if let Some(child) = self.child.borrow_mut().take() { + child.clear(); + } + } +} + +pub struct ToplevelNode { + pub common: NodeCommon, + pub surface: Rc, +} + +base!(ToplevelNode); + +impl Node for ToplevelNode { + fn into_kind(self: Rc) -> NodeKind { + NodeKind::Toplevel(self) + } + + fn clear(&self) { + self.common.clear(); + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum ContainerSplit { + Horizontal, + Vertical, +} + +pub struct ContainerNode { + pub common: NodeCommon, + pub split: Cell, + pub children: LinkedList>, +} + +base!(ContainerNode); + +impl Node for ContainerNode { + fn into_kind(self: Rc) -> NodeKind { + NodeKind::Container(self) + } + + fn clear(&self) { + for child in self.children.iter() { + child.clear(); + } + } +} diff --git a/src/utils/asyncevent.rs b/src/utils/asyncevent.rs new file mode 100644 index 00000000..53e31589 --- /dev/null +++ b/src/utils/asyncevent.rs @@ -0,0 +1,41 @@ +use crate::NumCell; +use std::cell::Cell; +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll, Waker}; + +#[derive(Default)] +pub struct AsyncEvent { + triggers: NumCell, + waker: Cell>, +} + +impl AsyncEvent { + pub fn trigger(&self) { + self.triggers.fetch_add(1); + if let Some(waker) = self.waker.take() { + waker.wake(); + } + } + + pub fn triggered(&self) -> AsyncEventTriggered { + AsyncEventTriggered { ae: self } + } +} + +pub struct AsyncEventTriggered<'a> { + ae: &'a AsyncEvent, +} + +impl<'a> Future for AsyncEventTriggered<'a> { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if self.ae.triggers.replace(0) == 0 { + self.ae.waker.set(Some(cx.waker().clone())); + Poll::Pending + } else { + Poll::Ready(()) + } + } +} diff --git a/src/utils/buffd/formatter.rs b/src/utils/buffd/formatter.rs index bb6e0732..cd201838 100644 --- a/src/utils/buffd/formatter.rs +++ b/src/utils/buffd/formatter.rs @@ -3,6 +3,7 @@ use crate::utils::buffd::buf_out::{BufFdOut, MsgFds}; use std::mem; use std::mem::MaybeUninit; use uapi::OwnedFd; +use crate::fixed::Fixed; pub struct MsgFormatter<'a> { buf: &'a mut BufFdOut, @@ -29,17 +30,17 @@ impl<'a> MsgFormatter<'a> { self } - pub fn fixed(&mut self, fixed: f64) -> &mut Self { - let int = (fixed * 256.0) as i32; - self.buf.write(uapi::as_maybe_uninit_bytes(&int)); + pub fn fixed(&mut self, fixed: Fixed) -> &mut Self { + self.buf.write(uapi::as_maybe_uninit_bytes(&fixed)); self } - pub fn string(&mut self, s: &str) -> &mut Self { + pub fn string + ?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.buf.write(uapi::as_maybe_uninit_bytes(s.as_bytes())); + self.buf.write(uapi::as_maybe_uninit_bytes(s)); let none = [MaybeUninit::new(0); 4]; self.buf.write(&none[..cap - len + 1]); self diff --git a/src/utils/buffd/parser.rs b/src/utils/buffd/parser.rs index 1abe5ce8..e7aafa81 100644 --- a/src/utils/buffd/parser.rs +++ b/src/utils/buffd/parser.rs @@ -1,15 +1,15 @@ use crate::globals::GlobalName; use crate::object::ObjectId; use crate::utils::buffd::BufFdIn; +use bstr::{BStr, ByteSlice}; use thiserror::Error; use uapi::OwnedFd; +use crate::fixed::Fixed; #[derive(Debug, Error)] pub enum MsgParserError { #[error("The message ended unexpectedly")] UnexpectedEof, - #[error("The message contained a non-utf8 string")] - NonUtf8, #[error("The message contained a string of size 0")] EmptyString, #[error("Message is missing a required file descriptor")] @@ -57,11 +57,11 @@ impl<'a, 'b> MsgParser<'a, 'b> { self.int().map(|i| GlobalName::from_raw(i as u32)) } - pub fn fixed(&mut self) -> Result { - self.int().map(|i| i as f64 / 256.0) + pub fn fixed(&mut self) -> Result { + self.int().map(|i| Fixed(i)) } - pub fn string(&mut self) -> Result<&'b str, MsgParserError> { + pub fn string(&mut self) -> Result<&'b BStr, MsgParserError> { let len = self.uint()? as usize; if len == 0 { return Err(MsgParserError::EmptyString); @@ -71,10 +71,7 @@ impl<'a, 'b> MsgParser<'a, 'b> { return Err(MsgParserError::UnexpectedEof); } let s = &self.data[self.pos..self.pos + len - 1]; - let s = match std::str::from_utf8(s) { - Ok(s) => s, - _ => return Err(MsgParserError::NonUtf8), - }; + let s = s.as_bstr(); self.pos += cap; Ok(s) } diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 73e64567..059ff96e 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,3 +1,4 @@ +pub mod asyncevent; pub mod buffd; pub mod copyhashmap; pub mod linkedlist; diff --git a/src/utils/numcell.rs b/src/utils/numcell.rs index b981b938..1c2007c1 100644 --- a/src/utils/numcell.rs +++ b/src/utils/numcell.rs @@ -11,6 +11,10 @@ impl NumCell { Self { t: Cell::new(t) } } + pub fn replace(&self, n: T) -> T { + self.t.replace(n) + } + pub fn load(&self) -> T where T: Copy, diff --git a/src/utils/ptr_ext.rs b/src/utils/ptr_ext.rs index 133b90cf..bf098523 100644 --- a/src/utils/ptr_ext.rs +++ b/src/utils/ptr_ext.rs @@ -1,24 +1,24 @@ -pub trait PtrExt { +pub trait PtrExt { unsafe fn deref<'a>(self) -> &'a T; } -pub trait MutPtrExt { +pub trait MutPtrExt { unsafe fn deref_mut<'a>(self) -> &'a mut T; } -impl PtrExt for *const T { +impl PtrExt for *const T { unsafe fn deref<'a>(self) -> &'a T { &*self } } -impl PtrExt for *mut T { +impl PtrExt for *mut T { unsafe fn deref<'a>(self) -> &'a T { &*self } } -impl MutPtrExt for *mut T { +impl MutPtrExt for *mut T { unsafe fn deref_mut<'a>(self) -> &'a mut T { &mut *self } diff --git a/src/utils/queue.rs b/src/utils/queue.rs index 7edf8b11..4c8be576 100644 --- a/src/utils/queue.rs +++ b/src/utils/queue.rs @@ -1,6 +1,7 @@ use std::cell::RefCell; use std::collections::VecDeque; use std::future::Future; +use std::mem; use std::pin::Pin; use std::task::{Context, Poll, Waker}; @@ -35,6 +36,10 @@ impl AsyncQueue { pub fn size(&self) -> usize { self.data.borrow().len() } + + pub fn clear(&self) { + mem::take(&mut *self.data.borrow_mut()); + } } pub struct AsyncQueuePop<'a, T> { diff --git a/src/wheel.rs b/src/wheel.rs index 838c4243..504d4fa4 100644 --- a/src/wheel.rs +++ b/src/wheel.rs @@ -1,10 +1,11 @@ -use crate::event_loop::{EventLoop, EventLoopDispatcher, EventLoopError}; +use crate::event_loop::{EventLoop, EventLoopDispatcher, EventLoopError, EventLoopId}; use crate::time::{Time, TimeError}; use crate::utils::copyhashmap::CopyHashMap; use crate::utils::numcell::NumCell; use std::cell::{Cell, RefCell}; use std::cmp::Reverse; use std::collections::BinaryHeap; +use std::error::Error; use std::rc::Rc; use std::time::Duration; use thiserror::Error; @@ -33,10 +34,10 @@ pub trait WheelDispatcher { #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] struct WheelEntry { expiration: Time, - id: u64, + id: WheelId, } -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct WheelId(u64); pub struct Wheel { @@ -45,8 +46,11 @@ pub struct Wheel { next_id: NumCell, start: Time, current_expiration: Cell>, - dispatchers: CopyHashMap>, + dispatchers: CopyHashMap>, + periodic_dispatchers: CopyHashMap>, expirations: RefCell>>, + id: EventLoopId, + el: Rc, } impl Wheel { @@ -63,7 +67,10 @@ impl Wheel { start: Time::now()?, current_expiration: Cell::new(None), dispatchers: CopyHashMap::new(), + periodic_dispatchers: Default::default(), expirations: RefCell::new(Default::default()), + id, + el: el.clone(), }); let wrapper = Rc::new(WheelWrapper { wheel: wheel.clone(), @@ -106,17 +113,56 @@ impl Wheel { } self.current_expiration.set(Some(expiration)); } - self.expirations.borrow_mut().push(Reverse(WheelEntry { - expiration, - id: id.0, - })); - self.dispatchers.set(id.0, dispatcher); + self.expirations + .borrow_mut() + .push(Reverse(WheelEntry { expiration, id })); + self.dispatchers.set(id, dispatcher); + Ok(()) + } + + pub fn periodic( + &self, + id: WheelId, + us: u64, + dispatcher: Rc, + ) -> Result<(), WheelError> { + self.check_destroyed()?; + let fd = match uapi::timerfd_create(c::CLOCK_MONOTONIC, c::TFD_CLOEXEC | c::TFD_NONBLOCK) { + Ok(fd) => fd, + Err(e) => return Err(WheelError::CreateFailed(e.into())), + }; + let tv_sec = (us / 1_000_000) as _; + let tv_nsec = (us % 1_000_000 * 1_000) as _; + let res = uapi::timerfd_settime( + fd.raw(), + 0, + &c::itimerspec { + it_interval: c::timespec { tv_sec, tv_nsec }, + it_value: c::timespec { tv_sec, tv_nsec }, + }, + ); + if let Err(e) = res { + return Err(WheelError::SetFailed(e.into())); + } + let el_id = self.el.id(); + let pd = Rc::new(PeriodicDispatcher { + fd: fd, + id: el_id, + el: self.el.clone(), + dispatcher, + }); + self.el + .insert(el_id, Some(pd.fd.raw()), c::EPOLLIN, pd.clone())?; + self.periodic_dispatchers.set(id, pd); Ok(()) } pub fn remove(&self, id: WheelId) { // log::trace!("removing {:?} from wheel", id); - self.dispatchers.remove(&id.0); + self.dispatchers.remove(&id); + if let Some(d) = self.periodic_dispatchers.remove(&id) { + let _ = self.el.remove(d.id); + } } } @@ -125,7 +171,10 @@ struct WheelWrapper { } impl EventLoopDispatcher for WheelWrapper { - fn dispatch(&self, events: i32) -> Result<(), Box> { + fn dispatch( + self: Rc, + events: i32, + ) -> Result<(), Box> { if events & (c::EPOLLERR | c::EPOLLHUP) != 0 { return Err(Box::new(WheelError::ErrorEvent)); } @@ -179,5 +228,30 @@ impl Drop for WheelWrapper { fn drop(&mut self) { self.wheel.destroyed.set(true); self.wheel.dispatchers.clear(); + let _ = self.wheel.el.remove(self.wheel.id); + } +} + +struct PeriodicDispatcher { + fd: OwnedFd, + id: EventLoopId, + el: Rc, + dispatcher: Rc, +} + +impl EventLoopDispatcher for PeriodicDispatcher { + fn dispatch(self: Rc, events: i32) -> Result<(), Box> { + if events & (c::EPOLLERR | c::EPOLLHUP) != 0 { + return Err(Box::new(WheelError::ErrorEvent)); + } + let mut n = 0u64; + while uapi::read(self.fd.raw(), &mut n).is_ok() {} + self.dispatcher.clone().dispatch() + } +} + +impl Drop for PeriodicDispatcher { + fn drop(&mut self) { + let _ = self.el.remove(self.id); } } diff --git a/src/xkbcommon/consts.rs b/src/xkbcommon/consts.rs new file mode 100644 index 00000000..6114fdcf --- /dev/null +++ b/src/xkbcommon/consts.rs @@ -0,0 +1,43 @@ +#![allow(dead_code)] + +cenum! { + XkbX11SetupXkbExtensionFlags, XKB_X11_SETUP_XKB_EXTENSION_FLAGS; + + XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS = 0, +} + +bitor!(XkbX11SetupXkbExtensionFlags); + +cenum! { + XkbLogLevel, XKB_LOG_LEVEL; + + XKB_LOG_LEVEL_CRITICAL = 10, + XKB_LOG_LEVEL_ERROR = 20, + XKB_LOG_LEVEL_WARNING = 30, + XKB_LOG_LEVEL_INFO = 40, + XKB_LOG_LEVEL_DEBUG = 50, +} + +cenum! { + XkbContextFlags, XKB_CONTEXT_FLAGS; + + XKB_CONTEXT_NO_FLAGS = 0, + XKB_CONTEXT_NO_DEFAULT_INCLUDES = 1 << 0, + XKB_CONTEXT_NO_ENVIRONMENT_NAMES = 1 << 1, +} + +bitor!(XkbContextFlags); + +cenum! { + XkbKeymapCompileFlags, XKB_KEYMAP_COMPILE_FLAGS; + + XKB_KEYMAP_COMPILE_NO_FLAGS = 0, +} + +bitor!(XkbKeymapCompileFlags); + +cenum! { + XkbKeymapFormat, XKB_KEYMAP_FORMAT; + + XKB_KEYMAP_FORMAT_TEXT_V1 = 1, +} diff --git a/src/xkbcommon/mod.rs b/src/xkbcommon/mod.rs new file mode 100644 index 00000000..5d67e434 --- /dev/null +++ b/src/xkbcommon/mod.rs @@ -0,0 +1,249 @@ +#![allow(non_camel_case_types)] + +mod consts; + +include!(concat!(env!("OUT_DIR"), "/xkbcommon_tys.rs")); + +use bstr::{BStr, ByteSlice}; +pub use consts::*; +use std::ffi::{CStr, VaList}; +use std::ops::Deref; +use std::ptr; + +use crate::utils::ptr_ext::PtrExt; +use libloading::Library; +use thiserror::Error; +use uapi::c; +use xcb_dl::ffi::xcb_connection_t; + +#[derive(Debug, Error)] +pub enum XkbCommonError { + #[error("xkbcommon-x11 could not be loaded")] + LoadXkbCommonX11(#[source] libloading::Error), + #[error("One of the xkbcommon-x11 symbols could not be loaded")] + LoadXkbCommonX11Sym(#[source] libloading::Error), + #[error("Could not create keymap from X11 device")] + CreateKeymapFromDevice, + #[error("Could not create state from X11 device")] + CreateStateFromDevice, + #[error("Could not create an xkbcommon context")] + CreateContext, + #[error("Could not convert the keymap to a string")] + AsStr, +} + +struct xkb_context; +struct xkb_keymap; +struct xkb_state; + +#[link(name = "xkbcommon")] +extern "C" { + fn xkb_context_new(flags: xkb_context_flags) -> *mut xkb_context; + fn xkb_context_unref(context: *mut xkb_context); + fn xkb_context_set_log_fn( + context: *mut xkb_context, + log_fn: unsafe extern "C" fn( + context: *mut xkb_context, + level: xkb_log_level, + format: *const c::c_char, + args: VaList, + ), + ); + fn xkb_keymap_get_as_string( + keymap: *mut xkb_keymap, + format: xkb_keymap_format, + ) -> *mut c::c_char; + fn xkb_keymap_unref(keymap: *mut xkb_keymap); + fn xkb_state_unref(state: *mut xkb_state); +} + +pub struct XkbContext { + context: *mut xkb_context, +} + +impl XkbContext { + pub fn new() -> Result { + let res = unsafe { xkb_context_new(XKB_CONTEXT_NO_FLAGS.raw() as _) }; + if res.is_null() { + return Err(XkbCommonError::CreateContext); + } + unsafe { + xkb_context_set_log_fn(res, xkbcommon_logger); + } + Ok(Self { context: res }) + } +} + +impl Drop for XkbContext { + fn drop(&mut self) { + unsafe { + xkb_context_unref(self.context); + } + } +} + +pub struct XkbKeymap { + keymap: *mut xkb_keymap, +} + +impl XkbKeymap { + pub fn as_str(&self) -> Result { + let res = + unsafe { xkb_keymap_get_as_string(self.keymap, XKB_KEYMAP_FORMAT_TEXT_V1.raw() as _) }; + if res.is_null() { + return Err(XkbCommonError::AsStr); + } + Ok(XkbKeymapStr { + s: unsafe { CStr::from_ptr(res).to_bytes().as_bstr() }, + }) + } +} + +impl Drop for XkbKeymap { + fn drop(&mut self) { + unsafe { + xkb_keymap_unref(self.keymap); + } + } +} + +pub struct XkbKeymapStr { + s: *const BStr, +} + +impl Deref for XkbKeymapStr { + type Target = BStr; + + fn deref(&self) -> &Self::Target { + unsafe { self.s.deref() } + } +} + +impl Drop for XkbKeymapStr { + fn drop(&mut self) { + unsafe { c::free(self.s as _) } + } +} + +pub struct XkbState { + state: *mut xkb_state, +} + +impl Drop for XkbState { + fn drop(&mut self) { + unsafe { + xkb_state_unref(self.state); + } + } +} + +pub struct XkbCommonX11 { + library: Library, + fns: XkbCommonX11Fns, +} + +struct XkbCommonX11Fns { + xkb_x11_keymap_new_from_device: unsafe fn( + context: *mut xkb_context, + c: *mut xcb_connection_t, + device_id: i32, + flags: xkb_x11_setup_xkb_extension_flags, + ) -> *mut xkb_keymap, + xkb_x11_state_new_from_device: unsafe fn( + keymap: *mut xkb_keymap, + c: *mut xcb_connection_t, + device_id: i32, + ) -> *mut xkb_state, +} + +impl XkbCommonX11 { + pub fn load() -> Result { + let library = unsafe { + match Library::new("libxkbcommon-x11.so") { + Ok(l) => l, + Err(e) => return Err(XkbCommonError::LoadXkbCommonX11(e)), + } + }; + let fns = match get_xkbcommon_x11_fns(&library) { + Ok(f) => f, + Err(e) => return Err(XkbCommonError::LoadXkbCommonX11Sym(e)), + }; + Ok(Self { library, fns }) + } + + pub unsafe fn keymap_from_device( + &self, + context: &XkbContext, + c: *mut xcb_connection_t, + device_id: i32, + flags: XkbX11SetupXkbExtensionFlags, + ) -> Result { + let res = (self.fns.xkb_x11_keymap_new_from_device)( + context.context, + c, + device_id, + flags.raw() as _, + ); + if res.is_null() { + return Err(XkbCommonError::CreateKeymapFromDevice); + } + Ok(XkbKeymap { keymap: res }) + } + + pub unsafe fn state_from_device( + &self, + keymap: &XkbKeymap, + c: *mut xcb_connection_t, + device_id: i32, + ) -> Result { + let res = (self.fns.xkb_x11_state_new_from_device)(keymap.keymap, c, device_id); + if res.is_null() { + return Err(XkbCommonError::CreateStateFromDevice); + } + Ok(XkbState { state: res }) + } +} + +fn get_xkbcommon_x11_fns(lib: &Library) -> Result { + macro_rules! syms { + ($($sym:ident,)*) => { + Ok(XkbCommonX11Fns { + $( + $sym: std::mem::transmute(lib.get::(concat!(stringify!($sym), "\0").as_bytes())?.into_raw().into_raw()), + )* + }) + } + } + unsafe { + syms! { + xkb_x11_keymap_new_from_device, + xkb_x11_state_new_from_device, + } + } +} + +unsafe extern "C" fn xkbcommon_logger( + _ctx: *mut xkb_context, + level: xkb_log_level, + format: *const c::c_char, + args: VaList, +) { + extern "C" { + fn vasprintf(buf: *mut *mut c::c_char, fmt: *const c::c_char, args: VaList) -> c::c_int; + } + let mut buf = ptr::null_mut(); + let res = vasprintf(&mut buf, format, args); + if res < 0 { + log::warn!("Could not vasprintf"); + } + let buf = std::slice::from_raw_parts(buf as *const u8, res as usize); + let buf = buf.as_bstr(); + let level = match XkbLogLevel(level) { + XKB_LOG_LEVEL_CRITICAL | XKB_LOG_LEVEL_ERROR => log::Level::Error, + XKB_LOG_LEVEL_WARNING => log::Level::Warn, + XKB_LOG_LEVEL_INFO => log::Level::Info, + XKB_LOG_LEVEL_DEBUG => log::Level::Debug, + _ => log::Level::Error, + }; + log::log!(level, "xkbcommon: {}", buf); +}