diff --git a/c/bridge.c b/c/bridge.c deleted file mode 100644 index daf254da..00000000 --- a/c/bridge.c +++ /dev/null @@ -1,19 +0,0 @@ -#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/backend.rs b/src/backend.rs index 44eb0e71..e5fb9901 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -1,6 +1,5 @@ use crate::fixed::Fixed; use std::rc::Rc; -use crate::xkbcommon::XkbKeymapStr; linear_ids!(OutputIds, OutputId); linear_ids!(SeatIds, SeatId); @@ -25,10 +24,23 @@ pub enum BackendEvent { NewSeat(Rc), } +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum KeyState { + Released, + Pressed, +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum ScrollAxis { + Horizontal, + Vertical, +} + #[derive(Debug)] pub enum SeatEvent { - Motion(OutputId, Fixed, Fixed), - Key(u32), - Keymap(XkbKeymapStr), - Modifiers(u32, u32, u32, u32) + OutputPosition(OutputId, Fixed, Fixed), + Motion(Fixed, Fixed), + Button(u32, KeyState), + Scroll(i32, ScrollAxis), + Key(u32, KeyState), } diff --git a/src/backends/xorg/mod.rs b/src/backends/xorg/mod.rs index ed1baf04..61b55513 100644 --- a/src/backends/xorg/mod.rs +++ b/src/backends/xorg/mod.rs @@ -1,4 +1,6 @@ -use crate::backend::{BackendEvent, Output, OutputId, Seat, SeatEvent, SeatId}; +use crate::backend::{ + BackendEvent, KeyState, Output, OutputId, ScrollAxis, Seat, SeatEvent, SeatId, +}; use crate::event_loop::{EventLoopDispatcher, EventLoopId}; use crate::fixed::Fixed; use crate::ifs::wl_buffer::WlBuffer; @@ -9,9 +11,6 @@ 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}; @@ -56,14 +55,11 @@ pub enum XorgBackendError { 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, @@ -71,7 +67,6 @@ struct XcbCon { input: Box, input_opcode: u8, xkb: Box, - xkb_event: u8, c: *mut ffi::xcb_connection_t, errors: XcbErrorParser, } @@ -93,7 +88,6 @@ impl XcbCon { input, input_opcode: 0, xkb, - xkb_event: 0, c, errors, }; @@ -127,12 +121,6 @@ impl XcbCon { ); con.errors.check(&con.xcb, res, err)?; - let xkb_ex = con - .xcb - .xcb_get_extension_data(con.c, con.xkb.xcb_xkb_id()); - assert!(xkb_ex.is_not_null()); - con.xkb_event = xkb_ex.deref().first_event; - Ok(con) } } @@ -172,8 +160,6 @@ pub struct XorgBackend { wheel_id: WheelId, state: Rc, con: XcbCon, - xkbcommon: XkbContext, - xkbcommonx11: XkbCommonX11, outputs: CopyHashMap>, seats: CopyHashMap>, mouse_seats: CopyHashMap>, @@ -193,8 +179,6 @@ impl XorgBackend { wheel_id, state: state.clone(), con, - xkbcommon: XkbContext::new()?, - xkbcommonx11: XkbCommonX11::load()?, outputs: Default::default(), seats: Default::default(), mouse_seats: Default::default(), @@ -356,21 +340,6 @@ impl XorgBackend { Ok(()) } - fn create_state(&self, device_id: ffi::xcb_input_device_id_t) -> Result { - unsafe { - let keymap = self.xkbcommonx11.keymap_from_device( - &self.xkbcommon, - self.con.c, - device_id as _, - XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS, - )?; - let state = - self.xkbcommonx11 - .state_from_device(&keymap, self.con.c, device_id as _)?; - Ok(state) - } - } - 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; @@ -402,17 +371,6 @@ impl XorgBackend { e ); } - let state = match self.create_state(info.deviceid) { - 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(), @@ -421,9 +379,10 @@ impl XorgBackend { removed: Cell::new(false), cb: RefCell::new(None), events: RefCell::new(Default::default()), - state, + button_map: Default::default(), + last_position: Cell::new((0, 0)), }); - seat.send_new_state(); + seat.update_button_map(); self.seats.set(info.deviceid, seat.clone()); self.mouse_seats.set(info.attachment, seat.clone()); self.state @@ -455,30 +414,6 @@ impl XorgBackend { ffi::XCB_CONFIGURE_NOTIFY => self.handle_configure(event)?, ffi::XCB_DESTROY_NOTIFY => self.handle_destroy(event)?, ffi::XCB_GE_GENERIC => self.handle_generic(event)?, - _ if event_type == self.con.xkb_event => self.handle_xkb(event)?, - _ => {} - } - Ok(()) - } - - fn handle_xkb( - self: &Rc, - event: &ffi::xcb_generic_event_t, - ) -> Result<(), XorgBackendError> { - let event = unsafe { &*(event as *const _ as *const ffi::xcb_xkb_map_notify_event_t) }; - let seat = match self.seats.get(&(event.device_id as _)) { - Some(s) => s, - _ => return Ok(()), - }; - match event.xkb_type { - ffi::XCB_XKB_MAP_NOTIFY | ffi::XCB_XKB_NEW_KEYBOARD_NOTIFY => { - - } - ffi::XCB_XKB_STATE_NOTIFY => { - let event = unsafe { - (event as *const _ as *const ffi::xcb_xkb_state_notify_event_t).deref() - }; - } _ => {} } Ok(()) @@ -501,21 +436,68 @@ impl XorgBackend { ) -> Result<(), XorgBackendError> { match event.event_type { ffi::XCB_INPUT_MOTION => self.handle_input_motion(event)?, - ffi::XCB_INPUT_KEY_PRESS => self.handle_input_key_press(event)?, + ffi::XCB_INPUT_ENTER => self.handle_input_enter(event)?, + ffi::XCB_INPUT_BUTTON_PRESS => { + self.handle_input_button_press(event, KeyState::Pressed)? + } + ffi::XCB_INPUT_BUTTON_RELEASE => { + self.handle_input_button_press(event, KeyState::Released)? + } + ffi::XCB_INPUT_KEY_PRESS => self.handle_input_key_press(event, KeyState::Pressed)?, + ffi::XCB_INPUT_KEY_RELEASE => self.handle_input_key_press(event, KeyState::Released)?, ffi::XCB_INPUT_HIERARCHY => self.handle_input_hierarchy(event)?, _ => {} } Ok(()) } + fn handle_input_button_press( + self: &Rc, + event: &ffi::xcb_ge_generic_event_t, + state: KeyState, + ) -> Result<(), XorgBackendError> { + let event = + unsafe { (event as *const _ as *const ffi::xcb_input_button_press_event_t).deref() }; + if let Some(seat) = self.mouse_seats.get(&event.deviceid) { + let button = seat.button_map.get(&event.detail).unwrap_or(event.detail); + if matches!(button, 4..=7) { + if state == KeyState::Pressed { + let (axis, val) = match button { + 4 => (ScrollAxis::Vertical, -15), + 5 => (ScrollAxis::Vertical, 15), + 6 => (ScrollAxis::Horizontal, -15), + 7 => (ScrollAxis::Horizontal, 15), + _ => unreachable!(), + }; + seat.event(SeatEvent::Scroll(val, axis)); + } + } else { + const BTN_LEFT: u32 = 0x110; + const BTN_RIGHT: u32 = 0x111; + const BTN_MIDDLE: u32 = 0x112; + const BTN_SIDE: u32 = 0x113; + let button = match button { + 0 => return Ok(()), + 1 => BTN_LEFT, + 2 => BTN_MIDDLE, + 3 => BTN_RIGHT, + n => n + BTN_SIDE - 8, + }; + seat.event(SeatEvent::Button(button, state)); + } + } + Ok(()) + } + fn handle_input_key_press( self: &Rc, event: &ffi::xcb_ge_generic_event_t, + state: KeyState, ) -> Result<(), XorgBackendError> { let event = unsafe { (event as *const _ as *const ffi::xcb_input_key_press_event_t).deref() }; if let Some(seat) = self.seats.get(&event.deviceid) { - seat.event(SeatEvent::Key(event.detail - 8)); + seat.event(SeatEvent::Key(event.detail - 8, state)); } Ok(()) } @@ -546,24 +528,41 @@ impl XorgBackend { Ok(()) } + fn handle_input_enter( + &self, + event: &ffi::xcb_ge_generic_event_t, + ) -> Result<(), XorgBackendError> { + let event = unsafe { (event as *const _ as *const ffi::xcb_input_enter_event_t).deref() }; + if let (Some(win), Some(seat)) = ( + self.outputs.get(&event.event), + self.mouse_seats.get(&event.deviceid), + ) { + seat.last_position.set((event.event_x, event.event_y)); + seat.event(SeatEvent::OutputPosition( + win.id, + Fixed::from_1616(event.event_x), + Fixed::from_1616(event.event_y), + )); + } + 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), - )); + let pos = (event.event_x, event.event_y); + let last_pos = seat.last_position.replace(pos); + if pos != last_pos { + let dx = Fixed::from_1616(pos.0 - last_pos.0); + let dy = Fixed::from_1616(pos.1 - last_pos.1); + seat.event(SeatEvent::Motion(dx, dy)); + } Ok(()) } @@ -647,8 +646,25 @@ impl XorgBackend { 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); + let node_extents = tl.common.extents.get(); + let surface_extents = tl.surface.surface.surface.effective_extents.get(); + self.render_surface( + image, + surface, + node_extents.x - surface_extents.x1, + node_extents.y - surface_extents.y1, + ); + image.fill_insert_border( + 255, + 0, + 0, + 255, + node_extents.x, + node_extents.y, + node_extents.x + node_extents.width as i32, + node_extents.y + node_extents.height as i32, + 2, + ); } fn render_surface( @@ -797,8 +813,8 @@ struct XorgSeat { removed: Cell, cb: RefCell>>, events: RefCell>, - last_modifiers: Cell<(u32, u32, u32, u32)>, - state: XkbState, + button_map: CopyHashMap, + last_position: Cell<(ffi::xcb_input_fp1616_t, ffi::xcb_input_fp1616_t)>, } impl XorgSeat { @@ -812,6 +828,38 @@ impl XorgSeat { self.events.borrow_mut().push_back(event); self.changed(); } + + fn update_button_map(&self) { + self.button_map.clear(); + unsafe { + let con = &self.backend.con; + let mut err = ptr::null_mut(); + let reply = con.input.xcb_input_get_device_button_mapping_reply( + con.c, + con.input + .xcb_input_get_device_button_mapping(con.c, self.mouse as _), + &mut err, + ); + let reply = match con.check(reply, err) { + Ok(r) => r, + Err(e) => { + log::error!( + "Could not get Xinput button map of device {}: {:#}", + self.mouse, + e + ); + return; + } + }; + let map = std::slice::from_raw_parts( + con.input.xcb_input_get_device_button_mapping_map(&*reply), + reply.map_size as _, + ); + for (i, map) in map.iter().copied().enumerate().rev() { + self.button_map.set(map as u32, i as u32 + 1); + } + } + } } impl Seat for XorgSeat { diff --git a/src/client/mod.rs b/src/client/mod.rs index 67edf8ed..8c9d4e11 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -7,7 +7,10 @@ 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_seat::wl_keyboard::{WlKeyboard, WlKeyboardError}; +use crate::ifs::wl_seat::wl_pointer::{WlPointer, WlPointerError}; +use crate::ifs::wl_seat::wl_touch::{WlTouch, WlTouchError}; +use crate::ifs::wl_seat::{WlSeatError, WlSeatId, WlSeatObj}; use crate::ifs::wl_shm::{WlShmError, WlShmObj}; use crate::ifs::wl_shm_pool::{WlShmPool, WlShmPoolError}; use crate::ifs::wl_subcompositor::{WlSubcompositorError, WlSubcompositorObj}; @@ -33,9 +36,10 @@ use std::mem; use std::rc::Rc; use thiserror::Error; use uapi::{c, OwnedFd}; -use crate::ifs::wl_seat::wl_keyboard::{WlKeyboard, WlKeyboardError}; -use crate::ifs::wl_seat::wl_pointer::{WlPointer, WlPointerError}; -use crate::ifs::wl_seat::wl_touch::{WlTouch, WlTouchError}; +use crate::ifs::wl_data_device::{WlDataDevice, WlDataDeviceError}; +use crate::ifs::wl_data_device_manager::{WlDataDeviceManagerError, WlDataDeviceManagerObj}; +use crate::ifs::wl_data_offer::{WlDataOffer, WlDataOfferError}; +use crate::ifs::wl_data_source::{WlDataSourceError, WlDataSource}; mod objects; mod tasks; @@ -68,6 +72,8 @@ pub enum ClientError { SurfaceDoesNotExist(WlSurfaceId), #[error("There is no xdg_surface with id {0}")] XdgSurfaceDoesNotExist(XdgSurfaceId), + #[error("There is no wl_seat with id {0}")] + WlSeatDoesNotExist(WlSeatId), #[error("Cannot parse the message")] ParserError(#[source] Box), #[error("Server tried to allocate more than 0x1_00_00_00 ids")] @@ -124,6 +130,14 @@ pub enum ClientError { WlTouchError(#[source] Box), #[error("Object {0} is not a display")] NotADisplay(ObjectId), + #[error("An error occurred in a `wl_data_device`")] + WlDataDeviceError(#[source] Box), + #[error("An error occurred in a `wl_data_device_manager`")] + WlDataDeviceManagerError(#[source] Box), + #[error("An error occurred in a `wl_data_offer`")] + WlDataOfferError(#[source] Box), + #[error("An error occurred in a `wl_data_source`")] + WlDataSourceError(#[source] Box), } efrom!(ClientError, ParserError, MsgParserError); @@ -147,6 +161,10 @@ efrom!(ClientError, WlSeatError, WlSeatError); efrom!(ClientError, WlTouchError, WlTouchError); efrom!(ClientError, WlPointerError, WlPointerError); efrom!(ClientError, WlKeyboardError, WlKeyboardError); +efrom!(ClientError, WlDataDeviceManagerError, WlDataDeviceManagerError); +efrom!(ClientError, WlDataDeviceError, WlDataDeviceError); +efrom!(ClientError, WlDataSourceError, WlDataSourceError); +efrom!(ClientError, WlDataOfferError, WlDataOfferError); impl ClientError { fn peer_closed(&self) -> bool { @@ -445,6 +463,13 @@ impl Client { } } + pub fn get_wl_seat(&self, id: WlSeatId) -> Result, ClientError> { + match self.objects.seats.get(&id) { + Some(r) => Ok(r), + _ => Err(ClientError::WlSeatDoesNotExist(id)), + } + } + fn simple_add_obj(&self, obj: &Rc, client: bool) -> Result<(), ClientError> { if client { self.objects.add_client_object(obj.clone()) @@ -519,10 +544,13 @@ simple_add_obj!(XdgPositioner); simple_add_obj!(XdgToplevel); simple_add_obj!(XdgPopup); simple_add_obj!(WlOutputObj); -simple_add_obj!(WlSeatObj); simple_add_obj!(WlKeyboard); simple_add_obj!(WlPointer); simple_add_obj!(WlTouch); +simple_add_obj!(WlDataDeviceManagerObj); +simple_add_obj!(WlDataDevice); +simple_add_obj!(WlDataOffer); +simple_add_obj!(WlDataSource); macro_rules! dedicated_add_obj { ($ty:ty, $field:ident) => { @@ -547,3 +575,4 @@ dedicated_add_obj!(WlSurface, surfaces); dedicated_add_obj!(XdgWmBaseObj, xdg_wm_bases); dedicated_add_obj!(XdgSurface, xdg_surfaces); dedicated_add_obj!(WlBuffer, buffers); +dedicated_add_obj!(WlSeatObj, seats); diff --git a/src/client/objects.rs b/src/client/objects.rs index 92df9fd1..3aa8e7d9 100644 --- a/src/client/objects.rs +++ b/src/client/objects.rs @@ -3,6 +3,7 @@ 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}; +use crate::ifs::wl_seat::{WlSeatId, WlSeatObj}; use crate::ifs::wl_surface::xdg_surface::{XdgSurface, XdgSurfaceId}; use crate::ifs::wl_surface::{WlSurface, WlSurfaceId}; use crate::ifs::xdg_wm_base::{XdgWmBaseId, XdgWmBaseObj}; @@ -22,6 +23,7 @@ pub struct Objects { pub regions: CopyHashMap>, pub buffers: CopyHashMap>, pub xdg_wm_bases: CopyHashMap>, + pub seats: CopyHashMap>, ids: RefCell>, } @@ -39,6 +41,7 @@ impl Objects { regions: Default::default(), buffers: Default::default(), xdg_wm_bases: Default::default(), + seats: Default::default(), ids: RefCell::new(vec![]), } } diff --git a/src/fixed.rs b/src/fixed.rs index 4171891c..7efc2b41 100644 --- a/src/fixed.rs +++ b/src/fixed.rs @@ -1,4 +1,5 @@ use std::fmt::{Debug, Display, Formatter}; +use std::ops::{Add, AddAssign, Sub, SubAssign}; #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] #[repr(transparent)] @@ -8,6 +9,18 @@ impl Fixed { pub fn from_1616(i: i32) -> Self { Self(i >> 8) } + + pub fn from_int(i: i32) -> Self { + Self(i << 8) + } + + pub fn round_down(self) -> i32 { + self.0 >> 8 + } + + pub fn apply_fract(self, i: i32) -> Self { + Self((i << 8) | (self.0 & 255)) + } } impl From for Fixed { @@ -22,12 +35,6 @@ impl From for f64 { } } -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) @@ -39,3 +46,31 @@ impl Display for Fixed { Display::fmt(&f64::from(*self), f) } } + +impl Sub for Fixed { + type Output = Self; + + fn sub(self, rhs: Self) -> Self::Output { + Self(self.0 - rhs.0) + } +} + +impl Add for Fixed { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + Self(self.0 + rhs.0) + } +} + +impl AddAssign for Fixed { + fn add_assign(&mut self, rhs: Self) { + self.0 += rhs.0; + } +} + +impl SubAssign for Fixed { + fn sub_assign(&mut self, rhs: Self) { + self.0 -= rhs.0; + } +} diff --git a/src/globals.rs b/src/globals.rs index efd53fb3..b1d5f97f 100644 --- a/src/globals.rs +++ b/src/globals.rs @@ -1,20 +1,21 @@ use crate::client::{Client, ClientError, DynEventFormatter, WlEvent}; use crate::ifs::wl_compositor::WlCompositorError; -use crate::ifs::wl_output::WlOutputError; +use crate::ifs::wl_output::{WlOutputError, WlOutputGlobal}; use crate::ifs::wl_registry::WlRegistry; -use crate::ifs::wl_seat::WlSeatError; +use crate::ifs::wl_seat::{WlSeatError, WlSeatGlobal}; use crate::ifs::wl_shm::WlShmError; use crate::ifs::wl_subcompositor::WlSubcompositorError; use crate::ifs::xdg_wm_base::XdgWmBaseError; use crate::object::{Interface, ObjectId}; use crate::utils::copyhashmap::CopyHashMap; -use crate::{NumCell, State}; +use crate::{NumCell, State, WlCompositorGlobal, WlDataDeviceManagerGlobal, WlShmGlobal, WlSubcompositorGlobal, XdgWmBaseGlobal}; use ahash::AHashSet; use std::fmt::{Display, Formatter}; use std::future::Future; use std::pin::Pin; use std::rc::Rc; use thiserror::Error; +use crate::ifs::wl_data_device_manager::WlDataDeviceManagerError; #[derive(Debug, Error)] pub enum GlobalError { @@ -34,6 +35,10 @@ pub enum GlobalError { WlOutputError(#[source] Box), #[error("An error occurred in a wl_seat")] WlSeatError(#[source] Box), + #[error("The output with id {0} does not exist")] + OutputDoesNotExist(GlobalName), + #[error("An error occurred in a wl_data_device_manager")] + WlDataDeviceManagerError(#[source] Box), } efrom!(GlobalError, WlCompositorError, WlCompositorError); @@ -42,6 +47,7 @@ efrom!(GlobalError, WlSubcompositorError, WlSubcompositorError); efrom!(GlobalError, XdgWmBaseError, XdgWmBaseError); efrom!(GlobalError, WlOutputError, WlOutputError); efrom!(GlobalError, WlSeatError, WlSeatError); +efrom!(GlobalError, WlDataDeviceManagerError, WlDataDeviceManagerError); #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub struct GlobalName(u32); @@ -73,6 +79,7 @@ pub trait GlobalBind { pub trait Global: GlobalBind { fn name(&self) -> GlobalName; + fn singleton(&self) -> bool; fn interface(&self) -> Interface; fn version(&self) -> u32; fn pre_remove(&self); @@ -82,6 +89,7 @@ pub trait Global: GlobalBind { pub struct Globals { next_name: NumCell, registry: CopyHashMap>, + outputs: CopyHashMap>, } impl Globals { @@ -89,6 +97,7 @@ impl Globals { Self { next_name: NumCell::new(1), registry: CopyHashMap::new(), + outputs: Default::default(), } } @@ -100,7 +109,7 @@ impl Globals { GlobalName(id) } - pub fn insert_no_broadcast<'a>(&'a self, global: Rc) { + fn insert_no_broadcast<'a>(&'a self, global: Rc) { self.insert_no_broadcast_(&global); } @@ -108,7 +117,7 @@ impl Globals { self.registry.set(global.name(), global.clone()); } - pub async fn insert<'a>(&'a self, state: &'a State, global: Rc) { + async fn insert<'a>(&'a self, state: &'a State, global: Rc) { self.insert_no_broadcast_(&global); self.broadcast(state, |r| r.global(&global)).await; } @@ -117,15 +126,11 @@ impl Globals { self.take(name, false) } - pub async fn remove( - &self, - state: &State, - name: GlobalName, - ) -> Result, GlobalError> { + pub async fn remove(&self, state: &State, name: GlobalName) -> Result<(), GlobalError> { let global = self.take(name, true)?; global.pre_remove(); self.broadcast(state, |r| r.global_remove(name)).await; - Ok(global) + Ok(()) } pub async fn notify_all( @@ -134,11 +139,19 @@ impl Globals { registry: &Rc, ) -> Result<(), GlobalError> { let globals = self.registry.lock(); - for global in globals.values() { - if let Err(e) = client.event(registry.global(global)).await { - return Err(GlobalError::SendAllError(Box::new(e))); - } + macro_rules! emit { + ($singleton:expr) => { + for global in globals.values() { + if global.singleton() == $singleton { + if let Err(e) = client.event(registry.global(global)).await { + return Err(GlobalError::SendAllError(Box::new(e))); + } + } + } + }; } + emit!(true); + emit!(false); Ok(()) } @@ -173,4 +186,94 @@ impl Globals { None => Err(GlobalError::GlobalDoesNotExist(name)), } } + + pub fn get_output(&self, output: GlobalName) -> Result, GlobalError> { + match self.outputs.get(&output) { + Some(o) => Ok(o), + _ => Err(GlobalError::OutputDoesNotExist(output)), + } + } } + +pub trait AddGlobal { + type RemoveGlobal<'a>: Future> + 'a; + type AddGlobal<'a>: Future + 'a; + + fn add_global<'a>(&'a self, state: &'a State, global: &'a Rc) -> Self::AddGlobal<'a>; + + fn add_global_no_broadcast(&self, global: &Rc); + + fn remove_global<'a>(&'a self, state: &'a State, global: &'a T) -> Self::RemoveGlobal<'a>; +} + +macro_rules! simple_add_global { + ($ty:ty) => { + impl AddGlobal<$ty> for Globals { + type RemoveGlobal<'a> = impl Future> + 'a; + type AddGlobal<'a> = impl Future + 'a; + + fn add_global<'a>( + &'a self, + state: &'a State, + global: &'a Rc<$ty>, + ) -> Self::AddGlobal<'a> { + self.insert(state, global.clone()) + } + + fn add_global_no_broadcast(&self, global: &Rc<$ty>) { + self.insert_no_broadcast(global.clone()); + } + + fn remove_global<'a>( + &'a self, + state: &'a State, + global: &'a $ty, + ) -> Self::RemoveGlobal<'a> { + self.remove(state, global.name()) + } + } + }; +} + +simple_add_global!(WlSeatGlobal); +simple_add_global!(WlCompositorGlobal); +simple_add_global!(WlShmGlobal); +simple_add_global!(WlSubcompositorGlobal); +simple_add_global!(XdgWmBaseGlobal); +simple_add_global!(WlDataDeviceManagerGlobal); + +macro_rules! dedicated_add_global { + ($ty:ty, $field:ident) => { + impl AddGlobal<$ty> for Globals { + type RemoveGlobal<'a> = impl Future> + 'a; + type AddGlobal<'a> = impl Future + 'a; + + fn add_global<'a>( + &'a self, + state: &'a State, + global: &'a Rc<$ty>, + ) -> Self::AddGlobal<'a> { + async move { + self.insert(state, global.clone()).await; + self.$field.set(global.name(), global.clone()); + } + } + + fn add_global_no_broadcast(&self, global: &Rc<$ty>) { + self.insert_no_broadcast(global.clone()); + self.$field.set(global.name(), global.clone()); + } + + fn remove_global<'a>( + &'a self, + state: &'a State, + global: &'a $ty, + ) -> Self::RemoveGlobal<'a> { + self.$field.remove(&global.name()); + self.remove(state, global.name()) + } + } + }; +} + +dedicated_add_global!(WlOutputGlobal, outputs); diff --git a/src/ifs/mod.rs b/src/ifs/mod.rs index 6a41934a..a452e148 100644 --- a/src/ifs/mod.rs +++ b/src/ifs/mod.rs @@ -1,6 +1,9 @@ pub mod wl_buffer; pub mod wl_callback; pub mod wl_compositor; +pub mod wl_data_device; +pub mod wl_data_device_manager; +pub mod wl_data_source; pub mod wl_display; pub mod wl_output; pub mod wl_region; @@ -12,3 +15,4 @@ pub mod wl_subcompositor; pub mod wl_surface; pub mod xdg_positioner; pub mod xdg_wm_base; +pub mod wl_data_offer; diff --git a/src/ifs/wl_compositor/mod.rs b/src/ifs/wl_compositor/mod.rs index 0fb4a5a2..d39bf42d 100644 --- a/src/ifs/wl_compositor/mod.rs +++ b/src/ifs/wl_compositor/mod.rs @@ -83,6 +83,10 @@ impl Global for WlCompositorGlobal { self.name } + fn singleton(&self) -> bool { + true + } + fn interface(&self) -> Interface { Interface::WlCompositor } diff --git a/src/ifs/wl_data_device/mod.rs b/src/ifs/wl_data_device/mod.rs new file mode 100644 index 00000000..62fc9fe6 --- /dev/null +++ b/src/ifs/wl_data_device/mod.rs @@ -0,0 +1,82 @@ +mod types; + +use crate::client::{AddObj, Client}; +use crate::object::{Interface, Object, ObjectId}; +use crate::utils::buffd::MsgParser; +use std::rc::Rc; +pub use types::*; + +const START_DRAG: u32 = 0; +const SET_SELECTION: u32 = 1; +const RELEASE: u32 = 2; + +const DATA_OFFER: u32 = 0; +const ENTER: u32 = 1; +const LEAVE: u32 = 2; +const MOTION: u32 = 4; +const DROP: u32 = 5; +const SELECTION: u32 = 5; + +const ROLE: u32 = 0; + +id!(WlDataDeviceId); + +pub struct WlDataDevice { + id: WlDataDeviceId, + client: Rc, +} + +impl WlDataDevice { + pub fn new(id: WlDataDeviceId, client: &Rc) -> Self { + Self { + id, + client: client.clone(), + } + } + + async fn start_drag(&self, parser: MsgParser<'_, '_>) -> Result<(), StartDragError> { + let _req: StartDrag = self.client.parse(self, parser)?; + Ok(()) + } + + async fn set_selection(&self, parser: MsgParser<'_, '_>) -> Result<(), SetSelectionError> { + let _req: SetSelection = self.client.parse(self, parser)?; + Ok(()) + } + + async fn release(&self, parser: MsgParser<'_, '_>) -> Result<(), ReleaseError> { + let _req: Release = self.client.parse(self, parser)?; + self.client.remove_obj(self).await?; + Ok(()) + } + + async fn handle_request_( + self: &Rc, + request: u32, + parser: MsgParser<'_, '_>, + ) -> Result<(), WlDataDeviceError> { + match request { + START_DRAG => self.start_drag(parser).await?, + SET_SELECTION => self.set_selection(parser).await?, + RELEASE => self.release(parser).await?, + _ => unreachable!(), + } + Ok(()) + } +} + +handle_request!(WlDataDevice); + +impl Object for WlDataDevice { + fn id(&self) -> ObjectId { + self.id.into() + } + + fn interface(&self) -> Interface { + Interface::WlDataDevice + } + + fn num_requests(&self) -> u32 { + RELEASE + 1 + } +} diff --git a/src/ifs/wl_data_device/types.rs b/src/ifs/wl_data_device/types.rs new file mode 100644 index 00000000..da5f00c2 --- /dev/null +++ b/src/ifs/wl_data_device/types.rs @@ -0,0 +1,242 @@ +use crate::client::{ClientError, EventFormatter, RequestParser}; +use crate::fixed::Fixed; +use crate::ifs::wl_data_device::{WlDataDevice, DATA_OFFER, DROP, ENTER, LEAVE, MOTION, SELECTION}; +use crate::ifs::wl_data_source::WlDataSourceId; +use crate::ifs::wl_surface::WlSurfaceId; +use crate::object::Object; +use crate::utils::buffd::{MsgFormatter, MsgParser, MsgParserError}; +use std::fmt::{Debug, Formatter}; +use std::rc::Rc; +use thiserror::Error; +use crate::ifs::wl_data_offer::WlDataOfferId; + +#[derive(Debug, Error)] +pub enum WlDataDeviceError { + #[error(transparent)] + ClientError(Box), + #[error("Could not process `start_drag` request")] + StartDragError(#[from] StartDragError), + #[error("Could not process `set_selection` request")] + SetSelectionError(#[from] SetSelectionError), + #[error("Could not process `release` request")] + ReleaseError(#[from] ReleaseError), +} +efrom!(WlDataDeviceError, ClientError, ClientError); + +#[derive(Debug, Error)] +pub enum StartDragError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), + #[error(transparent)] + ClientError(Box), +} +efrom!(StartDragError, ParseFailed, MsgParserError); +efrom!(StartDragError, ClientError, ClientError); + +#[derive(Debug, Error)] +pub enum SetSelectionError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), + #[error(transparent)] + ClientError(Box), +} +efrom!(SetSelectionError, ParseFailed, MsgParserError); +efrom!(SetSelectionError, ClientError, ClientError); + +#[derive(Debug, Error)] +pub enum ReleaseError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), + #[error(transparent)] + ClientError(Box), +} +efrom!(ReleaseError, ParseFailed, MsgParserError); +efrom!(ReleaseError, ClientError, ClientError); + +pub(super) struct StartDrag { + pub source: WlDataSourceId, + pub origin: WlSurfaceId, + pub icon: WlSurfaceId, + pub serial: u32, +} +impl RequestParser<'_> for StartDrag { + fn parse(parser: &mut MsgParser<'_, '_>) -> Result { + Ok(Self { + source: parser.object()?, + origin: parser.object()?, + icon: parser.object()?, + serial: parser.uint()?, + }) + } +} +impl Debug for StartDrag { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "start_drag(source: {}, origin: {}, icon: {}, serial: {})", + self.source, self.origin, self.icon, self.serial + ) + } +} + +pub(super) struct SetSelection { + pub source: WlDataSourceId, + pub serial: u32, +} +impl RequestParser<'_> for SetSelection { + fn parse(parser: &mut MsgParser<'_, '_>) -> Result { + Ok(Self { + source: parser.object()?, + serial: parser.uint()?, + }) + } +} +impl Debug for SetSelection { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "set_selection(source: {}, serial: {})", + self.source, self.serial, + ) + } +} + +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 DataOffer { + pub obj: Rc, + pub id: WlDataOfferId, +} +impl EventFormatter for DataOffer { + fn format(self: Box, fmt: &mut MsgFormatter<'_>) { + fmt.header(self.obj.id, DATA_OFFER).object(self.id); + } + fn obj(&self) -> &dyn Object { + &*self.obj + } +} +impl Debug for DataOffer { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "data_offer(id: {})", self.id) + } +} + +pub(super) struct Enter { + pub obj: Rc, + pub serial: u32, + pub surface: WlSurfaceId, + pub x: Fixed, + pub y: Fixed, + pub id: WlDataOfferId, +} +impl EventFormatter for Enter { + fn format(self: Box, fmt: &mut MsgFormatter<'_>) { + fmt.header(self.obj.id, ENTER) + .uint(self.serial) + .object(self.surface) + .fixed(self.x) + .fixed(self.y) + .object(self.id); + } + fn obj(&self) -> &dyn Object { + &*self.obj + } +} +impl Debug for Enter { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "enter(serial: {}, surface: {}, x: {}, y: {}, id: {})", + self.serial, self.surface, self.x, self.y, self.id + ) + } +} + +pub(super) struct Leave { + pub obj: Rc, +} +impl EventFormatter for Leave { + fn format(self: Box, fmt: &mut MsgFormatter<'_>) { + fmt.header(self.obj.id, LEAVE); + } + fn obj(&self) -> &dyn Object { + &*self.obj + } +} +impl Debug for Leave { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "leave()") + } +} + +pub(super) struct Motion { + pub obj: Rc, + pub time: u32, + pub x: Fixed, + pub y: Fixed, +} +impl EventFormatter for Motion { + fn format(self: Box, fmt: &mut MsgFormatter<'_>) { + fmt.header(self.obj.id, MOTION) + .uint(self.time) + .fixed(self.x) + .fixed(self.y); + } + fn obj(&self) -> &dyn Object { + &*self.obj + } +} +impl Debug for Motion { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "motion(time: {}, x: {}, y: {})", + self.time, self.x, self.y + ) + } +} + +pub(super) struct Drop { + pub obj: Rc, +} +impl EventFormatter for Drop { + fn format(self: Box, fmt: &mut MsgFormatter<'_>) { + fmt.header(self.obj.id, DROP); + } + fn obj(&self) -> &dyn Object { + &*self.obj + } +} +impl Debug for Drop { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "drop()") + } +} + +pub(super) struct Selection { + pub obj: Rc, + pub id: WlDataOfferId, +} +impl EventFormatter for Selection { + fn format(self: Box, fmt: &mut MsgFormatter<'_>) { + fmt.header(self.obj.id, SELECTION).object(self.id); + } + fn obj(&self) -> &dyn Object { + &*self.obj + } +} +impl Debug for Selection { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "selection(id: {})", self.id) + } +} diff --git a/src/ifs/wl_data_device_manager/mod.rs b/src/ifs/wl_data_device_manager/mod.rs new file mode 100644 index 00000000..012a75f9 --- /dev/null +++ b/src/ifs/wl_data_device_manager/mod.rs @@ -0,0 +1,121 @@ +mod types; + +use crate::client::{AddObj, Client}; +use crate::globals::{Global, GlobalName}; +use crate::object::{Interface, Object, ObjectId}; +use crate::utils::buffd::MsgParser; +use std::rc::Rc; +pub use types::*; +use crate::ifs::wl_data_device::WlDataDevice; +use crate::ifs::wl_data_source::WlDataSource; + +const CREATE_DATA_SOURCE: u32 = 0; +const GET_DATA_DEVICE: u32 = 1; + +const DND_NONE: u32 = 0; +const DND_COPY: u32 = 1; +const DND_MOVE: u32 = 2; +const DND_ASK: u32 = 4; + +id!(WlDataDeviceManagerId); + +pub struct WlDataDeviceManagerGlobal { + name: GlobalName, +} + +pub struct WlDataDeviceManagerObj { + id: WlDataDeviceManagerId, + client: Rc, +} + +impl WlDataDeviceManagerGlobal { + pub fn new(name: GlobalName) -> Self { + Self { name } + } + + async fn bind_( + self: Rc, + id: WlDataDeviceManagerId, + client: &Rc, + _version: u32, + ) -> Result<(), WlDataDeviceManagerError> { + let obj = Rc::new(WlDataDeviceManagerObj { + id, + client: client.clone(), + }); + client.add_client_obj(&obj)?; + Ok(()) + } +} + +impl WlDataDeviceManagerObj { + async fn create_data_source( + &self, + parser: MsgParser<'_, '_>, + ) -> Result<(), CreateDataSourceError> { + let req: CreateDataSource = self.client.parse(self, parser)?; + let res = Rc::new(WlDataSource::new(req.id, &self.client)); + self.client.add_client_obj(&res)?; + Ok(()) + } + + async fn get_data_device(&self, parser: MsgParser<'_, '_>) -> Result<(), GetDataDeviceError> { + let req: GetDataDevice = self.client.parse(self, parser)?; + let res = Rc::new(WlDataDevice::new(req.id, &self.client)); + self.client.add_client_obj(&res)?; + Ok(()) + } + + async fn handle_request_( + self: &Rc, + request: u32, + parser: MsgParser<'_, '_>, + ) -> Result<(), WlDataDeviceManagerError> { + match request { + CREATE_DATA_SOURCE => self.create_data_source(parser).await?, + GET_DATA_DEVICE => self.get_data_device(parser).await?, + _ => unreachable!(), + } + Ok(()) + } +} + +bind!(WlDataDeviceManagerGlobal); + +impl Global for WlDataDeviceManagerGlobal { + fn name(&self) -> GlobalName { + self.name + } + + fn singleton(&self) -> bool { + true + } + + fn interface(&self) -> Interface { + Interface::WlDataDeviceManager + } + + fn version(&self) -> u32 { + 3 + } + + fn pre_remove(&self) { + unreachable!() + } +} + +handle_request!(WlDataDeviceManagerObj); + +impl Object for WlDataDeviceManagerObj { + fn id(&self) -> ObjectId { + self.id.into() + } + + fn interface(&self) -> Interface { + Interface::WlDataDeviceManager + } + + fn num_requests(&self) -> u32 { + GET_DATA_DEVICE + 1 + } +} diff --git a/src/ifs/wl_data_device_manager/types.rs b/src/ifs/wl_data_device_manager/types.rs new file mode 100644 index 00000000..1b156e8d --- /dev/null +++ b/src/ifs/wl_data_device_manager/types.rs @@ -0,0 +1,72 @@ +use crate::client::{ClientError, RequestParser}; +use crate::ifs::wl_data_source::WlDataSourceId; +use crate::ifs::wl_seat::WlSeatId; +use crate::utils::buffd::{MsgParser, MsgParserError}; +use std::fmt::{Debug, Formatter}; +use thiserror::Error; +use crate::ifs::wl_data_device::WlDataDeviceId; + +#[derive(Debug, Error)] +pub enum WlDataDeviceManagerError { + #[error(transparent)] + ClientError(Box), + #[error("Could not process `create_data_source` request")] + CreateDataSourceError(#[from] CreateDataSourceError), + #[error("Could not process `get_data_device` request")] + GetDataDeviceError(#[from] GetDataDeviceError), +} +efrom!(WlDataDeviceManagerError, ClientError, ClientError); + +#[derive(Debug, Error)] +pub enum CreateDataSourceError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), + #[error(transparent)] + ClientError(Box), +} +efrom!(CreateDataSourceError, ParseFailed, MsgParserError); +efrom!(CreateDataSourceError, ClientError, ClientError); + +#[derive(Debug, Error)] +pub enum GetDataDeviceError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), + #[error(transparent)] + ClientError(Box), +} +efrom!(GetDataDeviceError, ParseFailed, MsgParserError); +efrom!(GetDataDeviceError, ClientError, ClientError); + +pub(super) struct CreateDataSource { + pub id: WlDataSourceId, +} +impl RequestParser<'_> for CreateDataSource { + fn parse(parser: &mut MsgParser<'_, '_>) -> Result { + Ok(Self { + id: parser.object()?, + }) + } +} +impl Debug for CreateDataSource { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "create_data_source(id: {})", self.id) + } +} + +pub(super) struct GetDataDevice { + pub id: WlDataDeviceId, + pub seat: WlSeatId, +} +impl RequestParser<'_> for GetDataDevice { + fn parse(parser: &mut MsgParser<'_, '_>) -> Result { + Ok(Self { + id: parser.object()?, + seat: parser.object()?, + }) + } +} +impl Debug for GetDataDevice { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "get_data_device(id: {}, seat: {})", self.id, self.seat,) + } +} diff --git a/src/ifs/wl_data_offer/mod.rs b/src/ifs/wl_data_offer/mod.rs new file mode 100644 index 00000000..c1d49dd0 --- /dev/null +++ b/src/ifs/wl_data_offer/mod.rs @@ -0,0 +1,89 @@ +mod types; + +use crate::client::{AddObj, Client}; +use crate::object::{Interface, Object, ObjectId}; +use crate::utils::buffd::MsgParser; +use std::rc::Rc; +pub use types::*; + +const ACCEPT: u32 = 0; +const RECEIVE: u32 = 1; +const DESTROY: u32 = 2; +const FINISH: u32 = 3; +const SET_ACTIONS: u32 = 4; + +const OFFER: u32 = 0; +const SOURCE_ACTIONS: u32 = 1; +const ACTION: u32 = 2; + +const INVALID_FINISH: u32 = 0; +const INVALID_ACTION_MASK: u32 = 1; +const INVALID_ACTION: u32 = 2; +const INVALID_OFFER: u32 = 3; + +id!(WlDataOfferId); + +pub struct WlDataOffer { + id: WlDataOfferId, + client: Rc, +} + +impl WlDataOffer { + async fn accept(&self, parser: MsgParser<'_, '_>) -> Result<(), AcceptError> { + let _req: Accept = self.client.parse(self, parser)?; + Ok(()) + } + + async fn receive(&self, parser: MsgParser<'_, '_>) -> Result<(), ReceiveError> { + let _req: Receive = self.client.parse(self, parser)?; + Ok(()) + } + + async fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), DestroyError> { + let _req: Destroy = self.client.parse(self, parser)?; + self.client.remove_obj(self).await?; + Ok(()) + } + + async fn finish(&self, parser: MsgParser<'_, '_>) -> Result<(), FinishError> { + let _req: Finish = self.client.parse(self, parser)?; + Ok(()) + } + + async fn set_actions(&self, parser: MsgParser<'_, '_>) -> Result<(), SetActionsError> { + let _req: SetActions = self.client.parse(self, parser)?; + Ok(()) + } + + async fn handle_request_( + self: &Rc, + request: u32, + parser: MsgParser<'_, '_>, + ) -> Result<(), WlDataOfferError> { + match request { + ACCEPT => self.accept(parser).await?, + RECEIVE => self.receive(parser).await?, + DESTROY => self.destroy(parser).await?, + FINISH => self.finish(parser).await?, + SET_ACTIONS => self.set_actions(parser).await?, + _ => unreachable!(), + } + Ok(()) + } +} + +handle_request!(WlDataOffer); + +impl Object for WlDataOffer { + fn id(&self) -> ObjectId { + self.id.into() + } + + fn interface(&self) -> Interface { + Interface::WlDataSource + } + + fn num_requests(&self) -> u32 { + SET_ACTIONS + 1 + } +} diff --git a/src/ifs/wl_data_offer/types.rs b/src/ifs/wl_data_offer/types.rs new file mode 100644 index 00000000..3cebee57 --- /dev/null +++ b/src/ifs/wl_data_offer/types.rs @@ -0,0 +1,218 @@ +use crate::client::{ClientError, EventFormatter, RequestParser}; +use crate::object::Object; +use crate::utils::buffd::{MsgFormatter, MsgParser, MsgParserError}; +use bstr::{BStr, BString}; +use std::fmt::{Debug, Formatter}; +use std::rc::Rc; +use thiserror::Error; +use uapi::OwnedFd; +use crate::ifs::wl_data_offer::{ACTION, OFFER, SOURCE_ACTIONS, WlDataOffer}; + +#[derive(Debug, Error)] +pub enum WlDataOfferError { + #[error(transparent)] + ClientError(Box), + #[error("Could not process `accept` request")] + AcceptError(#[from] AcceptError), + #[error("Could not process `receive` request")] + ReceiveError(#[from] ReceiveError), + #[error("Could not process `destroy` request")] + DestroyError(#[from] DestroyError), + #[error("Could not process `finish` request")] + FinishError(#[from] FinishError), + #[error("Could not process `set_actions` request")] + SetActionsError(#[from] SetActionsError), +} +efrom!(WlDataOfferError, ClientError, ClientError); + +#[derive(Debug, Error)] +pub enum AcceptError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), + #[error(transparent)] + ClientError(Box), +} +efrom!(AcceptError, ParseFailed, MsgParserError); +efrom!(AcceptError, ClientError, ClientError); + +#[derive(Debug, Error)] +pub enum ReceiveError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), + #[error(transparent)] + ClientError(Box), +} +efrom!(ReceiveError, ParseFailed, MsgParserError); +efrom!(ReceiveError, ClientError, ClientError); + +#[derive(Debug, Error)] +pub enum DestroyError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), + #[error(transparent)] + ClientError(Box), +} +efrom!(DestroyError, ParseFailed, MsgParserError); +efrom!(DestroyError, ClientError, ClientError); + +#[derive(Debug, Error)] +pub enum FinishError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), + #[error(transparent)] + ClientError(Box), +} +efrom!(FinishError, ParseFailed, MsgParserError); +efrom!(FinishError, ClientError, ClientError); + +#[derive(Debug, Error)] +pub enum SetActionsError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), + #[error(transparent)] + ClientError(Box), +} +efrom!(SetActionsError, ParseFailed, MsgParserError); +efrom!(SetActionsError, ClientError, ClientError); + +pub(super) struct Accept<'a> { + pub serial: u32, + pub mime_type: &'a BStr, +} +impl<'a> RequestParser<'a> for Accept<'a> { + fn parse(parser: &mut MsgParser<'_, 'a>) -> Result { + Ok(Self { + serial: parser.uint()?, + mime_type: parser.string()?, + }) + } +} +impl Debug for Accept<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "accept(serial: {}, mime_type: {:?})", self.serial, self.mime_type) + } +} + +pub(super) struct Receive<'a> { + pub mime_type: &'a BStr, + pub fd: OwnedFd, +} +impl<'a> RequestParser<'a> for Receive<'a> { + fn parse(parser: &mut MsgParser<'_, 'a>) -> Result { + Ok(Self { + mime_type: parser.string()?, + fd: parser.fd()?, + }) + } +} +impl Debug for Receive<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "receive(mime_type: {:?}, fd: {})", self.mime_type, self.fd.raw()) + } +} + +pub(super) struct Destroy; +impl RequestParser<'_> for Destroy { + fn parse(_parser: &mut MsgParser<'_, '_>) -> Result { + Ok(Self) + } +} +impl Debug for Destroy { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "destroy()") + } +} + +pub(super) struct Finish; +impl RequestParser<'_> for Finish { + fn parse(_parser: &mut MsgParser<'_, '_>) -> Result { + Ok(Self) + } +} +impl Debug for Finish { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "finish()") + } +} + +pub(super) struct SetActions { + pub dnd_actions: u32, + pub preferred_action: u32, +} +impl<'a> RequestParser<'a> for SetActions { + fn parse(parser: &mut MsgParser<'_, 'a>) -> Result { + Ok(Self { + dnd_actions: parser.uint()?, + preferred_action: parser.uint()?, + }) + } +} +impl Debug for SetActions { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "set_actions(dnd_actions: {}, preferred_action: {})", self.dnd_actions, self.preferred_action) + } +} + +pub(super) struct Offer { + pub obj: Rc, + pub mime_type: BString, +} +impl EventFormatter for Offer { + fn format(self: Box, fmt: &mut MsgFormatter<'_>) { + fmt.header(self.obj.id, OFFER).string(&self.mime_type); + } + fn obj(&self) -> &dyn Object { + &*self.obj + } +} +impl Debug for Offer { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "target(mime_type: {:?})", self.mime_type) + } +} + +pub(super) struct SourceActions { + pub obj: Rc, + pub source_actions: u32, +} +impl EventFormatter for SourceActions { + fn format(self: Box, fmt: &mut MsgFormatter<'_>) { + fmt.header(self.obj.id, SOURCE_ACTIONS) + .uint(self.source_actions); + } + fn obj(&self) -> &dyn Object { + &*self.obj + } +} +impl Debug for SourceActions { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "source_actions(source_actions: {})", + self.source_actions, + ) + } +} + +pub(super) struct Action { + pub obj: Rc, + pub dnd_action: u32, +} +impl EventFormatter for Action { + fn format(self: Box, fmt: &mut MsgFormatter<'_>) { + fmt.header(self.obj.id, ACTION) + .uint(self.dnd_action); + } + fn obj(&self) -> &dyn Object { + &*self.obj + } +} +impl Debug for Action { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "action(dnd_action: {})", + self.dnd_action, + ) + } +} diff --git a/src/ifs/wl_data_source/mod.rs b/src/ifs/wl_data_source/mod.rs new file mode 100644 index 00000000..7bc0211b --- /dev/null +++ b/src/ifs/wl_data_source/mod.rs @@ -0,0 +1,83 @@ +mod types; + +use crate::client::{AddObj, Client}; +use crate::object::{Interface, Object, ObjectId}; +use crate::utils::buffd::MsgParser; +use std::rc::Rc; +pub use types::*; + +const OFFER: u32 = 0; +const DESTROY: u32 = 1; +const SET_ACTIONS: u32 = 2; + +const TARGET: u32 = 0; +const SEND: u32 = 1; +const CANCELLED: u32 = 2; +const DND_DROP_PERFORMED: u32 = 4; +const DND_FINISHED: u32 = 5; +const ACTION: u32 = 5; + +const INVALID_ACTION_MASK: u32 = 0; +const INVALID_SOURCE: u32 = 1; + +id!(WlDataSourceId); + +pub struct WlDataSource { + id: WlDataSourceId, + client: Rc, +} + +impl WlDataSource { + pub fn new(id: WlDataSourceId, client: &Rc) -> Self { + Self { + id, + client: client.clone(), + } + } + + async fn offer(&self, parser: MsgParser<'_, '_>) -> Result<(), OfferError> { + let _req: Offer = self.client.parse(self, parser)?; + Ok(()) + } + + async fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), DestroyError> { + let _req: Destroy = self.client.parse(self, parser)?; + self.client.remove_obj(self).await?; + Ok(()) + } + + async fn set_actions(&self, parser: MsgParser<'_, '_>) -> Result<(), SetActionsError> { + let _req: SetActions = self.client.parse(self, parser)?; + Ok(()) + } + + async fn handle_request_( + self: &Rc, + request: u32, + parser: MsgParser<'_, '_>, + ) -> Result<(), WlDataSourceError> { + match request { + OFFER => self.offer(parser).await?, + DESTROY => self.destroy(parser).await?, + SET_ACTIONS => self.set_actions(parser).await?, + _ => unreachable!(), + } + Ok(()) + } +} + +handle_request!(WlDataSource); + +impl Object for WlDataSource { + fn id(&self) -> ObjectId { + self.id.into() + } + + fn interface(&self) -> Interface { + Interface::WlDataSource + } + + fn num_requests(&self) -> u32 { + SET_ACTIONS + 1 + } +} diff --git a/src/ifs/wl_data_source/types.rs b/src/ifs/wl_data_source/types.rs new file mode 100644 index 00000000..97c292c8 --- /dev/null +++ b/src/ifs/wl_data_source/types.rs @@ -0,0 +1,211 @@ +use crate::client::{ClientError, EventFormatter, RequestParser}; +use crate::ifs::wl_data_source::{ + WlDataSource, ACTION, CANCELLED, DND_DROP_PERFORMED, DND_FINISHED, SEND, TARGET, +}; +use crate::object::Object; +use crate::utils::buffd::{MsgFormatter, MsgParser, MsgParserError}; +use bstr::{BStr, BString}; +use std::fmt::{Debug, Formatter}; +use std::rc::Rc; +use thiserror::Error; +use uapi::OwnedFd; + +#[derive(Debug, Error)] +pub enum WlDataSourceError { + #[error(transparent)] + ClientError(Box), + #[error("Could not process `offer` request")] + OfferError(#[from] OfferError), + #[error("Could not process `destroy` request")] + DestroyError(#[from] DestroyError), + #[error("Could not process `set_actions` request")] + SetActionsError(#[from] SetActionsError), +} +efrom!(WlDataSourceError, ClientError, ClientError); + +#[derive(Debug, Error)] +pub enum OfferError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), + #[error(transparent)] + ClientError(Box), +} +efrom!(OfferError, ParseFailed, MsgParserError); +efrom!(OfferError, ClientError, ClientError); + +#[derive(Debug, Error)] +pub enum DestroyError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), + #[error(transparent)] + ClientError(Box), +} +efrom!(DestroyError, ParseFailed, MsgParserError); +efrom!(DestroyError, ClientError, ClientError); + +#[derive(Debug, Error)] +pub enum SetActionsError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), + #[error(transparent)] + ClientError(Box), +} +efrom!(SetActionsError, ParseFailed, MsgParserError); +efrom!(SetActionsError, ClientError, ClientError); + +pub(super) struct Offer<'a> { + pub mime_type: &'a BStr, +} +impl<'a> RequestParser<'a> for Offer<'a> { + fn parse(parser: &mut MsgParser<'_, 'a>) -> Result { + Ok(Self { + mime_type: parser.string()?, + }) + } +} +impl Debug for Offer<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "offer(mime_type: {:?})", self.mime_type) + } +} + +pub(super) struct Destroy; +impl RequestParser<'_> for Destroy { + fn parse(_parser: &mut MsgParser<'_, '_>) -> Result { + Ok(Self) + } +} +impl Debug for Destroy { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "destroy()",) + } +} + +pub(super) struct SetActions { + pub actions: u32, +} +impl RequestParser<'_> for SetActions { + fn parse(parser: &mut MsgParser<'_, '_>) -> Result { + Ok(Self { + actions: parser.uint()?, + }) + } +} +impl Debug for SetActions { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "set_actions(actions: {})", self.actions) + } +} + +pub(super) struct Target { + pub obj: Rc, + pub mime_type: BString, +} +impl EventFormatter for Target { + fn format(self: Box, fmt: &mut MsgFormatter<'_>) { + fmt.header(self.obj.id, TARGET).string(&self.mime_type); + } + fn obj(&self) -> &dyn Object { + &*self.obj + } +} +impl Debug for Target { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "target(mime_type: {:?})", self.mime_type) + } +} + +pub(super) struct Send { + pub obj: Rc, + pub mime_type: BString, + pub fd: Rc, +} +impl EventFormatter for Send { + fn format(self: Box, fmt: &mut MsgFormatter<'_>) { + fmt.header(self.obj.id, SEND) + .string(&self.mime_type) + .fd(self.fd); + } + fn obj(&self) -> &dyn Object { + &*self.obj + } +} +impl Debug for Send { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "send(mime_type: {:?}, fd: {})", + self.mime_type, + self.fd.raw() + ) + } +} + +pub(super) struct Cancelled { + pub obj: Rc, +} +impl EventFormatter for Cancelled { + fn format(self: Box, fmt: &mut MsgFormatter<'_>) { + fmt.header(self.obj.id, CANCELLED); + } + fn obj(&self) -> &dyn Object { + &*self.obj + } +} +impl Debug for Cancelled { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "cancelled()") + } +} + +pub(super) struct DndDropPerformed { + pub obj: Rc, +} +impl EventFormatter for DndDropPerformed { + fn format(self: Box, fmt: &mut MsgFormatter<'_>) { + fmt.header(self.obj.id, DND_DROP_PERFORMED); + } + fn obj(&self) -> &dyn Object { + &*self.obj + } +} +impl Debug for DndDropPerformed { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "dnd_drop_performed()") + } +} + +pub(super) struct DndFinished { + pub obj: Rc, +} +impl EventFormatter for DndFinished { + fn format(self: Box, fmt: &mut MsgFormatter<'_>) { + fmt.header(self.obj.id, DND_FINISHED); + } + fn obj(&self) -> &dyn Object { + &*self.obj + } +} +impl Debug for DndFinished { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "dnd_finished()") + } +} + +pub(super) struct Action { + pub obj: Rc, + pub dnd_action: u32, +} +impl EventFormatter for Action { + fn format(self: Box, fmt: &mut MsgFormatter<'_>) { + fmt.header(self.obj.id, ACTION).uint(self.dnd_action); + } + fn obj(&self) -> &dyn Object { + &*self.obj + } +} +impl Debug for Action { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "action(dnd_action: {})", self.dnd_action) + } +} diff --git a/src/ifs/wl_output/mod.rs b/src/ifs/wl_output/mod.rs index ea602276..48d44037 100644 --- a/src/ifs/wl_output/mod.rs +++ b/src/ifs/wl_output/mod.rs @@ -43,6 +43,8 @@ const MODE_PREFERRED: u32 = 2; pub struct WlOutputGlobal { name: GlobalName, output: Rc, + pub x: Cell, + pub y: Cell, width: Cell, height: Cell, bindings: CopyHashMap<(ClientId, WlOutputId), Rc>, @@ -53,6 +55,8 @@ impl WlOutputGlobal { Self { name, output: output.clone(), + x: Cell::new(0), + y: Cell::new(0), width: Cell::new(output.width()), height: Cell::new(output.height()), bindings: Default::default(), @@ -123,6 +127,10 @@ impl Global for WlOutputGlobal { self.name } + fn singleton(&self) -> bool { + false + } + fn interface(&self) -> Interface { Interface::WlOutput } diff --git a/src/ifs/wl_seat/mod.rs b/src/ifs/wl_seat/mod.rs index 97ebb46a..2cff5bf4 100644 --- a/src/ifs/wl_seat/mod.rs +++ b/src/ifs/wl_seat/mod.rs @@ -3,17 +3,26 @@ pub mod wl_keyboard; pub mod wl_pointer; pub mod wl_touch; -use crate::backend::{Seat, SeatEvent}; +use crate::backend::{KeyState, OutputId, ScrollAxis, Seat, SeatEvent}; use crate::client::{AddObj, Client, ClientId, DynEventFormatter}; +use crate::fixed::Fixed; use crate::globals::{Global, GlobalName}; +use crate::ifs::wl_seat::wl_keyboard::{WlKeyboard, WlKeyboardId}; +use crate::ifs::wl_seat::wl_pointer::{WlPointer, WlPointerId}; +use crate::ifs::wl_seat::wl_touch::WlTouch; use crate::object::{Interface, Object, ObjectId}; +use crate::tree::{Node, NodeBase, NodeKind, ToplevelNode}; use crate::utils::buffd::MsgParser; use crate::utils::copyhashmap::CopyHashMap; +use crate::xkbcommon::XkbContext; +use crate::State; +use ahash::AHashMap; +use bstr::ByteSlice; +use std::cell::{Cell, RefCell}; +use std::io::Write; use std::rc::Rc; pub use types::*; -use crate::ifs::wl_seat::wl_keyboard::WlKeyboard; -use crate::ifs::wl_seat::wl_pointer::WlPointer; -use crate::ifs::wl_seat::wl_touch::WlTouch; +use uapi::{c, OwnedFd}; id!(WlSeatId); @@ -33,23 +42,183 @@ const MISSING_CAPABILITY: u32 = 0; pub struct WlSeatGlobal { name: GlobalName, + state: Rc, seat: Rc, - bindings: CopyHashMap<(ClientId, WlSeatId), Rc>, + move_: Cell, + move_start_pos: Cell<(Fixed, Fixed)>, + extents_start_pos: Cell<(i32, i32)>, + pos: Cell<(Fixed, Fixed)>, + cursor_node: RefCell>, + bindings: RefCell>>>, + layout: Rc, + layout_size: u32, } impl WlSeatGlobal { - pub fn new(name: GlobalName, seat: &Rc) -> Self { + pub fn new(name: GlobalName, state: &Rc, seat: &Rc) -> Self { + let (layout, layout_size) = { + let ctx = XkbContext::new().unwrap(); + let keymap = ctx.default_keymap().unwrap(); + let string = keymap.as_str().unwrap(); + let mut memfd = + uapi::memfd_create("keymap", c::MFD_CLOEXEC | c::MFD_ALLOW_SEALING).unwrap(); + memfd.write_all(string.as_bytes()).unwrap(); + memfd.write_all(&[0]).unwrap(); + uapi::lseek(memfd.raw(), 0, c::SEEK_SET).unwrap(); + uapi::fcntl_add_seals( + memfd.raw(), + c::F_SEAL_SEAL | c::F_SEAL_GROW | c::F_SEAL_SHRINK | c::F_SEAL_WRITE, + ) + .unwrap(); + (Rc::new(memfd), (string.len() + 1) as _) + }; Self { name, + state: state.clone(), seat: seat.clone(), + move_: Cell::new(false), + move_start_pos: Cell::new((Fixed(0), Fixed(0))), + extents_start_pos: Cell::new((0, 0)), + pos: Cell::new((Fixed(0), Fixed(0))), + cursor_node: RefCell::new(state.root.clone()), bindings: Default::default(), + layout, + layout_size, + } + } + + pub fn move_(&self, node: &Rc) { + let cursor = self.cursor_node.borrow().clone(); + if cursor.id() == node.id() { + self.move_.set(true); + self.move_start_pos.set(self.pos.get()); + let ex = node.common.extents.get(); + self.extents_start_pos.set((ex.x, ex.y)); } } pub async fn event(&self, event: SeatEvent) { - log::debug!("se: {:?}", event); + match event { + SeatEvent::OutputPosition(o, x, y) => self.output_position_event(o, x, y).await, + SeatEvent::Motion(dx, dy) => self.motion_event(dx, dy).await, + SeatEvent::Button(b, s) => self.button_event(b, s).await, + SeatEvent::Scroll(d, a) => self.scroll_event(d, a).await, + SeatEvent::Key(k, s) => self.key_event(k, s).await, + } } + async fn output_position_event(&self, output: OutputId, mut x: Fixed, mut y: Fixed) { + let output = match self.state.outputs.get(&output) { + Some(o) => o, + _ => return, + }; + x += Fixed::from_int(output.x.get()); + y += Fixed::from_int(output.y.get()); + self.handle_new_position(x, y).await; + } + + fn for_each_pointer(&self, client: ClientId, mut f: C) + where + C: FnMut(&Rc), + { + let bindings = self.bindings.borrow(); + if let Some(hm) = bindings.get(&client) { + for seat in hm.values() { + let pointers = seat.pointers.lock(); + for pointer in pointers.values() { + f(pointer); + } + } + } + } + + async fn tl_pointer_event(&self, tl: &ToplevelNode, mut f: F) + where + F: FnMut(&Rc) -> DynEventFormatter, + { + let client = &tl.surface.surface.surface.client; + self.for_each_pointer(client.id, |p| { + client.event_locked(f(p)); + }); + let _ = client.flush().await; + } + + async fn handle_new_position(&self, x: Fixed, y: Fixed) { + self.pos.set((x, y)); + let cur_node = self.cursor_node.borrow().clone(); + if self.move_.get() { + if let NodeKind::Toplevel(tn) = cur_node.into_kind() { + let (move_start_x, move_start_y) = self.move_start_pos.get(); + let (move_start_ex, move_start_ey) = self.extents_start_pos.get(); + let mut ex = tn.common.extents.get(); + ex.x = (x - move_start_x).round_down() + move_start_ex; + ex.y = (y - move_start_y).round_down() + move_start_ey; + tn.common.extents.set(ex); + } + return; + } + let x_int = x.round_down(); + let y_int = y.round_down(); + let (node_dyn, x_int, y_int) = self.state.root.clone().find_node_at(x_int, y_int); + let mut x = x.apply_fract(x_int); + let mut y = x.apply_fract(y_int); + let node = node_dyn.clone().into_kind(); + let mut enter = false; + if node_dyn.id() != cur_node.id() { + if let NodeKind::Toplevel(tl) = cur_node.into_kind() { + self.tl_pointer_event(&tl, |p| p.leave(0, tl.surface.surface.surface.id)) + .await; + } + enter = true; + *self.cursor_node.borrow_mut() = node_dyn; + } + if let NodeKind::Toplevel(tl) = &node { + let ee = tl.surface.surface.surface.effective_extents.get(); + // log::trace!("{} {}", Fixed::from_int(ee.x1), Fixed::from_int(ee.y1)); + x += Fixed::from_int(ee.x1); + y += Fixed::from_int(ee.y1); + if enter { + self.tl_pointer_event(&tl, |p| p.enter(0, tl.surface.surface.surface.id, x, y)) + .await; + } + self.tl_pointer_event(&tl, |p| p.motion(0, x, y)).await; + } + } + + async fn motion_event(&self, dx: Fixed, dy: Fixed) { + let (x, y) = self.pos.get(); + self.handle_new_position(x + dx, y + dy).await; + } + + async fn button_event(&self, button: u32, state: KeyState) { + if state == KeyState::Released { + self.move_.set(false); + } + let node = self.cursor_node.borrow().clone().into_kind(); + if let NodeKind::Toplevel(node) = node { + let state = match state { + KeyState::Released => wl_pointer::RELEASED, + KeyState::Pressed => wl_pointer::PRESSED, + }; + self.tl_pointer_event(&node, |p| p.button(0, 0, button, state)) + .await; + } + } + + async fn scroll_event(&self, delta: i32, axis: ScrollAxis) { + let node = self.cursor_node.borrow().clone().into_kind(); + if let NodeKind::Toplevel(node) = node { + let axis = match axis { + ScrollAxis::Horizontal => wl_pointer::HORIZONTAL_SCROLL, + ScrollAxis::Vertical => wl_pointer::VERTICAL_SCROLL, + }; + self.tl_pointer_event(&node, |p| p.axis(0, axis, Fixed::from_int(delta))) + .await; + } + } + + async fn key_event(&self, key: u32, state: KeyState) {} + async fn bind_( self: Rc, id: WlSeatId, @@ -60,10 +229,18 @@ impl WlSeatGlobal { global: self.clone(), id, client: client.clone(), + pointers: Default::default(), + keyboards: Default::default(), }); client.add_client_obj(&obj)?; client.event(obj.capabilities()).await?; - self.bindings.set((client.id, id), obj.clone()); + { + let mut bindings = self.bindings.borrow_mut(); + let bindings = bindings + .entry(client.id) + .or_insert_with(|| Default::default()); + bindings.insert(id, obj.clone()); + } Ok(()) } } @@ -75,6 +252,10 @@ impl Global for WlSeatGlobal { self.name } + fn singleton(&self) -> bool { + false + } + fn interface(&self) -> Interface { Interface::WlSeat } @@ -88,7 +269,7 @@ impl Global for WlSeatGlobal { } fn break_loops(&self) { - self.bindings.clear(); + self.bindings.borrow_mut().clear(); } } @@ -96,6 +277,8 @@ pub struct WlSeatObj { global: Rc, id: WlSeatId, client: Rc, + pointers: CopyHashMap>, + keyboards: CopyHashMap>, } impl WlSeatObj { @@ -106,17 +289,36 @@ impl WlSeatObj { }) } - async fn get_pointer(self: &Rc, parser: MsgParser<'_, '_>) -> Result<(), GetPointerError> { + pub fn move_(&self, node: &Rc) { + self.global.move_(node); + } + + async fn get_pointer( + self: &Rc, + parser: MsgParser<'_, '_>, + ) -> Result<(), GetPointerError> { let req: GetPointer = self.client.parse(&**self, parser)?; let p = Rc::new(WlPointer::new(req.id, self)); self.client.add_client_obj(&p)?; + self.pointers.set(req.id, p); Ok(()) } - async fn get_keyboard(self: &Rc, parser: MsgParser<'_, '_>) -> Result<(), GetKeyboardError> { + async fn get_keyboard( + self: &Rc, + parser: MsgParser<'_, '_>, + ) -> Result<(), GetKeyboardError> { let req: GetKeyboard = self.client.parse(&**self, parser)?; let p = Rc::new(WlKeyboard::new(req.id, self)); self.client.add_client_obj(&p)?; + self.keyboards.set(req.id, p.clone()); + self.client + .event(p.keymap( + wl_keyboard::XKB_V1, + self.global.layout.clone(), + self.global.layout_size, + )) + .await?; Ok(()) } @@ -129,7 +331,12 @@ 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)); + { + let mut bindings = self.global.bindings.borrow_mut(); + if let Some(hm) = bindings.get_mut(&self.client.id) { + hm.remove(&self.id); + } + } self.client.remove_obj(self).await?; Ok(()) } @@ -166,6 +373,13 @@ impl Object for WlSeatObj { } fn break_loops(&self) { - self.global.bindings.remove(&(self.client.id, self.id)); + { + let mut bindings = self.global.bindings.borrow_mut(); + if let Some(hm) = bindings.get_mut(&self.client.id) { + hm.remove(&self.id); + } + } + self.pointers.clear(); + self.keyboards.clear(); } } diff --git a/src/ifs/wl_seat/types.rs b/src/ifs/wl_seat/types.rs index 30e222d3..df24882e 100644 --- a/src/ifs/wl_seat/types.rs +++ b/src/ifs/wl_seat/types.rs @@ -1,4 +1,6 @@ use crate::client::{ClientError, EventFormatter, RequestParser}; +use crate::ifs::wl_seat::wl_keyboard::WlKeyboardId; +use crate::ifs::wl_seat::wl_pointer::WlPointerId; use crate::ifs::wl_seat::wl_touch::WlTouchId; use crate::ifs::wl_seat::{WlSeatObj, CAPABILITIES, NAME}; use crate::object::Object; @@ -6,8 +8,6 @@ use crate::utils::buffd::{MsgFormatter, MsgParser, MsgParserError}; use std::fmt::{Debug, Formatter}; use std::rc::Rc; use thiserror::Error; -use crate::ifs::wl_seat::wl_keyboard::WlKeyboardId; -use crate::ifs::wl_seat::wl_pointer::WlPointerId; #[derive(Debug, Error)] pub enum WlSeatError { diff --git a/src/ifs/wl_seat/wl_keyboard/mod.rs b/src/ifs/wl_seat/wl_keyboard/mod.rs index 5924509e..a4d2cb6d 100644 --- a/src/ifs/wl_seat/wl_keyboard/mod.rs +++ b/src/ifs/wl_seat/wl_keyboard/mod.rs @@ -19,7 +19,7 @@ const MODIFIERS: u32 = 4; const REPEAT_INFO: u32 = 5; const NO_KEYMAP: u32 = 0; -const XKB_V1: u32 = 1; +pub(super) const XKB_V1: u32 = 1; const RELEASED: u32 = 0; const PRESSED: u32 = 1; @@ -108,6 +108,7 @@ impl WlKeyboard { async fn release(&self, parser: MsgParser<'_, '_>) -> Result<(), ReleaseError> { let _req: Release = self.seat.client.parse(self, parser)?; + self.seat.keyboards.remove(&self.id); self.seat.client.remove_obj(self).await?; Ok(()) } diff --git a/src/ifs/wl_seat/wl_pointer/mod.rs b/src/ifs/wl_seat/wl_pointer/mod.rs index eb00cf96..e379361f 100644 --- a/src/ifs/wl_seat/wl_pointer/mod.rs +++ b/src/ifs/wl_seat/wl_pointer/mod.rs @@ -24,11 +24,11 @@ const AXIS_DISCRETE: u32 = 8; const ROLE: u32 = 0; -const RELEASED: u32 = 0; -const PRESSED: u32 = 1; +pub(super) const RELEASED: u32 = 0; +pub(super) const PRESSED: u32 = 1; -const VERTICAL_SCROLL: u32 = 0; -const HORIZONTAL_SCROLL: u32 = 1; +pub(super) const VERTICAL_SCROLL: u32 = 0; +pub(super) const HORIZONTAL_SCROLL: u32 = 1; const WHEEL: u32 = 0; const FINGER: u32 = 1; @@ -136,13 +136,13 @@ impl WlPointer { } async fn set_cursor(&self, parser: MsgParser<'_, '_>) -> Result<(), SetCursorError> { - let _req: Release = self.seat.client.parse(self, parser)?; - self.seat.client.remove_obj(self).await?; + let _req: SetCursor = self.seat.client.parse(self, parser)?; Ok(()) } async fn release(&self, parser: MsgParser<'_, '_>) -> Result<(), ReleaseError> { let _req: Release = self.seat.client.parse(self, parser)?; + self.seat.pointers.remove(&self.id); self.seat.client.remove_obj(self).await?; Ok(()) } @@ -153,6 +153,7 @@ impl WlPointer { parser: MsgParser<'_, '_>, ) -> Result<(), WlPointerError> { match request { + SET_CURSOR => self.set_cursor(parser).await?, RELEASE => self.release(parser).await?, _ => unreachable!(), } diff --git a/src/ifs/wl_seat/wl_pointer/types.rs b/src/ifs/wl_seat/wl_pointer/types.rs index 987811fa..e87102eb 100644 --- a/src/ifs/wl_seat/wl_pointer/types.rs +++ b/src/ifs/wl_seat/wl_pointer/types.rs @@ -184,7 +184,7 @@ impl Debug for Button { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!( f, - "button(serial: {}, time: {}, button: {}, state: {})", + "button(serial: {}, time: {}, button: 0x{:x}, state: {})", self.serial, self.time, self.button, self.state ) } diff --git a/src/ifs/wl_seat/wl_touch/mod.rs b/src/ifs/wl_seat/wl_touch/mod.rs index dd167e4c..9fa9658a 100644 --- a/src/ifs/wl_seat/wl_touch/mod.rs +++ b/src/ifs/wl_seat/wl_touch/mod.rs @@ -1,6 +1,6 @@ mod types; -use crate::client::{AddObj}; +use crate::client::AddObj; use crate::ifs::wl_seat::WlSeatObj; use crate::object::{Interface, Object, ObjectId}; use crate::utils::buffd::MsgParser; diff --git a/src/ifs/wl_shm/mod.rs b/src/ifs/wl_shm/mod.rs index 15527bc1..58493aa5 100644 --- a/src/ifs/wl_shm/mod.rs +++ b/src/ifs/wl_shm/mod.rs @@ -89,6 +89,10 @@ impl Global for WlShmGlobal { self.name } + fn singleton(&self) -> bool { + true + } + fn interface(&self) -> Interface { Interface::WlShm } diff --git a/src/ifs/wl_subcompositor/mod.rs b/src/ifs/wl_subcompositor/mod.rs index 4fffbad5..ab4c02a3 100644 --- a/src/ifs/wl_subcompositor/mod.rs +++ b/src/ifs/wl_subcompositor/mod.rs @@ -84,6 +84,10 @@ impl Global for WlSubcompositorGlobal { self.name } + fn singleton(&self) -> bool { + true + } + fn interface(&self) -> Interface { Interface::WlSubcompositor } diff --git a/src/ifs/wl_surface/mod.rs b/src/ifs/wl_surface/mod.rs index 75b3ee85..bb2483a0 100644 --- a/src/ifs/wl_surface/mod.rs +++ b/src/ifs/wl_surface/mod.rs @@ -60,13 +60,14 @@ impl SurfaceRole { } pub struct WlSurface { - id: WlSurfaceId, + pub id: WlSurfaceId, pub client: Rc, role: Cell, pending: PendingState, input_region: Cell>, opaque_region: Cell>, pub extents: Cell, + pub effective_extents: Cell, pub buffer: RefCell>>, pub children: RefCell>>, role_data: RefCell, @@ -105,8 +106,15 @@ struct XdgSurfaceData { requested_serial: u32, acked_serial: Option, role: XdgSurfaceRole, + extents: Option, role_data: XdgSurfaceRoleData, popups: CopyHashMap>, + pending: PendingXdgSurfaceData, +} + +#[derive(Default)] +struct PendingXdgSurfaceData { + extents: Cell>, } #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -193,6 +201,7 @@ impl WlSurface { input_region: Cell::new(None), opaque_region: Cell::new(None), extents: Default::default(), + effective_extents: Default::default(), buffer: RefCell::new(None), children: Default::default(), role_data: RefCell::new(RoleData::None), @@ -350,7 +359,7 @@ impl WlSurface { surface: td.toplevel.clone(), }); td.node = Some(ToplevelNodeHolder { node: node.clone() }); - let link = output.floating.append(node.clone()); + let link = output.floating.add_last(node.clone()); node.common .floating_outputs .borrow_mut() @@ -402,6 +411,8 @@ impl WlSurface { } fn do_commit(&self) { + let mut xdg_extents = None; + let mut td_node = None; { let mut rd = self.role_data.borrow_mut(); match &mut *rd { @@ -416,7 +427,15 @@ impl WlSurface { ss.y = y; } } - RoleData::XdgSurface(xdg) => {} + RoleData::XdgSurface(xdg) => { + if let Some(extents) = xdg.pending.extents.take() { + xdg.extents = Some(extents); + } + xdg_extents = xdg.extents; + if let XdgSurfaceRoleData::Toplevel(tl) = &xdg.role_data { + td_node = tl.node.as_ref().map(|n| n.node.clone()); + } + } } } { @@ -444,6 +463,28 @@ impl WlSurface { if !committed_any_children { self.calculate_extents(); } + let mut effective_extents = self.extents.get(); + if let Some(extents) = xdg_extents { + effective_extents.x1 = effective_extents.x1.max(extents.x1); + effective_extents.y1 = effective_extents.y1.max(extents.y1); + effective_extents.x2 = effective_extents.x2.min(extents.x2); + effective_extents.y2 = effective_extents.y2.min(extents.y2); + if effective_extents.x1 > effective_extents.x2 { + effective_extents.x1 = 0; + effective_extents.x2 = 0; + } + if effective_extents.y1 > effective_extents.y2 { + effective_extents.y1 = 0; + effective_extents.y2 = 0; + } + } + if let Some(node) = td_node { + let mut td_extents = node.common.extents.get(); + td_extents.width = (effective_extents.x2 - effective_extents.x1) as u32; + td_extents.height = (effective_extents.y2 - effective_extents.y1) as u32; + node.common.extents.set(td_extents); + } + self.effective_extents.set(effective_extents); } async fn commit(&self, parser: MsgParser<'_, '_>) -> Result<(), CommitError> { diff --git a/src/ifs/wl_surface/wl_subsurface/mod.rs b/src/ifs/wl_surface/wl_subsurface/mod.rs index ca083f7e..61f50cec 100644 --- a/src/ifs/wl_surface/wl_subsurface/mod.rs +++ b/src/ifs/wl_surface/wl_subsurface/mod.rs @@ -112,7 +112,7 @@ impl WlSubsurface { let data = data.get_or_insert_with(|| Default::default()); data.subsurfaces .insert(self.surface.id, self.surface.clone()); - data.above.prepend(StackElement { + data.above.add_first(StackElement { pending: Cell::new(true), surface: self.surface.clone(), }) @@ -168,8 +168,8 @@ impl WlSubsurface { }; if sibling == self.parent.id { let node = match above { - true => pdata.above.prepend(element), - _ => pdata.below.append(element), + true => pdata.above.add_first(element), + _ => pdata.below.add_last(element), }; data.pending.node = Some(node); } else { diff --git a/src/ifs/wl_surface/xdg_surface/mod.rs b/src/ifs/wl_surface/xdg_surface/mod.rs index 57d6dc55..fe4eb921 100644 --- a/src/ifs/wl_surface/xdg_surface/mod.rs +++ b/src/ifs/wl_surface/xdg_surface/mod.rs @@ -6,7 +6,7 @@ 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::{ - RoleData, SurfaceRole, WlSurface, XdgPopupData, XdgSurfaceData, XdgSurfaceRole, + RoleData, SurfaceExtents, SurfaceRole, WlSurface, XdgPopupData, XdgSurfaceData, XdgSurfaceRole, XdgSurfaceRoleData, XdgToplevelData, }; use crate::ifs::xdg_wm_base::XdgWmBaseObj; @@ -73,8 +73,10 @@ impl XdgSurface { requested_serial: 0, acked_serial: None, role: XdgSurfaceRole::None, + extents: None, role_data: XdgSurfaceRoleData::None, popups: Default::default(), + pending: Default::default(), })); Ok(()) } @@ -176,7 +178,20 @@ impl XdgSurface { &self, parser: MsgParser<'_, '_>, ) -> Result<(), SetWindowGeometryError> { - let _req: SetWindowGeometry = self.surface.client.parse(self, parser)?; + let req: SetWindowGeometry = self.surface.client.parse(self, parser)?; + if req.height <= 0 || req.width <= 0 { + return Err(SetWindowGeometryError::NonPositiveWidthHeight); + } + let mut rd = self.surface.role_data.borrow_mut(); + if let RoleData::XdgSurface(xdg) = rd.deref_mut() { + let extents = SurfaceExtents { + x1: req.x, + y1: req.y, + x2: req.x + req.width, + y2: req.y + req.height, + }; + xdg.pending.extents.set(Some(extents)); + } Ok(()) } diff --git a/src/ifs/wl_surface/xdg_surface/types.rs b/src/ifs/wl_surface/xdg_surface/types.rs index d98f3226..163d3914 100644 --- a/src/ifs/wl_surface/xdg_surface/types.rs +++ b/src/ifs/wl_surface/xdg_surface/types.rs @@ -74,6 +74,8 @@ pub enum SetWindowGeometryError { ParseFailed(#[source] Box), #[error(transparent)] ClientError(Box), + #[error("Tried no set a non-positive width/height")] + NonPositiveWidthHeight, } efrom!(SetWindowGeometryError, ParseFailed, MsgParserError); efrom!(SetWindowGeometryError, ClientError, ClientError); 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 dbb9b1b3..9d044115 100644 --- a/src/ifs/wl_surface/xdg_surface/xdg_toplevel/mod.rs +++ b/src/ifs/wl_surface/xdg_surface/xdg_toplevel/mod.rs @@ -5,6 +5,7 @@ use crate::ifs::wl_surface::{RoleData, XdgSurfaceRoleData}; use crate::object::{Interface, Object, ObjectId}; use crate::utils::buffd::MsgParser; use num_derive::FromPrimitive; +use std::ops::Deref; use std::rc::Rc; pub use types::*; @@ -97,7 +98,16 @@ impl XdgToplevel { } async fn move_(&self, parser: MsgParser<'_, '_>) -> Result<(), MoveError> { - let _req: Move = self.surface.surface.client.parse(self, parser)?; + let req: Move = self.surface.surface.client.parse(self, parser)?; + let rd = self.surface.surface.role_data.borrow(); + if let RoleData::XdgSurface(xdg) = rd.deref() { + if let XdgSurfaceRoleData::Toplevel(tl) = &xdg.role_data { + if let Some(node) = tl.node.as_ref() { + let seat = self.surface.surface.client.get_wl_seat(req.seat)?; + seat.move_(&node.node); + } + } + } Ok(()) } diff --git a/src/ifs/xdg_wm_base/mod.rs b/src/ifs/xdg_wm_base/mod.rs index a0ade427..e0da002a 100644 --- a/src/ifs/xdg_wm_base/mod.rs +++ b/src/ifs/xdg_wm_base/mod.rs @@ -130,6 +130,10 @@ impl Global for XdgWmBaseGlobal { self.name } + fn singleton(&self) -> bool { + true + } + fn interface(&self) -> Interface { Interface::XdgWmBase } diff --git a/src/main.rs b/src/main.rs index 3788e93e..22043893 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,7 @@ use crate::backends::xorg::{XorgBackend, XorgBackendError}; use crate::client::Clients; use crate::clientmem::ClientMemError; use crate::event_loop::EventLoopError; -use crate::globals::Globals; +use crate::globals::{AddGlobal, Globals}; use crate::ifs::wl_compositor::WlCompositorGlobal; use crate::ifs::wl_shm::WlShmGlobal; use crate::ifs::wl_subcompositor::WlSubcompositorGlobal; @@ -30,6 +30,7 @@ use log::LevelFilter; use std::rc::Rc; use thiserror::Error; use wheel::Wheel; +use crate::ifs::wl_data_device_manager::WlDataDeviceManagerGlobal; #[macro_use] mod macros; @@ -91,10 +92,11 @@ fn main_() -> Result<(), MainError> { let wheel = Wheel::install(&el)?; let engine = AsyncEngine::install(&el, &wheel)?; let globals = Globals::new(); - globals.insert_no_broadcast(Rc::new(WlCompositorGlobal::new(globals.name()))); - 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()))); + globals.add_global_no_broadcast(&Rc::new(WlCompositorGlobal::new(globals.name()))); + globals.add_global_no_broadcast(&Rc::new(WlShmGlobal::new(globals.name()))); + globals.add_global_no_broadcast(&Rc::new(WlSubcompositorGlobal::new(globals.name()))); + globals.add_global_no_broadcast(&Rc::new(XdgWmBaseGlobal::new(globals.name()))); + globals.add_global_no_broadcast(&Rc::new(WlDataDeviceManagerGlobal::new(globals.name()))); let node_ids = NodeIds::default(); let state = Rc::new(State { eng: engine.clone(), @@ -111,6 +113,7 @@ fn main_() -> Result<(), MainError> { output_handlers: Default::default(), seat_ids: Default::default(), seat_handlers: Default::default(), + outputs: Default::default(), }); let _global_event_handler = engine.spawn(tasks::handle_backend_events(state.clone())); Acceptor::install(&state)?; diff --git a/src/object.rs b/src/object.rs index ba1e5cec..835b6482 100644 --- a/src/object.rs +++ b/src/object.rs @@ -56,6 +56,10 @@ pub enum Interface { WlPointer, WlKeyboard, WlSubcompositor, + WlDataDeviceManager, + WlDataDevice, + WlDataSource, + WlDataOffer, XdgWmBase, XdgPositioner, WlSurface, @@ -92,6 +96,10 @@ impl Interface { Interface::WlTouch => "wl_touch", Interface::WlPointer => "wl_pointer", Interface::WlKeyboard => "wl_keyboard", + Interface::WlDataDeviceManager => "wl_data_device_manager", + Interface::WlDataDevice => "wl_data_device", + Interface::WlDataSource => "wl_data_source", + Interface::WlDataOffer => "wl_data_offer", } } } diff --git a/src/pixman/mod.rs b/src/pixman/mod.rs index bf50ec62..e6dd0e65 100644 --- a/src/pixman/mod.rs +++ b/src/pixman/mod.rs @@ -231,13 +231,22 @@ where } pub fn fill(&self, r: u8, g: u8, b: u8, a: u8) -> Result<(), PixmanError> { + self.fill_rect(r, g, b, a, 0, 0, self.width as _, self.height as _) + } + + pub fn fill_rect( + &self, + r: u8, + g: u8, + b: u8, + a: u8, + x1: i32, + y1: i32, + x2: i32, + y2: i32, + ) -> Result<(), PixmanError> { self.memory.access(|_| { - let bx = Box32 { - x1: 0, - y1: 0, - x2: self.width as _, - y2: self.height as _, - }; + let bx = Box32 { x1, y1, x2, y2 }; let color = Color { red: (r as u16) << 8, green: (g as u16) << 8, @@ -251,6 +260,70 @@ where Ok(()) } + pub fn fill_insert_border( + &self, + r: u8, + g: u8, + b: u8, + a: u8, + x1: i32, + y1: i32, + x2: i32, + y2: i32, + width: i32, + ) -> Result<(), PixmanError> { + self.memory.access(|_| { + let mut bx = [ + Box32 { + x1, + y1, + x2, + y2: y1 + width, + }, + Box32 { + x1: x2 - width, + y1, + x2, + y2, + }, + Box32 { + x1, + y1, + x2: x1 + width, + y2, + }, + Box32 { + x1, + y1: y2 - width, + x2, + y2, + }, + ]; + for bx in &mut bx { + bx.x1 = bx.x1.max(0).min(self.width as i32); + bx.x2 = bx.x2.max(0).min(self.width as i32); + bx.y1 = bx.y1.max(0).min(self.height as i32); + bx.y2 = bx.y2.max(0).min(self.height as i32); + } + 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, + bx.len() as _, + bx.as_ptr(), + ); + } + })?; + Ok(()) + } + pub fn add_image(&self, over: &Image, x: i32, y: i32) -> Result<(), PixmanError> where U: PixmanMemory, diff --git a/src/state.rs b/src/state.rs index bb67c330..79c1421c 100644 --- a/src/state.rs +++ b/src/state.rs @@ -3,8 +3,10 @@ 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::globals::{AddGlobal, Globals}; +use crate::ifs::wl_output::WlOutputGlobal; use crate::tree::{DisplayNode, NodeIds}; +use crate::utils::copyhashmap::CopyHashMap; use crate::utils::numcell::NumCell; use crate::utils::queue::AsyncQueue; use crate::Wheel; @@ -27,4 +29,14 @@ pub struct State { pub backend_events: AsyncQueue, pub output_handlers: RefCell>>, pub seat_handlers: RefCell>>, + pub outputs: CopyHashMap>, +} + +impl State { + pub async fn add_global(&self, global: &Rc) + where + Globals: AddGlobal, + { + self.globals.add_global(self, global).await + } } diff --git a/src/tasks.rs b/src/tasks.rs index 967734c3..0c36ec63 100644 --- a/src/tasks.rs +++ b/src/tasks.rs @@ -83,7 +83,8 @@ impl OutputHandler { 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; + self.state.add_global(&global).await; + self.state.outputs.set(self.output.id(), global.clone()); loop { if self.output.removed() { break; @@ -97,6 +98,7 @@ impl OutputHandler { global.update_properties().await; ae.triggered().await; } + self.state.outputs.remove(&self.output.id()); self.state.globals.remove(&self.state, name).await; self.state .output_handlers @@ -118,8 +120,8 @@ impl SeatHandler { 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; + let global = Rc::new(WlSeatGlobal::new(name, &self.state, &self.seat)); + self.state.add_global(&global).await; loop { if self.seat.removed() { break; diff --git a/src/time.rs b/src/time.rs index 67ee0e45..cbdc56d4 100644 --- a/src/time.rs +++ b/src/time.rs @@ -76,7 +76,7 @@ impl Sub