autocommit 2022-01-06 19:08:32 CET
This commit is contained in:
parent
cbbc41a463
commit
4a939477a2
51 changed files with 3438 additions and 207 deletions
1
src/backends/mod.rs
Normal file
1
src/backends/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
pub mod xorg;
|
||||
787
src/backends/xorg/mod.rs
Normal file
787
src/backends/xorg/mod.rs
Normal file
|
|
@ -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<EventLoopError>),
|
||||
#[error("The timer wheel caused an error")]
|
||||
WheelError(#[source] Box<WheelError>),
|
||||
#[error("Could not allocate and map image memory")]
|
||||
ServerMemError(#[source] Box<ServerMemError>),
|
||||
#[error("Pixman returned an error")]
|
||||
PixmanError(#[source] Box<PixmanError>),
|
||||
#[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<XkbCommonError>),
|
||||
}
|
||||
efrom!(XorgBackendError, EventLoopError, EventLoopError);
|
||||
efrom!(XorgBackendError, ServerMemError, ServerMemError);
|
||||
efrom!(XorgBackendError, PixmanError, PixmanError);
|
||||
efrom!(XorgBackendError, WheelError, WheelError);
|
||||
efrom!(XorgBackendError, XkbCommon, XkbCommonError);
|
||||
|
||||
struct XcbCon {
|
||||
xcb: Box<Xcb>,
|
||||
shm: Box<XcbShm>,
|
||||
input: Box<XcbXinput>,
|
||||
input_opcode: u8,
|
||||
xkb: Box<XcbXkb>,
|
||||
c: *mut ffi::xcb_connection_t,
|
||||
errors: XcbErrorParser,
|
||||
}
|
||||
|
||||
impl XcbCon {
|
||||
fn new() -> Result<Self, XorgBackendError> {
|
||||
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<T>(
|
||||
&self,
|
||||
reply: *mut T,
|
||||
err: *mut ffi::xcb_generic_error_t,
|
||||
) -> Result<XcbBox<T>, 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<State>,
|
||||
con: XcbCon,
|
||||
xkbcommon: XkbContext,
|
||||
xkbcommonx11: XkbCommonX11,
|
||||
outputs: CopyHashMap<ffi::xcb_window_t, Rc<XorgOutput>>,
|
||||
seats: CopyHashMap<ffi::xcb_input_device_id_t, Rc<XorgSeat>>,
|
||||
mouse_seats: CopyHashMap<ffi::xcb_input_device_id_t, Rc<XorgSeat>>,
|
||||
}
|
||||
|
||||
impl XorgBackend {
|
||||
pub fn new(state: &Rc<State>) -> Result<Rc<Self>, 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<Self>) -> 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<Self>,
|
||||
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<Self>, 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<Self>) -> 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<Self>,
|
||||
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<Self>,
|
||||
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<Self>,
|
||||
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<Self>,
|
||||
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<Rc<ServerMem>>, node: Rc<dyn Node>) {
|
||||
match node.into_kind() {
|
||||
NodeKind::Display(_) => {}
|
||||
NodeKind::Output(_) => {}
|
||||
NodeKind::Toplevel(tl) => self.render_toplevel(image, &tl),
|
||||
NodeKind::Container(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn render_toplevel(&self, image: &Image<Rc<ServerMem>>, tl: &Rc<ToplevelNode>) {
|
||||
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<Rc<ServerMem>>,
|
||||
surface: &Rc<WlSurface>,
|
||||
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<Rc<ServerMem>>, buffer: &Rc<WlBuffer>, 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<Self>, events: i32) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
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<Self>) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
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<XorgBackend>,
|
||||
window: ffi::xcb_window_t,
|
||||
removed: Cell<bool>,
|
||||
width: Cell<u32>,
|
||||
height: Cell<u32>,
|
||||
image: RefCell<Option<Image<Rc<ServerMem>>>>,
|
||||
cb: RefCell<Option<Rc<dyn Fn()>>>,
|
||||
}
|
||||
|
||||
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<dyn Fn()>) {
|
||||
*self.cb.borrow_mut() = Some(cb);
|
||||
}
|
||||
}
|
||||
|
||||
struct XorgSeat {
|
||||
id: SeatId,
|
||||
backend: Rc<XorgBackend>,
|
||||
kb: ffi::xcb_input_device_id_t,
|
||||
mouse: ffi::xcb_input_device_id_t,
|
||||
removed: Cell<bool>,
|
||||
cb: RefCell<Option<Rc<dyn Fn()>>>,
|
||||
events: RefCell<VecDeque<SeatEvent>>,
|
||||
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<SeatEvent> {
|
||||
self.events.borrow_mut().pop_front()
|
||||
}
|
||||
|
||||
fn on_change(&self, cb: Rc<dyn Fn()>) {
|
||||
*self.cb.borrow_mut() = Some(cb);
|
||||
}
|
||||
}
|
||||
|
||||
fn fp1616_to_f64(i: ffi::xcb_input_fp1616_t) -> f64 {
|
||||
i as f64 / (1 << 16) as f64
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue