834 lines
27 KiB
Rust
834 lines
27 KiB
Rust
mod allocator;
|
|
mod input;
|
|
mod monitor;
|
|
mod present;
|
|
mod transaction;
|
|
mod video;
|
|
|
|
use {
|
|
crate::{
|
|
async_engine::SpawnedFuture,
|
|
backend::{
|
|
Backend, ButtonState, InputDevice, InputDeviceAccelProfile, InputDeviceCapability,
|
|
InputDeviceClickMethod, InputDeviceGroupId, InputDeviceId, InputEvent, KeyState, Leds,
|
|
TransformMatrix, transaction::BackendConnectorTransactionError,
|
|
},
|
|
backends::metal::{
|
|
allocator::{RenderBufferError, ScanoutBufferError, ScanoutBufferErrors},
|
|
video::{
|
|
MetalDrmDeviceData, MetalLeaseData, MetalRenderContext, PendingDrmDevice,
|
|
PersistentDisplayData,
|
|
},
|
|
},
|
|
dbus::{DbusError, SignalHandler},
|
|
drm_feedback::DrmFeedback,
|
|
gfx_api::{GfxError, SyncFile},
|
|
ifs::{
|
|
wl_output::OutputId,
|
|
wl_seat::tablet::{
|
|
TabletId, TabletInit, TabletPadGroupInit, TabletPadId, TabletPadInit,
|
|
},
|
|
},
|
|
libinput::{
|
|
LibInput, LibInputAdapter, LibInputError,
|
|
consts::{
|
|
AccelProfile, ConfigClickMethod, LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
|
|
LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT, LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS,
|
|
LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER, LIBINPUT_CONFIG_CLICK_METHOD_NONE,
|
|
LIBINPUT_DEVICE_CAP_TABLET_PAD, LIBINPUT_DEVICE_CAP_TABLET_TOOL, Led,
|
|
},
|
|
device::{LibInputDevice, RegisteredDevice},
|
|
},
|
|
logind::{LogindError, Session},
|
|
state::State,
|
|
udev::{Udev, UdevError, UdevMonitor},
|
|
utils::{
|
|
clonecell::{CloneCell, UnsafeCellCloneSafe},
|
|
copyhashmap::CopyHashMap,
|
|
errorfmt::ErrorFmt,
|
|
hash_map_ext::HashMapExt,
|
|
numcell::NumCell,
|
|
oserror::{OsError, OsErrorExt2},
|
|
smallmap::SmallMap,
|
|
syncqueue::SyncQueue,
|
|
},
|
|
video::{drm::DrmError, gbm::GbmError},
|
|
},
|
|
bstr::ByteSlice,
|
|
std::{
|
|
cell::{Cell, RefCell},
|
|
error::Error,
|
|
ffi::{CStr, CString},
|
|
fmt::{Debug, Formatter},
|
|
future::pending,
|
|
rc::Rc,
|
|
},
|
|
thiserror::Error,
|
|
uapi::{OwnedFd, c},
|
|
};
|
|
|
|
#[derive(Debug, Error)]
|
|
pub enum MetalError {
|
|
#[error("Could not connect to the dbus system socket")]
|
|
DbusSystemSocket(#[source] DbusError),
|
|
#[error("Could not retrieve the logind session")]
|
|
LogindSession(#[source] LogindError),
|
|
#[error("Could not take control of the logind session")]
|
|
TakeControl(#[source] LogindError),
|
|
#[error("Could not enumerate devices")]
|
|
Enumerate(#[source] Box<Self>),
|
|
#[error(transparent)]
|
|
Udev(#[from] UdevError),
|
|
#[error(transparent)]
|
|
LibInput(#[from] LibInputError),
|
|
#[error("Dupfd failed")]
|
|
Dup(#[source] crate::utils::oserror::OsError),
|
|
#[error("Could not create GBM device")]
|
|
GbmDevice(#[source] GbmError),
|
|
#[error("Could not update the drm properties")]
|
|
UpdateProperties(#[source] DrmError),
|
|
#[error("Could not create a render context")]
|
|
CreateRenderContex(#[source] GfxError),
|
|
#[error("Could not perform modeset")]
|
|
Modeset(#[source] BackendConnectorTransactionError),
|
|
#[error("Could not enable atomic modesetting")]
|
|
AtomicModesetting(#[source] OsError),
|
|
#[error("Could not inspect a plane")]
|
|
CreatePlane(#[source] DrmError),
|
|
#[error("Could not inspect a crtc")]
|
|
CreateCrtc(#[source] DrmError),
|
|
#[error("Could not inspect an encoder")]
|
|
CreateEncoder(#[source] DrmError),
|
|
#[error(transparent)]
|
|
DrmError(#[from] DrmError),
|
|
#[error("Could not create device-paused signal handler")]
|
|
DevicePauseSignalHandler(#[source] DbusError),
|
|
#[error("Could not create device-resumed signal handler")]
|
|
DeviceResumeSignalHandler(#[source] DbusError),
|
|
#[error("Could not render the frame")]
|
|
RenderFrame(#[source] GfxError),
|
|
#[error("Could not perform atomic commit")]
|
|
Commit(#[source] DrmError),
|
|
#[error("The present configuration is out of date")]
|
|
OutOfDate,
|
|
#[error("Could not add connector to transaction")]
|
|
AddToTransaction(#[source] BackendConnectorTransactionError),
|
|
#[error("Could not calculate DRM state")]
|
|
CalculateDrmState(#[source] BackendConnectorTransactionError),
|
|
#[error("Could not calculate DRM change set")]
|
|
CalculateDrmChange(#[source] BackendConnectorTransactionError),
|
|
#[error("Could not create non-prime plane buffer")]
|
|
AllocateScanoutBuffer(#[source] Box<ScanoutBufferError>),
|
|
#[error("Could not create non-prime plane buffer")]
|
|
AllocateScanoutBufferPrime(#[source] ScanoutBufferErrors),
|
|
#[error("Could not copy frame to output device")]
|
|
CopyToDev(#[source] RenderBufferError),
|
|
#[error("The render device does not support the format")]
|
|
RenderUnsupportedFormat,
|
|
}
|
|
|
|
pub struct MetalBackend {
|
|
state: Rc<State>,
|
|
udev: Rc<Udev>,
|
|
monitor: Rc<UdevMonitor>,
|
|
monitor_fd: Rc<OwnedFd>,
|
|
libinput: Rc<LibInput>,
|
|
libinput_fd: Rc<OwnedFd>,
|
|
device_holder: Rc<DeviceHolder>,
|
|
session: Session,
|
|
pause_handler: Cell<Option<SignalHandler>>,
|
|
resume_handler: Cell<Option<SignalHandler>>,
|
|
ctx: CloneCell<Option<Rc<MetalRenderContext>>>,
|
|
signaled_sync_file: CloneCell<Option<SyncFile>>,
|
|
default_feedback: CloneCell<Option<Rc<DrmFeedback>>>,
|
|
persistent_display_data: CopyHashMap<Rc<OutputId>, Rc<PersistentDisplayData>>,
|
|
}
|
|
|
|
impl Debug for MetalBackend {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
f.debug_struct("MetalBackend").finish_non_exhaustive()
|
|
}
|
|
}
|
|
|
|
impl MetalBackend {
|
|
async fn run(self: Rc<Self>) -> Result<(), MetalError> {
|
|
let _monitor = self
|
|
.state
|
|
.eng
|
|
.spawn("monitor devices", self.clone().monitor_devices());
|
|
let _events = self.state.eng.spawn(
|
|
"handle libinput events",
|
|
self.clone().handle_libinput_events(),
|
|
);
|
|
if let Err(e) = self.enumerate_devices() {
|
|
return Err(MetalError::Enumerate(Box::new(e)));
|
|
}
|
|
pending().await
|
|
}
|
|
}
|
|
|
|
impl Backend for MetalBackend {
|
|
fn run(self: Rc<Self>) -> SpawnedFuture<Result<(), Box<dyn Error>>> {
|
|
let slf = self.clone();
|
|
self.state.eng.spawn("metal backend", async move {
|
|
slf.run().await?;
|
|
Ok(())
|
|
})
|
|
}
|
|
|
|
fn clear(&self) {
|
|
self.pause_handler.take();
|
|
self.resume_handler.take();
|
|
self.ctx.take();
|
|
self.device_holder.devices.clear();
|
|
for dev in self.device_holder.input_devices.take() {
|
|
if let Some(dev) = dev {
|
|
dev.inputdev.take();
|
|
dev.events.take();
|
|
dev.cb.take();
|
|
}
|
|
}
|
|
for dev in self.device_holder.drm_devices.lock().drain_values() {
|
|
dev.futures.clear();
|
|
for crtc in dev.dev.crtcs.values() {
|
|
crtc.connector.take();
|
|
crtc.pending_flip.take();
|
|
}
|
|
dev.dev.handle_events.handle_events.take();
|
|
dev.dev.on_change.clear();
|
|
let clear_lease = |lease: &mut MetalLeaseData| {
|
|
lease.connectors.clear();
|
|
lease.crtcs.clear();
|
|
lease.planes.clear();
|
|
};
|
|
for mut lease in dev.dev.leases.lock().drain_values() {
|
|
clear_lease(&mut lease);
|
|
}
|
|
for mut lease in dev.dev.leases_to_break.lock().drain_values() {
|
|
clear_lease(&mut lease);
|
|
}
|
|
for connector in dev.connectors.lock().drain_values() {
|
|
{
|
|
let d = &mut *connector.display.borrow_mut();
|
|
d.crtcs.clear();
|
|
}
|
|
connector.primary_plane.take();
|
|
connector.cursor_plane.take();
|
|
connector.crtc.take();
|
|
connector.on_change.clear();
|
|
connector.present_trigger.clear();
|
|
connector.active_framebuffer.take();
|
|
connector.next_framebuffer.take();
|
|
}
|
|
}
|
|
}
|
|
|
|
fn switch_to(&self, vtnr: u32) {
|
|
self.session.switch_to(vtnr, move |res| {
|
|
if let Err(e) = res {
|
|
log::error!("Could not switch to VT {}: {}", vtnr, ErrorFmt(e));
|
|
}
|
|
})
|
|
}
|
|
|
|
fn import_environment(&self) -> bool {
|
|
true
|
|
}
|
|
|
|
fn supports_presentation_feedback(&self) -> bool {
|
|
true
|
|
}
|
|
|
|
fn get_input_fds(&self) -> Vec<Rc<OwnedFd>> {
|
|
let mut res = vec![];
|
|
for dev in &*self.device_holder.input_devices.borrow() {
|
|
if let Some(dev) = dev
|
|
&& let Some(fd) = dev.fd.get()
|
|
{
|
|
res.push(fd);
|
|
}
|
|
}
|
|
res
|
|
}
|
|
}
|
|
|
|
fn dup_fd(fd: c::c_int) -> Result<Rc<OwnedFd>, MetalError> {
|
|
uapi::fcntl_dupfd_cloexec(fd, 0)
|
|
.map(Rc::new)
|
|
.map_os_err(MetalError::Dup)
|
|
}
|
|
|
|
pub async fn create(state: &Rc<State>) -> Result<Rc<MetalBackend>, MetalError> {
|
|
let socket = match state.dbus.system().await {
|
|
Ok(s) => s,
|
|
Err(e) => return Err(MetalError::DbusSystemSocket(e)),
|
|
};
|
|
let session = match Session::get(&socket).await {
|
|
Ok(s) => s,
|
|
Err(e) => return Err(MetalError::LogindSession(e)),
|
|
};
|
|
if let Err(e) = session.take_control().await {
|
|
return Err(MetalError::TakeControl(e));
|
|
}
|
|
let device_holder = Rc::new(DeviceHolder {
|
|
devices: Default::default(),
|
|
input_devices: Default::default(),
|
|
drm_devices: Default::default(),
|
|
pending_drm_devices: Default::default(),
|
|
num_pending_devices: Default::default(),
|
|
});
|
|
let udev = Rc::new(Udev::new()?);
|
|
let monitor = Rc::new(udev.create_monitor()?);
|
|
monitor.add_match_subsystem_devtype(Some(b"input"), None)?;
|
|
monitor.add_match_subsystem_devtype(Some(b"drm"), None)?;
|
|
monitor.enable_receiving()?;
|
|
let libinput = Rc::new(LibInput::new(device_holder.clone())?);
|
|
let monitor_fd = dup_fd(monitor.fd())?;
|
|
let libinput_fd = dup_fd(libinput.fd())?;
|
|
let metal = Rc::new(MetalBackend {
|
|
state: state.clone(),
|
|
udev,
|
|
monitor,
|
|
monitor_fd,
|
|
libinput,
|
|
libinput_fd,
|
|
device_holder,
|
|
session,
|
|
pause_handler: Default::default(),
|
|
resume_handler: Default::default(),
|
|
ctx: Default::default(),
|
|
signaled_sync_file: Default::default(),
|
|
default_feedback: Default::default(),
|
|
persistent_display_data: Default::default(),
|
|
});
|
|
metal.pause_handler.set(Some({
|
|
let mtl = metal.clone();
|
|
let sh = metal.session.on_pause(move |p| mtl.handle_device_pause(p));
|
|
match sh {
|
|
Ok(sh) => sh,
|
|
Err(e) => return Err(MetalError::DevicePauseSignalHandler(e)),
|
|
}
|
|
}));
|
|
metal.resume_handler.set(Some({
|
|
let mtl = metal.clone();
|
|
let sh = metal
|
|
.session
|
|
.on_resume(move |p| mtl.handle_device_resume(p));
|
|
match sh {
|
|
Ok(sh) => sh,
|
|
Err(e) => return Err(MetalError::DeviceResumeSignalHandler(e)),
|
|
}
|
|
}));
|
|
Ok(metal)
|
|
}
|
|
|
|
struct MetalInputDevice {
|
|
state: Rc<State>,
|
|
slot: usize,
|
|
id: InputDeviceId,
|
|
fully_initialized: Cell<bool>,
|
|
devnum: c::dev_t,
|
|
fd: CloneCell<Option<Rc<OwnedFd>>>,
|
|
inputdev: CloneCell<Option<Rc<RegisteredDevice>>>,
|
|
devnode: CString,
|
|
_sysname: CString,
|
|
syspath: CString,
|
|
removed: Cell<bool>,
|
|
events: SyncQueue<InputEvent>,
|
|
cb: CloneCell<Option<Rc<dyn Fn()>>>,
|
|
name: CloneCell<Rc<String>>,
|
|
transform_matrix: Cell<Option<TransformMatrix>>,
|
|
tablet_id: Cell<Option<TabletId>>,
|
|
tablet_pad_id: Cell<Option<TabletPadId>>,
|
|
|
|
// state
|
|
pressed_keys: SmallMap<u32, (), 5>,
|
|
pressed_buttons: SmallMap<u32, (), 2>,
|
|
|
|
// config
|
|
desired: InputDeviceProperties,
|
|
effective: InputDeviceProperties,
|
|
}
|
|
|
|
#[derive(Default)]
|
|
struct InputDeviceProperties {
|
|
left_handed: Cell<Option<bool>>,
|
|
accel_profile: Cell<Option<AccelProfile>>,
|
|
accel_speed: Cell<Option<f64>>,
|
|
tap_enabled: Cell<Option<bool>>,
|
|
drag_enabled: Cell<Option<bool>>,
|
|
drag_lock_enabled: Cell<Option<bool>>,
|
|
natural_scrolling_enabled: Cell<Option<bool>>,
|
|
calibration_matrix: Cell<Option<[[f32; 3]; 2]>>,
|
|
click_method: Cell<Option<ConfigClickMethod>>,
|
|
middle_button_emulation_enabled: Cell<Option<bool>>,
|
|
enabled_leds: Cell<Option<Led>>,
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
enum MetalDevice {
|
|
Input(Rc<MetalInputDevice>),
|
|
Drm(Rc<MetalDrmDeviceData>),
|
|
}
|
|
|
|
unsafe impl UnsafeCellCloneSafe for MetalDevice {}
|
|
|
|
struct DeviceHolder {
|
|
devices: CopyHashMap<c::dev_t, MetalDevice>,
|
|
input_devices: RefCell<Vec<Option<Rc<MetalInputDevice>>>>,
|
|
drm_devices: CopyHashMap<c::dev_t, Rc<MetalDrmDeviceData>>,
|
|
pending_drm_devices: CopyHashMap<c::dev_t, PendingDrmDevice>,
|
|
num_pending_devices: NumCell<u32>,
|
|
}
|
|
|
|
impl LibInputAdapter for DeviceHolder {
|
|
fn open(&self, path: &CStr) -> Result<OwnedFd, LibInputError> {
|
|
let stat = uapi::stat(path).map_os_err(LibInputError::Stat)?;
|
|
if let Some(MetalDevice::Input(d)) = self.devices.get(&stat.st_rdev)
|
|
&& let Some(fd) = d.fd.get()
|
|
{
|
|
return uapi::fcntl_dupfd_cloexec(fd.raw(), 0).map_os_err(LibInputError::DupFd);
|
|
}
|
|
Err(LibInputError::DeviceUnavailable)
|
|
}
|
|
}
|
|
|
|
impl MetalInputDevice {
|
|
fn apply_config(&self) {
|
|
if self.inputdev.is_none() {
|
|
return;
|
|
}
|
|
if let Some(lh) = self.desired.left_handed.get() {
|
|
self.set_left_handed(lh);
|
|
}
|
|
if let Some(profile) = self.desired.accel_profile.get() {
|
|
self.set_accel_profile_(profile);
|
|
}
|
|
if let Some(speed) = self.desired.accel_speed.get() {
|
|
self.set_accel_speed(speed);
|
|
}
|
|
if let Some(enabled) = self.desired.tap_enabled.get() {
|
|
self.set_tap_enabled(enabled);
|
|
}
|
|
if let Some(enabled) = self.desired.drag_enabled.get() {
|
|
self.set_drag_enabled(enabled);
|
|
}
|
|
if let Some(enabled) = self.desired.drag_lock_enabled.get() {
|
|
self.set_drag_lock_enabled(enabled);
|
|
}
|
|
if let Some(enabled) = self.desired.natural_scrolling_enabled.get() {
|
|
self.set_natural_scrolling_enabled(enabled);
|
|
}
|
|
if let Some(lh) = self.desired.calibration_matrix.get() {
|
|
self.set_calibration_matrix(lh);
|
|
}
|
|
if let Some(method) = self.desired.click_method.get() {
|
|
self.set_click_method_(method);
|
|
}
|
|
if let Some(enabled) = self.desired.middle_button_emulation_enabled.get() {
|
|
self.set_middle_button_emulation_enabled(enabled);
|
|
}
|
|
if let Some(led) = self.desired.enabled_leds.get() {
|
|
self.set_enabled_leds_(led);
|
|
}
|
|
self.fetch_effective();
|
|
}
|
|
|
|
fn fetch_effective(&self) {
|
|
let Some(dev) = self.inputdev.get() else {
|
|
return;
|
|
};
|
|
let device = dev.device();
|
|
if device.left_handed_available() {
|
|
self.effective.left_handed.set(Some(device.left_handed()));
|
|
}
|
|
if device.accel_available() {
|
|
self.effective
|
|
.accel_profile
|
|
.set(Some(device.accel_profile()));
|
|
self.effective.accel_speed.set(Some(device.accel_speed()));
|
|
}
|
|
if device.tap_available() {
|
|
self.effective.tap_enabled.set(Some(device.tap_enabled()));
|
|
self.effective.drag_enabled.set(Some(device.drag_enabled()));
|
|
self.effective
|
|
.drag_lock_enabled
|
|
.set(Some(device.drag_lock_enabled()));
|
|
}
|
|
if device.has_natural_scrolling() {
|
|
self.effective
|
|
.natural_scrolling_enabled
|
|
.set(Some(device.natural_scrolling_enabled()));
|
|
}
|
|
if device.has_calibration_matrix() {
|
|
self.effective
|
|
.calibration_matrix
|
|
.set(Some(device.get_calibration_matrix()));
|
|
}
|
|
if device.has_click_methods() {
|
|
self.effective.click_method.set(Some(device.click_method()));
|
|
}
|
|
if device.middle_button_emulation_available() {
|
|
self.effective
|
|
.middle_button_emulation_enabled
|
|
.set(Some(device.middle_button_emulation_enabled()));
|
|
}
|
|
}
|
|
|
|
fn pre_pause(&self) {
|
|
let time_usec = self.state.now_usec();
|
|
for (key, _) in self.pressed_keys.take() {
|
|
self.event(InputEvent::Key {
|
|
time_usec,
|
|
key,
|
|
state: KeyState::Released,
|
|
});
|
|
}
|
|
for (button, _) in self.pressed_buttons.take() {
|
|
self.event(InputEvent::Button {
|
|
time_usec,
|
|
button,
|
|
state: ButtonState::Released,
|
|
});
|
|
}
|
|
}
|
|
|
|
fn set_accel_profile_(&self, profile: AccelProfile) {
|
|
self.desired.accel_profile.set(Some(profile));
|
|
if let Some(dev) = self.inputdev.get()
|
|
&& dev.device().accel_available()
|
|
{
|
|
dev.device().set_accel_profile(profile);
|
|
self.effective
|
|
.accel_profile
|
|
.set(Some(dev.device().accel_profile()));
|
|
}
|
|
}
|
|
|
|
fn set_click_method_(&self, method: ConfigClickMethod) {
|
|
self.desired.click_method.set(Some(method));
|
|
if let Some(dev) = self.inputdev.get()
|
|
&& dev.device().has_click_methods()
|
|
{
|
|
dev.device().set_click_method(method);
|
|
self.effective
|
|
.click_method
|
|
.set(Some(dev.device().click_method()));
|
|
}
|
|
}
|
|
|
|
fn set_enabled_leds_(&self, led: Led) {
|
|
self.desired.enabled_leds.set(Some(led));
|
|
if let Some(dev) = self.inputdev.get() {
|
|
dev.device().led_update(led);
|
|
self.effective.enabled_leds.set(Some(led));
|
|
}
|
|
}
|
|
}
|
|
|
|
impl InputDevice for MetalInputDevice {
|
|
fn id(&self) -> InputDeviceId {
|
|
self.id
|
|
}
|
|
|
|
fn removed(&self) -> bool {
|
|
self.removed.get()
|
|
}
|
|
|
|
fn event(&self) -> Option<InputEvent> {
|
|
self.events.pop()
|
|
}
|
|
|
|
fn on_change(&self, cb: Rc<dyn Fn()>) {
|
|
self.cb.set(Some(cb));
|
|
}
|
|
|
|
fn grab(&self, _grab: bool) {
|
|
// nothing
|
|
}
|
|
|
|
fn has_capability(&self, cap: InputDeviceCapability) -> bool {
|
|
let li = cap.to_libinput();
|
|
match self.inputdev.get() {
|
|
Some(dev) => dev.device().has_cap(li),
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
fn left_handed(&self) -> Option<bool> {
|
|
self.effective.left_handed.get()
|
|
}
|
|
|
|
fn set_left_handed(&self, left_handed: bool) {
|
|
self.desired.left_handed.set(Some(left_handed));
|
|
if let Some(dev) = self.inputdev.get()
|
|
&& dev.device().left_handed_available()
|
|
{
|
|
dev.device().set_left_handed(left_handed);
|
|
self.effective
|
|
.left_handed
|
|
.set(Some(dev.device().left_handed()));
|
|
}
|
|
}
|
|
|
|
fn accel_profile(&self) -> Option<InputDeviceAccelProfile> {
|
|
let p = self.effective.accel_profile.get()?;
|
|
let p = match p {
|
|
LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT => InputDeviceAccelProfile::Flat,
|
|
LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE => InputDeviceAccelProfile::Adaptive,
|
|
_ => return None,
|
|
};
|
|
Some(p)
|
|
}
|
|
|
|
fn set_accel_profile(&self, profile: InputDeviceAccelProfile) {
|
|
let profile = match profile {
|
|
InputDeviceAccelProfile::Flat => LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT,
|
|
InputDeviceAccelProfile::Adaptive => LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
|
|
};
|
|
self.set_accel_profile_(profile);
|
|
}
|
|
|
|
fn accel_speed(&self) -> Option<f64> {
|
|
self.effective.accel_speed.get()
|
|
}
|
|
|
|
fn set_accel_speed(&self, speed: f64) {
|
|
self.desired.accel_speed.set(Some(speed));
|
|
if let Some(dev) = self.inputdev.get()
|
|
&& dev.device().accel_available()
|
|
{
|
|
dev.device().set_accel_speed(speed);
|
|
self.effective
|
|
.accel_speed
|
|
.set(Some(dev.device().accel_speed()));
|
|
}
|
|
}
|
|
|
|
fn transform_matrix(&self) -> Option<TransformMatrix> {
|
|
self.transform_matrix.get()
|
|
}
|
|
|
|
fn set_transform_matrix(&self, matrix: TransformMatrix) {
|
|
self.transform_matrix.set(Some(matrix));
|
|
}
|
|
|
|
fn calibration_matrix(&self) -> Option<[[f32; 3]; 2]> {
|
|
self.effective.calibration_matrix.get()
|
|
}
|
|
|
|
fn set_calibration_matrix(&self, m: [[f32; 3]; 2]) {
|
|
self.desired.calibration_matrix.set(Some(m));
|
|
if let Some(dev) = self.inputdev.get()
|
|
&& dev.device().has_calibration_matrix()
|
|
{
|
|
dev.device().set_calibration_matrix(m);
|
|
self.effective
|
|
.calibration_matrix
|
|
.set(Some(dev.device().get_calibration_matrix()));
|
|
}
|
|
}
|
|
|
|
fn name(&self) -> Rc<String> {
|
|
self.name.get()
|
|
}
|
|
|
|
fn dev_t(&self) -> Option<c::dev_t> {
|
|
Some(self.devnum)
|
|
}
|
|
|
|
fn tap_enabled(&self) -> Option<bool> {
|
|
self.effective.tap_enabled.get()
|
|
}
|
|
|
|
fn set_tap_enabled(&self, enabled: bool) {
|
|
self.desired.tap_enabled.set(Some(enabled));
|
|
if let Some(dev) = self.inputdev.get()
|
|
&& dev.device().tap_available()
|
|
{
|
|
dev.device().set_tap_enabled(enabled);
|
|
self.effective
|
|
.tap_enabled
|
|
.set(Some(dev.device().tap_enabled()));
|
|
}
|
|
}
|
|
|
|
fn drag_enabled(&self) -> Option<bool> {
|
|
self.effective.drag_enabled.get()
|
|
}
|
|
|
|
fn set_drag_enabled(&self, enabled: bool) {
|
|
self.desired.drag_enabled.set(Some(enabled));
|
|
if let Some(dev) = self.inputdev.get()
|
|
&& dev.device().tap_available()
|
|
{
|
|
dev.device().set_drag_enabled(enabled);
|
|
self.effective
|
|
.drag_enabled
|
|
.set(Some(dev.device().drag_enabled()));
|
|
}
|
|
}
|
|
|
|
fn drag_lock_enabled(&self) -> Option<bool> {
|
|
self.effective.drag_lock_enabled.get()
|
|
}
|
|
|
|
fn set_drag_lock_enabled(&self, enabled: bool) {
|
|
self.desired.drag_lock_enabled.set(Some(enabled));
|
|
if let Some(dev) = self.inputdev.get()
|
|
&& dev.device().tap_available()
|
|
{
|
|
dev.device().set_drag_lock_enabled(enabled);
|
|
self.effective
|
|
.drag_lock_enabled
|
|
.set(Some(dev.device().drag_lock_enabled()));
|
|
}
|
|
}
|
|
|
|
fn natural_scrolling_enabled(&self) -> Option<bool> {
|
|
self.effective.natural_scrolling_enabled.get()
|
|
}
|
|
|
|
fn set_natural_scrolling_enabled(&self, enabled: bool) {
|
|
self.desired.natural_scrolling_enabled.set(Some(enabled));
|
|
if let Some(dev) = self.inputdev.get()
|
|
&& dev.device().has_natural_scrolling()
|
|
{
|
|
dev.device().set_natural_scrolling_enabled(enabled);
|
|
self.effective
|
|
.natural_scrolling_enabled
|
|
.set(Some(dev.device().natural_scrolling_enabled()));
|
|
}
|
|
}
|
|
|
|
fn click_method(&self) -> Option<InputDeviceClickMethod> {
|
|
let p = self.effective.click_method.get()?;
|
|
let p = match p {
|
|
LIBINPUT_CONFIG_CLICK_METHOD_NONE => InputDeviceClickMethod::None,
|
|
LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS => InputDeviceClickMethod::ButtonAreas,
|
|
LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER => InputDeviceClickMethod::Clickfinger,
|
|
_ => return None,
|
|
};
|
|
Some(p)
|
|
}
|
|
|
|
fn set_click_method(&self, method: InputDeviceClickMethod) {
|
|
let method = match method {
|
|
InputDeviceClickMethod::None => LIBINPUT_CONFIG_CLICK_METHOD_NONE,
|
|
InputDeviceClickMethod::ButtonAreas => LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS,
|
|
InputDeviceClickMethod::Clickfinger => LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER,
|
|
};
|
|
self.set_click_method_(method);
|
|
}
|
|
|
|
fn middle_button_emulation_enabled(&self) -> Option<bool> {
|
|
self.effective.middle_button_emulation_enabled.get()
|
|
}
|
|
|
|
fn set_middle_button_emulation_enabled(&self, enabled: bool) {
|
|
self.desired
|
|
.middle_button_emulation_enabled
|
|
.set(Some(enabled));
|
|
if let Some(dev) = self.inputdev.get()
|
|
&& dev.device().middle_button_emulation_available()
|
|
{
|
|
dev.device().set_middle_button_emulation_enabled(enabled);
|
|
self.effective
|
|
.middle_button_emulation_enabled
|
|
.set(Some(dev.device().middle_button_emulation_enabled()));
|
|
}
|
|
}
|
|
|
|
fn tablet_info(&self) -> Option<Box<TabletInit>> {
|
|
let dev = self.inputdev.get()?;
|
|
let dev = dev.device();
|
|
if !dev.has_cap(LIBINPUT_DEVICE_CAP_TABLET_TOOL) {
|
|
return None;
|
|
}
|
|
let id = match self.tablet_id.get() {
|
|
Some(id) => id,
|
|
None => {
|
|
let id = self.state.tablet_ids.next();
|
|
self.tablet_id.set(Some(id));
|
|
id
|
|
}
|
|
};
|
|
Some(Box::new(TabletInit {
|
|
id,
|
|
group: self.get_device_group(&dev),
|
|
name: dev.name(),
|
|
pid: dev.product(),
|
|
vid: dev.vendor(),
|
|
bustype: dev.bustype(),
|
|
path: self.syspath.as_bytes().as_bstr().to_string(),
|
|
}))
|
|
}
|
|
|
|
fn tablet_pad_info(&self) -> Option<Box<TabletPadInit>> {
|
|
let dev = self.inputdev.get()?;
|
|
let dev = dev.device();
|
|
if !dev.has_cap(LIBINPUT_DEVICE_CAP_TABLET_PAD) {
|
|
return None;
|
|
}
|
|
let id = match self.tablet_pad_id.get() {
|
|
Some(id) => id,
|
|
None => {
|
|
let id = self.state.tablet_pad_ids.next();
|
|
self.tablet_pad_id.set(Some(id));
|
|
id
|
|
}
|
|
};
|
|
let buttons = dev.pad_num_buttons();
|
|
let strips = dev.pad_num_strips();
|
|
let rings = dev.pad_num_rings();
|
|
let dials = dev.pad_num_dials();
|
|
let mut groups = vec![];
|
|
for n in 0..dev.pad_num_mode_groups() {
|
|
let Some(group) = dev.pad_mode_group(n) else {
|
|
break;
|
|
};
|
|
groups.push(TabletPadGroupInit {
|
|
buttons: (0..buttons).filter(|b| group.has_button(*b)).collect(),
|
|
rings: (0..rings).filter(|b| group.has_ring(*b)).collect(),
|
|
strips: (0..strips).filter(|b| group.has_strip(*b)).collect(),
|
|
dials: (0..dials).filter(|b| group.has_dial(*b)).collect(),
|
|
modes: group.num_modes(),
|
|
mode: group.mode(),
|
|
});
|
|
}
|
|
Some(Box::new(TabletPadInit {
|
|
id,
|
|
group: self.get_device_group(&dev),
|
|
path: self.syspath.as_bytes().as_bstr().to_string(),
|
|
buttons,
|
|
strips,
|
|
rings,
|
|
dials,
|
|
groups,
|
|
}))
|
|
}
|
|
|
|
fn set_enabled_leds(&self, leds: Leds) {
|
|
let led = Led(leds.0 as _);
|
|
self.set_enabled_leds_(led);
|
|
}
|
|
}
|
|
|
|
impl MetalInputDevice {
|
|
fn event(&self, event: InputEvent) {
|
|
self.events.push(event);
|
|
if let Some(cb) = self.cb.get() {
|
|
cb();
|
|
}
|
|
}
|
|
|
|
fn get_device_group(&self, dev: &LibInputDevice) -> InputDeviceGroupId {
|
|
let group = dev.device_group();
|
|
let mut id = group.user_data();
|
|
if id == 0 {
|
|
id = self.state.input_device_group_ids.next().raw();
|
|
group.set_user_data(id);
|
|
}
|
|
InputDeviceGroupId::from_raw(id)
|
|
}
|
|
}
|