Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion anvil/src/libei.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pub fn listen_eis(handle: &calloop::LoopHandle<'static, AnvilState<UdevData>>) {
let _ = seat.add_keyboard("virtual keyboard", XkbConfig::default());
seat.add_pointer("virtual pointer");
seat.add_pointer_absolute("virtual absolute pointer", &[]);
seat.add_touch("virtual touch");
seat.add_touch("virtual touch", &[]);
}
EiInputEvent::Disconnected => {}
EiInputEvent::Event(event) => {
Expand Down
4 changes: 3 additions & 1 deletion src/backend/libei/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ impl InputBackend for EiInput {
type TouchUpEvent = request::TouchUp;
type TouchMotionEvent = request::TouchMotion;
type TouchCancelEvent = request::TouchCancel;
type TouchFrameEvent = input::UnusedEvent;
type TouchFrameEvent = request::Frame;

type TabletToolAxisEvent = input::UnusedEvent;
type TabletToolProximityEvent = input::UnusedEvent;
Expand Down Expand Up @@ -102,6 +102,8 @@ impl<T: request::DeviceEvent + request::EventTime> input::Event<EiInput> for T {
}
}

impl input::TouchFrameEvent<EiInput> for request::Frame {}

impl input::KeyboardKeyEvent<EiInput> for request::KeyboardKey {
fn key_code(&self) -> input::Keycode {
input::Keycode::from(self.key + 8)
Expand Down
13 changes: 11 additions & 2 deletions src/backend/libei/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
//! let _ = seat.add_keyboard("virtual keyboard", XkbConfig::default());
//! seat.add_pointer("virtual pointer");
//! seat.add_pointer_absolute("virtual absolute pointer", &[]);
//! seat.add_touch("virtual touch");
//! seat.add_touch("virtual touch", &[]);
//! }
//! EiInputEvent::Disconnected => {}
//! EiInputEvent::Event(event) => {
Expand Down Expand Up @@ -299,7 +299,16 @@ fn convert_request(request: EisRequest) -> Option<InputEvent<EiInput>> {
EisRequest::TouchMotion(event) => Some(InputEvent::TouchMotion { event }),
EisRequest::TouchCancel(event) => Some(InputEvent::TouchCancel { event }),
EisRequest::DeviceClosed(event) => Some(InputEvent::DeviceRemoved { device: event.device }),
EisRequest::Frame(_) => None,
// `ei_device.frame` is not touch-specific: it commits whatever the client has sent
// for that device. The capability check is sufficient here because reis only emits
// a frame for a device that had pending events of its own, and every device we
// create has a single capability. So a frame on a touch-capable device
// necessarily contains touch events. This would need revisiting if a device ever
// advertised `Touch` alongside another capability.
EisRequest::Frame(event) => event
.device
.has_capability(reis::request::DeviceCapability::Touch)
.then_some(InputEvent::TouchFrame { event }),
// `TextKeysym`/`TextUtf8` are surfaced as `EiInputEvent::TextKeysym`/`TextUtf8` directly,
// so they never reach here.
EisRequest::TextKeysym(_)
Expand Down
78 changes: 51 additions & 27 deletions src/backend/libei/seat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ impl EiInputSeat {
keyboard: None,
pointer: None,
pointer_absolute: None,
pointer_absolute_regions: Vec::new(),
regions: Vec::new(),
touch: None,
text: None,
device_keyboard: None,
Expand Down Expand Up @@ -162,7 +162,7 @@ impl EiInputSeat {
let mut inner = self.0.lock().unwrap();
inner.device_pointer_absolute = None;
inner.pointer_absolute = Some(name.to_string());
inner.pointer_absolute_regions = regions.to_vec();
inner.regions = regions.to_vec();
inner.refresh_devices();
}

Expand All @@ -171,18 +171,38 @@ impl EiInputSeat {
let mut inner = self.0.lock().unwrap();
inner.device_pointer_absolute = None;
inner.pointer_absolute = None;
inner.pointer_absolute_regions.clear();
inner.flush();
}

/// Add a touch device to the EI seat
///
/// `regions` describes the logical coordinate space(s) the client may send absolute touch
/// coordinates in (see [`EiRegion`]).
///
/// Calling on a seat that already has a touch device will remove
/// that device and add a new one.
pub fn add_touch(&self, name: &str) {
pub fn add_touch(&self, name: &str, regions: &[EiRegion]) {
let mut inner = self.0.lock().unwrap();
inner.device_touch = None;
inner.touch = Some(name.to_string());
inner.regions = regions.to_vec();
inner.refresh_devices();
}

/// Update the coordinate regions advertised to the client, recreating whichever absolute
/// devices (absolute pointer, touch) this seat already has.
///
/// Regions are immutable once a device is created, so the devices have to be replaced for a
/// client to see a new output layout/scale.
pub fn update_regions(&self, regions: &[EiRegion]) {
let mut inner = self.0.lock().unwrap();
inner.regions = regions.to_vec();
if inner.pointer_absolute.is_some() {
inner.device_pointer_absolute = None;
}
if inner.touch.is_some() {
inner.device_touch = None;
}
inner.refresh_devices();
}

Expand Down Expand Up @@ -239,7 +259,7 @@ struct EiInputSeatInner {
pointer_absolute: Option<String>,
// Regions advertised on the absolute pointer device, describing the logical
// coordinate space(s) the client may address (see `EiRegion`).
pointer_absolute_regions: Vec<EiRegion>,
regions: Vec<EiRegion>,
touch: Option<String>,
text: Option<String>,
// Devices created in response to client bind
Expand Down Expand Up @@ -309,31 +329,12 @@ impl EiInputSeatInner {
.contains(DeviceCapability::PointerAbsolute)
{
if let Some(name) = self.pointer_absolute.as_ref() {
let regions = self.pointer_absolute_regions.clone();
let regions = self.regions.clone();
let device = self.seat.add_device(
Some(name),
DeviceType::Virtual,
DeviceCapability::PointerAbsolute | DeviceCapability::Button | DeviceCapability::Scroll,
|device| {
// Advertise the coordinate space(s) the client may point within.
// These `ei_device.region` events must be sent after the device is
// created but before `done`, which is exactly when this closure runs.
for region in &regions {
// `region_mapping_id` applies to the region created by the
// next `region` request, so send it first when present.
if let Some(mapping_id) = &region.mapping_id {
device.device().region_mapping_id(mapping_id);
}
// `ei_device.region` coordinates are unsigned
device.device().region(
region.rect.loc.x.max(0) as u32,
region.rect.loc.y.max(0) as u32,
region.rect.size.w.max(0) as u32,
region.rect.size.h.max(0) as u32,
region.scale,
);
}
},
|device| advertise_regions(device, &regions),
);
device.resumed();
let _ = self.event_sender.send(InputEvent::DeviceAdded {
Expand All @@ -345,11 +346,12 @@ impl EiInputSeatInner {

if self.device_touch.is_none() && self.bound_capabilities.contains(DeviceCapability::Touch) {
if let Some(name) = self.touch.as_ref() {
let regions = self.regions.clone();
let device = self.seat.add_device(
Some(name),
DeviceType::Virtual,
DeviceCapability::Touch.into(),
|_| {},
|device| advertise_regions(device, &regions),
);
device.resumed();
let _ = self.event_sender.send(InputEvent::DeviceAdded {
Expand Down Expand Up @@ -380,6 +382,28 @@ impl EiInputSeatInner {
}
}

/// Advertise the coordinate space(s) a client may address on an absolute device.
///
/// These `ei_device.region` events must be sent after the device is created but before `done`,
/// i.e. from within `add_device`'s callback.
fn advertise_regions(device: &reis::request::Device, regions: &[EiRegion]) {
for region in regions {
// `region_mapping_id` applies to the region created by the next `region` request,
// so send it first when present.
if let Some(mapping_id) = &region.mapping_id {
device.device().region_mapping_id(mapping_id);
}
// `ei_device.region` coordinates are unsigned
device.device().region(
region.rect.loc.x.max(0) as u32,
region.rect.loc.y.max(0) as u32,
region.rect.size.w.max(0) as u32,
region.rect.size.h.max(0) as u32,
region.scale,
);
}
}

// Helper that remove the device on drop, and send `DeviceRemoved`
#[derive(Debug)]
struct DeviceDropWrapper {
Expand Down
Loading