config: allow mapping input devices to outputs
This commit is contained in:
parent
efdca4de49
commit
86e283d255
18 changed files with 420 additions and 13 deletions
|
|
@ -127,6 +127,10 @@ pub enum DeviceCommand {
|
|||
Attach(AttachArgs),
|
||||
/// Detach the device from its seat.
|
||||
Detach,
|
||||
/// Maps this device to an output.
|
||||
MapToOutput(MapToOutputArgs),
|
||||
/// Removes the mapping from this device to an output.
|
||||
RemoveMapping,
|
||||
}
|
||||
|
||||
#[derive(ValueEnum, Debug, Clone)]
|
||||
|
|
@ -196,6 +200,12 @@ pub struct SetTransformMatrixArgs {
|
|||
pub m22: f64,
|
||||
}
|
||||
|
||||
#[derive(Args, Debug, Clone)]
|
||||
pub struct MapToOutputArgs {
|
||||
/// The output to map to.
|
||||
pub output: String,
|
||||
}
|
||||
|
||||
#[derive(Args, Debug, Clone)]
|
||||
pub struct AttachArgs {
|
||||
/// The seat to attach to.
|
||||
|
|
@ -261,6 +271,7 @@ struct InputDevice {
|
|||
pub natural_scrolling_enabled: Option<bool>,
|
||||
pub px_per_wheel_scroll: Option<f64>,
|
||||
pub transform_matrix: Option<[[f64; 2]; 2]>,
|
||||
pub output: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
|
|
@ -564,6 +575,26 @@ impl Input {
|
|||
let map = self.handle_keymap(input).await;
|
||||
stdout().write_all(&map).unwrap();
|
||||
}
|
||||
DeviceCommand::MapToOutput(a) => {
|
||||
self.handle_error(input, |e| {
|
||||
eprintln!("Could not map the device to an output: {}", e);
|
||||
});
|
||||
tc.send(jay_input::MapToOutput {
|
||||
self_id: input,
|
||||
id: args.device,
|
||||
output: Some(&a.output),
|
||||
});
|
||||
}
|
||||
DeviceCommand::RemoveMapping => {
|
||||
self.handle_error(input, |e| {
|
||||
eprintln!("Could not remove the output mapping: {}", e);
|
||||
});
|
||||
tc.send(jay_input::MapToOutput {
|
||||
self_id: input,
|
||||
id: args.device,
|
||||
output: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
tc.round_trip().await;
|
||||
}
|
||||
|
|
@ -694,6 +725,9 @@ impl Input {
|
|||
if let Some(v) = &device.transform_matrix {
|
||||
println!("{prefix} transform matrix: {:?}", v);
|
||||
}
|
||||
if let Some(v) = &device.output {
|
||||
println!("{prefix} mapped to output: {}", v);
|
||||
}
|
||||
}
|
||||
|
||||
async fn get(self: &Rc<Self>, input: JayInputId) -> Data {
|
||||
|
|
@ -757,8 +791,15 @@ impl Input {
|
|||
.then_some(msg.natural_scrolling_enabled != 0),
|
||||
px_per_wheel_scroll: is_pointer.then_some(msg.px_per_wheel_scroll),
|
||||
transform_matrix: uapi::pod_read(msg.transform_matrix).ok(),
|
||||
output: None,
|
||||
});
|
||||
});
|
||||
jay_input::InputDeviceOutput::handle(tc, input, data.clone(), |data, msg| {
|
||||
let mut data = data.borrow_mut();
|
||||
if let Some(last) = data.input_device.last_mut() {
|
||||
last.output = Some(msg.output.to_string());
|
||||
}
|
||||
});
|
||||
tc.round_trip().await;
|
||||
let x = data.borrow_mut().clone();
|
||||
x
|
||||
|
|
|
|||
|
|
@ -338,6 +338,23 @@ impl ConfigProxyHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_set_input_device_connector(
|
||||
&self,
|
||||
input_device: InputDevice,
|
||||
connector: Connector,
|
||||
) -> Result<(), CphError> {
|
||||
let dev = self.get_device_handler_data(input_device)?;
|
||||
let output = self.get_output_node(connector)?;
|
||||
dev.set_output(Some(&output.global));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_remove_input_mapping(&self, input_device: InputDevice) -> Result<(), CphError> {
|
||||
let dev = self.get_device_handler_data(input_device)?;
|
||||
dev.set_output(None);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_set_status(&self, status: &str) {
|
||||
self.state.set_status(status);
|
||||
}
|
||||
|
|
@ -1790,6 +1807,15 @@ impl ConfigProxyHandler {
|
|||
ClientMessage::SetFocusFollowsMouseMode { seat, mode } => self
|
||||
.handle_set_focus_follows_mouse_mode(seat, mode)
|
||||
.wrn("set_focus_follows_mouse_mode")?,
|
||||
ClientMessage::SetInputDeviceConnector {
|
||||
input_device,
|
||||
connector,
|
||||
} => self
|
||||
.handle_set_input_device_connector(input_device, connector)
|
||||
.wrn("set_input_device_connector")?,
|
||||
ClientMessage::RemoveInputMapping { input_device } => self
|
||||
.handle_remove_input_mapping(input_device)
|
||||
.wrn("remove_input_mapping")?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -129,6 +129,15 @@ impl JayInput {
|
|||
.map(uapi::as_bytes)
|
||||
.unwrap_or_default(),
|
||||
});
|
||||
if let Some(output) = data.data.output.get() {
|
||||
if let Some(output) = output.get() {
|
||||
self.client.event(InputDeviceOutput {
|
||||
self_id: self.id,
|
||||
id: data.id.raw(),
|
||||
output: &output.connector.name,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn device(&self, id: u32) -> Result<Rc<DeviceHandlerData>, JayInputError> {
|
||||
|
|
@ -389,6 +398,32 @@ impl JayInputRequestHandler for JayInput {
|
|||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn map_to_output(&self, req: MapToOutput<'_>, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.or_error(|| {
|
||||
let dev = self.device(req.id)?;
|
||||
match req.output {
|
||||
Some(output) => {
|
||||
let namelc = output.to_ascii_lowercase();
|
||||
let c = self
|
||||
.client
|
||||
.state
|
||||
.root
|
||||
.outputs
|
||||
.lock()
|
||||
.values()
|
||||
.find(|c| c.global.connector.name.to_ascii_lowercase() == namelc)
|
||||
.cloned();
|
||||
match c {
|
||||
Some(c) => dev.set_output(Some(&c.global)),
|
||||
_ => return Err(JayInputError::OutputNotConnected),
|
||||
}
|
||||
}
|
||||
_ => dev.set_output(None),
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
|
|
@ -418,5 +453,7 @@ pub enum JayInputError {
|
|||
ClientMemError(#[from] ClientMemError),
|
||||
#[error("Could not parse keymap")]
|
||||
XkbCommonError(#[from] XkbCommonError),
|
||||
#[error("Output is not connected")]
|
||||
OutputNotConnected,
|
||||
}
|
||||
efrom!(JayInputError, ClientError);
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ use {
|
|||
zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1,
|
||||
DynDataSource, IpcError,
|
||||
},
|
||||
wl_output::WlOutputGlobal,
|
||||
wl_seat::{
|
||||
gesture_owner::GestureOwnerHolder,
|
||||
kb_owner::KbOwnerHolder,
|
||||
|
|
@ -1163,4 +1164,17 @@ impl DeviceHandlerData {
|
|||
seat.handle_xkb_state_change(&old.borrow(), &new.borrow());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_output(&self, output: Option<&WlOutputGlobal>) {
|
||||
match output {
|
||||
None => {
|
||||
log::info!("Removing output mapping of {}", self.device.name());
|
||||
self.output.take();
|
||||
}
|
||||
Some(o) => {
|
||||
log::info!("Mapping {} to {}", self.device.name(), o.connector.name);
|
||||
self.output.set(Some(o.opt.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ use {
|
|||
jay_seat_events::JaySeatEvents,
|
||||
jay_workspace_watcher::JayWorkspaceWatcher,
|
||||
wl_drm::WlDrmGlobal,
|
||||
wl_output::{OutputId, PersistentOutputState},
|
||||
wl_output::{OutputGlobalOpt, OutputId, PersistentOutputState},
|
||||
wl_seat::{SeatIds, WlSeatGlobal},
|
||||
wl_surface::{
|
||||
wl_subsurface::SubsurfaceIds,
|
||||
|
|
@ -262,6 +262,7 @@ pub struct DeviceHandlerData {
|
|||
pub devnode: Option<String>,
|
||||
pub keymap: CloneCell<Option<Rc<XkbKeymap>>>,
|
||||
pub xkb_state: CloneCell<Option<Rc<RefCell<XkbState>>>>,
|
||||
pub output: CloneCell<Option<Rc<OutputGlobalOpt>>>,
|
||||
}
|
||||
|
||||
pub struct ConnectorData {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ pub fn handle(state: &Rc<State>, dev: Rc<dyn InputDevice>) {
|
|||
devnode: props.devnode,
|
||||
keymap: Default::default(),
|
||||
xkb_state: Default::default(),
|
||||
output: Default::default(),
|
||||
});
|
||||
let ae = Rc::new(AsyncEvent::default());
|
||||
let oh = DeviceHandler {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue