Merge pull request #190 from mahkoh/jorth/tablet
wayland: implement tablet-v2
This commit is contained in:
commit
ff666e2e91
89 changed files with 6231 additions and 758 deletions
|
|
@ -723,10 +723,6 @@ fn write_request_handler<W: Write>(
|
|||
messages: &ParseResult,
|
||||
) -> Result<()> {
|
||||
writeln!(f)?;
|
||||
// TODO: remove this after https://github.com/mahkoh/jay/pull/190
|
||||
if camel_obj_name == "ZwpTabletToolV2" {
|
||||
writeln!(f, " #[allow(dead_code)]")?;
|
||||
}
|
||||
writeln!(
|
||||
f,
|
||||
" pub trait {camel_obj_name}RequestHandler: crate::object::Object + Sized {{"
|
||||
|
|
|
|||
|
|
@ -474,6 +474,22 @@ You can use the `configure-input` action to change these settings at runtime.
|
|||
|
||||
See the specification for more details.
|
||||
|
||||
### Mapping Tablets to Outputs
|
||||
|
||||
You can map tablets to outputs using the `output` property:
|
||||
|
||||
```toml
|
||||
[[outputs]]
|
||||
name = "left"
|
||||
match.serial-number = "33K03894SL0"
|
||||
|
||||
[[inputs]]
|
||||
match.name = "Wacom Bamboo Comic 2FG Pen"
|
||||
output.name = "left"
|
||||
```
|
||||
|
||||
See the specification for more details.
|
||||
|
||||
# Theming
|
||||
|
||||
You can configure the colors, sizes, and fonts used by the compositor with the top-level `theme` table.
|
||||
|
|
|
|||
|
|
@ -165,6 +165,7 @@ Jay supports the following wayland protocols:
|
|||
| zwp_pointer_gestures_v1 | 3 | |
|
||||
| zwp_primary_selection_device_manager_v1 | 1 | |
|
||||
| zwp_relative_pointer_manager_v1 | 1 | |
|
||||
| zwp_tablet_manager_v2 | 1 | |
|
||||
| zwp_text_input_manager_v3 | 1 | |
|
||||
| zwp_virtual_keyboard_manager_v1 | 1 | Yes |
|
||||
| zxdg_decoration_manager_v1 | 1 | |
|
||||
|
|
@ -182,5 +183,4 @@ The following features are currently not supported but might get implemented in
|
|||
|
||||
- Fine-grained damage tracking.
|
||||
- Touch support.
|
||||
- Tablet support.
|
||||
- Tearing updates of fullscreen games.
|
||||
|
|
|
|||
|
|
@ -88,7 +88,9 @@ pub(crate) struct Client {
|
|||
response: RefCell<Vec<Response>>,
|
||||
on_new_seat: RefCell<Option<Callback<Seat>>>,
|
||||
on_new_input_device: RefCell<Option<Callback<InputDevice>>>,
|
||||
on_input_device_removed: RefCell<Option<Callback<InputDevice>>>,
|
||||
on_connector_connected: RefCell<Option<Callback<Connector>>>,
|
||||
on_connector_disconnected: RefCell<Option<Callback<Connector>>>,
|
||||
on_graphics_initialized: Cell<Option<Box<dyn FnOnce()>>>,
|
||||
on_devices_enumerated: Cell<Option<Box<dyn FnOnce()>>>,
|
||||
on_new_connector: RefCell<Option<Callback<Connector>>>,
|
||||
|
|
@ -216,7 +218,9 @@ pub unsafe extern "C" fn init(
|
|||
response: Default::default(),
|
||||
on_new_seat: Default::default(),
|
||||
on_new_input_device: Default::default(),
|
||||
on_input_device_removed: Default::default(),
|
||||
on_connector_connected: Default::default(),
|
||||
on_connector_disconnected: Default::default(),
|
||||
on_graphics_initialized: Default::default(),
|
||||
on_devices_enumerated: Default::default(),
|
||||
on_new_connector: Default::default(),
|
||||
|
|
@ -605,6 +609,10 @@ impl Client {
|
|||
*self.on_new_input_device.borrow_mut() = Some(cb(f));
|
||||
}
|
||||
|
||||
pub fn on_input_device_removed<F: FnMut(InputDevice) + 'static>(&self, f: F) {
|
||||
*self.on_input_device_removed.borrow_mut() = Some(cb(f));
|
||||
}
|
||||
|
||||
pub fn on_switch_event<F: FnMut(SwitchEvent) + 'static>(
|
||||
&self,
|
||||
input_device: InputDevice,
|
||||
|
|
@ -818,6 +826,10 @@ impl Client {
|
|||
*self.on_connector_connected.borrow_mut() = Some(cb(f));
|
||||
}
|
||||
|
||||
pub fn on_connector_disconnected<F: FnMut(Connector) + 'static>(&self, f: F) {
|
||||
*self.on_connector_disconnected.borrow_mut() = Some(cb(f));
|
||||
}
|
||||
|
||||
pub fn on_graphics_initialized<F: FnOnce() + 'static>(&self, f: F) {
|
||||
self.on_graphics_initialized.set(Some(Box::new(f)));
|
||||
}
|
||||
|
|
@ -943,6 +955,17 @@ impl Client {
|
|||
self.send(&ClientMessage::SetFocusFollowsMouseMode { seat, mode })
|
||||
}
|
||||
|
||||
pub fn set_input_device_connector(&self, input_device: InputDevice, connector: Connector) {
|
||||
self.send(&ClientMessage::SetInputDeviceConnector {
|
||||
input_device,
|
||||
connector,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn remove_input_mapping(&self, input_device: InputDevice) {
|
||||
self.send(&ClientMessage::RemoveInputMapping { input_device })
|
||||
}
|
||||
|
||||
pub fn parse_keymap(&self, keymap: &str) -> Keymap {
|
||||
let res = self.send_with_response(&ClientMessage::ParseKeymap { keymap });
|
||||
get_response!(res, Keymap(0), ParseKeymap { keymap });
|
||||
|
|
@ -1272,6 +1295,10 @@ impl Client {
|
|||
}
|
||||
ServerMessage::DelInputDevice { device } => {
|
||||
self.on_switch_event.borrow_mut().remove(&device);
|
||||
let handler = self.on_input_device_removed.borrow_mut().clone();
|
||||
if let Some(handler) = handler {
|
||||
run_cb("input device removed", &handler, device);
|
||||
}
|
||||
}
|
||||
ServerMessage::ConnectorConnect { device } => {
|
||||
let handler = self.on_connector_connected.borrow_mut().clone();
|
||||
|
|
@ -1279,7 +1306,12 @@ impl Client {
|
|||
run_cb("connector connected", &handler, device);
|
||||
}
|
||||
}
|
||||
ServerMessage::ConnectorDisconnect { .. } => {}
|
||||
ServerMessage::ConnectorDisconnect { device } => {
|
||||
let handler = self.on_connector_disconnected.borrow_mut().clone();
|
||||
if let Some(handler) = handler {
|
||||
run_cb("connector disconnected", &handler, device);
|
||||
}
|
||||
}
|
||||
ServerMessage::NewConnector { device } => {
|
||||
let handler = self.on_new_connector.borrow_mut().clone();
|
||||
if let Some(handler) = handler {
|
||||
|
|
|
|||
|
|
@ -476,6 +476,13 @@ pub enum ClientMessage<'a> {
|
|||
seat: Seat,
|
||||
mode: FocusFollowsMouseMode,
|
||||
},
|
||||
SetInputDeviceConnector {
|
||||
input_device: InputDevice,
|
||||
connector: Connector,
|
||||
},
|
||||
RemoveInputMapping {
|
||||
input_device: InputDevice,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
|
|
|
|||
|
|
@ -143,6 +143,20 @@ impl InputDevice {
|
|||
pub fn on_switch_event<F: FnMut(SwitchEvent) + 'static>(self, f: F) {
|
||||
get!().on_switch_event(self, f)
|
||||
}
|
||||
|
||||
/// Maps this input device to a connector.
|
||||
///
|
||||
/// The connector should be connected.
|
||||
///
|
||||
/// This should be used for touch screens and graphics tablets.
|
||||
pub fn set_connector(self, connector: Connector) {
|
||||
get!().set_input_device_connector(self, connector);
|
||||
}
|
||||
|
||||
/// Removes the mapping of this device to a connector.
|
||||
pub fn remove_mapping(self) {
|
||||
get!().remove_input_mapping(self);
|
||||
}
|
||||
}
|
||||
|
||||
/// A seat.
|
||||
|
|
@ -449,6 +463,11 @@ pub fn on_new_input_device<F: FnMut(InputDevice) + 'static>(f: F) {
|
|||
get!().on_new_input_device(f)
|
||||
}
|
||||
|
||||
/// Sets a closure to run when an input device has been removed.
|
||||
pub fn on_input_device_removed<F: FnMut(InputDevice) + 'static>(f: F) {
|
||||
get!().on_input_device_removed(f)
|
||||
}
|
||||
|
||||
/// Sets the maximum time between two clicks to be registered as a double click by the
|
||||
/// compositor.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -275,6 +275,11 @@ pub fn on_connector_connected<F: FnMut(Connector) + 'static>(f: F) {
|
|||
get!().on_connector_connected(f)
|
||||
}
|
||||
|
||||
/// Sets the callback to be called when a connector is disconnected from an output device.
|
||||
pub fn on_connector_disconnected<F: FnMut(Connector) + 'static>(f: F) {
|
||||
get!().on_connector_disconnected(f)
|
||||
}
|
||||
|
||||
/// Sets the callback to be called when the graphics of the compositor have been initialized.
|
||||
///
|
||||
/// This callback is only invoked once during the lifetime of the compositor. This is a good place
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
- Focus-follows-mouse can now be disabled.
|
||||
- Add support for pointer-gestures-unstable-v1.
|
||||
- Configs can now handle switch events (laptop lid closed/opened).
|
||||
- Add support for tablet-v2.
|
||||
|
||||
# 1.1.0 (2024-04-22)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,14 @@ use {
|
|||
drm_feedback::DrmFeedback,
|
||||
fixed::Fixed,
|
||||
gfx_api::{GfxFramebuffer, SyncFile},
|
||||
ifs::wl_seat::wl_pointer::{CONTINUOUS, FINGER, HORIZONTAL_SCROLL, VERTICAL_SCROLL, WHEEL},
|
||||
ifs::wl_seat::{
|
||||
tablet::{
|
||||
PadButtonState, TabletInit, TabletPadId, TabletPadInit, TabletRingEventSource,
|
||||
TabletStripEventSource, TabletToolChanges, TabletToolId, TabletToolInit,
|
||||
ToolButtonState,
|
||||
},
|
||||
wl_pointer::{CONTINUOUS, FINGER, HORIZONTAL_SCROLL, VERTICAL_SCROLL, WHEEL},
|
||||
},
|
||||
libinput::consts::DeviceCapability,
|
||||
video::drm::{ConnectorType, DrmConnector, DrmError, DrmVersion},
|
||||
},
|
||||
|
|
@ -126,6 +133,8 @@ pub trait HardwareCursor: Debug {
|
|||
|
||||
pub type TransformMatrix = [[f64; 2]; 2];
|
||||
|
||||
linear_ids!(InputDeviceGroupIds, InputDeviceGroupId, usize);
|
||||
|
||||
pub trait InputDevice {
|
||||
fn id(&self) -> InputDeviceId;
|
||||
fn removed(&self) -> bool;
|
||||
|
|
@ -169,6 +178,12 @@ pub trait InputDevice {
|
|||
None
|
||||
}
|
||||
fn set_natural_scrolling_enabled(&self, enabled: bool);
|
||||
fn tablet_info(&self) -> Option<Box<TabletInit>> {
|
||||
None
|
||||
}
|
||||
fn tablet_pad_info(&self) -> Option<Box<TabletPadInit>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
|
||||
|
|
@ -321,6 +336,53 @@ pub enum InputEvent {
|
|||
time_usec: u64,
|
||||
event: SwitchEvent,
|
||||
},
|
||||
|
||||
TabletToolAdded {
|
||||
time_usec: u64,
|
||||
init: Box<TabletToolInit>,
|
||||
},
|
||||
TabletToolChanged {
|
||||
time_usec: u64,
|
||||
id: TabletToolId,
|
||||
changes: Box<TabletToolChanges>,
|
||||
},
|
||||
TabletToolButton {
|
||||
time_usec: u64,
|
||||
id: TabletToolId,
|
||||
button: u32,
|
||||
state: ToolButtonState,
|
||||
},
|
||||
TabletToolRemoved {
|
||||
time_usec: u64,
|
||||
id: TabletToolId,
|
||||
},
|
||||
|
||||
TabletPadButton {
|
||||
time_usec: u64,
|
||||
id: TabletPadId,
|
||||
button: u32,
|
||||
state: PadButtonState,
|
||||
},
|
||||
TabletPadModeSwitch {
|
||||
time_usec: u64,
|
||||
pad: TabletPadId,
|
||||
group: u32,
|
||||
mode: u32,
|
||||
},
|
||||
TabletPadRing {
|
||||
time_usec: u64,
|
||||
pad: TabletPadId,
|
||||
ring: u32,
|
||||
source: Option<TabletRingEventSource>,
|
||||
angle: Option<f64>,
|
||||
},
|
||||
TabletPadStrip {
|
||||
time_usec: u64,
|
||||
pad: TabletPadId,
|
||||
strip: u32,
|
||||
source: Option<TabletStripEventSource>,
|
||||
position: Option<f64>,
|
||||
},
|
||||
}
|
||||
|
||||
pub enum DrmEvent {
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ use {
|
|||
crate::{
|
||||
async_engine::SpawnedFuture,
|
||||
backend::{
|
||||
Backend, InputDevice, InputDeviceAccelProfile, InputDeviceCapability, InputDeviceId,
|
||||
InputEvent, KeyState, TransformMatrix,
|
||||
Backend, InputDevice, InputDeviceAccelProfile, InputDeviceCapability,
|
||||
InputDeviceGroupId, InputDeviceId, InputEvent, KeyState, TransformMatrix,
|
||||
},
|
||||
backends::metal::video::{
|
||||
MetalDrmDeviceData, MetalLeaseData, MetalRenderContext, PendingDrmDevice,
|
||||
|
|
@ -15,12 +15,16 @@ use {
|
|||
dbus::{DbusError, SignalHandler},
|
||||
drm_feedback::DrmFeedback,
|
||||
gfx_api::GfxError,
|
||||
ifs::wl_seat::tablet::{
|
||||
TabletId, TabletInit, TabletPadGroupInit, TabletPadId, TabletPadInit,
|
||||
},
|
||||
libinput::{
|
||||
consts::{
|
||||
AccelProfile, LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
|
||||
LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT,
|
||||
LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT, LIBINPUT_DEVICE_CAP_TABLET_PAD,
|
||||
LIBINPUT_DEVICE_CAP_TABLET_TOOL,
|
||||
},
|
||||
device::RegisteredDevice,
|
||||
device::{LibInputDevice, RegisteredDevice},
|
||||
LibInput, LibInputAdapter, LibInputError,
|
||||
},
|
||||
logind::{LogindError, Session},
|
||||
|
|
@ -41,6 +45,7 @@ use {
|
|||
gbm::GbmError,
|
||||
},
|
||||
},
|
||||
bstr::ByteSlice,
|
||||
std::{
|
||||
any::Any,
|
||||
cell::{Cell, RefCell},
|
||||
|
|
@ -335,6 +340,7 @@ pub async fn create(state: &Rc<State>) -> Result<Rc<MetalBackend>, MetalError> {
|
|||
}
|
||||
|
||||
struct MetalInputDevice {
|
||||
state: Rc<State>,
|
||||
slot: usize,
|
||||
id: InputDeviceId,
|
||||
devnum: c::dev_t,
|
||||
|
|
@ -342,11 +348,14 @@ struct MetalInputDevice {
|
|||
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>,
|
||||
|
|
@ -646,6 +655,71 @@ impl InputDevice for MetalInputDevice {
|
|||
fn natural_scrolling_enabled(&self) -> Option<bool> {
|
||||
self.effective.natural_scrolling_enabled.get()
|
||||
}
|
||||
|
||||
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(),
|
||||
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 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(),
|
||||
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,
|
||||
groups,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl MetalInputDevice {
|
||||
|
|
@ -655,4 +729,14 @@ impl MetalInputDevice {
|
|||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,14 +3,25 @@ use {
|
|||
backend::{AxisSource, InputEvent, KeyState, ScrollAxis},
|
||||
backends::metal::MetalBackend,
|
||||
fixed::Fixed,
|
||||
ifs::wl_seat::tablet::{
|
||||
PadButtonState, TabletRingEventSource, TabletStripEventSource, TabletTool2dChange,
|
||||
TabletToolCapability, TabletToolChanges, TabletToolId, TabletToolInit,
|
||||
TabletToolPositionChange, TabletToolType, TabletToolWheelChange, ToolButtonState,
|
||||
},
|
||||
libinput::{
|
||||
consts::{
|
||||
LIBINPUT_BUTTON_STATE_PRESSED, LIBINPUT_KEY_STATE_PRESSED,
|
||||
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
|
||||
LIBINPUT_SWITCH_LID, LIBINPUT_SWITCH_STATE_OFF, LIBINPUT_SWITCH_STATE_ON,
|
||||
LIBINPUT_SWITCH_TABLET_MODE,
|
||||
LIBINPUT_BUTTON_STATE_PRESSED, LIBINPUT_BUTTON_STATE_RELEASED,
|
||||
LIBINPUT_KEY_STATE_PRESSED, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL,
|
||||
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, LIBINPUT_SWITCH_LID,
|
||||
LIBINPUT_SWITCH_STATE_OFF, LIBINPUT_SWITCH_STATE_ON, LIBINPUT_SWITCH_TABLET_MODE,
|
||||
LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER, LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER,
|
||||
LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN, LIBINPUT_TABLET_TOOL_TIP_DOWN,
|
||||
LIBINPUT_TABLET_TOOL_TIP_UP, LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH,
|
||||
LIBINPUT_TABLET_TOOL_TYPE_BRUSH, LIBINPUT_TABLET_TOOL_TYPE_ERASER,
|
||||
LIBINPUT_TABLET_TOOL_TYPE_LENS, LIBINPUT_TABLET_TOOL_TYPE_MOUSE,
|
||||
LIBINPUT_TABLET_TOOL_TYPE_PEN, LIBINPUT_TABLET_TOOL_TYPE_PENCIL,
|
||||
},
|
||||
event::LibInputEvent,
|
||||
event::{LibInputEvent, LibInputEventTabletTool},
|
||||
},
|
||||
utils::{bitflags::BitflagsExt, errorfmt::ErrorFmt},
|
||||
},
|
||||
|
|
@ -103,6 +114,13 @@ impl MetalBackend {
|
|||
c::LIBINPUT_EVENT_GESTURE_HOLD_BEGIN => self.handle_gesture_hold_begin(event),
|
||||
c::LIBINPUT_EVENT_GESTURE_HOLD_END => self.handle_gesture_hold_end(event),
|
||||
c::LIBINPUT_EVENT_SWITCH_TOGGLE => self.handle_switch_toggle(event),
|
||||
c::LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY => self.handle_tablet_tool_proximity(event),
|
||||
c::LIBINPUT_EVENT_TABLET_TOOL_AXIS => self.handle_tablet_tool_axis(event),
|
||||
c::LIBINPUT_EVENT_TABLET_TOOL_BUTTON => self.handle_tablet_tool_button(event),
|
||||
c::LIBINPUT_EVENT_TABLET_TOOL_TIP => self.handle_tablet_tool_tip(event),
|
||||
c::LIBINPUT_EVENT_TABLET_PAD_BUTTON => self.handle_tablet_pad_button(event),
|
||||
c::LIBINPUT_EVENT_TABLET_PAD_RING => self.handle_tablet_pad_ring(event),
|
||||
c::LIBINPUT_EVENT_TABLET_PAD_STRIP => self.handle_tablet_pad_strip(event),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
@ -320,4 +338,211 @@ impl MetalBackend {
|
|||
event: switch_event,
|
||||
});
|
||||
}
|
||||
|
||||
fn get_tool_id(&self, event: &LibInputEventTabletTool) -> TabletToolId {
|
||||
let tool = event.tool();
|
||||
let mut user_data = tool.user_data();
|
||||
if user_data == 0 {
|
||||
user_data = self.state.tablet_tool_ids.next().raw();
|
||||
tool.set_user_data(user_data);
|
||||
}
|
||||
TabletToolId::from_raw(user_data)
|
||||
}
|
||||
|
||||
fn build_tablet_tool_changed(
|
||||
&self,
|
||||
event: &LibInputEventTabletTool,
|
||||
down: Option<bool>,
|
||||
) -> InputEvent {
|
||||
let mut changes = Box::<TabletToolChanges>::default();
|
||||
changes.down = down;
|
||||
if event.x_has_changed() || event.y_has_changed() {
|
||||
changes.pos = Some(TabletTool2dChange {
|
||||
x: TabletToolPositionChange {
|
||||
x: event.x_transformed(1),
|
||||
dx: event.dx(),
|
||||
},
|
||||
y: TabletToolPositionChange {
|
||||
x: event.y_transformed(1),
|
||||
dx: event.dy(),
|
||||
},
|
||||
})
|
||||
}
|
||||
if event.pressure_has_changed() {
|
||||
changes.pressure = Some(event.pressure());
|
||||
}
|
||||
if event.distance_has_changed() {
|
||||
changes.distance = Some(event.distance());
|
||||
}
|
||||
if event.tilt_x_has_changed() || event.tilt_y_has_changed() {
|
||||
changes.tilt = Some(TabletTool2dChange {
|
||||
x: event.tilt_x(),
|
||||
y: event.tilt_y(),
|
||||
});
|
||||
}
|
||||
if event.rotation_has_changed() {
|
||||
changes.rotation = Some(event.rotation());
|
||||
}
|
||||
if event.slider_has_changed() {
|
||||
changes.slider = Some(event.slider_position());
|
||||
}
|
||||
if event.wheel_has_changed() {
|
||||
changes.wheel = Some(TabletToolWheelChange {
|
||||
degrees: event.wheel_delta(),
|
||||
clicks: event.wheel_delta_discrete(),
|
||||
});
|
||||
}
|
||||
InputEvent::TabletToolChanged {
|
||||
time_usec: event.time_usec(),
|
||||
id: self.get_tool_id(event),
|
||||
changes,
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_tablet_tool_proximity(self: &Rc<Self>, event: LibInputEvent) {
|
||||
let (event, dev) = unpack!(self, event, tablet_tool_event);
|
||||
let id = self.get_tool_id(&event);
|
||||
if event.proximity_state() == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN {
|
||||
let Some(tablet_id) = dev.tablet_id.get() else {
|
||||
return;
|
||||
};
|
||||
let tool = event.tool();
|
||||
dev.event(InputEvent::TabletToolAdded {
|
||||
time_usec: event.time_usec(),
|
||||
init: Box::new(TabletToolInit {
|
||||
tablet_id,
|
||||
id,
|
||||
type_: match tool.type_() {
|
||||
LIBINPUT_TABLET_TOOL_TYPE_PEN => TabletToolType::Pen,
|
||||
LIBINPUT_TABLET_TOOL_TYPE_ERASER => TabletToolType::Eraser,
|
||||
LIBINPUT_TABLET_TOOL_TYPE_BRUSH => TabletToolType::Brush,
|
||||
LIBINPUT_TABLET_TOOL_TYPE_PENCIL => TabletToolType::Pencil,
|
||||
LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH => TabletToolType::Airbrush,
|
||||
LIBINPUT_TABLET_TOOL_TYPE_MOUSE => TabletToolType::Mouse,
|
||||
LIBINPUT_TABLET_TOOL_TYPE_LENS => TabletToolType::Lens,
|
||||
_ => return,
|
||||
},
|
||||
hardware_serial: tool.serial(),
|
||||
hardware_id_wacom: tool.tool_id(),
|
||||
capabilities: {
|
||||
let mut caps = vec![];
|
||||
macro_rules! add_cap {
|
||||
($f:ident, $cap:ident) => {
|
||||
if tool.$f() {
|
||||
caps.push(TabletToolCapability::$cap);
|
||||
}
|
||||
};
|
||||
}
|
||||
add_cap!(has_tilt, Tilt);
|
||||
add_cap!(has_pressure, Pressure);
|
||||
add_cap!(has_distance, Distance);
|
||||
add_cap!(has_rotation, Rotation);
|
||||
add_cap!(has_slider, Slider);
|
||||
add_cap!(has_wheel, Wheel);
|
||||
caps
|
||||
},
|
||||
}),
|
||||
});
|
||||
dev.event(self.build_tablet_tool_changed(&event, None));
|
||||
} else {
|
||||
dev.event(InputEvent::TabletToolRemoved {
|
||||
time_usec: event.time_usec(),
|
||||
id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_tablet_tool_tip(self: &Rc<Self>, event: LibInputEvent) {
|
||||
let (event, dev) = unpack!(self, event, tablet_tool_event);
|
||||
let down = match event.tip_state() {
|
||||
LIBINPUT_TABLET_TOOL_TIP_UP => false,
|
||||
LIBINPUT_TABLET_TOOL_TIP_DOWN => true,
|
||||
_ => return,
|
||||
};
|
||||
dev.event(self.build_tablet_tool_changed(&event, Some(down)));
|
||||
}
|
||||
|
||||
fn handle_tablet_tool_axis(self: &Rc<Self>, event: LibInputEvent) {
|
||||
let (event, dev) = unpack!(self, event, tablet_tool_event);
|
||||
dev.event(self.build_tablet_tool_changed(&event, None));
|
||||
}
|
||||
|
||||
fn handle_tablet_tool_button(self: &Rc<Self>, event: LibInputEvent) {
|
||||
let (event, dev) = unpack!(self, event, tablet_tool_event);
|
||||
dev.event(InputEvent::TabletToolButton {
|
||||
time_usec: event.time_usec(),
|
||||
id: self.get_tool_id(&event),
|
||||
button: event.button(),
|
||||
state: match event.button_state() {
|
||||
LIBINPUT_BUTTON_STATE_RELEASED => ToolButtonState::Released,
|
||||
LIBINPUT_BUTTON_STATE_PRESSED => ToolButtonState::Pressed,
|
||||
_ => return,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
fn handle_tablet_pad_button(self: &Rc<Self>, event: LibInputEvent) {
|
||||
let (event, dev) = unpack!(self, event, tablet_pad_event);
|
||||
let id = match dev.tablet_pad_id.get() {
|
||||
None => return,
|
||||
Some(id) => id,
|
||||
};
|
||||
let state = match event.button_state() {
|
||||
LIBINPUT_BUTTON_STATE_RELEASED => PadButtonState::Released,
|
||||
LIBINPUT_BUTTON_STATE_PRESSED => PadButtonState::Pressed,
|
||||
_ => return,
|
||||
};
|
||||
dev.event(InputEvent::TabletPadModeSwitch {
|
||||
time_usec: event.time_usec(),
|
||||
pad: id,
|
||||
group: event.mode_group().index(),
|
||||
mode: event.mode(),
|
||||
});
|
||||
dev.event(InputEvent::TabletPadButton {
|
||||
time_usec: event.time_usec(),
|
||||
id,
|
||||
button: event.button_number(),
|
||||
state,
|
||||
});
|
||||
}
|
||||
|
||||
fn handle_tablet_pad_ring(self: &Rc<Self>, event: LibInputEvent) {
|
||||
let (event, dev) = unpack!(self, event, tablet_pad_event);
|
||||
dev.event(InputEvent::TabletPadRing {
|
||||
time_usec: event.time_usec(),
|
||||
pad: match dev.tablet_pad_id.get() {
|
||||
None => return,
|
||||
Some(id) => id,
|
||||
},
|
||||
ring: event.ring_number(),
|
||||
source: match event.ring_source() {
|
||||
LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER => Some(TabletRingEventSource::Finger),
|
||||
_ => None,
|
||||
},
|
||||
angle: match event.ring_position() {
|
||||
n if n == -1.0 => None,
|
||||
n => Some(n),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
fn handle_tablet_pad_strip(self: &Rc<Self>, event: LibInputEvent) {
|
||||
let (event, dev) = unpack!(self, event, tablet_pad_event);
|
||||
dev.event(InputEvent::TabletPadStrip {
|
||||
time_usec: event.time_usec(),
|
||||
pad: match dev.tablet_pad_id.get() {
|
||||
None => return,
|
||||
Some(id) => id,
|
||||
},
|
||||
strip: event.strip_number(),
|
||||
source: match event.strip_source() {
|
||||
LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER => Some(TabletStripEventSource::Finger),
|
||||
_ => None,
|
||||
},
|
||||
position: match event.strip_position() {
|
||||
n if n == -1.0 => None,
|
||||
n => Some(n),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -293,6 +293,7 @@ impl MetalBackend {
|
|||
let devnum = dev.devnum();
|
||||
let devnode = dev.devnode()?;
|
||||
let sysname = dev.sysname()?;
|
||||
let syspath = dev.syspath()?;
|
||||
log::info!("Device added: {}", devnode.to_bytes().as_bstr());
|
||||
let mut slots = self.device_holder.input_devices.borrow_mut();
|
||||
let slot = 'slot: {
|
||||
|
|
@ -305,6 +306,7 @@ impl MetalBackend {
|
|||
slots.len() - 1
|
||||
};
|
||||
let dev = Rc::new(MetalInputDevice {
|
||||
state: self.state.clone(),
|
||||
slot,
|
||||
id: device_id,
|
||||
devnum,
|
||||
|
|
@ -312,6 +314,7 @@ impl MetalBackend {
|
|||
inputdev: Default::default(),
|
||||
devnode: devnode.to_owned(),
|
||||
_sysname: sysname.to_owned(),
|
||||
syspath: syspath.to_owned(),
|
||||
removed: Cell::new(false),
|
||||
events: Default::default(),
|
||||
cb: Default::default(),
|
||||
|
|
@ -321,6 +324,8 @@ impl MetalBackend {
|
|||
desired: Default::default(),
|
||||
transform_matrix: Default::default(),
|
||||
effective: Default::default(),
|
||||
tablet_id: Default::default(),
|
||||
tablet_pad_id: Default::default(),
|
||||
});
|
||||
slots[slot] = Some(dev.clone());
|
||||
self.device_holder
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use {
|
||||
crate::{
|
||||
cli::{GlobalArgs, SeatTestArgs},
|
||||
fixed::Fixed,
|
||||
ifs::wl_seat::wl_pointer::{PendingScroll, CONTINUOUS, FINGER, WHEEL},
|
||||
tools::tool_client::{with_tool_client, Handle, ToolClient},
|
||||
wire::{
|
||||
|
|
@ -8,7 +9,13 @@ use {
|
|||
jay_seat_events::{
|
||||
Axis120, AxisFrame, AxisInverted, AxisPx, AxisSource, AxisStop, Button, HoldBegin,
|
||||
HoldEnd, Key, Modifiers, PinchBegin, PinchEnd, PinchUpdate, PointerAbs, PointerRel,
|
||||
SwipeBegin, SwipeEnd, SwipeUpdate, SwitchEvent,
|
||||
SwipeBegin, SwipeEnd, SwipeUpdate, SwitchEvent, TabletPadButton,
|
||||
TabletPadModeSwitch, TabletPadRingAngle, TabletPadRingFrame, TabletPadRingSource,
|
||||
TabletPadRingStop, TabletPadStripFrame, TabletPadStripPosition,
|
||||
TabletPadStripSource, TabletPadStripStop, TabletToolButton, TabletToolDistance,
|
||||
TabletToolDown, TabletToolFrame, TabletToolMotion, TabletToolPressure,
|
||||
TabletToolProximityIn, TabletToolProximityOut, TabletToolRotation,
|
||||
TabletToolSlider, TabletToolTilt, TabletToolUp, TabletToolWheel,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -42,6 +49,36 @@ impl SeatTest {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone)]
|
||||
pub struct PendingTabletTool {
|
||||
proximity_in: bool,
|
||||
proximity_out: bool,
|
||||
down: bool,
|
||||
up: bool,
|
||||
pos: Option<(Fixed, Fixed)>,
|
||||
pressure: Option<f64>,
|
||||
distance: Option<f64>,
|
||||
tilt: Option<(f64, f64)>,
|
||||
rotation: Option<f64>,
|
||||
slider: Option<f64>,
|
||||
wheel: Option<(f64, i32)>,
|
||||
button: Option<(u32, u32)>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone)]
|
||||
pub struct PendingTabletPadStrip {
|
||||
source: u32,
|
||||
pos: Option<f64>,
|
||||
stop: bool,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone)]
|
||||
pub struct PendingTabletPadRing {
|
||||
source: u32,
|
||||
degrees: Option<f64>,
|
||||
stop: bool,
|
||||
}
|
||||
|
||||
async fn run(seat_test: Rc<SeatTest>) {
|
||||
let tc = &seat_test.tc;
|
||||
let comp = tc.jay_compositor().await;
|
||||
|
|
@ -344,6 +381,208 @@ async fn run(seat_test: Rc<SeatTest>) {
|
|||
);
|
||||
}
|
||||
});
|
||||
let tt = Rc::new(RefCell::new(PendingTabletTool::default()));
|
||||
TabletToolProximityIn::handle(tc, se, tt.clone(), move |tt, _| {
|
||||
tt.borrow_mut().proximity_in = true;
|
||||
});
|
||||
TabletToolProximityOut::handle(tc, se, tt.clone(), move |tt, _| {
|
||||
tt.borrow_mut().proximity_out = true;
|
||||
});
|
||||
TabletToolDown::handle(tc, se, tt.clone(), move |tt, _| {
|
||||
tt.borrow_mut().down = true;
|
||||
});
|
||||
TabletToolUp::handle(tc, se, tt.clone(), move |tt, _| {
|
||||
tt.borrow_mut().up = true;
|
||||
});
|
||||
TabletToolMotion::handle(tc, se, tt.clone(), move |tt, ev| {
|
||||
tt.borrow_mut().pos = Some((ev.x, ev.y));
|
||||
});
|
||||
TabletToolPressure::handle(tc, se, tt.clone(), move |tt, ev| {
|
||||
tt.borrow_mut().pressure = Some(ev.pressure);
|
||||
});
|
||||
TabletToolDistance::handle(tc, se, tt.clone(), move |tt, ev| {
|
||||
tt.borrow_mut().distance = Some(ev.distance);
|
||||
});
|
||||
TabletToolTilt::handle(tc, se, tt.clone(), move |tt, ev| {
|
||||
tt.borrow_mut().tilt = Some((ev.tilt_x, ev.tilt_y));
|
||||
});
|
||||
TabletToolRotation::handle(tc, se, tt.clone(), move |tt, ev| {
|
||||
tt.borrow_mut().rotation = Some(ev.degrees);
|
||||
});
|
||||
TabletToolSlider::handle(tc, se, tt.clone(), move |tt, ev| {
|
||||
tt.borrow_mut().slider = Some(ev.position);
|
||||
});
|
||||
TabletToolWheel::handle(tc, se, tt.clone(), move |tt, ev| {
|
||||
tt.borrow_mut().wheel = Some((ev.degrees, ev.clicks));
|
||||
});
|
||||
TabletToolButton::handle(tc, se, tt.clone(), move |tt, ev| {
|
||||
tt.borrow_mut().button = Some((ev.button, ev.state));
|
||||
});
|
||||
let st = seat_test.clone();
|
||||
TabletToolFrame::handle(tc, se, tt.clone(), move |tt, ev| {
|
||||
let tt = tt.take();
|
||||
if !all && ev.seat != seat {
|
||||
return;
|
||||
}
|
||||
if all {
|
||||
print!("Seat: {}, ", st.name(ev.seat));
|
||||
}
|
||||
print!(
|
||||
"Time: {:.4}, Device: {}, Tool: {}",
|
||||
time(ev.time_usec),
|
||||
ev.input_device,
|
||||
ev.tool,
|
||||
);
|
||||
if tt.proximity_in {
|
||||
print!(", proximity in");
|
||||
}
|
||||
if tt.proximity_out {
|
||||
print!(", proximity out");
|
||||
}
|
||||
if tt.down {
|
||||
print!(", down");
|
||||
}
|
||||
if tt.up {
|
||||
print!(", up");
|
||||
}
|
||||
if let Some((x, y)) = tt.pos {
|
||||
print!(", pos: {x}x{y}");
|
||||
}
|
||||
if let Some(val) = tt.pressure {
|
||||
print!(", pressure: {val}");
|
||||
}
|
||||
if let Some(val) = tt.distance {
|
||||
print!(", distance: {val}");
|
||||
}
|
||||
if let Some((x, y)) = tt.tilt {
|
||||
print!(", tilt: {x}x{y}");
|
||||
}
|
||||
if let Some(val) = tt.rotation {
|
||||
print!(", rotation: {val}");
|
||||
}
|
||||
if let Some(val) = tt.slider {
|
||||
print!(", slider: {val}");
|
||||
}
|
||||
if let Some((degrees, clicks)) = tt.wheel {
|
||||
print!(", wheel degrees: {degrees}, wheel clicks: {clicks}");
|
||||
}
|
||||
if let Some((button, state)) = tt.button {
|
||||
let dir = match state {
|
||||
0 => "up",
|
||||
_ => "down",
|
||||
};
|
||||
print!(", button {button} {dir}");
|
||||
}
|
||||
println!();
|
||||
});
|
||||
let st = seat_test.clone();
|
||||
TabletPadModeSwitch::handle(tc, se, (), move |_, ev| {
|
||||
if all || ev.seat == seat {
|
||||
if all {
|
||||
print!("Seat: {}, ", st.name(ev.seat));
|
||||
}
|
||||
println!(
|
||||
"Time: {:.4}, Device: {}, mode switch: {}",
|
||||
time(ev.time_usec),
|
||||
ev.input_device,
|
||||
ev.mode,
|
||||
);
|
||||
}
|
||||
});
|
||||
let st = seat_test.clone();
|
||||
TabletPadButton::handle(tc, se, (), move |_, ev| {
|
||||
if all || ev.seat == seat {
|
||||
if all {
|
||||
print!("Seat: {}, ", st.name(ev.seat));
|
||||
}
|
||||
let dir = match ev.state {
|
||||
0 => "up",
|
||||
_ => "down",
|
||||
};
|
||||
println!(
|
||||
"Time: {:.4}, Device: {}, Button {} {dir}",
|
||||
time(ev.time_usec),
|
||||
ev.input_device,
|
||||
ev.button,
|
||||
);
|
||||
}
|
||||
});
|
||||
let tt = Rc::new(RefCell::new(PendingTabletPadStrip::default()));
|
||||
TabletPadStripSource::handle(tc, se, tt.clone(), move |tt, ev| {
|
||||
tt.borrow_mut().source = ev.source;
|
||||
});
|
||||
TabletPadStripPosition::handle(tc, se, tt.clone(), move |tt, ev| {
|
||||
tt.borrow_mut().pos = Some(ev.position);
|
||||
});
|
||||
TabletPadStripStop::handle(tc, se, tt.clone(), move |tt, _| {
|
||||
tt.borrow_mut().stop = true;
|
||||
});
|
||||
let st = seat_test.clone();
|
||||
TabletPadStripFrame::handle(tc, se, tt.clone(), move |tt, ev| {
|
||||
let tt = tt.take();
|
||||
if !all && ev.seat != seat {
|
||||
return;
|
||||
}
|
||||
if all {
|
||||
print!("Seat: {}, ", st.name(ev.seat));
|
||||
}
|
||||
print!(
|
||||
"Time: {:.4}, Device: {}, Strip: {}",
|
||||
time(ev.time_usec),
|
||||
ev.input_device,
|
||||
ev.strip,
|
||||
);
|
||||
let source = match tt.source {
|
||||
1 => "finger",
|
||||
_ => "unknown",
|
||||
};
|
||||
print!(", source: {source}");
|
||||
if let Some(pos) = tt.pos {
|
||||
print!(", pos: {pos}");
|
||||
}
|
||||
if tt.stop {
|
||||
print!(", stop");
|
||||
}
|
||||
println!();
|
||||
});
|
||||
let tt = Rc::new(RefCell::new(PendingTabletPadRing::default()));
|
||||
TabletPadRingSource::handle(tc, se, tt.clone(), move |tt, ev| {
|
||||
tt.borrow_mut().source = ev.source;
|
||||
});
|
||||
TabletPadRingAngle::handle(tc, se, tt.clone(), move |tt, ev| {
|
||||
tt.borrow_mut().degrees = Some(ev.degrees);
|
||||
});
|
||||
TabletPadRingStop::handle(tc, se, tt.clone(), move |tt, _| {
|
||||
tt.borrow_mut().stop = true;
|
||||
});
|
||||
let st = seat_test.clone();
|
||||
TabletPadRingFrame::handle(tc, se, tt.clone(), move |tt, ev| {
|
||||
let tt = tt.take();
|
||||
if !all && ev.seat != seat {
|
||||
return;
|
||||
}
|
||||
if all {
|
||||
print!("Seat: {}, ", st.name(ev.seat));
|
||||
}
|
||||
print!(
|
||||
"Time: {:.4}, Device: {}, Ring: {}",
|
||||
time(ev.time_usec),
|
||||
ev.input_device,
|
||||
ev.ring,
|
||||
);
|
||||
let source = match tt.source {
|
||||
1 => "finger",
|
||||
_ => "unknown",
|
||||
};
|
||||
print!(", source: {source}");
|
||||
if let Some(val) = tt.degrees {
|
||||
print!(", degrees: {val}");
|
||||
}
|
||||
if tt.stop {
|
||||
print!(", stop");
|
||||
}
|
||||
println!();
|
||||
});
|
||||
pending::<()>().await;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ use {
|
|||
copyhashmap::{CopyHashMap, Locked},
|
||||
errorfmt::ErrorFmt,
|
||||
numcell::NumCell,
|
||||
pending_serial::PendingSerial,
|
||||
trim::AsciiTrim,
|
||||
},
|
||||
wire::WlRegistryId,
|
||||
|
|
@ -350,6 +351,10 @@ impl Client {
|
|||
self.state.next_serial(Some(self))
|
||||
}
|
||||
|
||||
pub fn pending_serial(&self) -> PendingSerial<'_> {
|
||||
PendingSerial::new(self)
|
||||
}
|
||||
|
||||
pub fn new_id<T: From<ObjectId>>(&self) -> Result<T, ClientError> {
|
||||
self.objects.id(self)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ use {
|
|||
wl_output::WlOutput,
|
||||
wl_region::WlRegion,
|
||||
wl_registry::WlRegistry,
|
||||
wl_seat::{wl_pointer::WlPointer, WlSeat},
|
||||
wl_seat::{tablet::zwp_tablet_tool_v2::ZwpTabletToolV2, wl_pointer::WlPointer, WlSeat},
|
||||
wl_surface::{
|
||||
xdg_surface::{xdg_toplevel::XdgToplevel, XdgSurface},
|
||||
WlSurface,
|
||||
|
|
@ -35,7 +35,7 @@ use {
|
|||
WlDataSourceId, WlOutputId, WlPointerId, WlRegionId, WlRegistryId, WlSeatId,
|
||||
WlSurfaceId, WpDrmLeaseConnectorV1Id, WpLinuxDrmSyncobjTimelineV1Id, XdgPositionerId,
|
||||
XdgSurfaceId, XdgToplevelId, XdgWmBaseId, ZwlrDataControlSourceV1Id,
|
||||
ZwpPrimarySelectionSourceV1Id,
|
||||
ZwpPrimarySelectionSourceV1Id, ZwpTabletToolV2Id,
|
||||
},
|
||||
},
|
||||
std::{cell::RefCell, mem, rc::Rc},
|
||||
|
|
@ -65,6 +65,7 @@ pub struct Objects {
|
|||
pub zwlr_data_sources: CopyHashMap<ZwlrDataControlSourceV1Id, Rc<ZwlrDataControlSourceV1>>,
|
||||
pub jay_toplevels: CopyHashMap<JayToplevelId, Rc<JayToplevel>>,
|
||||
pub drm_lease_outputs: CopyHashMap<WpDrmLeaseConnectorV1Id, Rc<WpDrmLeaseConnectorV1>>,
|
||||
pub tablet_tools: CopyHashMap<ZwpTabletToolV2Id, Rc<ZwpTabletToolV2>>,
|
||||
ids: RefCell<Vec<usize>>,
|
||||
}
|
||||
|
||||
|
|
@ -96,6 +97,7 @@ impl Objects {
|
|||
zwlr_data_sources: Default::default(),
|
||||
jay_toplevels: Default::default(),
|
||||
drm_lease_outputs: Default::default(),
|
||||
tablet_tools: Default::default(),
|
||||
ids: RefCell::new(vec![]),
|
||||
}
|
||||
}
|
||||
|
|
@ -131,6 +133,7 @@ impl Objects {
|
|||
self.zwlr_data_sources.clear();
|
||||
self.jay_toplevels.clear();
|
||||
self.drm_lease_outputs.clear();
|
||||
self.tablet_tools.clear();
|
||||
}
|
||||
|
||||
pub fn id<T>(&self, client_data: &Client) -> Result<T, ClientError>
|
||||
|
|
|
|||
|
|
@ -236,6 +236,14 @@ fn start_compositor2(
|
|||
explicit_sync_enabled: Cell::new(true),
|
||||
keyboard_state_ids: Default::default(),
|
||||
security_context_acceptors: Default::default(),
|
||||
cursor_user_group_ids: Default::default(),
|
||||
cursor_user_ids: Default::default(),
|
||||
cursor_user_groups: Default::default(),
|
||||
cursor_user_group_hardware_cursor: Default::default(),
|
||||
input_device_group_ids: Default::default(),
|
||||
tablet_ids: Default::default(),
|
||||
tablet_tool_ids: Default::default(),
|
||||
tablet_pad_ids: Default::default(),
|
||||
});
|
||||
state.tracker.register(ClientId::from_raw(0));
|
||||
create_dummy_output(&state);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -956,7 +973,7 @@ impl ConfigProxyHandler {
|
|||
if size < 0 {
|
||||
return Err(CphError::NegativeCursorSize);
|
||||
}
|
||||
seat.set_cursor_size(size as _);
|
||||
seat.cursor_group().set_cursor_size(size as _);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -972,14 +989,7 @@ impl ConfigProxyHandler {
|
|||
use_hardware_cursor: bool,
|
||||
) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
if use_hardware_cursor {
|
||||
for other in self.state.globals.seats.lock().values() {
|
||||
if other.id() != seat.id() {
|
||||
other.set_hardware_cursor(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
seat.set_hardware_cursor(use_hardware_cursor);
|
||||
seat.cursor_group().set_hardware_cursor(use_hardware_cursor);
|
||||
self.state.refresh_hardware_cursors();
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -1037,18 +1047,7 @@ impl ConfigProxyHandler {
|
|||
if x < 0 || y < 0 || x > MAX_EXTENTS || y > MAX_EXTENTS {
|
||||
return Err(CphError::InvalidConnectorPosition(x, y));
|
||||
}
|
||||
let old_pos = connector.global.pos.get();
|
||||
connector.set_position(x, y);
|
||||
let seats = self.state.globals.seats.lock();
|
||||
for seat in seats.values() {
|
||||
if seat.get_output().id == connector.id {
|
||||
let seat_pos = seat.position();
|
||||
seat.set_position(
|
||||
seat_pos.0.round_down() + x - old_pos.x1(),
|
||||
seat_pos.1.round_down() + y - old_pos.y1(),
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -1808,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(())
|
||||
}
|
||||
|
|
|
|||
462
src/cursor_user.rs
Normal file
462
src/cursor_user.rs
Normal file
|
|
@ -0,0 +1,462 @@
|
|||
use {
|
||||
crate::{
|
||||
cursor::{Cursor, KnownCursor, DEFAULT_CURSOR_SIZE},
|
||||
fixed::Fixed,
|
||||
rect::Rect,
|
||||
state::State,
|
||||
tree::OutputNode,
|
||||
utils::{
|
||||
clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, rc_eq::rc_eq,
|
||||
transform_ext::TransformExt,
|
||||
},
|
||||
},
|
||||
std::{cell::Cell, ops::Deref, rc::Rc},
|
||||
};
|
||||
|
||||
linear_ids!(CursorUserGroupIds, CursorUserGroupId, u64);
|
||||
linear_ids!(CursorUserIds, CursorUserId, u64);
|
||||
|
||||
pub trait CursorUserOwner {
|
||||
fn output_changed(&self, output: &Rc<OutputNode>);
|
||||
}
|
||||
|
||||
pub struct CursorUserGroup {
|
||||
pub id: CursorUserGroupId,
|
||||
state: Rc<State>,
|
||||
active_id: Cell<Option<CursorUserId>>,
|
||||
active: CloneCell<Option<Rc<CursorUser>>>,
|
||||
users: CopyHashMap<CursorUserId, Rc<CursorUser>>,
|
||||
hardware_cursor: Cell<bool>,
|
||||
size: Cell<u32>,
|
||||
latest_output: CloneCell<Rc<OutputNode>>,
|
||||
}
|
||||
|
||||
pub struct CursorUser {
|
||||
pub id: CursorUserId,
|
||||
group: Rc<CursorUserGroup>,
|
||||
desired_known_cursor: Cell<Option<KnownCursor>>,
|
||||
cursor: CloneCell<Option<Rc<dyn Cursor>>>,
|
||||
output: CloneCell<Rc<OutputNode>>,
|
||||
output_pos: Cell<Rect>,
|
||||
pos: Cell<(Fixed, Fixed)>,
|
||||
owner: CloneCell<Option<Rc<dyn CursorUserOwner>>>,
|
||||
}
|
||||
|
||||
impl CursorUserGroup {
|
||||
pub fn create(state: &Rc<State>) -> Rc<Self> {
|
||||
let output = state
|
||||
.root
|
||||
.outputs
|
||||
.lock()
|
||||
.values()
|
||||
.next()
|
||||
.cloned()
|
||||
.or_else(|| state.dummy_output.get())
|
||||
.unwrap();
|
||||
let hardware_cursor = state.cursor_user_group_hardware_cursor.is_none();
|
||||
let group = Rc::new(Self {
|
||||
id: state.cursor_user_group_ids.next(),
|
||||
state: state.clone(),
|
||||
active_id: Default::default(),
|
||||
active: Default::default(),
|
||||
users: Default::default(),
|
||||
hardware_cursor: Cell::new(hardware_cursor),
|
||||
size: Cell::new(*DEFAULT_CURSOR_SIZE),
|
||||
latest_output: CloneCell::new(output),
|
||||
});
|
||||
state.add_cursor_size(*DEFAULT_CURSOR_SIZE);
|
||||
state.cursor_user_groups.set(group.id, group.clone());
|
||||
if hardware_cursor {
|
||||
state
|
||||
.cursor_user_group_hardware_cursor
|
||||
.set(Some(group.clone()));
|
||||
}
|
||||
group
|
||||
}
|
||||
|
||||
pub fn deactivate(&self) {
|
||||
if self.hardware_cursor.get() {
|
||||
self.remove_hardware_cursor();
|
||||
}
|
||||
self.active_id.take();
|
||||
self.active.take();
|
||||
self.state.damage();
|
||||
}
|
||||
|
||||
pub fn latest_output(&self) -> Rc<OutputNode> {
|
||||
self.latest_output.get()
|
||||
}
|
||||
|
||||
fn remove_hardware_cursor(&self) {
|
||||
self.state.hardware_tick_cursor.push(None);
|
||||
self.state.disable_hardware_cursors();
|
||||
self.state.cursor_user_group_hardware_cursor.take();
|
||||
}
|
||||
|
||||
pub fn detach(&self) {
|
||||
self.deactivate();
|
||||
self.latest_output
|
||||
.set(self.state.dummy_output.get().unwrap());
|
||||
self.state.remove_cursor_size(self.size.get());
|
||||
self.state.cursor_user_groups.remove(&self.id);
|
||||
for (_, user) in self.users.lock().drain() {
|
||||
user.detach();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_user(self: &Rc<Self>) -> Rc<CursorUser> {
|
||||
let output = self.latest_output.get();
|
||||
let user = Rc::new(CursorUser {
|
||||
id: self.state.cursor_user_ids.next(),
|
||||
group: self.clone(),
|
||||
desired_known_cursor: Cell::new(None),
|
||||
cursor: Default::default(),
|
||||
pos: Cell::new(self.output_center(&output)),
|
||||
output_pos: Cell::new(output.global.pos.get()),
|
||||
output: CloneCell::new(output),
|
||||
owner: Default::default(),
|
||||
});
|
||||
self.users.set(user.id, user.clone());
|
||||
user
|
||||
}
|
||||
|
||||
pub fn set_visible(&self, visible: bool) {
|
||||
if let Some(user) = self.active.get() {
|
||||
if let Some(cursor) = user.cursor.get() {
|
||||
cursor.set_visible(visible);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn active(&self) -> Option<Rc<CursorUser>> {
|
||||
self.active.get()
|
||||
}
|
||||
|
||||
pub fn render_ctx_changed(&self) {
|
||||
for user in self.users.lock().values() {
|
||||
if let Some(cursor) = user.desired_known_cursor.get() {
|
||||
user.set_known(cursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reload_known_cursor(&self) {
|
||||
for user in self.users.lock().values() {
|
||||
user.reload_known_cursor();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_hardware_cursor(self: &Rc<Self>, hardware_cursor: bool) {
|
||||
if self.hardware_cursor.replace(hardware_cursor) == hardware_cursor {
|
||||
return;
|
||||
}
|
||||
if hardware_cursor {
|
||||
let prev = self
|
||||
.state
|
||||
.cursor_user_group_hardware_cursor
|
||||
.set(Some(self.clone()));
|
||||
if let Some(prev) = prev {
|
||||
prev.hardware_cursor.set(false);
|
||||
}
|
||||
match self.active.get() {
|
||||
None => self.remove_hardware_cursor(),
|
||||
Some(a) => a.update_hardware_cursor(),
|
||||
}
|
||||
} else {
|
||||
self.remove_hardware_cursor();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hardware_cursor(&self) -> bool {
|
||||
self.hardware_cursor.get()
|
||||
}
|
||||
|
||||
pub fn set_cursor_size(&self, size: u32) {
|
||||
let old = self.size.replace(size);
|
||||
if size != old {
|
||||
self.state.remove_cursor_size(old);
|
||||
self.state.add_cursor_size(size);
|
||||
self.reload_known_cursor();
|
||||
}
|
||||
}
|
||||
|
||||
fn output_center(&self, output: &Rc<OutputNode>) -> (Fixed, Fixed) {
|
||||
let pos = output.global.pos.get();
|
||||
let x = Fixed::from_int((pos.x1() + pos.x2()) / 2);
|
||||
let y = Fixed::from_int((pos.y1() + pos.y2()) / 2);
|
||||
(x, y)
|
||||
}
|
||||
|
||||
pub fn first_output_connected(&self, output: &Rc<OutputNode>) {
|
||||
self.latest_output.set(output.clone());
|
||||
let (x, y) = self.output_center(output);
|
||||
for user in self.users.lock().values() {
|
||||
user.set_output(output);
|
||||
user.set_position(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn output_disconnected(&self, output: &Rc<OutputNode>, next: &Rc<OutputNode>) {
|
||||
if self.latest_output.get().id == output.id {
|
||||
self.latest_output.set(next.clone());
|
||||
}
|
||||
let (x, y) = self.output_center(next);
|
||||
for user in self.users.lock().values() {
|
||||
if user.output.get().id == output.id {
|
||||
user.set_output(next);
|
||||
user.set_position(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn output_pos_changed(&self, output: &Rc<OutputNode>) {
|
||||
let (x, y) = self.output_center(output);
|
||||
for user in self.users.lock().values() {
|
||||
if user.output.get().id == output.id {
|
||||
user.output_pos.set(output.global.pos.get());
|
||||
user.set_position(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CursorUser {
|
||||
pub fn set_owner(&self, owner: Rc<dyn CursorUserOwner>) {
|
||||
self.owner.set(Some(owner));
|
||||
}
|
||||
|
||||
pub fn detach(&self) {
|
||||
self.set(None);
|
||||
self.owner.take();
|
||||
self.group.users.remove(&self.id);
|
||||
if self.group.active_id.get() == Some(self.id) {
|
||||
self.group.active_id.take();
|
||||
self.group.active.take();
|
||||
self.group.state.damage();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn activate(self: &Rc<Self>) {
|
||||
if self.group.active_id.replace(Some(self.id)) == Some(self.id) {
|
||||
return;
|
||||
}
|
||||
self.group.latest_output.set(self.output.get());
|
||||
self.group.active.set(Some(self.clone()));
|
||||
self.update_hardware_cursor();
|
||||
self.group.state.damage();
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "it"), allow(dead_code))]
|
||||
pub fn desired_known_cursor(&self) -> Option<KnownCursor> {
|
||||
self.desired_known_cursor.get()
|
||||
}
|
||||
|
||||
pub fn set_known(&self, cursor: KnownCursor) {
|
||||
self.desired_known_cursor.set(Some(cursor));
|
||||
let cursors = match self.group.state.cursors.get() {
|
||||
Some(c) => c,
|
||||
None => {
|
||||
self.set_cursor2(None);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let tpl = match cursor {
|
||||
KnownCursor::Default => &cursors.default,
|
||||
KnownCursor::ContextMenu => &cursors.context_menu,
|
||||
KnownCursor::Help => &cursors.help,
|
||||
KnownCursor::Pointer => &cursors.pointer,
|
||||
KnownCursor::Progress => &cursors.progress,
|
||||
KnownCursor::Wait => &cursors.wait,
|
||||
KnownCursor::Cell => &cursors.cell,
|
||||
KnownCursor::Crosshair => &cursors.crosshair,
|
||||
KnownCursor::Text => &cursors.text,
|
||||
KnownCursor::VerticalText => &cursors.vertical_text,
|
||||
KnownCursor::Alias => &cursors.alias,
|
||||
KnownCursor::Copy => &cursors.copy,
|
||||
KnownCursor::Move => &cursors.r#move,
|
||||
KnownCursor::NoDrop => &cursors.no_drop,
|
||||
KnownCursor::NotAllowed => &cursors.not_allowed,
|
||||
KnownCursor::Grab => &cursors.grab,
|
||||
KnownCursor::Grabbing => &cursors.grabbing,
|
||||
KnownCursor::EResize => &cursors.e_resize,
|
||||
KnownCursor::NResize => &cursors.n_resize,
|
||||
KnownCursor::NeResize => &cursors.ne_resize,
|
||||
KnownCursor::NwResize => &cursors.nw_resize,
|
||||
KnownCursor::SResize => &cursors.s_resize,
|
||||
KnownCursor::SeResize => &cursors.se_resize,
|
||||
KnownCursor::SwResize => &cursors.sw_resize,
|
||||
KnownCursor::WResize => &cursors.w_resize,
|
||||
KnownCursor::EwResize => &cursors.ew_resize,
|
||||
KnownCursor::NsResize => &cursors.ns_resize,
|
||||
KnownCursor::NeswResize => &cursors.nesw_resize,
|
||||
KnownCursor::NwseResize => &cursors.nwse_resize,
|
||||
KnownCursor::ColResize => &cursors.col_resize,
|
||||
KnownCursor::RowResize => &cursors.row_resize,
|
||||
KnownCursor::AllScroll => &cursors.all_scroll,
|
||||
KnownCursor::ZoomIn => &cursors.zoom_in,
|
||||
KnownCursor::ZoomOut => &cursors.zoom_out,
|
||||
};
|
||||
self.set_cursor2(Some(tpl.instantiate(self.group.size.get())));
|
||||
}
|
||||
|
||||
fn set_output(&self, output: &Rc<OutputNode>) {
|
||||
self.output.set(output.clone());
|
||||
self.output_pos.set(output.global.pos.get());
|
||||
if self.is_active() {
|
||||
self.group.latest_output.set(output.clone());
|
||||
}
|
||||
if let Some(cursor) = self.cursor.get() {
|
||||
cursor.set_output(output);
|
||||
}
|
||||
if let Some(owner) = self.owner.get() {
|
||||
owner.output_changed(output);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn output(&self) -> Rc<OutputNode> {
|
||||
self.output.get()
|
||||
}
|
||||
|
||||
pub fn get(&self) -> Option<Rc<dyn Cursor>> {
|
||||
self.cursor.get()
|
||||
}
|
||||
|
||||
pub fn set(&self, cursor: Option<Rc<dyn Cursor>>) {
|
||||
self.set_cursor2(cursor);
|
||||
self.desired_known_cursor.set(None);
|
||||
}
|
||||
|
||||
fn is_active(&self) -> bool {
|
||||
self.group.active_id.get() == Some(self.id)
|
||||
}
|
||||
|
||||
fn set_cursor2(&self, cursor: Option<Rc<dyn Cursor>>) {
|
||||
if let Some(old) = self.cursor.get() {
|
||||
if let Some(new) = cursor.as_ref() {
|
||||
if rc_eq(&old, new) {
|
||||
self.update_hardware_cursor();
|
||||
return;
|
||||
}
|
||||
}
|
||||
old.handle_unset();
|
||||
}
|
||||
if let Some(cursor) = cursor.as_ref() {
|
||||
cursor.clone().handle_set();
|
||||
cursor.set_output(&self.output.get());
|
||||
}
|
||||
self.cursor.set(cursor.clone());
|
||||
self.update_hardware_cursor();
|
||||
}
|
||||
|
||||
pub fn position(&self) -> (Fixed, Fixed) {
|
||||
self.pos.get()
|
||||
}
|
||||
|
||||
pub fn set_position(&self, mut x: Fixed, mut y: Fixed) -> (Fixed, Fixed) {
|
||||
let x_int = x.round_down();
|
||||
let y_int = y.round_down();
|
||||
if !self.output_pos.get().contains(x_int, y_int) {
|
||||
let (output, x_tmp, y_tmp) = self.group.state.find_closest_output(x_int, y_int);
|
||||
self.set_output(&output);
|
||||
x = x.apply_fract(x_tmp);
|
||||
y = y.apply_fract(y_tmp);
|
||||
}
|
||||
self.pos.set((x, y));
|
||||
self.update_hardware_cursor_(false);
|
||||
(x, y)
|
||||
}
|
||||
|
||||
pub fn update_hardware_cursor(&self) {
|
||||
self.update_hardware_cursor_(true);
|
||||
}
|
||||
|
||||
fn hardware_cursor(&self) -> bool {
|
||||
self.is_active() && self.group.hardware_cursor.get()
|
||||
}
|
||||
|
||||
fn update_hardware_cursor_(&self, render: bool) {
|
||||
if !self.hardware_cursor() {
|
||||
return;
|
||||
}
|
||||
let cursor = self.cursor.get();
|
||||
self.group.state.hardware_tick_cursor.push(cursor.clone());
|
||||
let cursor = match cursor {
|
||||
Some(c) => c,
|
||||
_ => {
|
||||
self.group.state.disable_hardware_cursors();
|
||||
return;
|
||||
}
|
||||
};
|
||||
if render {
|
||||
cursor.tick();
|
||||
}
|
||||
let (x, y) = self.pos.get();
|
||||
for output in self.group.state.root.outputs.lock().values() {
|
||||
if let Some(hc) = output.hardware_cursor.get() {
|
||||
let transform = output.global.persistent.transform.get();
|
||||
let render = render | output.hardware_cursor_needs_render.take();
|
||||
let scale = output.global.persistent.scale.get();
|
||||
let extents = cursor.extents_at_scale(scale);
|
||||
let (hc_width, hc_height) = hc.size();
|
||||
if render {
|
||||
let (max_width, max_height) = transform.maybe_swap((hc_width, hc_height));
|
||||
if extents.width() > max_width || extents.height() > max_height {
|
||||
hc.set_enabled(false);
|
||||
hc.commit();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let opos = output.global.pos.get();
|
||||
let (x_rel, y_rel);
|
||||
if scale == 1 {
|
||||
x_rel = x.round_down() - opos.x1();
|
||||
y_rel = y.round_down() - opos.y1();
|
||||
} else {
|
||||
let scalef = scale.to_f64();
|
||||
x_rel = ((x - Fixed::from_int(opos.x1())).to_f64() * scalef).round() as i32;
|
||||
y_rel = ((y - Fixed::from_int(opos.y1())).to_f64() * scalef).round() as i32;
|
||||
}
|
||||
let (width, height) = output.global.pixel_size();
|
||||
if extents.intersects(&Rect::new_sized(-x_rel, -y_rel, width, height).unwrap()) {
|
||||
if render {
|
||||
let buffer = hc.get_buffer();
|
||||
let res = buffer.render_hardware_cursor(
|
||||
cursor.deref(),
|
||||
&self.group.state,
|
||||
scale,
|
||||
transform,
|
||||
);
|
||||
match res {
|
||||
Ok(sync_file) => {
|
||||
hc.set_sync_file(sync_file);
|
||||
hc.swap_buffer();
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Could not render hardware cursor: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
hc.set_enabled(true);
|
||||
let mode = output.global.mode.get();
|
||||
let (x_rel, y_rel) =
|
||||
transform.apply_point(mode.width, mode.height, (x_rel, y_rel));
|
||||
let (hot_x, hot_y) =
|
||||
transform.apply_point(hc_width, hc_height, (-extents.x1(), -extents.y1()));
|
||||
hc.set_position(x_rel - hot_x, y_rel - hot_y);
|
||||
} else {
|
||||
if render {
|
||||
output.hardware_cursor_needs_render.set(true);
|
||||
}
|
||||
hc.set_enabled(false);
|
||||
}
|
||||
hc.commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn reload_known_cursor(&self) {
|
||||
if let Some(kc) = self.desired_known_cursor.get() {
|
||||
self.set_known(kc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -345,7 +345,7 @@ impl dyn GfxFramebuffer {
|
|||
if let Some(rect) = cursor_rect {
|
||||
let seats = state.globals.lock_seats();
|
||||
for seat in seats.values() {
|
||||
let (mut x, mut y) = seat.get_position();
|
||||
let (x, y) = seat.pointer_cursor().position();
|
||||
if let Some(im) = seat.input_method() {
|
||||
for (_, popup) in &im.popups {
|
||||
if popup.surface.node_visible() {
|
||||
|
|
@ -380,12 +380,16 @@ impl dyn GfxFramebuffer {
|
|||
}
|
||||
}
|
||||
if render_cursor {
|
||||
if let Some(cursor) = seat.get_cursor() {
|
||||
if render_hardware_cursor || !seat.hardware_cursor() {
|
||||
cursor.tick();
|
||||
x -= Fixed::from_int(rect.x1());
|
||||
y -= Fixed::from_int(rect.y1());
|
||||
cursor.render(&mut renderer, x, y);
|
||||
let cursor_user_group = seat.cursor_group();
|
||||
if render_hardware_cursor || !cursor_user_group.hardware_cursor() {
|
||||
if let Some(cursor_user) = cursor_user_group.active() {
|
||||
if let Some(cursor) = cursor_user.get() {
|
||||
cursor.tick();
|
||||
let (mut x, mut y) = cursor_user.position();
|
||||
x -= Fixed::from_int(rect.x1());
|
||||
y -= Fixed::from_int(rect.y1());
|
||||
cursor.render(&mut renderer, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ use {
|
|||
wl_registry::WlRegistry,
|
||||
wl_seat::{
|
||||
ext_transient_seat_manager_v1::ExtTransientSeatManagerV1Global,
|
||||
tablet::zwp_tablet_manager_v2::ZwpTabletManagerV2Global,
|
||||
text_input::{
|
||||
zwp_input_method_manager_v2::ZwpInputMethodManagerV2Global,
|
||||
zwp_text_input_manager_v3::ZwpTextInputManagerV3Global,
|
||||
|
|
@ -193,6 +194,7 @@ impl Globals {
|
|||
add_singleton!(XdgWmDialogV1Global);
|
||||
add_singleton!(ExtTransientSeatManagerV1Global);
|
||||
add_singleton!(ZwpPointerGesturesV1Global);
|
||||
add_singleton!(ZwpTabletManagerV2Global);
|
||||
}
|
||||
|
||||
pub fn add_backend_singletons(&self, backend: &Rc<dyn Backend>) {
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ impl JayInput {
|
|||
name: data.seat_name(),
|
||||
repeat_rate: data.get_rate().0,
|
||||
repeat_delay: data.get_rate().1,
|
||||
hardware_cursor: data.hardware_cursor() as _,
|
||||
hardware_cursor: data.cursor_group().hardware_cursor() as _,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -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> {
|
||||
|
|
@ -202,7 +211,8 @@ impl JayInputRequestHandler for JayInput {
|
|||
) -> Result<(), Self::Error> {
|
||||
self.or_error(|| {
|
||||
let seat = self.seat(req.seat)?;
|
||||
seat.set_hardware_cursor(req.use_hardware_cursor != 0);
|
||||
seat.cursor_group()
|
||||
.set_hardware_cursor(req.use_hardware_cursor != 0);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
|
@ -316,7 +326,7 @@ impl JayInputRequestHandler for JayInput {
|
|||
fn set_cursor_size(&self, req: SetCursorSize, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.or_error(|| {
|
||||
let seat = self.seat(req.seat)?;
|
||||
seat.set_cursor_size(req.size);
|
||||
seat.cursor_group().set_cursor_size(req.size);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
|
@ -388,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! {
|
||||
|
|
@ -417,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);
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ impl JayPointerRequestHandler for JayPointer {
|
|||
if pointer_node.node_client_id() != Some(self.client.id) {
|
||||
return Ok(());
|
||||
}
|
||||
self.seat.set_known_cursor(cursor);
|
||||
self.seat.pointer_cursor().set_known(cursor);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,14 @@ use {
|
|||
backend::{InputDeviceId, KeyState},
|
||||
client::Client,
|
||||
fixed::Fixed,
|
||||
ifs::wl_seat::{wl_pointer::PendingScroll, SeatId},
|
||||
ifs::wl_seat::{
|
||||
tablet::{
|
||||
PadButtonState, TabletRingEventSource, TabletStripEventSource, TabletTool,
|
||||
TabletToolChanges, TabletToolId, ToolButtonState,
|
||||
},
|
||||
wl_pointer::PendingScroll,
|
||||
SeatId,
|
||||
},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
wire::{jay_seat_events::*, JaySeatEventsId},
|
||||
|
|
@ -236,6 +243,231 @@ impl JaySeatEvents {
|
|||
event: event as _,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_tablet_tool_proximity_in(
|
||||
&self,
|
||||
seat: SeatId,
|
||||
tablet: InputDeviceId,
|
||||
tool: TabletToolId,
|
||||
time_usec: u64,
|
||||
) {
|
||||
self.client
|
||||
.event(TabletToolProximityIn { self_id: self.id });
|
||||
self.client.event(TabletToolFrame {
|
||||
self_id: self.id,
|
||||
seat: seat.raw(),
|
||||
time_usec,
|
||||
input_device: tablet.raw(),
|
||||
tool: tool.raw() as _,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_tablet_tool_proximity_out(
|
||||
&self,
|
||||
seat: SeatId,
|
||||
tablet: InputDeviceId,
|
||||
tool: TabletToolId,
|
||||
time_usec: u64,
|
||||
) {
|
||||
self.client
|
||||
.event(TabletToolProximityOut { self_id: self.id });
|
||||
self.client.event(TabletToolFrame {
|
||||
self_id: self.id,
|
||||
seat: seat.raw(),
|
||||
time_usec,
|
||||
input_device: tablet.raw(),
|
||||
tool: tool.raw() as _,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_tablet_tool_changes(
|
||||
&self,
|
||||
seat: SeatId,
|
||||
tablet: InputDeviceId,
|
||||
tool: &TabletTool,
|
||||
time_usec: u64,
|
||||
changes: &TabletToolChanges,
|
||||
) {
|
||||
let self_id = self.id;
|
||||
if let Some(down) = changes.down {
|
||||
match down {
|
||||
true => self.client.event(TabletToolDown { self_id }),
|
||||
false => self.client.event(TabletToolUp { self_id }),
|
||||
}
|
||||
}
|
||||
if changes.pos.is_some() {
|
||||
let (x, y) = tool.cursor().position();
|
||||
self.client.event(TabletToolMotion { self_id, x, y });
|
||||
}
|
||||
if let Some(val) = changes.pressure {
|
||||
self.client.event(TabletToolPressure {
|
||||
self_id,
|
||||
pressure: val,
|
||||
});
|
||||
}
|
||||
if let Some(val) = changes.distance {
|
||||
self.client.event(TabletToolDistance {
|
||||
self_id,
|
||||
distance: val,
|
||||
});
|
||||
}
|
||||
if let Some(val) = changes.tilt {
|
||||
self.client.event(TabletToolTilt {
|
||||
self_id,
|
||||
tilt_x: val.x,
|
||||
tilt_y: val.y,
|
||||
});
|
||||
}
|
||||
if let Some(val) = changes.rotation {
|
||||
self.client.event(TabletToolRotation {
|
||||
self_id,
|
||||
degrees: val,
|
||||
});
|
||||
}
|
||||
if let Some(val) = changes.slider {
|
||||
self.client.event(TabletToolSlider {
|
||||
self_id,
|
||||
position: val,
|
||||
});
|
||||
}
|
||||
if let Some(val) = changes.wheel {
|
||||
self.client.event(TabletToolWheel {
|
||||
self_id,
|
||||
degrees: val.degrees,
|
||||
clicks: val.clicks,
|
||||
});
|
||||
}
|
||||
self.client.event(TabletToolFrame {
|
||||
self_id: self.id,
|
||||
seat: seat.raw(),
|
||||
time_usec,
|
||||
input_device: tablet.raw(),
|
||||
tool: tool.id.raw() as _,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_tablet_tool_button(
|
||||
&self,
|
||||
seat: SeatId,
|
||||
tablet: InputDeviceId,
|
||||
tool: &TabletTool,
|
||||
time_usec: u64,
|
||||
button: u32,
|
||||
state: ToolButtonState,
|
||||
) {
|
||||
self.client.event(TabletToolButton {
|
||||
self_id: self.id,
|
||||
button,
|
||||
state: state as _,
|
||||
});
|
||||
self.client.event(TabletToolFrame {
|
||||
self_id: self.id,
|
||||
seat: seat.raw(),
|
||||
time_usec,
|
||||
input_device: tablet.raw(),
|
||||
tool: tool.id.raw() as _,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_tablet_pad_mode_switch(
|
||||
&self,
|
||||
seat: SeatId,
|
||||
pad: InputDeviceId,
|
||||
time_usec: u64,
|
||||
group: u32,
|
||||
mode: u32,
|
||||
) {
|
||||
self.client.event(TabletPadModeSwitch {
|
||||
self_id: self.id,
|
||||
seat: seat.raw(),
|
||||
time_usec,
|
||||
input_device: pad.raw(),
|
||||
group,
|
||||
mode,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_tablet_pad_button(
|
||||
&self,
|
||||
seat: SeatId,
|
||||
pad: InputDeviceId,
|
||||
time_usec: u64,
|
||||
button: u32,
|
||||
state: PadButtonState,
|
||||
) {
|
||||
self.client.event(TabletPadButton {
|
||||
self_id: self.id,
|
||||
seat: seat.raw(),
|
||||
time_usec,
|
||||
input_device: pad.raw(),
|
||||
button,
|
||||
state: state as _,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_tablet_pad_strip(
|
||||
&self,
|
||||
seat: SeatId,
|
||||
pad: InputDeviceId,
|
||||
time_usec: u64,
|
||||
strip: u32,
|
||||
source: Option<TabletStripEventSource>,
|
||||
position: Option<f64>,
|
||||
) {
|
||||
if let Some(source) = source {
|
||||
self.client.event(TabletPadStripSource {
|
||||
self_id: self.id,
|
||||
source: source as _,
|
||||
});
|
||||
}
|
||||
if let Some(position) = position {
|
||||
self.client.event(TabletPadStripPosition {
|
||||
self_id: self.id,
|
||||
position,
|
||||
});
|
||||
} else {
|
||||
self.client.event(TabletPadStripStop { self_id: self.id });
|
||||
}
|
||||
self.client.event(TabletPadStripFrame {
|
||||
self_id: self.id,
|
||||
seat: seat.raw(),
|
||||
time_usec,
|
||||
input_device: pad.raw(),
|
||||
strip,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_tablet_pad_ring(
|
||||
&self,
|
||||
seat: SeatId,
|
||||
pad: InputDeviceId,
|
||||
time_usec: u64,
|
||||
ring: u32,
|
||||
source: Option<TabletRingEventSource>,
|
||||
degrees: Option<f64>,
|
||||
) {
|
||||
if let Some(source) = source {
|
||||
self.client.event(TabletPadRingSource {
|
||||
self_id: self.id,
|
||||
source: source as _,
|
||||
});
|
||||
}
|
||||
if let Some(degrees) = degrees {
|
||||
self.client.event(TabletPadRingAngle {
|
||||
self_id: self.id,
|
||||
degrees,
|
||||
});
|
||||
} else {
|
||||
self.client.event(TabletPadRingStop { self_id: self.id });
|
||||
}
|
||||
self.client.event(TabletPadRingFrame {
|
||||
self_id: self.id,
|
||||
seat: seat.raw(),
|
||||
time_usec,
|
||||
input_device: pad.raw(),
|
||||
ring,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl JaySeatEventsRequestHandler for JaySeatEvents {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ pub mod ext_transient_seat_v1;
|
|||
mod gesture_owner;
|
||||
mod kb_owner;
|
||||
mod pointer_owner;
|
||||
pub mod tablet;
|
||||
pub mod text_input;
|
||||
pub mod wl_keyboard;
|
||||
pub mod wl_pointer;
|
||||
|
|
@ -22,7 +23,7 @@ use {
|
|||
crate::{
|
||||
async_engine::SpawnedFuture,
|
||||
client::{Client, ClientError, ClientId},
|
||||
cursor::{Cursor, KnownCursor, DEFAULT_CURSOR_SIZE},
|
||||
cursor_user::{CursorUser, CursorUserGroup, CursorUserOwner},
|
||||
fixed::Fixed,
|
||||
globals::{Global, GlobalName},
|
||||
ifs::{
|
||||
|
|
@ -41,10 +42,12 @@ use {
|
|||
zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1,
|
||||
DynDataSource, IpcError,
|
||||
},
|
||||
wl_output::WlOutputGlobal,
|
||||
wl_seat::{
|
||||
gesture_owner::GestureOwnerHolder,
|
||||
kb_owner::KbOwnerHolder,
|
||||
pointer_owner::PointerOwnerHolder,
|
||||
tablet::TabletSeatData,
|
||||
text_input::{
|
||||
zwp_input_method_keyboard_grab_v2::ZwpInputMethodKeyboardGrabV2,
|
||||
zwp_input_method_v2::ZwpInputMethodV2, zwp_text_input_v3::ZwpTextInputV3,
|
||||
|
|
@ -67,13 +70,13 @@ use {
|
|||
state::{DeviceHandlerData, State},
|
||||
time::now_usec,
|
||||
tree::{
|
||||
generic_node_visitor, ContainerNode, ContainerSplit, Direction, FloatNode, FoundNode,
|
||||
Node, OutputNode, ToplevelNode, WorkspaceNode,
|
||||
generic_node_visitor, ContainerNode, ContainerSplit, Direction, FoundNode, Node,
|
||||
OutputNode, ToplevelNode, WorkspaceNode,
|
||||
},
|
||||
utils::{
|
||||
asyncevent::AsyncEvent, bindings::PerClientBindings, clonecell::CloneCell,
|
||||
copyhashmap::CopyHashMap, errorfmt::ErrorFmt, linkedlist::LinkedNode, numcell::NumCell,
|
||||
rc_eq::rc_eq, smallmap::SmallMap, transform_ext::TransformExt,
|
||||
rc_eq::rc_eq, smallmap::SmallMap,
|
||||
},
|
||||
wire::{
|
||||
wl_seat::*, ExtIdleNotificationV1Id, WlDataDeviceId, WlKeyboardId, WlPointerId,
|
||||
|
|
@ -140,11 +143,7 @@ pub struct WlSeatGlobal {
|
|||
name: GlobalName,
|
||||
state: Rc<State>,
|
||||
seat_name: String,
|
||||
move_: Cell<bool>,
|
||||
move_start_pos: Cell<(Fixed, Fixed)>,
|
||||
extents_start_pos: Cell<(i32, i32)>,
|
||||
pos_time_usec: Cell<u64>,
|
||||
pos: Cell<(Fixed, Fixed)>,
|
||||
pointer_stack: RefCell<Vec<Rc<dyn Node>>>,
|
||||
pointer_stack_modified: Cell<bool>,
|
||||
found_tree: RefCell<Vec<FoundNode>>,
|
||||
|
|
@ -165,7 +164,8 @@ pub struct WlSeatGlobal {
|
|||
seat_xkb_state: CloneCell<Rc<RefCell<XkbState>>>,
|
||||
latest_kb_state: CloneCell<Rc<dyn DynKeyboardState>>,
|
||||
xkb_states: CopyHashMap<KeymapId, Weak<RefCell<XkbState>>>,
|
||||
cursor: CloneCell<Option<Rc<dyn Cursor>>>,
|
||||
cursor_user_group: Rc<CursorUserGroup>,
|
||||
pointer_cursor: Rc<CursorUser>,
|
||||
tree_changed: Rc<AsyncEvent>,
|
||||
selection: CloneCell<Option<Rc<dyn DynDataSource>>>,
|
||||
selection_serial: Cell<u32>,
|
||||
|
|
@ -176,13 +176,9 @@ pub struct WlSeatGlobal {
|
|||
gesture_owner: GestureOwnerHolder,
|
||||
dropped_dnd: RefCell<Option<DroppedDnd>>,
|
||||
shortcuts: RefCell<AHashMap<u32, SmallMap<u32, u32, 2>>>,
|
||||
queue_link: Cell<Option<LinkedNode<Rc<Self>>>>,
|
||||
queue_link: RefCell<Option<LinkedNode<Rc<Self>>>>,
|
||||
tree_changed_handler: Cell<Option<SpawnedFuture<()>>>,
|
||||
output: CloneCell<Rc<OutputNode>>,
|
||||
desired_known_cursor: Cell<Option<KnownCursor>>,
|
||||
changes: NumCell<u32>,
|
||||
cursor_size: Cell<u32>,
|
||||
hardware_cursor: Cell<bool>,
|
||||
constraint: CloneCell<Option<Rc<SeatConstraint>>>,
|
||||
idle_notifications: CopyHashMap<(ClientId, ExtIdleNotificationV1Id), Rc<ExtIdleNotificationV1>>,
|
||||
last_input_usec: Cell<u64>,
|
||||
|
|
@ -195,17 +191,12 @@ pub struct WlSeatGlobal {
|
|||
swipe_bindings: PerClientBindings<ZwpPointerGestureSwipeV1>,
|
||||
pinch_bindings: PerClientBindings<ZwpPointerGesturePinchV1>,
|
||||
hold_bindings: PerClientBindings<ZwpPointerGestureHoldV1>,
|
||||
tablet: TabletSeatData,
|
||||
}
|
||||
|
||||
const CHANGE_CURSOR_MOVED: u32 = 1 << 0;
|
||||
const CHANGE_TREE: u32 = 1 << 1;
|
||||
|
||||
impl Drop for WlSeatGlobal {
|
||||
fn drop(&mut self) {
|
||||
self.state.remove_cursor_size(self.cursor_size.get());
|
||||
}
|
||||
}
|
||||
|
||||
impl WlSeatGlobal {
|
||||
pub fn new(name: GlobalName, seat_name: &str, state: &Rc<State>) -> Rc<Self> {
|
||||
let seat_xkb_state = state
|
||||
|
|
@ -215,16 +206,15 @@ impl WlSeatGlobal {
|
|||
.unwrap();
|
||||
let xkb_states = CopyHashMap::new();
|
||||
xkb_states.set(state.default_keymap.id, Rc::downgrade(&seat_xkb_state));
|
||||
let cursor_user_group = CursorUserGroup::create(state);
|
||||
let cursor_user = cursor_user_group.create_user();
|
||||
cursor_user.activate();
|
||||
let slf = Rc::new(Self {
|
||||
id: state.seat_ids.next(),
|
||||
name,
|
||||
state: state.clone(),
|
||||
seat_name: seat_name.to_string(),
|
||||
move_: Cell::new(false),
|
||||
move_start_pos: Cell::new((Fixed(0), Fixed(0))),
|
||||
extents_start_pos: Cell::new((0, 0)),
|
||||
pos_time_usec: Cell::new(0),
|
||||
pos: Cell::new((Fixed(0), Fixed(0))),
|
||||
pointer_stack: RefCell::new(vec![]),
|
||||
pointer_stack_modified: Cell::new(false),
|
||||
found_tree: RefCell::new(vec![]),
|
||||
|
|
@ -238,7 +228,8 @@ impl WlSeatGlobal {
|
|||
seat_xkb_state: CloneCell::new(seat_xkb_state.clone()),
|
||||
latest_kb_state: CloneCell::new(seat_xkb_state.clone()),
|
||||
xkb_states,
|
||||
cursor: Default::default(),
|
||||
cursor_user_group,
|
||||
pointer_cursor: cursor_user,
|
||||
tree_changed: Default::default(),
|
||||
selection: Default::default(),
|
||||
selection_serial: Cell::new(0),
|
||||
|
|
@ -249,13 +240,9 @@ impl WlSeatGlobal {
|
|||
gesture_owner: Default::default(),
|
||||
dropped_dnd: RefCell::new(None),
|
||||
shortcuts: Default::default(),
|
||||
queue_link: Cell::new(None),
|
||||
queue_link: Default::default(),
|
||||
tree_changed_handler: Cell::new(None),
|
||||
output: CloneCell::new(state.dummy_output.get().unwrap()),
|
||||
desired_known_cursor: Cell::new(None),
|
||||
changes: NumCell::new(CHANGE_CURSOR_MOVED | CHANGE_TREE),
|
||||
cursor_size: Cell::new(*DEFAULT_CURSOR_SIZE),
|
||||
hardware_cursor: Cell::new(state.globals.seats.len() == 0),
|
||||
constraint: Default::default(),
|
||||
idle_notifications: Default::default(),
|
||||
last_input_usec: Cell::new(now_usec()),
|
||||
|
|
@ -269,8 +256,9 @@ impl WlSeatGlobal {
|
|||
swipe_bindings: Default::default(),
|
||||
pinch_bindings: Default::default(),
|
||||
hold_bindings: Default::default(),
|
||||
tablet: Default::default(),
|
||||
});
|
||||
state.add_cursor_size(*DEFAULT_CURSOR_SIZE);
|
||||
slf.pointer_cursor.set_owner(slf.clone());
|
||||
let seat = slf.clone();
|
||||
let future = state.eng.spawn(async move {
|
||||
loop {
|
||||
|
|
@ -297,109 +285,6 @@ impl WlSeatGlobal {
|
|||
self.pointer_owner.toplevel_drag()
|
||||
}
|
||||
|
||||
pub fn set_hardware_cursor(&self, hardware_cursor: bool) {
|
||||
self.hardware_cursor.set(hardware_cursor);
|
||||
}
|
||||
|
||||
pub fn hardware_cursor(&self) -> bool {
|
||||
self.hardware_cursor.get()
|
||||
}
|
||||
|
||||
fn update_hardware_cursor_position(&self) {
|
||||
self.update_hardware_cursor_(false);
|
||||
}
|
||||
|
||||
pub fn update_hardware_cursor(&self) {
|
||||
self.update_hardware_cursor_(true);
|
||||
}
|
||||
|
||||
fn update_hardware_cursor_(&self, render: bool) {
|
||||
if !self.hardware_cursor.get() {
|
||||
return;
|
||||
}
|
||||
let cursor = match self.get_cursor() {
|
||||
Some(c) => c,
|
||||
_ => {
|
||||
self.state.disable_hardware_cursors();
|
||||
return;
|
||||
}
|
||||
};
|
||||
if render {
|
||||
cursor.tick();
|
||||
}
|
||||
let (x, y) = self.get_position();
|
||||
for output in self.state.root.outputs.lock().values() {
|
||||
if let Some(hc) = output.hardware_cursor.get() {
|
||||
let transform = output.global.persistent.transform.get();
|
||||
let render = render | output.hardware_cursor_needs_render.take();
|
||||
let scale = output.global.persistent.scale.get();
|
||||
let extents = cursor.extents_at_scale(scale);
|
||||
let (hc_width, hc_height) = hc.size();
|
||||
if render {
|
||||
let (max_width, max_height) = transform.maybe_swap((hc_width, hc_height));
|
||||
if extents.width() > max_width || extents.height() > max_height {
|
||||
hc.set_enabled(false);
|
||||
hc.commit();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let opos = output.global.pos.get();
|
||||
let (x_rel, y_rel);
|
||||
if scale == 1 {
|
||||
x_rel = x.round_down() - opos.x1();
|
||||
y_rel = y.round_down() - opos.y1();
|
||||
} else {
|
||||
let scalef = scale.to_f64();
|
||||
x_rel = ((x - Fixed::from_int(opos.x1())).to_f64() * scalef).round() as i32;
|
||||
y_rel = ((y - Fixed::from_int(opos.y1())).to_f64() * scalef).round() as i32;
|
||||
}
|
||||
let (width, height) = output.global.pixel_size();
|
||||
if extents.intersects(&Rect::new_sized(-x_rel, -y_rel, width, height).unwrap()) {
|
||||
if render {
|
||||
let buffer = hc.get_buffer();
|
||||
let res = buffer.render_hardware_cursor(
|
||||
cursor.deref(),
|
||||
&self.state,
|
||||
scale,
|
||||
transform,
|
||||
);
|
||||
match res {
|
||||
Ok(sync_file) => {
|
||||
hc.set_sync_file(sync_file);
|
||||
hc.swap_buffer();
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Could not render hardware cursor: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
hc.set_enabled(true);
|
||||
let mode = output.global.mode.get();
|
||||
let (x_rel, y_rel) =
|
||||
transform.apply_point(mode.width, mode.height, (x_rel, y_rel));
|
||||
let (hot_x, hot_y) =
|
||||
transform.apply_point(hc_width, hc_height, (-extents.x1(), -extents.y1()));
|
||||
hc.set_position(x_rel - hot_x, y_rel - hot_y);
|
||||
} else {
|
||||
if render {
|
||||
output.hardware_cursor_needs_render.set(true);
|
||||
}
|
||||
hc.set_enabled(false);
|
||||
}
|
||||
hc.commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_cursor_size(&self, size: u32) {
|
||||
let old = self.cursor_size.replace(size);
|
||||
if size != old {
|
||||
self.state.remove_cursor_size(old);
|
||||
self.state.add_cursor_size(size);
|
||||
self.reload_known_cursor();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_data_device(&self, device: &Rc<WlDataDevice>) {
|
||||
let mut dd = self.data_devices.borrow_mut();
|
||||
dd.entry(device.client.id)
|
||||
|
|
@ -458,7 +343,7 @@ impl WlSeatGlobal {
|
|||
}
|
||||
|
||||
pub fn get_output(&self) -> Rc<OutputNode> {
|
||||
self.output.get()
|
||||
self.cursor_user_group.latest_output()
|
||||
}
|
||||
|
||||
pub fn set_workspace(&self, ws: &Rc<WorkspaceNode>) {
|
||||
|
|
@ -501,8 +386,12 @@ impl WlSeatGlobal {
|
|||
}
|
||||
|
||||
pub fn mark_last_active(self: &Rc<Self>) {
|
||||
self.queue_link
|
||||
.set(Some(self.state.seat_queue.add_last(self.clone())));
|
||||
let link = &mut *self.queue_link.borrow_mut();
|
||||
if let Some(link) = link {
|
||||
self.state.seat_queue.add_last_existing(link)
|
||||
} else {
|
||||
*link = Some(self.state.seat_queue.add_last(self.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn disable_pointer_constraint(&self) {
|
||||
|
|
@ -519,7 +408,7 @@ impl WlSeatGlobal {
|
|||
fn maybe_constrain_pointer_node(&self) {
|
||||
if let Some(pn) = self.pointer_node() {
|
||||
if let Some(surface) = pn.node_into_surface() {
|
||||
let (mut x, mut y) = self.pos.get();
|
||||
let (mut x, mut y) = self.pointer_cursor.position();
|
||||
let (sx, sy) = surface.buffer_abs_pos.get().position();
|
||||
x -= Fixed::from_int(sx);
|
||||
y -= Fixed::from_int(sy);
|
||||
|
|
@ -610,47 +499,6 @@ impl WlSeatGlobal {
|
|||
self.kb_owner.ungrab(self);
|
||||
}
|
||||
|
||||
pub fn set_position(&self, x: i32, y: i32) {
|
||||
self.pos.set((Fixed::from_int(x), Fixed::from_int(y)));
|
||||
self.update_hardware_cursor_position();
|
||||
self.trigger_tree_changed();
|
||||
let output = 'set_output: {
|
||||
let outputs = self.state.root.outputs.lock();
|
||||
for output in outputs.values() {
|
||||
if output.global.pos.get().contains(x, y) {
|
||||
break 'set_output output.clone();
|
||||
}
|
||||
}
|
||||
self.state.dummy_output.get().unwrap()
|
||||
};
|
||||
self.set_output(&output);
|
||||
}
|
||||
|
||||
fn set_output(&self, output: &Rc<OutputNode>) {
|
||||
self.output.set(output.clone());
|
||||
if let Some(cursor) = self.cursor.get() {
|
||||
cursor.set_output(output);
|
||||
}
|
||||
if let Some(dnd) = self.pointer_owner.dnd_icon() {
|
||||
dnd.set_output(output);
|
||||
}
|
||||
if let Some(drag) = self.pointer_owner.toplevel_drag() {
|
||||
if let Some(tl) = drag.toplevel.get() {
|
||||
tl.xdg.set_output(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn position(&self) -> (Fixed, Fixed) {
|
||||
self.pos.get()
|
||||
}
|
||||
|
||||
pub fn render_ctx_changed(&self) {
|
||||
if let Some(cursor) = self.desired_known_cursor.get() {
|
||||
self.set_known_cursor(cursor);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn kb_parent_container(&self) -> Option<Rc<ContainerNode>> {
|
||||
if let Some(tl) = self.keyboard_node.get().node_toplevel() {
|
||||
if let Some(parent) = tl.tl_data().parent.get() {
|
||||
|
|
@ -879,7 +727,7 @@ impl WlSeatGlobal {
|
|||
serial: u32,
|
||||
) -> Result<(), WlSeatError> {
|
||||
if let Some(icon) = &icon {
|
||||
icon.set_output(&self.output.get());
|
||||
icon.set_output(&self.pointer_cursor.output());
|
||||
}
|
||||
self.pointer_owner
|
||||
.start_drag(self, origin, source, icon, serial)
|
||||
|
|
@ -971,89 +819,6 @@ impl WlSeatGlobal {
|
|||
self.primary_selection.get()
|
||||
}
|
||||
|
||||
pub fn reload_known_cursor(&self) {
|
||||
if let Some(kc) = self.desired_known_cursor.get() {
|
||||
self.set_known_cursor(kc);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "it"), allow(dead_code))]
|
||||
pub fn get_desired_known_cursor(&self) -> Option<KnownCursor> {
|
||||
self.desired_known_cursor.get()
|
||||
}
|
||||
|
||||
pub fn set_known_cursor(&self, cursor: KnownCursor) {
|
||||
self.desired_known_cursor.set(Some(cursor));
|
||||
let cursors = match self.state.cursors.get() {
|
||||
Some(c) => c,
|
||||
None => {
|
||||
self.set_cursor2(None);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let tpl = match cursor {
|
||||
KnownCursor::Default => &cursors.default,
|
||||
KnownCursor::ContextMenu => &cursors.context_menu,
|
||||
KnownCursor::Help => &cursors.help,
|
||||
KnownCursor::Pointer => &cursors.pointer,
|
||||
KnownCursor::Progress => &cursors.progress,
|
||||
KnownCursor::Wait => &cursors.wait,
|
||||
KnownCursor::Cell => &cursors.cell,
|
||||
KnownCursor::Crosshair => &cursors.crosshair,
|
||||
KnownCursor::Text => &cursors.text,
|
||||
KnownCursor::VerticalText => &cursors.vertical_text,
|
||||
KnownCursor::Alias => &cursors.alias,
|
||||
KnownCursor::Copy => &cursors.copy,
|
||||
KnownCursor::Move => &cursors.r#move,
|
||||
KnownCursor::NoDrop => &cursors.no_drop,
|
||||
KnownCursor::NotAllowed => &cursors.not_allowed,
|
||||
KnownCursor::Grab => &cursors.grab,
|
||||
KnownCursor::Grabbing => &cursors.grabbing,
|
||||
KnownCursor::EResize => &cursors.e_resize,
|
||||
KnownCursor::NResize => &cursors.n_resize,
|
||||
KnownCursor::NeResize => &cursors.ne_resize,
|
||||
KnownCursor::NwResize => &cursors.nw_resize,
|
||||
KnownCursor::SResize => &cursors.s_resize,
|
||||
KnownCursor::SeResize => &cursors.se_resize,
|
||||
KnownCursor::SwResize => &cursors.sw_resize,
|
||||
KnownCursor::WResize => &cursors.w_resize,
|
||||
KnownCursor::EwResize => &cursors.ew_resize,
|
||||
KnownCursor::NsResize => &cursors.ns_resize,
|
||||
KnownCursor::NeswResize => &cursors.nesw_resize,
|
||||
KnownCursor::NwseResize => &cursors.nwse_resize,
|
||||
KnownCursor::ColResize => &cursors.col_resize,
|
||||
KnownCursor::RowResize => &cursors.row_resize,
|
||||
KnownCursor::AllScroll => &cursors.all_scroll,
|
||||
KnownCursor::ZoomIn => &cursors.zoom_in,
|
||||
KnownCursor::ZoomOut => &cursors.zoom_out,
|
||||
};
|
||||
self.set_cursor2(Some(tpl.instantiate(self.cursor_size.get())));
|
||||
}
|
||||
|
||||
pub fn set_app_cursor(&self, cursor: Option<Rc<dyn Cursor>>) {
|
||||
self.set_cursor2(cursor);
|
||||
self.desired_known_cursor.set(None);
|
||||
}
|
||||
|
||||
fn set_cursor2(&self, cursor: Option<Rc<dyn Cursor>>) {
|
||||
if let Some(old) = self.cursor.get() {
|
||||
if let Some(new) = cursor.as_ref() {
|
||||
if rc_eq(&old, new) {
|
||||
self.update_hardware_cursor();
|
||||
return;
|
||||
}
|
||||
}
|
||||
old.handle_unset();
|
||||
}
|
||||
if let Some(cursor) = cursor.as_ref() {
|
||||
cursor.clone().handle_set();
|
||||
cursor.set_output(&self.output.get());
|
||||
}
|
||||
self.cursor.set(cursor.clone());
|
||||
self.state.hardware_tick_cursor.push(cursor);
|
||||
self.update_hardware_cursor();
|
||||
}
|
||||
|
||||
pub fn dnd_icon(&self) -> Option<Rc<WlSurface>> {
|
||||
self.pointer_owner.dnd_icon()
|
||||
}
|
||||
|
|
@ -1062,12 +827,12 @@ impl WlSeatGlobal {
|
|||
self.pointer_owner.remove_dnd_icon();
|
||||
}
|
||||
|
||||
pub fn get_position(&self) -> (Fixed, Fixed) {
|
||||
self.pos.get()
|
||||
pub fn pointer_cursor(&self) -> &Rc<CursorUser> {
|
||||
&self.pointer_cursor
|
||||
}
|
||||
|
||||
pub fn get_cursor(&self) -> Option<Rc<dyn Cursor>> {
|
||||
self.cursor.get()
|
||||
pub fn cursor_group(&self) -> &Rc<CursorUserGroup> {
|
||||
&self.cursor_user_group
|
||||
}
|
||||
|
||||
pub fn clear(self: &Rc<Self>) {
|
||||
|
|
@ -1084,15 +849,14 @@ impl WlSeatGlobal {
|
|||
self.data_devices.borrow_mut().clear();
|
||||
self.primary_selection_devices.borrow_mut().clear();
|
||||
self.wlr_data_devices.clear();
|
||||
self.cursor.set(None);
|
||||
self.cursor_user_group.detach();
|
||||
self.selection.set(None);
|
||||
self.primary_selection.set(None);
|
||||
self.pointer_owner.clear();
|
||||
self.kb_owner.clear();
|
||||
*self.dropped_dnd.borrow_mut() = None;
|
||||
self.queue_link.set(None);
|
||||
self.queue_link.take();
|
||||
self.tree_changed_handler.set(None);
|
||||
self.output.set(self.state.dummy_output.get().unwrap());
|
||||
self.constraint.take();
|
||||
self.text_inputs.borrow_mut().clear();
|
||||
self.text_input.take();
|
||||
|
|
@ -1101,6 +865,8 @@ impl WlSeatGlobal {
|
|||
self.swipe_bindings.clear();
|
||||
self.pinch_bindings.clear();
|
||||
self.hold_bindings.clear();
|
||||
self.cursor_user_group.detach();
|
||||
self.tablet_clear();
|
||||
}
|
||||
|
||||
pub fn id(&self) -> SeatId {
|
||||
|
|
@ -1158,9 +924,7 @@ impl WlSeatGlobal {
|
|||
}
|
||||
|
||||
pub fn set_visible(&self, visible: bool) {
|
||||
if let Some(cursor) = self.cursor.get() {
|
||||
cursor.set_visible(visible);
|
||||
}
|
||||
self.cursor_user_group.set_visible(visible);
|
||||
if let Some(icon) = self.dnd_icon() {
|
||||
icon.set_visible(visible);
|
||||
}
|
||||
|
|
@ -1193,6 +957,19 @@ impl WlSeatGlobal {
|
|||
}
|
||||
}
|
||||
|
||||
impl CursorUserOwner for WlSeatGlobal {
|
||||
fn output_changed(&self, output: &Rc<OutputNode>) {
|
||||
if let Some(dnd) = self.pointer_owner.dnd_icon() {
|
||||
dnd.set_output(output);
|
||||
}
|
||||
if let Some(drag) = self.pointer_owner.toplevel_drag() {
|
||||
if let Some(tl) = drag.toplevel.get() {
|
||||
tl.xdg.set_output(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
global_base!(WlSeatGlobal, WlSeat, WlSeatError);
|
||||
|
||||
impl Global for WlSeatGlobal {
|
||||
|
|
@ -1235,10 +1012,6 @@ impl WlSeat {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn move_(&self, node: &Rc<FloatNode>) {
|
||||
self.global.move_(node);
|
||||
}
|
||||
|
||||
pub fn keymap_fd(&self, state: &KeyboardState) -> Result<Rc<OwnedFd>, WlKeyboardError> {
|
||||
if self.version >= READ_ONLY_KEYMAP_SINCE {
|
||||
return Ok(state.map.clone());
|
||||
|
|
@ -1355,7 +1128,7 @@ impl DeviceHandlerData {
|
|||
pub fn set_seat(&self, seat: Option<Rc<WlSeatGlobal>>) {
|
||||
let old = self.seat.set(seat.clone());
|
||||
if let Some(old) = old {
|
||||
if let Some(new) = seat {
|
||||
if let Some(new) = &seat {
|
||||
if old.id() == new.id() {
|
||||
return;
|
||||
}
|
||||
|
|
@ -1364,8 +1137,22 @@ impl DeviceHandlerData {
|
|||
let xkb_state = &mut *xkb_state.borrow_mut();
|
||||
xkb_state.reset();
|
||||
old.handle_xkb_state_change(xkb_state, xkb_state);
|
||||
if let Some(info) = &self.tablet_init {
|
||||
old.tablet_remove_tablet(info.id);
|
||||
}
|
||||
if let Some(info) = &self.tablet_pad_init {
|
||||
old.tablet_remove_tablet_pad(info.id);
|
||||
}
|
||||
}
|
||||
self.update_xkb_state();
|
||||
if let Some(seat) = &seat {
|
||||
if let Some(info) = &self.tablet_init {
|
||||
seat.tablet_add_tablet(self.device.id(), info);
|
||||
}
|
||||
if let Some(info) = &self.tablet_pad_init {
|
||||
seat.tablet_add_tablet_pad(self.device.id(), info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_keymap(&self, keymap: Option<Rc<XkbKeymap>>) {
|
||||
|
|
@ -1397,4 +1184,26 @@ 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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_rect(&self, state: &State) -> Rect {
|
||||
if let Some(output) = self.output.get() {
|
||||
if let Some(output) = output.get() {
|
||||
return output.pos.get();
|
||||
}
|
||||
}
|
||||
state.root.extents.get()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ use {
|
|||
DynDataSource,
|
||||
},
|
||||
wl_seat::{
|
||||
tablet::{TabletPad, TabletPadId, TabletTool, TabletToolId},
|
||||
text_input::TextDisconnectReason,
|
||||
wl_keyboard::{self, WlKeyboard},
|
||||
wl_pointer::{
|
||||
|
|
@ -25,13 +26,13 @@ use {
|
|||
},
|
||||
zwp_pointer_constraints_v1::{ConstraintType, SeatConstraintStatus},
|
||||
zwp_relative_pointer_v1::ZwpRelativePointerV1,
|
||||
Dnd, SeatId, WlSeat, WlSeatGlobal, CHANGE_CURSOR_MOVED,
|
||||
Dnd, SeatId, WlSeat, WlSeatGlobal, CHANGE_CURSOR_MOVED, CHANGE_TREE,
|
||||
},
|
||||
wl_surface::{xdg_surface::xdg_popup::XdgPopup, WlSurface},
|
||||
},
|
||||
object::Version,
|
||||
state::DeviceHandlerData,
|
||||
tree::{Direction, FloatNode, Node, ToplevelNode},
|
||||
tree::{Direction, Node, ToplevelNode},
|
||||
utils::{bitflags::BitflagsExt, smallmap::SmallMap},
|
||||
wire::WlDataOfferId,
|
||||
xkbcommon::{KeyboardState, XkbState, XKB_KEY_DOWN, XKB_KEY_UP},
|
||||
|
|
@ -55,6 +56,8 @@ pub struct NodeSeatState {
|
|||
gesture_foci: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>,
|
||||
pointer_grabs: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>,
|
||||
dnd_targets: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>,
|
||||
tablet_pad_foci: SmallMap<TabletPadId, Rc<TabletPad>, 1>,
|
||||
tablet_tool_foci: SmallMap<TabletToolId, Rc<TabletTool>, 1>,
|
||||
}
|
||||
|
||||
impl NodeSeatState {
|
||||
|
|
@ -92,6 +95,22 @@ impl NodeSeatState {
|
|||
self.pointer_grabs.remove(&seat.id);
|
||||
}
|
||||
|
||||
pub(super) fn add_tablet_pad_focus(&self, pad: &Rc<TabletPad>) {
|
||||
self.tablet_pad_foci.insert(pad.id, pad.clone());
|
||||
}
|
||||
|
||||
pub(super) fn remove_tablet_pad_focus(&self, pad: &TabletPad) {
|
||||
self.tablet_pad_foci.remove(&pad.id);
|
||||
}
|
||||
|
||||
pub(super) fn add_tablet_tool_focus(&self, tool: &Rc<TabletTool>) {
|
||||
self.tablet_tool_foci.insert(tool.id, tool.clone());
|
||||
}
|
||||
|
||||
pub(super) fn remove_tablet_tool_focus(&self, tool: &TabletTool) {
|
||||
self.tablet_tool_foci.remove(&tool.id);
|
||||
}
|
||||
|
||||
pub(super) fn add_dnd_target(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
self.dnd_targets.insert(seat.id, seat.clone());
|
||||
}
|
||||
|
|
@ -120,8 +139,7 @@ impl NodeSeatState {
|
|||
seat.kb_owner.set_kb_node(&seat, seat.state.root.clone());
|
||||
// log::info!("keyboard_node = root");
|
||||
if focus_last {
|
||||
seat.output
|
||||
.get()
|
||||
seat.get_output()
|
||||
.node_do_focus(&seat, Direction::Unspecified);
|
||||
}
|
||||
}
|
||||
|
|
@ -164,6 +182,12 @@ impl NodeSeatState {
|
|||
seat.pointer_stack_modified.set(true);
|
||||
seat.state.tree_changed();
|
||||
}
|
||||
while let Some((_, tool)) = self.tablet_tool_foci.pop() {
|
||||
tool.tool_owner.focus_root(&tool);
|
||||
}
|
||||
while let Some((_, pad)) = self.tablet_pad_foci.pop() {
|
||||
pad.pad_owner.focus_root(&pad);
|
||||
}
|
||||
self.release_kb_focus2(focus_last);
|
||||
}
|
||||
|
||||
|
|
@ -204,7 +228,13 @@ impl WlSeatGlobal {
|
|||
| InputEvent::PinchEnd { time_usec, .. }
|
||||
| InputEvent::HoldBegin { time_usec, .. }
|
||||
| InputEvent::HoldEnd { time_usec, .. }
|
||||
| InputEvent::SwitchEvent { time_usec, .. } => {
|
||||
| InputEvent::SwitchEvent { time_usec, .. }
|
||||
| InputEvent::TabletToolChanged { time_usec, .. }
|
||||
| InputEvent::TabletToolButton { time_usec, .. }
|
||||
| InputEvent::TabletPadButton { time_usec, .. }
|
||||
| InputEvent::TabletPadModeSwitch { time_usec, .. }
|
||||
| InputEvent::TabletPadRing { time_usec, .. }
|
||||
| InputEvent::TabletPadStrip { time_usec, .. } => {
|
||||
self.last_input_usec.set(time_usec);
|
||||
if self.idle_notifications.is_not_empty() {
|
||||
for (_, notification) in self.idle_notifications.lock().drain() {
|
||||
|
|
@ -215,7 +245,39 @@ impl WlSeatGlobal {
|
|||
InputEvent::AxisPx { .. }
|
||||
| InputEvent::AxisSource { .. }
|
||||
| InputEvent::AxisStop { .. }
|
||||
| InputEvent::Axis120 { .. } => {}
|
||||
| InputEvent::Axis120 { .. }
|
||||
| InputEvent::TabletToolAdded { .. }
|
||||
| InputEvent::TabletToolRemoved { .. } => {}
|
||||
}
|
||||
match event {
|
||||
InputEvent::ConnectorPosition { .. }
|
||||
| InputEvent::Motion { .. }
|
||||
| InputEvent::Button { .. }
|
||||
| InputEvent::AxisFrame { .. }
|
||||
| InputEvent::SwipeBegin { .. }
|
||||
| InputEvent::SwipeUpdate { .. }
|
||||
| InputEvent::SwipeEnd { .. }
|
||||
| InputEvent::PinchBegin { .. }
|
||||
| InputEvent::PinchUpdate { .. }
|
||||
| InputEvent::PinchEnd { .. }
|
||||
| InputEvent::HoldBegin { .. }
|
||||
| InputEvent::HoldEnd { .. } => {
|
||||
self.pointer_cursor.activate();
|
||||
}
|
||||
InputEvent::Key { .. } => {}
|
||||
InputEvent::AxisPx { .. } => {}
|
||||
InputEvent::AxisSource { .. } => {}
|
||||
InputEvent::AxisStop { .. } => {}
|
||||
InputEvent::Axis120 { .. } => {}
|
||||
InputEvent::SwitchEvent { .. } => {}
|
||||
InputEvent::TabletToolAdded { .. } => {}
|
||||
InputEvent::TabletToolChanged { .. } => {}
|
||||
InputEvent::TabletToolButton { .. } => {}
|
||||
InputEvent::TabletToolRemoved { .. } => {}
|
||||
InputEvent::TabletPadButton { .. } => {}
|
||||
InputEvent::TabletPadModeSwitch { .. } => {}
|
||||
InputEvent::TabletPadRing { .. } => {}
|
||||
InputEvent::TabletPadStrip { .. } => {}
|
||||
}
|
||||
match event {
|
||||
InputEvent::Key {
|
||||
|
|
@ -306,6 +368,49 @@ impl WlSeatGlobal {
|
|||
InputEvent::SwitchEvent { time_usec, event } => {
|
||||
self.switch_event(dev.device.id(), time_usec, event)
|
||||
}
|
||||
InputEvent::TabletToolAdded { time_usec, init } => {
|
||||
self.tablet_handle_new_tool(time_usec, &init)
|
||||
}
|
||||
InputEvent::TabletToolChanged {
|
||||
time_usec,
|
||||
id,
|
||||
changes: change,
|
||||
} => self.tablet_event_tool_changes(id, time_usec, dev.get_rect(&self.state), &change),
|
||||
InputEvent::TabletToolButton {
|
||||
time_usec,
|
||||
id,
|
||||
button,
|
||||
state,
|
||||
} => self.tablet_event_tool_button(id, time_usec, button, state),
|
||||
InputEvent::TabletToolRemoved { time_usec, id } => {
|
||||
self.tablet_handle_remove_tool(time_usec, id)
|
||||
}
|
||||
InputEvent::TabletPadButton {
|
||||
time_usec,
|
||||
id,
|
||||
button,
|
||||
state,
|
||||
} => self.tablet_event_pad_button(id, time_usec, button, state),
|
||||
InputEvent::TabletPadModeSwitch {
|
||||
time_usec,
|
||||
pad,
|
||||
group,
|
||||
mode,
|
||||
} => self.tablet_event_pad_mode_switch(pad, time_usec, group, mode),
|
||||
InputEvent::TabletPadRing {
|
||||
time_usec,
|
||||
pad,
|
||||
ring,
|
||||
source,
|
||||
angle,
|
||||
} => self.tablet_event_pad_ring(pad, ring, source, angle, time_usec),
|
||||
InputEvent::TabletPadStrip {
|
||||
time_usec,
|
||||
pad,
|
||||
strip,
|
||||
source,
|
||||
position,
|
||||
} => self.tablet_event_pad_strip(pad, strip, source, position, time_usec),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -320,10 +425,10 @@ impl WlSeatGlobal {
|
|||
Some(o) => o,
|
||||
_ => return,
|
||||
};
|
||||
self.set_output(&output);
|
||||
let pos = output.global.pos.get();
|
||||
x += Fixed::from_int(pos.x1());
|
||||
y += Fixed::from_int(pos.y1());
|
||||
(x, y) = self.pointer_cursor.set_position(x, y);
|
||||
if let Some(c) = self.constraint.get() {
|
||||
if c.ty == ConstraintType::Lock || !c.contains(x.round_down(), y.round_down()) {
|
||||
c.deactivate();
|
||||
|
|
@ -332,7 +437,7 @@ impl WlSeatGlobal {
|
|||
self.state.for_each_seat_tester(|t| {
|
||||
t.send_pointer_abs(self.id, time_usec, x, y);
|
||||
});
|
||||
self.set_new_position(time_usec, x, y);
|
||||
self.cursor_moved(time_usec);
|
||||
}
|
||||
|
||||
fn motion_event(
|
||||
|
|
@ -356,7 +461,7 @@ impl WlSeatGlobal {
|
|||
Some(c) if c.ty == ConstraintType::Lock => true,
|
||||
_ => false,
|
||||
};
|
||||
let (mut x, mut y) = self.pos.get();
|
||||
let (mut x, mut y) = self.pointer_cursor.position();
|
||||
if !locked {
|
||||
x += dx;
|
||||
y += dy;
|
||||
|
|
@ -383,34 +488,8 @@ impl WlSeatGlobal {
|
|||
dy_unaccelerated,
|
||||
);
|
||||
});
|
||||
let output = self.output.get();
|
||||
let pos = output.global.pos.get();
|
||||
let mut x_int = x.round_down();
|
||||
let mut y_int = y.round_down();
|
||||
if !pos.contains(x_int, y_int) {
|
||||
'warp: {
|
||||
let outputs = self.state.root.outputs.lock();
|
||||
for output in outputs.values() {
|
||||
if output.global.pos.get().contains(x_int, y_int) {
|
||||
self.set_output(output);
|
||||
break 'warp;
|
||||
}
|
||||
}
|
||||
if x_int < pos.x1() {
|
||||
x_int = pos.x1();
|
||||
} else if x_int >= pos.x2() {
|
||||
x_int = pos.x2() - 1;
|
||||
}
|
||||
if y_int < pos.y1() {
|
||||
y_int = pos.y1();
|
||||
} else if y_int >= pos.y2() {
|
||||
y_int = pos.y2() - 1;
|
||||
}
|
||||
x = Fixed::from_int(x_int);
|
||||
y = Fixed::from_int(y_int);
|
||||
}
|
||||
}
|
||||
self.set_new_position(time_usec, x, y);
|
||||
self.pointer_cursor.set_position(x, y);
|
||||
self.cursor_moved(time_usec);
|
||||
}
|
||||
|
||||
fn button_event(self: &Rc<Self>, time_usec: u64, button: u32, state: KeyState) {
|
||||
|
|
@ -631,13 +710,6 @@ impl WlSeatGlobal {
|
|||
self.pointer_stack.borrow().last().cloned()
|
||||
}
|
||||
|
||||
pub fn move_(&self, node: &Rc<FloatNode>) {
|
||||
self.move_.set(true);
|
||||
self.move_start_pos.set(self.pos.get());
|
||||
let ex = node.position.get();
|
||||
self.extents_start_pos.set((ex.x1(), ex.y1()));
|
||||
}
|
||||
|
||||
pub fn focus_toplevel(self: &Rc<Self>, n: Rc<dyn ToplevelNode>) {
|
||||
let node = match n.tl_focus_child(self.id) {
|
||||
Some(n) => n,
|
||||
|
|
@ -783,10 +855,8 @@ impl WlSeatGlobal {
|
|||
// client.flush();
|
||||
}
|
||||
|
||||
fn set_new_position(self: &Rc<Self>, time_usec: u64, x: Fixed, y: Fixed) {
|
||||
fn cursor_moved(self: &Rc<Self>, time_usec: u64) {
|
||||
self.pos_time_usec.set(time_usec);
|
||||
self.pos.set((x, y));
|
||||
self.update_hardware_cursor_position();
|
||||
self.changes.or_assign(CHANGE_CURSOR_MOVED);
|
||||
self.apply_changes();
|
||||
}
|
||||
|
|
@ -820,6 +890,9 @@ impl WlSeatGlobal {
|
|||
pub(super) fn apply_changes(self: &Rc<Self>) {
|
||||
self.state.damage();
|
||||
self.pointer_owner.apply_changes(self);
|
||||
if self.changes.get().contains(CHANGE_TREE) {
|
||||
self.tablet_apply_changes();
|
||||
}
|
||||
self.changes.set(0);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ impl KbOwner for DefaultKbOwner {
|
|||
// log::info!("focus {}", node.node_id());
|
||||
node.clone().node_on_focus(seat);
|
||||
seat.keyboard_node.set(node.clone());
|
||||
seat.tablet_on_keyboard_node_change();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -274,7 +274,7 @@ impl<T: SimplePointerOwnerUsecase> PointerOwner for SimplePointerOwner<T> {
|
|||
}
|
||||
|
||||
fn apply_changes(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
let (x, y) = seat.pos.get();
|
||||
let (x, y) = seat.pointer_cursor.position();
|
||||
let mut found_tree = seat.found_tree.borrow_mut();
|
||||
let mut stack = seat.pointer_stack.borrow_mut();
|
||||
let x_int = x.round_down();
|
||||
|
|
@ -408,7 +408,7 @@ impl<T: SimplePointerOwnerUsecase> PointerOwner for SimpleGrabPointerOwner<T> {
|
|||
}
|
||||
|
||||
fn apply_changes(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
let (x, y) = seat.pos.get();
|
||||
let (x, y) = seat.pointer_cursor.position();
|
||||
let pos = self.node.node_absolute_position();
|
||||
let (x_int, y_int) = pos.translate(x.round_down(), y.round_down());
|
||||
// log::info!("apply_changes");
|
||||
|
|
@ -493,7 +493,7 @@ impl PointerOwner for DndPointerOwner {
|
|||
}
|
||||
|
||||
fn apply_changes(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
let (x, y) = seat.pos.get();
|
||||
let (x, y) = seat.pointer_cursor.position();
|
||||
let (x_int, y_int) = (x.round_down(), y.round_down());
|
||||
let (node, x_int, y_int) = {
|
||||
let mut found_tree = seat.found_tree.borrow_mut();
|
||||
|
|
@ -781,7 +781,7 @@ impl<S: ToplevelSelector> NodeSelectorUsecase for SelectToplevelUsecase<S> {
|
|||
if let Some(tl) = &tl {
|
||||
tl.tl_data().render_highlight.fetch_add(1);
|
||||
if !tl.tl_admits_children() {
|
||||
seat.set_known_cursor(KnownCursor::Pointer);
|
||||
seat.pointer_cursor().set_known(KnownCursor::Pointer);
|
||||
}
|
||||
damage = true;
|
||||
}
|
||||
|
|
@ -829,7 +829,7 @@ impl<S: WorkspaceSelector> NodeSelectorUsecase for SelectWorkspaceUsecase<S> {
|
|||
let ws = node.clone().node_into_workspace();
|
||||
if let Some(ws) = &ws {
|
||||
ws.render_highlight.fetch_add(1);
|
||||
seat.set_known_cursor(KnownCursor::Pointer);
|
||||
seat.pointer_cursor().set_known(KnownCursor::Pointer);
|
||||
damage = true;
|
||||
}
|
||||
if let Some(prev) = self.latest.set(ws) {
|
||||
|
|
|
|||
393
src/ifs/wl_seat/tablet.rs
Normal file
393
src/ifs/wl_seat/tablet.rs
Normal file
|
|
@ -0,0 +1,393 @@
|
|||
use {
|
||||
crate::{
|
||||
backend::{InputDeviceGroupId, InputDeviceId},
|
||||
cursor_user::CursorUser,
|
||||
ifs::{
|
||||
wl_seat::{
|
||||
tablet::{
|
||||
pad_owner::PadOwnerHolder, tablet_bindings::TabletBindings,
|
||||
tool_owner::ToolOwnerHolder, zwp_tablet_pad_group_v2::ZwpTabletPadGroupV2,
|
||||
zwp_tablet_pad_ring_v2::ZwpTabletPadRingV2,
|
||||
zwp_tablet_pad_strip_v2::ZwpTabletPadStripV2,
|
||||
zwp_tablet_pad_v2::ZwpTabletPadV2, zwp_tablet_seat_v2::ZwpTabletSeatV2,
|
||||
zwp_tablet_tool_v2::ZwpTabletToolV2, zwp_tablet_v2::ZwpTabletV2,
|
||||
},
|
||||
WlSeatGlobal,
|
||||
},
|
||||
wl_surface::WlSurface,
|
||||
},
|
||||
object::Version,
|
||||
time::now_usec,
|
||||
tree::{FoundNode, Node},
|
||||
utils::{bindings::PerClientBindings, clonecell::CloneCell, copyhashmap::CopyHashMap},
|
||||
},
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
rc::Rc,
|
||||
},
|
||||
};
|
||||
|
||||
mod pad;
|
||||
mod pad_owner;
|
||||
mod tablet_bindings;
|
||||
mod tool;
|
||||
pub mod tool_owner;
|
||||
pub mod zwp_tablet_manager_v2;
|
||||
pub mod zwp_tablet_pad_group_v2;
|
||||
pub mod zwp_tablet_pad_ring_v2;
|
||||
pub mod zwp_tablet_pad_strip_v2;
|
||||
pub mod zwp_tablet_pad_v2;
|
||||
pub mod zwp_tablet_seat_v2;
|
||||
pub mod zwp_tablet_tool_v2;
|
||||
pub mod zwp_tablet_v2;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TabletSeatData {
|
||||
seats: PerClientBindings<ZwpTabletSeatV2>,
|
||||
tablets: CopyHashMap<TabletId, Rc<Tablet>>,
|
||||
tools: CopyHashMap<TabletToolId, Rc<TabletTool>>,
|
||||
pads: CopyHashMap<TabletPadId, Rc<TabletPad>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TabletInit {
|
||||
pub id: TabletId,
|
||||
pub group: InputDeviceGroupId,
|
||||
pub name: String,
|
||||
pub pid: u32,
|
||||
pub vid: u32,
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TabletToolInit {
|
||||
pub tablet_id: TabletId,
|
||||
pub id: TabletToolId,
|
||||
pub type_: TabletToolType,
|
||||
pub hardware_serial: u64,
|
||||
pub hardware_id_wacom: u64,
|
||||
pub capabilities: Vec<TabletToolCapability>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TabletPadInit {
|
||||
pub id: TabletPadId,
|
||||
pub group: InputDeviceGroupId,
|
||||
pub path: String,
|
||||
pub buttons: u32,
|
||||
pub strips: u32,
|
||||
pub rings: u32,
|
||||
pub groups: Vec<TabletPadGroupInit>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TabletPadGroupInit {
|
||||
pub buttons: Vec<u32>,
|
||||
pub rings: Vec<u32>,
|
||||
pub strips: Vec<u32>,
|
||||
pub modes: u32,
|
||||
pub mode: u32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum PadButtonState {
|
||||
Released,
|
||||
Pressed,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum ToolButtonState {
|
||||
Released,
|
||||
Pressed,
|
||||
}
|
||||
|
||||
linear_ids!(TabletIds, TabletId);
|
||||
|
||||
pub struct Tablet {
|
||||
_id: TabletId,
|
||||
dev: InputDeviceId,
|
||||
group: InputDeviceGroupId,
|
||||
name: String,
|
||||
pid: u32,
|
||||
vid: u32,
|
||||
path: String,
|
||||
bindings: TabletBindings<ZwpTabletV2>,
|
||||
tools: CopyHashMap<TabletToolId, Rc<TabletTool>>,
|
||||
pads: CopyHashMap<TabletPadId, Rc<TabletPad>>,
|
||||
tree: RefCell<Vec<FoundNode>>,
|
||||
seat: Rc<WlSeatGlobal>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum TabletToolType {
|
||||
Pen,
|
||||
Eraser,
|
||||
Brush,
|
||||
Pencil,
|
||||
Airbrush,
|
||||
#[allow(dead_code)]
|
||||
Finger,
|
||||
Mouse,
|
||||
Lens,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum TabletToolCapability {
|
||||
Tilt,
|
||||
Pressure,
|
||||
Distance,
|
||||
Rotation,
|
||||
Slider,
|
||||
Wheel,
|
||||
}
|
||||
|
||||
linear_ids!(TabletToolIds, TabletToolId, usize);
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TabletToolOpt {
|
||||
tool: CloneCell<Option<Rc<TabletTool>>>,
|
||||
}
|
||||
|
||||
pub struct TabletTool {
|
||||
pub id: TabletToolId,
|
||||
opt: Rc<TabletToolOpt>,
|
||||
tablet: Rc<Tablet>,
|
||||
type_: TabletToolType,
|
||||
hardware_serial: u64,
|
||||
hardware_id_wacom: u64,
|
||||
capabilities: Vec<TabletToolCapability>,
|
||||
bindings: TabletBindings<ZwpTabletToolV2>,
|
||||
node: CloneCell<Rc<dyn Node>>,
|
||||
pub(super) tool_owner: ToolOwnerHolder,
|
||||
cursor: Rc<CursorUser>,
|
||||
|
||||
down: Cell<bool>,
|
||||
pressure: Cell<f64>,
|
||||
distance: Cell<f64>,
|
||||
tilt_x: Cell<f64>,
|
||||
tilt_y: Cell<f64>,
|
||||
rotation: Cell<f64>,
|
||||
slider: Cell<f64>,
|
||||
}
|
||||
|
||||
linear_ids!(TabletPadIds, TabletPadId);
|
||||
|
||||
pub struct TabletPad {
|
||||
pub id: TabletPadId,
|
||||
dev: InputDeviceId,
|
||||
seat: Rc<WlSeatGlobal>,
|
||||
group: InputDeviceGroupId,
|
||||
tablet: CloneCell<Option<Rc<Tablet>>>,
|
||||
path: String,
|
||||
buttons: u32,
|
||||
bindings: TabletBindings<ZwpTabletPadV2>,
|
||||
groups: Vec<Rc<TabletPadGroup>>,
|
||||
strips: Vec<Rc<TabletPadStrip>>,
|
||||
rings: Vec<Rc<TabletPadRing>>,
|
||||
node: CloneCell<Rc<dyn Node>>,
|
||||
pub(super) pad_owner: PadOwnerHolder,
|
||||
}
|
||||
|
||||
pub struct TabletPadGroup {
|
||||
buttons: Vec<u32>,
|
||||
mode: Cell<u32>,
|
||||
modes: u32,
|
||||
rings: Vec<u32>,
|
||||
strips: Vec<u32>,
|
||||
bindings: TabletBindings<ZwpTabletPadGroupV2>,
|
||||
}
|
||||
|
||||
pub struct TabletPadStrip {
|
||||
bindings: TabletBindings<ZwpTabletPadStripV2>,
|
||||
}
|
||||
|
||||
pub struct TabletPadRing {
|
||||
bindings: TabletBindings<ZwpTabletPadRingV2>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum TabletRingEventSource {
|
||||
Finger,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum TabletStripEventSource {
|
||||
Finger,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TabletToolChanges {
|
||||
pub down: Option<bool>,
|
||||
pub pos: Option<TabletTool2dChange<TabletToolPositionChange>>,
|
||||
pub pressure: Option<f64>,
|
||||
pub distance: Option<f64>,
|
||||
pub tilt: Option<TabletTool2dChange<f64>>,
|
||||
pub rotation: Option<f64>,
|
||||
pub slider: Option<f64>,
|
||||
pub wheel: Option<TabletToolWheelChange>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct TabletTool2dChange<T> {
|
||||
pub x: T,
|
||||
pub y: T,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct TabletToolPositionChange {
|
||||
pub x: f64,
|
||||
pub dx: f64,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct TabletToolWheelChange {
|
||||
pub degrees: f64,
|
||||
pub clicks: i32,
|
||||
}
|
||||
|
||||
impl WlSeatGlobal {
|
||||
fn tablet_add_seat(&self, seat: &Rc<ZwpTabletSeatV2>) {
|
||||
self.tablet.seats.add(&seat.client, seat);
|
||||
for tablet in self.tablet.tablets.lock().values() {
|
||||
seat.announce_tablet(tablet);
|
||||
}
|
||||
for tool in self.tablet.tools.lock().values() {
|
||||
seat.announce_tool(tool);
|
||||
}
|
||||
for pad in self.tablet.pads.lock().values() {
|
||||
seat.announce_pad(pad);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tablet_add_tablet(self: &Rc<Self>, dev: InputDeviceId, init: &TabletInit) {
|
||||
let tablet = Rc::new(Tablet {
|
||||
_id: init.id,
|
||||
dev,
|
||||
group: init.group,
|
||||
name: init.name.clone(),
|
||||
pid: init.pid,
|
||||
vid: init.vid,
|
||||
path: init.path.clone(),
|
||||
bindings: Default::default(),
|
||||
tools: Default::default(),
|
||||
pads: Default::default(),
|
||||
tree: Default::default(),
|
||||
seat: self.clone(),
|
||||
});
|
||||
self.tablet.tablets.set(init.id, tablet.clone());
|
||||
self.tablet_for_each_seat_obj(|s| s.announce_tablet(&tablet));
|
||||
for pad in self.tablet.pads.lock().values() {
|
||||
if pad.tablet.is_none() && pad.group == init.group {
|
||||
self.connect_tablet_and_pad(&tablet, pad);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn tablet_for_each_seat_obj(&self, mut f: impl FnMut(&Rc<ZwpTabletSeatV2>)) {
|
||||
for seats in self.tablet.seats.borrow().values() {
|
||||
for seat in seats.values() {
|
||||
f(seat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tablet_clear(&self) {
|
||||
self.tablet.seats.clear();
|
||||
for (_, tablet) in self.tablet.tablets.lock().drain() {
|
||||
tablet.pads.clear();
|
||||
tablet.bindings.clear();
|
||||
tablet.tools.clear();
|
||||
}
|
||||
for (_, tool) in self.tablet.tools.lock().drain() {
|
||||
tool.cursor.detach();
|
||||
tool.opt.tool.take();
|
||||
tool.tool_owner.destroy(&tool);
|
||||
tool.bindings.clear();
|
||||
}
|
||||
for (_, pad) in self.tablet.pads.lock().drain() {
|
||||
pad.pad_owner.destroy(&pad);
|
||||
pad.tablet.take();
|
||||
pad.bindings.clear();
|
||||
for group in &pad.groups {
|
||||
group.bindings.clear();
|
||||
}
|
||||
for rings in &pad.rings {
|
||||
rings.bindings.clear();
|
||||
}
|
||||
for strips in &pad.strips {
|
||||
strips.bindings.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tablet_remove_tablet(self: &Rc<Self>, id: TabletId) {
|
||||
let Some(tablet) = self.tablet.tablets.remove(&id) else {
|
||||
return;
|
||||
};
|
||||
for (_, tool) in tablet.tools.lock().drain() {
|
||||
self.tablet_handle_remove_tool(now_usec(), tool.id);
|
||||
}
|
||||
for (_, pad) in tablet.pads.lock().drain() {
|
||||
pad.pad_owner.destroy(&pad);
|
||||
pad.tablet.take();
|
||||
}
|
||||
for (_, binding) in tablet.bindings.lock().drain() {
|
||||
binding.send_removed();
|
||||
}
|
||||
}
|
||||
|
||||
fn connect_tablet_and_pad(self: &Rc<Self>, tablet: &Rc<Tablet>, pad: &Rc<TabletPad>) {
|
||||
pad.tablet.set(Some(tablet.clone()));
|
||||
tablet.pads.set(pad.id, pad.clone());
|
||||
pad.pad_owner.update_node(pad);
|
||||
}
|
||||
|
||||
pub fn tablet_on_keyboard_node_change(self: &Rc<Self>) {
|
||||
if self.tablet.pads.is_empty() {
|
||||
return;
|
||||
}
|
||||
for pad in self.tablet.pads.lock().values() {
|
||||
if pad.tablet.is_some() {
|
||||
pad.pad_owner.update_node(pad);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn tablet_for_each_seat(&self, surface: &WlSurface, f: impl FnMut(&ZwpTabletSeatV2)) {
|
||||
self.tablet
|
||||
.seats
|
||||
.for_each(surface.client.id, Version::ALL, f)
|
||||
}
|
||||
|
||||
pub(super) fn tablet_apply_changes(self: &Rc<Self>) {
|
||||
if self.tablet.tools.is_empty() {
|
||||
return;
|
||||
}
|
||||
let now = now_usec();
|
||||
for tool in self.tablet.tools.lock().values() {
|
||||
tool.tool_owner.apply_changes(tool, now, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn normalizei(n: f64) -> i32 {
|
||||
(65535.0 * n) as i32
|
||||
}
|
||||
|
||||
fn normalizeu(n: f64) -> u32 {
|
||||
normalizei(n) as u32
|
||||
}
|
||||
|
||||
impl TabletTool {
|
||||
pub fn cursor(&self) -> &Rc<CursorUser> {
|
||||
&self.cursor
|
||||
}
|
||||
|
||||
pub fn node(&self) -> Rc<dyn Node> {
|
||||
self.node.get()
|
||||
}
|
||||
|
||||
pub fn seat(&self) -> &Rc<WlSeatGlobal> {
|
||||
&self.tablet.seat
|
||||
}
|
||||
}
|
||||
290
src/ifs/wl_seat/tablet/pad.rs
Normal file
290
src/ifs/wl_seat/tablet/pad.rs
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
use {
|
||||
crate::{
|
||||
backend::InputDeviceId,
|
||||
fixed::Fixed,
|
||||
ifs::{
|
||||
wl_seat::{
|
||||
tablet::{
|
||||
normalizeu, zwp_tablet_pad_v2::ZwpTabletPadV2, zwp_tablet_v2::ZwpTabletV2,
|
||||
PadButtonState, TabletPad, TabletPadGroup, TabletPadId, TabletPadInit,
|
||||
TabletPadRing, TabletPadStrip, TabletRingEventSource, TabletStripEventSource,
|
||||
},
|
||||
WlSeatGlobal,
|
||||
},
|
||||
wl_surface::WlSurface,
|
||||
},
|
||||
time::{now_usec, usec_to_msec},
|
||||
utils::clonecell::CloneCell,
|
||||
},
|
||||
std::{cell::Cell, rc::Rc},
|
||||
};
|
||||
|
||||
impl WlSeatGlobal {
|
||||
pub fn tablet_add_tablet_pad(self: &Rc<Self>, dev: InputDeviceId, init: &TabletPadInit) {
|
||||
let mut strips = Vec::new();
|
||||
for _ in 0..init.strips {
|
||||
strips.push(Rc::new(TabletPadStrip {
|
||||
bindings: Default::default(),
|
||||
}));
|
||||
}
|
||||
let mut rings = Vec::new();
|
||||
for _ in 0..init.rings {
|
||||
rings.push(Rc::new(TabletPadRing {
|
||||
bindings: Default::default(),
|
||||
}));
|
||||
}
|
||||
let mut groups = Vec::new();
|
||||
for group_init in &init.groups {
|
||||
groups.push(Rc::new(TabletPadGroup {
|
||||
buttons: group_init.buttons.clone(),
|
||||
mode: Cell::new(group_init.mode),
|
||||
modes: group_init.modes,
|
||||
rings: group_init.rings.clone(),
|
||||
strips: group_init.strips.clone(),
|
||||
bindings: Default::default(),
|
||||
}));
|
||||
}
|
||||
let pad = Rc::new(TabletPad {
|
||||
id: init.id,
|
||||
dev,
|
||||
seat: self.clone(),
|
||||
group: init.group,
|
||||
tablet: Default::default(),
|
||||
path: init.path.clone(),
|
||||
buttons: init.buttons,
|
||||
bindings: Default::default(),
|
||||
groups,
|
||||
strips,
|
||||
rings,
|
||||
node: CloneCell::new(self.state.root.clone()),
|
||||
pad_owner: Default::default(),
|
||||
});
|
||||
self.tablet.pads.set(init.id, pad.clone());
|
||||
self.tablet_for_each_seat_obj(|s| s.announce_pad(&pad));
|
||||
for tablet in self.tablet.tablets.lock().values() {
|
||||
if tablet.group == init.group {
|
||||
self.connect_tablet_and_pad(tablet, &pad);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tablet_remove_tablet_pad(self: &Rc<Self>, id: TabletPadId) {
|
||||
let Some(pad) = self.tablet.pads.remove(&id) else {
|
||||
return;
|
||||
};
|
||||
pad.pad_owner.destroy(&pad);
|
||||
if let Some(tablet) = pad.tablet.take() {
|
||||
tablet.pads.remove(&pad.id);
|
||||
}
|
||||
for (_, binding) in pad.bindings.lock().drain() {
|
||||
binding.send_removed();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tablet_event_pad_mode_switch(
|
||||
self: &Rc<Self>,
|
||||
pad: TabletPadId,
|
||||
time_usec: u64,
|
||||
group_idx: u32,
|
||||
mode: u32,
|
||||
) {
|
||||
if let Some(pad) = self.tablet.pads.get(&pad) {
|
||||
if let Some(group) = pad.groups.get(group_idx as usize) {
|
||||
if group.mode.replace(mode) != mode {
|
||||
self.state.for_each_seat_tester(|t| {
|
||||
t.send_tablet_pad_mode_switch(self.id, pad.dev, time_usec, group_idx, mode)
|
||||
});
|
||||
if pad.tablet.is_some() {
|
||||
let node = pad.node.get();
|
||||
node.node_on_tablet_pad_mode_switch(&pad, group, time_usec, mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tablet_event_pad_button(
|
||||
self: &Rc<Self>,
|
||||
pad: TabletPadId,
|
||||
time_usec: u64,
|
||||
button: u32,
|
||||
state: PadButtonState,
|
||||
) {
|
||||
if let Some(pad) = self.tablet.pads.get(&pad) {
|
||||
self.state.for_each_seat_tester(|t| {
|
||||
t.send_tablet_pad_button(self.id, pad.dev, time_usec, button, state)
|
||||
});
|
||||
if pad.tablet.is_some() {
|
||||
pad.pad_owner.button(&pad, time_usec, button, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tablet_event_pad_ring(
|
||||
self: &Rc<Self>,
|
||||
pad: TabletPadId,
|
||||
ring: u32,
|
||||
source: Option<TabletRingEventSource>,
|
||||
angle: Option<f64>,
|
||||
time_usec: u64,
|
||||
) {
|
||||
if let Some(pad) = self.tablet.pads.get(&pad) {
|
||||
self.state.for_each_seat_tester(|t| {
|
||||
t.send_tablet_pad_ring(self.id, pad.dev, time_usec, ring, source, angle)
|
||||
});
|
||||
if pad.tablet.is_some() {
|
||||
if let Some(ring) = pad.rings.get(ring as usize) {
|
||||
let node = self.keyboard_node.get();
|
||||
node.node_on_tablet_pad_ring(&pad, ring, source, angle, time_usec);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tablet_event_pad_strip(
|
||||
self: &Rc<Self>,
|
||||
pad: TabletPadId,
|
||||
strip: u32,
|
||||
source: Option<TabletStripEventSource>,
|
||||
position: Option<f64>,
|
||||
time_usec: u64,
|
||||
) {
|
||||
if let Some(pad) = self.tablet.pads.get(&pad) {
|
||||
self.state.for_each_seat_tester(|t| {
|
||||
t.send_tablet_pad_strip(self.id, pad.dev, time_usec, strip, source, position)
|
||||
});
|
||||
if pad.tablet.is_some() {
|
||||
if let Some(strip) = pad.strips.get(strip as usize) {
|
||||
let node = pad.node.get();
|
||||
node.node_on_tablet_pad_strip(&pad, strip, source, position, time_usec);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TabletPad {
|
||||
fn for_each_pair(&self, n: &WlSurface, mut f: impl FnMut(&ZwpTabletV2, &ZwpTabletPadV2)) {
|
||||
let Some(tablet) = self.tablet.get() else {
|
||||
return;
|
||||
};
|
||||
self.seat.tablet_for_each_seat(n, |s| {
|
||||
let Some(tablet) = tablet.bindings.get(s) else {
|
||||
return;
|
||||
};
|
||||
let Some(pad) = self.bindings.get(s) else {
|
||||
return;
|
||||
};
|
||||
f(&tablet, &pad);
|
||||
})
|
||||
}
|
||||
|
||||
fn for_each_entered(&self, n: &WlSurface, mut f: impl FnMut(&ZwpTabletPadV2)) {
|
||||
self.seat.tablet_for_each_seat(n, |s| {
|
||||
let Some(pad) = self.bindings.get(s) else {
|
||||
return;
|
||||
};
|
||||
if pad.entered.get() {
|
||||
f(&pad);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn surface_enter(self: &Rc<Self>, n: &WlSurface) {
|
||||
let mut serial = n.client.pending_serial();
|
||||
let time = usec_to_msec(now_usec());
|
||||
self.for_each_pair(n, |tablet, pad| {
|
||||
pad.send_enter(serial.get(), &tablet, n);
|
||||
for group in &self.groups {
|
||||
let mode = group.mode.get();
|
||||
if let Some(group) = group.bindings.get(&pad.seat) {
|
||||
group.send_mode_switch(time, serial.get(), mode);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn surface_leave(self: &Rc<Self>, n: &WlSurface) {
|
||||
let mut serial = n.client.pending_serial();
|
||||
self.for_each_entered(n, |pad| {
|
||||
pad.send_leave(serial.get(), n);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn surface_ring(
|
||||
self: &Rc<Self>,
|
||||
n: &WlSurface,
|
||||
ring: &Rc<TabletPadRing>,
|
||||
source: Option<TabletRingEventSource>,
|
||||
angle: Option<f64>,
|
||||
time_usec: u64,
|
||||
) {
|
||||
let time = usec_to_msec(time_usec);
|
||||
self.seat.tablet_for_each_seat(n, |s| {
|
||||
if let Some(ring) = ring.bindings.get(&s) {
|
||||
if let Some(source) = source {
|
||||
ring.send_source(source);
|
||||
}
|
||||
if let Some(angle) = angle {
|
||||
ring.send_angle(Fixed::from_f64(angle));
|
||||
} else {
|
||||
ring.send_stop();
|
||||
}
|
||||
ring.send_frame(time);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn surface_strip(
|
||||
self: &Rc<Self>,
|
||||
n: &WlSurface,
|
||||
strip: &Rc<TabletPadStrip>,
|
||||
source: Option<TabletStripEventSource>,
|
||||
position: Option<f64>,
|
||||
time_usec: u64,
|
||||
) {
|
||||
let time = usec_to_msec(time_usec);
|
||||
self.for_each_entered(n, |pad| {
|
||||
if let Some(strip) = strip.bindings.get(&pad.seat) {
|
||||
if let Some(source) = source {
|
||||
strip.send_source(source);
|
||||
}
|
||||
if let Some(position) = position {
|
||||
strip.send_position(normalizeu(position));
|
||||
} else {
|
||||
strip.send_stop();
|
||||
}
|
||||
strip.send_frame(time);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn surface_mode_switch(
|
||||
self: &Rc<Self>,
|
||||
n: &WlSurface,
|
||||
group: &Rc<TabletPadGroup>,
|
||||
time_usec: u64,
|
||||
mode: u32,
|
||||
) {
|
||||
let time = usec_to_msec(time_usec);
|
||||
let mut serial = n.client.pending_serial();
|
||||
self.for_each_entered(n, |pad| {
|
||||
if let Some(group) = group.bindings.get(&pad.seat) {
|
||||
group.send_mode_switch(time, serial.get(), mode);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn surface_button(
|
||||
self: &Rc<Self>,
|
||||
n: &WlSurface,
|
||||
time_usec: u64,
|
||||
button: u32,
|
||||
state: PadButtonState,
|
||||
) {
|
||||
let time = usec_to_msec(time_usec);
|
||||
self.for_each_entered(n, |pad| {
|
||||
pad.send_button(time, button, state);
|
||||
})
|
||||
}
|
||||
}
|
||||
135
src/ifs/wl_seat/tablet/pad_owner.rs
Normal file
135
src/ifs/wl_seat/tablet/pad_owner.rs
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
use {
|
||||
crate::{
|
||||
ifs::wl_seat::tablet::{PadButtonState, TabletPad},
|
||||
time::now_usec,
|
||||
tree::Node,
|
||||
utils::{clonecell::CloneCell, smallmap::SmallMap},
|
||||
},
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
pub struct PadOwnerHolder {
|
||||
default: Rc<DefaultPadOwner>,
|
||||
owner: CloneCell<Rc<dyn PadOwner>>,
|
||||
}
|
||||
|
||||
trait PadOwner {
|
||||
fn revert_to_default(&self, pad: &Rc<TabletPad>, time_usec: u64);
|
||||
fn update_node(&self, pad: &Rc<TabletPad>);
|
||||
fn button(&self, pad: &Rc<TabletPad>, time_usec: u64, button: u32, state: PadButtonState);
|
||||
}
|
||||
|
||||
struct DefaultPadOwner;
|
||||
|
||||
struct GrabPadOwner {
|
||||
buttons: SmallMap<u32, (), 4>,
|
||||
node: Rc<dyn Node>,
|
||||
}
|
||||
|
||||
impl Default for PadOwnerHolder {
|
||||
fn default() -> Self {
|
||||
let default = Rc::new(DefaultPadOwner);
|
||||
Self {
|
||||
owner: CloneCell::new(default.clone()),
|
||||
default,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PadOwnerHolder {
|
||||
pub fn update_node(&self, pad: &Rc<TabletPad>) {
|
||||
self.owner.get().update_node(pad);
|
||||
}
|
||||
|
||||
pub fn destroy(&self, pad: &Rc<TabletPad>) {
|
||||
self.owner.get().revert_to_default(pad, now_usec());
|
||||
let prev = pad.node.set(pad.seat.state.root.clone());
|
||||
prev.node_on_tablet_pad_leave(pad);
|
||||
prev.node_seat_state().remove_tablet_pad_focus(pad);
|
||||
}
|
||||
|
||||
pub fn button(&self, pad: &Rc<TabletPad>, time_usec: u64, button: u32, state: PadButtonState) {
|
||||
self.owner.get().button(pad, time_usec, button, state);
|
||||
}
|
||||
|
||||
pub fn focus_root(&self, pad: &Rc<TabletPad>) {
|
||||
self.owner.get().revert_to_default(pad, now_usec());
|
||||
let node = pad.seat.state.root.clone();
|
||||
pad.focus_node(node);
|
||||
}
|
||||
|
||||
fn set_default_owner(&self) {
|
||||
self.owner.set(self.default.clone());
|
||||
}
|
||||
}
|
||||
|
||||
impl TabletPad {
|
||||
fn focus_node(self: &Rc<Self>, node: Rc<dyn Node>) {
|
||||
let prev = self.node.set(node.clone());
|
||||
if node.node_id() != prev.node_id() {
|
||||
prev.node_on_tablet_pad_leave(self);
|
||||
prev.node_seat_state().remove_tablet_pad_focus(self);
|
||||
node.node_seat_state().add_tablet_pad_focus(self);
|
||||
node.node_on_tablet_pad_enter(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PadOwner for DefaultPadOwner {
|
||||
fn revert_to_default(&self, _pad: &Rc<TabletPad>, _time_usec: u64) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
fn update_node(&self, pad: &Rc<TabletPad>) {
|
||||
let node = pad.seat.keyboard_node.get();
|
||||
pad.focus_node(node);
|
||||
}
|
||||
|
||||
fn button(&self, pad: &Rc<TabletPad>, time_usec: u64, button: u32, state: PadButtonState) {
|
||||
if state != PadButtonState::Pressed {
|
||||
return;
|
||||
}
|
||||
let node = pad.node.get();
|
||||
let owner = Rc::new(GrabPadOwner {
|
||||
buttons: Default::default(),
|
||||
node,
|
||||
});
|
||||
pad.pad_owner.owner.set(owner.clone());
|
||||
owner.button(pad, time_usec, button, state);
|
||||
}
|
||||
}
|
||||
|
||||
impl PadOwner for GrabPadOwner {
|
||||
fn revert_to_default(&self, pad: &Rc<TabletPad>, time_usec: u64) {
|
||||
for (button, _) in &self.buttons {
|
||||
self.node
|
||||
.node_on_tablet_pad_button(pad, time_usec, button, PadButtonState::Released);
|
||||
}
|
||||
pad.pad_owner.set_default_owner();
|
||||
}
|
||||
|
||||
fn update_node(&self, _pad: &Rc<TabletPad>) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
fn button(&self, pad: &Rc<TabletPad>, time_usec: u64, button: u32, state: PadButtonState) {
|
||||
match state {
|
||||
PadButtonState::Released => {
|
||||
if self.buttons.remove(&button).is_none() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
PadButtonState::Pressed => {
|
||||
if self.buttons.insert(button, ()).is_some() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.node
|
||||
.node_on_tablet_pad_button(pad, time_usec, button, state);
|
||||
if self.buttons.is_empty() {
|
||||
pad.pad_owner.set_default_owner();
|
||||
pad.pad_owner.default.update_node(pad);
|
||||
}
|
||||
}
|
||||
}
|
||||
43
src/ifs/wl_seat/tablet/tablet_bindings.rs
Normal file
43
src/ifs/wl_seat/tablet/tablet_bindings.rs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
use {
|
||||
crate::{
|
||||
client::ClientId,
|
||||
ifs::wl_seat::tablet::zwp_tablet_seat_v2::ZwpTabletSeatV2,
|
||||
utils::copyhashmap::{CopyHashMap, Locked},
|
||||
wire::ZwpTabletSeatV2Id,
|
||||
},
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
pub struct TabletBindings<T> {
|
||||
bindings: CopyHashMap<(ClientId, ZwpTabletSeatV2Id), Rc<T>>,
|
||||
}
|
||||
|
||||
impl<T> Default for TabletBindings<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
bindings: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TabletBindings<T> {
|
||||
pub fn add(&self, seat: &ZwpTabletSeatV2, t: &Rc<T>) {
|
||||
self.bindings.set((seat.client.id, seat.id), t.clone());
|
||||
}
|
||||
|
||||
pub fn get(&self, seat: &ZwpTabletSeatV2) -> Option<Rc<T>> {
|
||||
self.bindings.get(&(seat.client.id, seat.id))
|
||||
}
|
||||
|
||||
pub fn remove(&self, seat: &ZwpTabletSeatV2) -> Option<Rc<T>> {
|
||||
self.bindings.remove(&(seat.client.id, seat.id))
|
||||
}
|
||||
|
||||
pub fn lock(&self) -> Locked<'_, (ClientId, ZwpTabletSeatV2Id), Rc<T>> {
|
||||
self.bindings.lock()
|
||||
}
|
||||
|
||||
pub fn clear(&self) {
|
||||
self.bindings.clear();
|
||||
}
|
||||
}
|
||||
274
src/ifs/wl_seat/tablet/tool.rs
Normal file
274
src/ifs/wl_seat/tablet/tool.rs
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
use {
|
||||
crate::{
|
||||
cursor::KnownCursor,
|
||||
fixed::Fixed,
|
||||
ifs::{
|
||||
wl_seat::{
|
||||
tablet::{
|
||||
normalizei, normalizeu, zwp_tablet_tool_v2::ZwpTabletToolV2,
|
||||
zwp_tablet_v2::ZwpTabletV2, TabletTool, TabletToolChanges, TabletToolId,
|
||||
TabletToolInit, TabletToolOpt, TabletToolType, ToolButtonState,
|
||||
},
|
||||
WlSeatGlobal,
|
||||
},
|
||||
wl_surface::WlSurface,
|
||||
},
|
||||
rect::Rect,
|
||||
time::usec_to_msec,
|
||||
utils::clonecell::CloneCell,
|
||||
},
|
||||
std::{cell::Cell, rc::Rc},
|
||||
};
|
||||
|
||||
impl WlSeatGlobal {
|
||||
pub fn tablet_handle_remove_tool(self: &Rc<Self>, time_usec: u64, id: TabletToolId) {
|
||||
let Some(tool) = self.tablet.tools.remove(&id) else {
|
||||
return;
|
||||
};
|
||||
self.state.for_each_seat_tester(|t| {
|
||||
t.send_tablet_tool_proximity_out(self.id, tool.tablet.dev, tool.id, time_usec)
|
||||
});
|
||||
tool.opt.tool.take();
|
||||
tool.cursor.detach();
|
||||
tool.tool_owner.destroy(&tool);
|
||||
for (_, binding) in tool.bindings.lock().drain() {
|
||||
binding.send_removed();
|
||||
}
|
||||
tool.tablet.tools.remove(&id);
|
||||
}
|
||||
|
||||
pub fn tablet_handle_new_tool(self: &Rc<Self>, time_usec: u64, init: &TabletToolInit) {
|
||||
let Some(tablet) = self.tablet.tablets.get(&init.tablet_id) else {
|
||||
return;
|
||||
};
|
||||
let tool = Rc::new(TabletTool {
|
||||
id: init.id,
|
||||
opt: Default::default(),
|
||||
tablet,
|
||||
type_: init.type_,
|
||||
hardware_serial: init.hardware_serial,
|
||||
hardware_id_wacom: init.hardware_id_wacom,
|
||||
capabilities: init.capabilities.clone(),
|
||||
bindings: Default::default(),
|
||||
node: CloneCell::new(self.state.root.clone()),
|
||||
tool_owner: Default::default(),
|
||||
cursor: self.cursor_user_group.create_user(),
|
||||
down: Cell::new(false),
|
||||
pressure: Cell::new(0.0),
|
||||
distance: Cell::new(0.0),
|
||||
tilt_x: Cell::new(0.0),
|
||||
tilt_y: Cell::new(0.0),
|
||||
rotation: Cell::new(0.0),
|
||||
slider: Cell::new(0.0),
|
||||
});
|
||||
tool.opt.tool.set(Some(tool.clone()));
|
||||
tool.cursor.set_known(KnownCursor::Default);
|
||||
self.tablet.tools.set(init.id, tool.clone());
|
||||
self.state.for_each_seat_tester(|t| {
|
||||
t.send_tablet_tool_proximity_in(self.id, tool.tablet.dev, tool.id, time_usec)
|
||||
});
|
||||
self.tablet_for_each_seat_obj(|s| s.announce_tool(&tool));
|
||||
}
|
||||
|
||||
pub fn tablet_event_tool_button(
|
||||
self: &Rc<Self>,
|
||||
id: TabletToolId,
|
||||
time_usec: u64,
|
||||
button: u32,
|
||||
state: ToolButtonState,
|
||||
) {
|
||||
let Some(tool) = self.tablet.tools.get(&id) else {
|
||||
return;
|
||||
};
|
||||
self.state.for_each_seat_tester(|t| {
|
||||
t.send_tablet_tool_button(self.id, tool.tablet.dev, &tool, time_usec, button, state);
|
||||
});
|
||||
tool.cursor.activate();
|
||||
tool.tool_owner.button(&tool, time_usec, button, state);
|
||||
}
|
||||
|
||||
pub fn tablet_event_tool_changes(
|
||||
self: &Rc<Self>,
|
||||
id: TabletToolId,
|
||||
time_usec: u64,
|
||||
rect: Rect,
|
||||
changes: &TabletToolChanges,
|
||||
) {
|
||||
let Some(tool) = self.tablet.tools.get(&id) else {
|
||||
return;
|
||||
};
|
||||
self.state.for_each_seat_tester(|t| {
|
||||
t.send_tablet_tool_changes(self.id, tool.tablet.dev, &tool, time_usec, changes);
|
||||
});
|
||||
if let Some(val) = changes.down {
|
||||
tool.down.set(val);
|
||||
}
|
||||
if let Some(val) = changes.pressure {
|
||||
tool.pressure.set(val);
|
||||
}
|
||||
if let Some(val) = changes.distance {
|
||||
tool.distance.set(val);
|
||||
}
|
||||
if let Some(val) = changes.tilt {
|
||||
tool.tilt_x.set(val.x);
|
||||
tool.tilt_y.set(val.y);
|
||||
}
|
||||
if let Some(val) = changes.rotation {
|
||||
tool.rotation.set(val);
|
||||
}
|
||||
if let Some(val) = changes.slider {
|
||||
tool.slider.set(val);
|
||||
}
|
||||
if let Some(delta) = changes.pos {
|
||||
let (x, y) = match tool.type_ {
|
||||
TabletToolType::Mouse | TabletToolType::Lens => {
|
||||
let (mut x, mut y) = tool.cursor.position();
|
||||
x += Fixed::from_f64(delta.x.dx);
|
||||
y += Fixed::from_f64(delta.y.dx);
|
||||
(x, y)
|
||||
}
|
||||
TabletToolType::Pen
|
||||
| TabletToolType::Eraser
|
||||
| TabletToolType::Brush
|
||||
| TabletToolType::Pencil
|
||||
| TabletToolType::Airbrush
|
||||
| TabletToolType::Finger => {
|
||||
let x = Fixed::from_f64(rect.x1() as f64 + (rect.width() as f64 * delta.x.x));
|
||||
let y = Fixed::from_f64(rect.y1() as f64 + (rect.height() as f64 * delta.y.x));
|
||||
(x, y)
|
||||
}
|
||||
};
|
||||
tool.cursor.set_position(x, y);
|
||||
}
|
||||
tool.cursor.activate();
|
||||
tool.tool_owner
|
||||
.apply_changes(&tool, time_usec, Some(changes));
|
||||
}
|
||||
}
|
||||
|
||||
impl TabletTool {
|
||||
fn for_each_pair(&self, n: &WlSurface, mut f: impl FnMut(&ZwpTabletV2, &ZwpTabletToolV2)) {
|
||||
self.tablet.seat.tablet_for_each_seat(n, |s| {
|
||||
let Some(tablet) = self.tablet.bindings.get(s) else {
|
||||
return;
|
||||
};
|
||||
let Some(tool) = self.bindings.get(s) else {
|
||||
return;
|
||||
};
|
||||
f(&tablet, &tool);
|
||||
})
|
||||
}
|
||||
|
||||
fn for_each_entered(&self, n: &WlSurface, mut f: impl FnMut(&ZwpTabletToolV2)) {
|
||||
self.tablet.seat.tablet_for_each_seat(n, |s| {
|
||||
let Some(tool) = self.bindings.get(s) else {
|
||||
return;
|
||||
};
|
||||
if !tool.entered.get() {
|
||||
return;
|
||||
}
|
||||
f(&tool);
|
||||
})
|
||||
}
|
||||
|
||||
pub fn surface_leave(&self, n: &WlSurface, time_usec: u64) {
|
||||
let time = usec_to_msec(time_usec);
|
||||
self.for_each_entered(n, |t| {
|
||||
t.send_proximity_out();
|
||||
t.send_frame(time);
|
||||
})
|
||||
}
|
||||
|
||||
pub fn surface_enter(&self, n: &WlSurface, time_usec: u64, x: Fixed, y: Fixed) {
|
||||
let time = usec_to_msec(time_usec);
|
||||
let mut serial = n.client.pending_serial();
|
||||
self.for_each_pair(n, |tablet, tool| {
|
||||
tool.send_proximity_in(serial.get(), tablet, n);
|
||||
tool.send_motion(x, y);
|
||||
tool.send_pressure(normalizeu(self.pressure.get()));
|
||||
tool.send_distance(normalizeu(self.distance.get()));
|
||||
tool.send_tilt(
|
||||
Fixed::from_f64(self.tilt_x.get()),
|
||||
Fixed::from_f64(self.tilt_y.get()),
|
||||
);
|
||||
tool.send_rotation(Fixed::from_f64(self.rotation.get()));
|
||||
tool.send_slider(normalizei(self.slider.get()));
|
||||
tool.send_frame(time);
|
||||
})
|
||||
}
|
||||
|
||||
pub fn surface_button(
|
||||
&self,
|
||||
n: &WlSurface,
|
||||
time_usec: u64,
|
||||
button: u32,
|
||||
state: ToolButtonState,
|
||||
) {
|
||||
let time = usec_to_msec(time_usec);
|
||||
let mut serial = n.client.pending_serial();
|
||||
self.for_each_entered(n, |tool| {
|
||||
tool.send_button(serial.get(), button, state);
|
||||
tool.send_frame(time);
|
||||
});
|
||||
if state == ToolButtonState::Pressed {
|
||||
if let Some(node) = n.get_focus_node(self.tablet.seat.id) {
|
||||
self.tablet.seat.focus_node(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn surface_apply_changes(
|
||||
&self,
|
||||
n: &WlSurface,
|
||||
time_usec: u64,
|
||||
changes: Option<&TabletToolChanges>,
|
||||
x: Fixed,
|
||||
y: Fixed,
|
||||
) {
|
||||
let mut serial = n.client.pending_serial();
|
||||
let time = usec_to_msec(time_usec);
|
||||
self.for_each_entered(n, |tool| {
|
||||
if let Some(changes) = changes {
|
||||
if let Some(val) = changes.down {
|
||||
match val {
|
||||
false => tool.send_up(),
|
||||
true => tool.send_down(serial.get()),
|
||||
}
|
||||
}
|
||||
if let Some(val) = changes.pressure {
|
||||
tool.send_pressure(normalizeu(val));
|
||||
}
|
||||
if let Some(val) = changes.distance {
|
||||
tool.send_distance(normalizeu(val));
|
||||
}
|
||||
if let Some(val) = changes.tilt {
|
||||
tool.send_tilt(Fixed::from_f64(val.x), Fixed::from_f64(val.y));
|
||||
}
|
||||
if let Some(val) = changes.rotation {
|
||||
tool.send_rotation(Fixed::from_f64(val));
|
||||
}
|
||||
if let Some(val) = changes.slider {
|
||||
tool.send_slider(normalizei(val));
|
||||
}
|
||||
if let Some(val) = changes.wheel {
|
||||
tool.send_wheel(Fixed::from_f64(val.degrees), val.clicks);
|
||||
}
|
||||
}
|
||||
tool.send_motion(x, y);
|
||||
tool.send_frame(time);
|
||||
});
|
||||
if let Some(changes) = changes {
|
||||
if changes.down == Some(true) {
|
||||
if let Some(node) = n.get_focus_node(self.tablet.seat.id) {
|
||||
self.tablet.seat.focus_node(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TabletToolOpt {
|
||||
pub fn get(&self) -> Option<Rc<TabletTool>> {
|
||||
self.tool.get()
|
||||
}
|
||||
}
|
||||
213
src/ifs/wl_seat/tablet/tool_owner.rs
Normal file
213
src/ifs/wl_seat/tablet/tool_owner.rs
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
use {
|
||||
crate::{
|
||||
fixed::Fixed,
|
||||
ifs::wl_seat::tablet::{TabletTool, TabletToolChanges, ToolButtonState},
|
||||
time::now_usec,
|
||||
tree::{FindTreeUsecase, FoundNode, Node},
|
||||
utils::{clonecell::CloneCell, smallmap::SmallMap},
|
||||
},
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
pub struct ToolOwnerHolder {
|
||||
default: Rc<DefaultToolOwner>,
|
||||
owner: CloneCell<Rc<dyn ToolOwner>>,
|
||||
}
|
||||
|
||||
struct DefaultToolOwner;
|
||||
|
||||
struct GrabToolOwner {
|
||||
buttons: SmallMap<u32, (), 4>,
|
||||
node: Rc<dyn Node>,
|
||||
}
|
||||
|
||||
impl Default for ToolOwnerHolder {
|
||||
fn default() -> Self {
|
||||
let default = Rc::new(DefaultToolOwner);
|
||||
Self {
|
||||
owner: CloneCell::new(default.clone()),
|
||||
default,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToolOwnerHolder {
|
||||
pub fn destroy(&self, tool: &Rc<TabletTool>) {
|
||||
let root = tool.tablet.seat.state.root.clone();
|
||||
let prev = tool.node.set(root);
|
||||
prev.node_on_tablet_tool_leave(tool, now_usec());
|
||||
prev.node_seat_state().remove_tablet_tool_focus(tool);
|
||||
}
|
||||
|
||||
pub fn focus_root(&self, tool: &Rc<TabletTool>) {
|
||||
self.owner.set(self.default.clone());
|
||||
let root = tool.tablet.seat.state.root.clone();
|
||||
tool.set_node(root, now_usec());
|
||||
}
|
||||
|
||||
pub fn button(
|
||||
&self,
|
||||
tool: &Rc<TabletTool>,
|
||||
time_usec: u64,
|
||||
button: u32,
|
||||
state: ToolButtonState,
|
||||
) {
|
||||
self.owner.get().button(tool, time_usec, button, state);
|
||||
}
|
||||
|
||||
pub fn apply_changes(
|
||||
&self,
|
||||
tool: &Rc<TabletTool>,
|
||||
time_usec: u64,
|
||||
changes: Option<&TabletToolChanges>,
|
||||
) {
|
||||
self.owner.get().apply_changes(tool, time_usec, changes);
|
||||
}
|
||||
}
|
||||
|
||||
trait ToolOwner {
|
||||
fn button(&self, tool: &Rc<TabletTool>, time_usec: u64, button: u32, state: ToolButtonState);
|
||||
fn apply_changes(
|
||||
&self,
|
||||
tool: &Rc<TabletTool>,
|
||||
time_usec: u64,
|
||||
changes: Option<&TabletToolChanges>,
|
||||
);
|
||||
}
|
||||
|
||||
impl TabletTool {
|
||||
fn set_node(self: &Rc<Self>, node: Rc<dyn Node>, time_usec: u64) {
|
||||
let prev = self.node.set(node.clone());
|
||||
if prev.node_id() != node.node_id() {
|
||||
prev.node_on_tablet_tool_leave(self, time_usec);
|
||||
prev.node_seat_state().remove_tablet_tool_focus(self);
|
||||
let (tool_x, tool_y) = self.cursor.position();
|
||||
let (node_x, node_y) = node.node_absolute_position().position();
|
||||
node.node_seat_state().add_tablet_tool_focus(self);
|
||||
node.node_on_tablet_tool_enter(self, time_usec, tool_x - node_x, tool_y - node_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToolOwner for DefaultToolOwner {
|
||||
fn button(&self, tool: &Rc<TabletTool>, time_usec: u64, button: u32, state: ToolButtonState) {
|
||||
if state == ToolButtonState::Released {
|
||||
return;
|
||||
}
|
||||
let owner = Rc::new(GrabToolOwner {
|
||||
buttons: Default::default(),
|
||||
node: tool.node.get(),
|
||||
});
|
||||
tool.tool_owner.owner.set(owner.clone());
|
||||
owner.button(tool, time_usec, button, state);
|
||||
}
|
||||
|
||||
fn apply_changes(
|
||||
&self,
|
||||
tool: &Rc<TabletTool>,
|
||||
time_usec: u64,
|
||||
changes: Option<&TabletToolChanges>,
|
||||
) {
|
||||
let change = handle_position_change(tool);
|
||||
let node = change.node;
|
||||
if change.changed {
|
||||
tool.set_node(node.clone(), time_usec);
|
||||
} else {
|
||||
node.clone()
|
||||
.node_on_tablet_tool_apply_changes(tool, time_usec, changes, change.x, change.y);
|
||||
}
|
||||
if tool.down.get() {
|
||||
tool.tool_owner.owner.set(Rc::new(GrabToolOwner {
|
||||
buttons: Default::default(),
|
||||
node,
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GrabToolOwner {
|
||||
fn maybe_revert(&self, tool: &Rc<TabletTool>) {
|
||||
if !tool.down.get() && self.buttons.is_empty() {
|
||||
tool.tool_owner.owner.set(tool.tool_owner.default.clone());
|
||||
tool.tablet.seat.tree_changed.trigger();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToolOwner for GrabToolOwner {
|
||||
fn button(&self, tool: &Rc<TabletTool>, time_usec: u64, button: u32, state: ToolButtonState) {
|
||||
match state {
|
||||
ToolButtonState::Released => {
|
||||
if self.buttons.remove(&button).is_none() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
ToolButtonState::Pressed => {
|
||||
if self.buttons.insert(button, ()).is_some() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.node
|
||||
.node_on_tablet_tool_button(tool, time_usec, button, state);
|
||||
self.maybe_revert(tool);
|
||||
}
|
||||
|
||||
fn apply_changes(
|
||||
&self,
|
||||
tool: &Rc<TabletTool>,
|
||||
time_usec: u64,
|
||||
changes: Option<&TabletToolChanges>,
|
||||
) {
|
||||
let (x, y) = tool.cursor.position();
|
||||
let node_pos = self.node.node_absolute_position();
|
||||
self.node.clone().node_on_tablet_tool_apply_changes(
|
||||
tool,
|
||||
time_usec,
|
||||
changes,
|
||||
x - node_pos.x1(),
|
||||
y - node_pos.y1(),
|
||||
);
|
||||
self.maybe_revert(tool);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_position_change(tool: &Rc<TabletTool>) -> UpdatedNode {
|
||||
let (x, y) = tool.cursor.position();
|
||||
let x_int = x.round_down();
|
||||
let y_int = y.round_down();
|
||||
let tree = &mut *tool.tablet.tree.borrow_mut();
|
||||
tree.push(FoundNode {
|
||||
node: tool.tablet.seat.state.root.clone(),
|
||||
x: x_int,
|
||||
y: y_int,
|
||||
});
|
||||
tool.tablet
|
||||
.seat
|
||||
.state
|
||||
.root
|
||||
.node_find_tree_at(x_int, y_int, tree, FindTreeUsecase::None);
|
||||
let mut update = UpdatedNode {
|
||||
node: tool.node.get(),
|
||||
x,
|
||||
y,
|
||||
changed: false,
|
||||
};
|
||||
if let Some(last) = tree.last() {
|
||||
if last.node.node_id() != update.node.node_id() {
|
||||
update.changed = true;
|
||||
update.node = last.node.clone();
|
||||
}
|
||||
update.x = x.apply_fract(last.x);
|
||||
update.y = y.apply_fract(last.y);
|
||||
}
|
||||
tree.clear();
|
||||
update
|
||||
}
|
||||
|
||||
struct UpdatedNode {
|
||||
node: Rc<dyn Node>,
|
||||
changed: bool,
|
||||
x: Fixed,
|
||||
y: Fixed,
|
||||
}
|
||||
104
src/ifs/wl_seat/tablet/zwp_tablet_manager_v2.rs
Normal file
104
src/ifs/wl_seat/tablet/zwp_tablet_manager_v2.rs
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
globals::{Global, GlobalName},
|
||||
ifs::wl_seat::tablet::zwp_tablet_seat_v2::ZwpTabletSeatV2,
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
wire::{zwp_tablet_manager_v2::*, ZwpTabletManagerV2Id},
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
pub struct ZwpTabletManagerV2Global {
|
||||
pub name: GlobalName,
|
||||
}
|
||||
|
||||
pub struct ZwpTabletManagerV2 {
|
||||
pub id: ZwpTabletManagerV2Id,
|
||||
pub client: Rc<Client>,
|
||||
pub tracker: Tracker<Self>,
|
||||
pub version: Version,
|
||||
}
|
||||
|
||||
impl ZwpTabletManagerV2Global {
|
||||
pub fn new(name: GlobalName) -> Self {
|
||||
Self { name }
|
||||
}
|
||||
|
||||
fn bind_(
|
||||
self: Rc<Self>,
|
||||
id: ZwpTabletManagerV2Id,
|
||||
client: &Rc<Client>,
|
||||
version: Version,
|
||||
) -> Result<(), ZwpTabletManagerV2Error> {
|
||||
let obj = Rc::new(ZwpTabletManagerV2 {
|
||||
id,
|
||||
client: client.clone(),
|
||||
tracker: Default::default(),
|
||||
version,
|
||||
});
|
||||
track!(client, obj);
|
||||
client.add_client_obj(&obj)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
global_base!(
|
||||
ZwpTabletManagerV2Global,
|
||||
ZwpTabletManagerV2,
|
||||
ZwpTabletManagerV2Error
|
||||
);
|
||||
|
||||
impl Global for ZwpTabletManagerV2Global {
|
||||
fn singleton(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn version(&self) -> u32 {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
simple_add_global!(ZwpTabletManagerV2Global);
|
||||
|
||||
impl ZwpTabletManagerV2RequestHandler for ZwpTabletManagerV2 {
|
||||
type Error = ZwpTabletManagerV2Error;
|
||||
|
||||
fn get_tablet_seat(&self, req: GetTabletSeat, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
let seat = self.client.lookup(req.seat)?.global.clone();
|
||||
let obj = Rc::new(ZwpTabletSeatV2 {
|
||||
id: req.tablet_seat,
|
||||
client: self.client.clone(),
|
||||
seat: seat.clone(),
|
||||
tracker: Default::default(),
|
||||
version: self.version,
|
||||
});
|
||||
track!(self.client, obj);
|
||||
self.client.add_client_obj(&obj)?;
|
||||
seat.tablet_add_seat(&obj);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = ZwpTabletManagerV2;
|
||||
version = self.version;
|
||||
}
|
||||
|
||||
impl Object for ZwpTabletManagerV2 {}
|
||||
|
||||
simple_add_obj!(ZwpTabletManagerV2);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ZwpTabletManagerV2Error {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
}
|
||||
efrom!(ZwpTabletManagerV2Error, ClientError);
|
||||
101
src/ifs/wl_seat/tablet/zwp_tablet_pad_group_v2.rs
Normal file
101
src/ifs/wl_seat/tablet/zwp_tablet_pad_group_v2.rs
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
ifs::wl_seat::tablet::{
|
||||
zwp_tablet_pad_ring_v2::ZwpTabletPadRingV2,
|
||||
zwp_tablet_pad_strip_v2::ZwpTabletPadStripV2, zwp_tablet_seat_v2::ZwpTabletSeatV2,
|
||||
TabletPadGroup,
|
||||
},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
wire::{zwp_tablet_pad_group_v2::*, ZwpTabletPadGroupV2Id},
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
pub struct ZwpTabletPadGroupV2 {
|
||||
pub id: ZwpTabletPadGroupV2Id,
|
||||
pub client: Rc<Client>,
|
||||
pub tracker: Tracker<Self>,
|
||||
pub version: Version,
|
||||
pub seat: Rc<ZwpTabletSeatV2>,
|
||||
pub group: Rc<TabletPadGroup>,
|
||||
}
|
||||
|
||||
impl ZwpTabletPadGroupV2 {
|
||||
pub fn detach(&self) {
|
||||
self.group.bindings.remove(&self.seat);
|
||||
}
|
||||
|
||||
pub fn send_buttons(&self, buttons: &[u32]) {
|
||||
self.client.event(Buttons {
|
||||
self_id: self.id,
|
||||
buttons,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_ring(&self, ring: &ZwpTabletPadRingV2) {
|
||||
self.client.event(Ring {
|
||||
self_id: self.id,
|
||||
ring: ring.id,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_strip(&self, strip: &ZwpTabletPadStripV2) {
|
||||
self.client.event(Strip {
|
||||
self_id: self.id,
|
||||
strip: strip.id,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_modes(&self, modes: u32) {
|
||||
self.client.event(Modes {
|
||||
self_id: self.id,
|
||||
modes,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_done(&self) {
|
||||
self.client.event(Done { self_id: self.id });
|
||||
}
|
||||
|
||||
pub fn send_mode_switch(&self, time: u32, serial: u32, mode: u32) {
|
||||
self.client.event(ModeSwitch {
|
||||
self_id: self.id,
|
||||
time,
|
||||
serial,
|
||||
mode,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl ZwpTabletPadGroupV2RequestHandler for ZwpTabletPadGroupV2 {
|
||||
type Error = ZwpTabletPadGroupV2Error;
|
||||
|
||||
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.detach();
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = ZwpTabletPadGroupV2;
|
||||
version = self.version;
|
||||
}
|
||||
|
||||
impl Object for ZwpTabletPadGroupV2 {
|
||||
fn break_loops(&self) {
|
||||
self.detach();
|
||||
}
|
||||
}
|
||||
|
||||
simple_add_obj!(ZwpTabletPadGroupV2);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ZwpTabletPadGroupV2Error {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
}
|
||||
efrom!(ZwpTabletPadGroupV2Error, ClientError);
|
||||
90
src/ifs/wl_seat/tablet/zwp_tablet_pad_ring_v2.rs
Normal file
90
src/ifs/wl_seat/tablet/zwp_tablet_pad_ring_v2.rs
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
fixed::Fixed,
|
||||
ifs::wl_seat::tablet::{
|
||||
zwp_tablet_seat_v2::ZwpTabletSeatV2, TabletPadRing, TabletRingEventSource,
|
||||
},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
wire::{zwp_tablet_pad_ring_v2::*, ZwpTabletPadRingV2Id},
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
pub struct ZwpTabletPadRingV2 {
|
||||
pub id: ZwpTabletPadRingV2Id,
|
||||
pub client: Rc<Client>,
|
||||
pub tracker: Tracker<Self>,
|
||||
pub version: Version,
|
||||
pub seat: Rc<ZwpTabletSeatV2>,
|
||||
pub ring: Rc<TabletPadRing>,
|
||||
}
|
||||
|
||||
impl ZwpTabletPadRingV2 {
|
||||
pub fn detach(&self) {
|
||||
self.ring.bindings.remove(&self.seat);
|
||||
}
|
||||
|
||||
pub fn send_source(&self, source: TabletRingEventSource) {
|
||||
self.client.event(Source {
|
||||
self_id: self.id,
|
||||
source: match source {
|
||||
TabletRingEventSource::Finger => 1,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_angle(&self, degrees: Fixed) {
|
||||
self.client.event(Angle {
|
||||
self_id: self.id,
|
||||
degrees,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_stop(&self) {
|
||||
self.client.event(Stop { self_id: self.id });
|
||||
}
|
||||
|
||||
pub fn send_frame(&self, time: u32) {
|
||||
self.client.event(Frame {
|
||||
self_id: self.id,
|
||||
time,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl ZwpTabletPadRingV2RequestHandler for ZwpTabletPadRingV2 {
|
||||
type Error = ZwpTabletPadRingV2Error;
|
||||
|
||||
fn set_feedback(&self, _req: SetFeedback<'_>, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.detach();
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = ZwpTabletPadRingV2;
|
||||
version = self.version;
|
||||
}
|
||||
|
||||
impl Object for ZwpTabletPadRingV2 {
|
||||
fn break_loops(&self) {
|
||||
self.detach();
|
||||
}
|
||||
}
|
||||
|
||||
simple_add_obj!(ZwpTabletPadRingV2);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ZwpTabletPadRingV2Error {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
}
|
||||
efrom!(ZwpTabletPadRingV2Error, ClientError);
|
||||
89
src/ifs/wl_seat/tablet/zwp_tablet_pad_strip_v2.rs
Normal file
89
src/ifs/wl_seat/tablet/zwp_tablet_pad_strip_v2.rs
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
ifs::wl_seat::tablet::{
|
||||
zwp_tablet_seat_v2::ZwpTabletSeatV2, TabletPadStrip, TabletStripEventSource,
|
||||
},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
wire::{zwp_tablet_pad_strip_v2::*, ZwpTabletPadStripV2Id},
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
pub struct ZwpTabletPadStripV2 {
|
||||
pub id: ZwpTabletPadStripV2Id,
|
||||
pub client: Rc<Client>,
|
||||
pub tracker: Tracker<Self>,
|
||||
pub version: Version,
|
||||
pub seat: Rc<ZwpTabletSeatV2>,
|
||||
pub strip: Rc<TabletPadStrip>,
|
||||
}
|
||||
|
||||
impl ZwpTabletPadStripV2 {
|
||||
pub fn detach(&self) {
|
||||
self.strip.bindings.remove(&self.seat);
|
||||
}
|
||||
|
||||
pub fn send_source(&self, source: TabletStripEventSource) {
|
||||
self.client.event(Source {
|
||||
self_id: self.id,
|
||||
source: match source {
|
||||
TabletStripEventSource::Finger => 1,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_position(&self, position: u32) {
|
||||
self.client.event(Position {
|
||||
self_id: self.id,
|
||||
position,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_stop(&self) {
|
||||
self.client.event(Stop { self_id: self.id });
|
||||
}
|
||||
|
||||
pub fn send_frame(&self, time: u32) {
|
||||
self.client.event(Frame {
|
||||
self_id: self.id,
|
||||
time,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl ZwpTabletPadStripV2RequestHandler for ZwpTabletPadStripV2 {
|
||||
type Error = ZwpTabletPadStripV2Error;
|
||||
|
||||
fn set_feedback(&self, _req: SetFeedback<'_>, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.detach();
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = ZwpTabletPadStripV2;
|
||||
version = self.version;
|
||||
}
|
||||
|
||||
impl Object for ZwpTabletPadStripV2 {
|
||||
fn break_loops(&self) {
|
||||
self.detach();
|
||||
}
|
||||
}
|
||||
|
||||
simple_add_obj!(ZwpTabletPadStripV2);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ZwpTabletPadStripV2Error {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
}
|
||||
efrom!(ZwpTabletPadStripV2Error, ClientError);
|
||||
127
src/ifs/wl_seat/tablet/zwp_tablet_pad_v2.rs
Normal file
127
src/ifs/wl_seat/tablet/zwp_tablet_pad_v2.rs
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
ifs::{
|
||||
wl_seat::tablet::{
|
||||
zwp_tablet_pad_group_v2::ZwpTabletPadGroupV2, zwp_tablet_seat_v2::ZwpTabletSeatV2,
|
||||
zwp_tablet_v2::ZwpTabletV2, PadButtonState, TabletPad,
|
||||
},
|
||||
wl_surface::WlSurface,
|
||||
},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
wire::{zwp_tablet_pad_v2::*, ZwpTabletPadV2Id},
|
||||
},
|
||||
std::{cell::Cell, rc::Rc},
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
pub struct ZwpTabletPadV2 {
|
||||
pub id: ZwpTabletPadV2Id,
|
||||
pub client: Rc<Client>,
|
||||
pub tracker: Tracker<Self>,
|
||||
pub version: Version,
|
||||
pub seat: Rc<ZwpTabletSeatV2>,
|
||||
pub pad: Rc<TabletPad>,
|
||||
pub entered: Cell<bool>,
|
||||
}
|
||||
|
||||
impl ZwpTabletPadV2 {
|
||||
pub fn detach(&self) {
|
||||
self.pad.bindings.remove(&self.seat);
|
||||
}
|
||||
|
||||
pub fn send_group(&self, group: &ZwpTabletPadGroupV2) {
|
||||
self.client.event(Group {
|
||||
self_id: self.id,
|
||||
pad_group: group.id,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_path(&self, path: &str) {
|
||||
self.client.event(Path {
|
||||
self_id: self.id,
|
||||
path,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_buttons(&self, buttons: u32) {
|
||||
self.client.event(Buttons {
|
||||
self_id: self.id,
|
||||
buttons,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_done(&self) {
|
||||
self.client.event(Done { self_id: self.id });
|
||||
}
|
||||
|
||||
pub fn send_button(&self, time: u32, button: u32, state: PadButtonState) {
|
||||
self.client.event(Button {
|
||||
self_id: self.id,
|
||||
time,
|
||||
button,
|
||||
state: match state {
|
||||
PadButtonState::Released => 0,
|
||||
PadButtonState::Pressed => 1,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_enter(&self, serial: u32, tablet: &ZwpTabletV2, surface: &WlSurface) {
|
||||
self.entered.set(true);
|
||||
self.client.event(Enter {
|
||||
self_id: self.id,
|
||||
serial,
|
||||
tablet: tablet.id,
|
||||
surface: surface.id,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_leave(&self, serial: u32, surface: &WlSurface) {
|
||||
self.entered.set(false);
|
||||
self.client.event(Leave {
|
||||
self_id: self.id,
|
||||
serial,
|
||||
surface: surface.id,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_removed(&self) {
|
||||
self.client.event(Removed { self_id: self.id });
|
||||
}
|
||||
}
|
||||
|
||||
impl ZwpTabletPadV2RequestHandler for ZwpTabletPadV2 {
|
||||
type Error = ZwpTabletPadV2Error;
|
||||
|
||||
fn set_feedback(&self, _req: SetFeedback<'_>, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.detach();
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = ZwpTabletPadV2;
|
||||
version = self.version;
|
||||
}
|
||||
|
||||
impl Object for ZwpTabletPadV2 {
|
||||
fn break_loops(&self) {
|
||||
self.detach();
|
||||
}
|
||||
}
|
||||
|
||||
simple_add_obj!(ZwpTabletPadV2);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ZwpTabletPadV2Error {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
}
|
||||
efrom!(ZwpTabletPadV2Error, ClientError);
|
||||
221
src/ifs/wl_seat/tablet/zwp_tablet_seat_v2.rs
Normal file
221
src/ifs/wl_seat/tablet/zwp_tablet_seat_v2.rs
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
ifs::wl_seat::{
|
||||
tablet::{
|
||||
zwp_tablet_pad_group_v2::ZwpTabletPadGroupV2,
|
||||
zwp_tablet_pad_ring_v2::ZwpTabletPadRingV2,
|
||||
zwp_tablet_pad_strip_v2::ZwpTabletPadStripV2, zwp_tablet_pad_v2::ZwpTabletPadV2,
|
||||
zwp_tablet_tool_v2::ZwpTabletToolV2, zwp_tablet_v2::ZwpTabletV2, Tablet, TabletPad,
|
||||
TabletTool,
|
||||
},
|
||||
WlSeatGlobal,
|
||||
},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
wire::{zwp_tablet_seat_v2::*, ZwpTabletSeatV2Id},
|
||||
},
|
||||
std::{cell::Cell, rc::Rc},
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
pub struct ZwpTabletSeatV2 {
|
||||
pub id: ZwpTabletSeatV2Id,
|
||||
pub client: Rc<Client>,
|
||||
pub seat: Rc<WlSeatGlobal>,
|
||||
pub tracker: Tracker<Self>,
|
||||
pub version: Version,
|
||||
}
|
||||
|
||||
impl ZwpTabletSeatV2 {
|
||||
pub fn detach(&self) {
|
||||
self.seat.tablet.seats.remove(&self.client, self);
|
||||
}
|
||||
|
||||
pub fn announce_tablet(self: &Rc<Self>, tablet: &Rc<Tablet>) {
|
||||
let id = match self.client.new_id() {
|
||||
Ok(id) => id,
|
||||
Err(e) => {
|
||||
self.client.error(e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let obj = Rc::new(ZwpTabletV2 {
|
||||
id,
|
||||
client: self.client.clone(),
|
||||
seat: self.clone(),
|
||||
tracker: Default::default(),
|
||||
version: self.version,
|
||||
tablet: tablet.clone(),
|
||||
});
|
||||
track!(self.client, obj);
|
||||
self.client.add_server_obj(&obj);
|
||||
self.send_tablet_added(&obj);
|
||||
obj.send_name(&tablet.name);
|
||||
obj.send_id(tablet.vid, tablet.pid);
|
||||
obj.send_path(&tablet.path);
|
||||
obj.send_done();
|
||||
tablet.bindings.add(self, &obj);
|
||||
}
|
||||
|
||||
pub fn announce_tool(self: &Rc<Self>, tool: &Rc<TabletTool>) {
|
||||
let id = match self.client.new_id() {
|
||||
Ok(id) => id,
|
||||
Err(e) => {
|
||||
self.client.error(e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let obj = Rc::new(ZwpTabletToolV2 {
|
||||
id,
|
||||
client: self.client.clone(),
|
||||
seat: self.clone(),
|
||||
tool: tool.opt.clone(),
|
||||
tracker: Default::default(),
|
||||
version: self.version,
|
||||
entered: Cell::new(false),
|
||||
});
|
||||
track!(self.client, obj);
|
||||
self.client.add_server_obj(&obj);
|
||||
self.send_tool_added(&obj);
|
||||
obj.send_type(tool.type_);
|
||||
obj.send_hardware_serial(tool.hardware_serial);
|
||||
obj.send_hardware_id_wacom(tool.hardware_id_wacom);
|
||||
for cap in &tool.capabilities {
|
||||
obj.send_capability(*cap);
|
||||
}
|
||||
obj.send_done();
|
||||
tool.bindings.add(self, &obj);
|
||||
}
|
||||
|
||||
pub fn announce_pad(self: &Rc<Self>, pad: &Rc<TabletPad>) {
|
||||
macro_rules! id {
|
||||
() => {
|
||||
match self.client.new_id() {
|
||||
Ok(id) => id,
|
||||
Err(e) => {
|
||||
self.client.error(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
let obj = Rc::new(ZwpTabletPadV2 {
|
||||
id: id!(),
|
||||
client: self.client.clone(),
|
||||
seat: self.clone(),
|
||||
tracker: Default::default(),
|
||||
version: self.version,
|
||||
pad: pad.clone(),
|
||||
entered: Cell::new(false),
|
||||
});
|
||||
track!(self.client, obj);
|
||||
self.client.add_server_obj(&obj);
|
||||
self.send_pad_added(&obj);
|
||||
obj.send_path(&pad.path);
|
||||
obj.send_buttons(pad.buttons);
|
||||
for group in &pad.groups {
|
||||
let group_obj = Rc::new(ZwpTabletPadGroupV2 {
|
||||
id: id!(),
|
||||
client: self.client.clone(),
|
||||
seat: self.clone(),
|
||||
tracker: Default::default(),
|
||||
version: self.version,
|
||||
group: group.clone(),
|
||||
});
|
||||
track!(self.client, group_obj);
|
||||
self.client.add_server_obj(&group_obj);
|
||||
obj.send_group(&group_obj);
|
||||
group_obj.send_buttons(&group.buttons);
|
||||
group_obj.send_modes(group.modes);
|
||||
for ring in &group.rings {
|
||||
let Some(ring) = pad.rings.get(*ring as usize) else {
|
||||
continue;
|
||||
};
|
||||
let ring_obj = Rc::new(ZwpTabletPadRingV2 {
|
||||
id: id!(),
|
||||
client: self.client.clone(),
|
||||
seat: self.clone(),
|
||||
tracker: Default::default(),
|
||||
version: self.version,
|
||||
ring: ring.clone(),
|
||||
});
|
||||
track!(self.client, ring_obj);
|
||||
self.client.add_server_obj(&ring_obj);
|
||||
group_obj.send_ring(&ring_obj);
|
||||
ring.bindings.add(self, &ring_obj);
|
||||
}
|
||||
for strip in &group.strips {
|
||||
let Some(strip) = pad.strips.get(*strip as usize) else {
|
||||
continue;
|
||||
};
|
||||
let strip_obj = Rc::new(ZwpTabletPadStripV2 {
|
||||
id: id!(),
|
||||
client: self.client.clone(),
|
||||
seat: self.clone(),
|
||||
tracker: Default::default(),
|
||||
version: self.version,
|
||||
strip: strip.clone(),
|
||||
});
|
||||
track!(self.client, strip_obj);
|
||||
self.client.add_server_obj(&strip_obj);
|
||||
group_obj.send_strip(&strip_obj);
|
||||
strip.bindings.add(self, &strip_obj);
|
||||
}
|
||||
group_obj.send_done();
|
||||
}
|
||||
obj.send_done();
|
||||
pad.bindings.add(self, &obj);
|
||||
}
|
||||
|
||||
fn send_tablet_added(&self, tablet: &ZwpTabletV2) {
|
||||
self.client.event(TabletAdded {
|
||||
self_id: self.id,
|
||||
id: tablet.id,
|
||||
});
|
||||
}
|
||||
|
||||
fn send_tool_added(&self, tool: &ZwpTabletToolV2) {
|
||||
self.client.event(ToolAdded {
|
||||
self_id: self.id,
|
||||
id: tool.id,
|
||||
});
|
||||
}
|
||||
|
||||
fn send_pad_added(&self, pad: &ZwpTabletPadV2) {
|
||||
self.client.event(PadAdded {
|
||||
self_id: self.id,
|
||||
id: pad.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl ZwpTabletSeatV2RequestHandler for ZwpTabletSeatV2 {
|
||||
type Error = ZwpTabletSeatV2Error;
|
||||
|
||||
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.detach();
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = ZwpTabletSeatV2;
|
||||
version = self.version;
|
||||
}
|
||||
|
||||
impl Object for ZwpTabletSeatV2 {
|
||||
fn break_loops(&self) {
|
||||
self.detach();
|
||||
}
|
||||
}
|
||||
|
||||
simple_add_obj!(ZwpTabletSeatV2);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ZwpTabletSeatV2Error {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
}
|
||||
efrom!(ZwpTabletSeatV2Error, ClientError);
|
||||
252
src/ifs/wl_seat/tablet/zwp_tablet_tool_v2.rs
Normal file
252
src/ifs/wl_seat/tablet/zwp_tablet_tool_v2.rs
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
cursor::Cursor,
|
||||
fixed::Fixed,
|
||||
ifs::{
|
||||
wl_seat::tablet::{
|
||||
zwp_tablet_seat_v2::ZwpTabletSeatV2, zwp_tablet_v2::ZwpTabletV2,
|
||||
TabletToolCapability, TabletToolOpt, TabletToolType, ToolButtonState,
|
||||
},
|
||||
wl_surface::{WlSurface, WlSurfaceError},
|
||||
},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
wire::{zwp_tablet_tool_v2::*, ZwpTabletToolV2Id},
|
||||
},
|
||||
std::{cell::Cell, rc::Rc},
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
pub struct ZwpTabletToolV2 {
|
||||
pub id: ZwpTabletToolV2Id,
|
||||
pub client: Rc<Client>,
|
||||
pub tracker: Tracker<Self>,
|
||||
pub version: Version,
|
||||
pub seat: Rc<ZwpTabletSeatV2>,
|
||||
pub tool: Rc<TabletToolOpt>,
|
||||
pub entered: Cell<bool>,
|
||||
}
|
||||
|
||||
pub const BTN_TOOL_PEN: u32 = 0x140;
|
||||
pub const BTN_TOOL_RUBBER: u32 = 0x141;
|
||||
pub const BTN_TOOL_BRUSH: u32 = 0x142;
|
||||
pub const BTN_TOOL_PENCIL: u32 = 0x143;
|
||||
pub const BTN_TOOL_AIRBRUSH: u32 = 0x144;
|
||||
pub const BTN_TOOL_FINGER: u32 = 0x145;
|
||||
pub const BTN_TOOL_MOUSE: u32 = 0x146;
|
||||
pub const BTN_TOOL_LENS: u32 = 0x147;
|
||||
|
||||
impl ZwpTabletToolV2 {
|
||||
pub fn detach(&self) {
|
||||
if let Some(tool) = self.tool.get() {
|
||||
tool.bindings.remove(&self.seat);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_type(&self, tool_type: TabletToolType) {
|
||||
self.client.event(Type {
|
||||
self_id: self.id,
|
||||
tool_type: match tool_type {
|
||||
TabletToolType::Pen => BTN_TOOL_PEN,
|
||||
TabletToolType::Eraser => BTN_TOOL_RUBBER,
|
||||
TabletToolType::Brush => BTN_TOOL_BRUSH,
|
||||
TabletToolType::Pencil => BTN_TOOL_PENCIL,
|
||||
TabletToolType::Airbrush => BTN_TOOL_AIRBRUSH,
|
||||
TabletToolType::Finger => BTN_TOOL_FINGER,
|
||||
TabletToolType::Mouse => BTN_TOOL_MOUSE,
|
||||
TabletToolType::Lens => BTN_TOOL_LENS,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_hardware_serial(&self, serial: u64) {
|
||||
self.client.event(HardwareSerial {
|
||||
self_id: self.id,
|
||||
hardware_serial_hi: (serial >> 32) as _,
|
||||
hardware_serial_lo: serial as _,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_hardware_id_wacom(&self, id: u64) {
|
||||
self.client.event(HardwareIdWacom {
|
||||
self_id: self.id,
|
||||
hardware_id_hi: (id >> 32) as _,
|
||||
hardware_id_lo: id as _,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_capability(&self, capability: TabletToolCapability) {
|
||||
self.client.event(Capability {
|
||||
self_id: self.id,
|
||||
capability: match capability {
|
||||
TabletToolCapability::Tilt => 1,
|
||||
TabletToolCapability::Pressure => 2,
|
||||
TabletToolCapability::Distance => 3,
|
||||
TabletToolCapability::Rotation => 4,
|
||||
TabletToolCapability::Slider => 5,
|
||||
TabletToolCapability::Wheel => 6,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_done(&self) {
|
||||
self.client.event(Done { self_id: self.id });
|
||||
}
|
||||
|
||||
pub fn send_removed(&self) {
|
||||
self.client.event(Removed { self_id: self.id });
|
||||
}
|
||||
|
||||
pub fn send_proximity_in(&self, serial: u32, tablet: &ZwpTabletV2, surface: &WlSurface) {
|
||||
self.entered.set(true);
|
||||
self.client.event(ProximityIn {
|
||||
self_id: self.id,
|
||||
serial,
|
||||
tablet: tablet.id,
|
||||
surface: surface.id,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_proximity_out(&self) {
|
||||
self.entered.set(false);
|
||||
self.client.event(ProximityOut { self_id: self.id });
|
||||
}
|
||||
|
||||
pub fn send_down(&self, serial: u32) {
|
||||
self.client.event(Down {
|
||||
self_id: self.id,
|
||||
serial,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_up(&self) {
|
||||
self.client.event(Up { self_id: self.id });
|
||||
}
|
||||
|
||||
pub fn send_motion(&self, x: Fixed, y: Fixed) {
|
||||
self.client.event(Motion {
|
||||
self_id: self.id,
|
||||
x,
|
||||
y,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_pressure(&self, pressure: u32) {
|
||||
self.client.event(Pressure {
|
||||
self_id: self.id,
|
||||
pressure,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_distance(&self, distance: u32) {
|
||||
self.client.event(Distance {
|
||||
self_id: self.id,
|
||||
distance,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_tilt(&self, tilt_x: Fixed, tilt_y: Fixed) {
|
||||
self.client.event(Tilt {
|
||||
self_id: self.id,
|
||||
tilt_x,
|
||||
tilt_y,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_rotation(&self, degrees: Fixed) {
|
||||
self.client.event(Rotation {
|
||||
self_id: self.id,
|
||||
degrees,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_slider(&self, position: i32) {
|
||||
self.client.event(Slider {
|
||||
self_id: self.id,
|
||||
position,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_wheel(&self, degrees: Fixed, clicks: i32) {
|
||||
self.client.event(Wheel {
|
||||
self_id: self.id,
|
||||
degrees,
|
||||
clicks,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_button(&self, serial: u32, button: u32, state: ToolButtonState) {
|
||||
self.client.event(Button {
|
||||
self_id: self.id,
|
||||
serial,
|
||||
button,
|
||||
state: match state {
|
||||
ToolButtonState::Released => 0,
|
||||
ToolButtonState::Pressed => 1,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_frame(&self, time: u32) {
|
||||
self.client.event(Frame {
|
||||
self_id: self.id,
|
||||
time,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl ZwpTabletToolV2RequestHandler for ZwpTabletToolV2 {
|
||||
type Error = ZwpTabletToolV2Error;
|
||||
|
||||
fn set_cursor(&self, req: SetCursor, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
let Some(tool) = self.tool.get() else {
|
||||
return Ok(());
|
||||
};
|
||||
if !self.seat.client.valid_serial(req.serial) {
|
||||
log::warn!("Client tried to set_cursor with an invalid serial");
|
||||
return Ok(());
|
||||
}
|
||||
let mut cursor_opt = None;
|
||||
if req.surface.is_some() {
|
||||
let surface = self.seat.client.lookup(req.surface)?;
|
||||
let cursor = surface.get_cursor(&tool.cursor)?;
|
||||
cursor.set_hotspot(req.hotspot_x, req.hotspot_y);
|
||||
cursor_opt = Some(cursor as Rc<dyn Cursor>);
|
||||
}
|
||||
if tool.node.get().node_client_id() != Some(self.seat.client.id) {
|
||||
return Ok(());
|
||||
}
|
||||
tool.cursor.set(cursor_opt);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.detach();
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = ZwpTabletToolV2;
|
||||
version = self.version;
|
||||
}
|
||||
|
||||
impl Object for ZwpTabletToolV2 {
|
||||
fn break_loops(&self) {
|
||||
self.detach();
|
||||
}
|
||||
}
|
||||
|
||||
dedicated_add_obj!(ZwpTabletToolV2, ZwpTabletToolV2Id, tablet_tools);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ZwpTabletToolV2Error {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
#[error(transparent)]
|
||||
WlSurfaceError(Box<WlSurfaceError>),
|
||||
}
|
||||
efrom!(ZwpTabletToolV2Error, ClientError);
|
||||
efrom!(ZwpTabletToolV2Error, WlSurfaceError);
|
||||
86
src/ifs/wl_seat/tablet/zwp_tablet_v2.rs
Normal file
86
src/ifs/wl_seat/tablet/zwp_tablet_v2.rs
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
ifs::wl_seat::tablet::{zwp_tablet_seat_v2::ZwpTabletSeatV2, Tablet},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
wire::{zwp_tablet_v2::*, ZwpTabletV2Id},
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
pub struct ZwpTabletV2 {
|
||||
pub id: ZwpTabletV2Id,
|
||||
pub client: Rc<Client>,
|
||||
pub tracker: Tracker<Self>,
|
||||
pub version: Version,
|
||||
pub seat: Rc<ZwpTabletSeatV2>,
|
||||
pub tablet: Rc<Tablet>,
|
||||
}
|
||||
|
||||
impl ZwpTabletV2 {
|
||||
fn detach(&self) {
|
||||
self.tablet.bindings.remove(&self.seat);
|
||||
}
|
||||
|
||||
pub fn send_name(&self, name: &str) {
|
||||
self.client.event(Name {
|
||||
self_id: self.id,
|
||||
name,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_id(&self, vid: u32, pid: u32) {
|
||||
self.client.event(Id {
|
||||
self_id: self.id,
|
||||
vid,
|
||||
pid,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_path(&self, path: &str) {
|
||||
self.client.event(Path {
|
||||
self_id: self.id,
|
||||
path,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_done(&self) {
|
||||
self.client.event(Done { self_id: self.id });
|
||||
}
|
||||
|
||||
pub fn send_removed(&self) {
|
||||
self.client.event(Removed { self_id: self.id });
|
||||
}
|
||||
}
|
||||
|
||||
impl ZwpTabletV2RequestHandler for ZwpTabletV2 {
|
||||
type Error = ZwpTabletV2Error;
|
||||
|
||||
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.detach();
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = ZwpTabletV2;
|
||||
version = self.version;
|
||||
}
|
||||
|
||||
impl Object for ZwpTabletV2 {
|
||||
fn break_loops(&self) {
|
||||
self.detach();
|
||||
}
|
||||
}
|
||||
|
||||
simple_add_obj!(ZwpTabletV2);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ZwpTabletV2Error {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
}
|
||||
efrom!(ZwpTabletV2Error, ClientError);
|
||||
|
|
@ -186,7 +186,7 @@ impl WlPointerRequestHandler for WlPointer {
|
|||
let mut cursor_opt = None;
|
||||
if req.surface.is_some() {
|
||||
let surface = self.seat.client.lookup(req.surface)?;
|
||||
let cursor = surface.get_cursor(&self.seat.global)?;
|
||||
let cursor = surface.get_cursor(&self.seat.global.pointer_cursor)?;
|
||||
cursor.set_hotspot(req.hotspot_x, req.hotspot_y);
|
||||
cursor_opt = Some(cursor as Rc<dyn Cursor>);
|
||||
}
|
||||
|
|
@ -211,7 +211,7 @@ impl WlPointerRequestHandler for WlPointer {
|
|||
// );
|
||||
// return Ok(());
|
||||
// }
|
||||
self.seat.global.set_app_cursor(cursor_opt);
|
||||
self.seat.global.pointer_cursor().set(cursor_opt);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ use {
|
|||
crate::{
|
||||
backend::KeyState,
|
||||
client::{Client, ClientError},
|
||||
cursor_user::{CursorUser, CursorUserId},
|
||||
drm_feedback::DrmFeedback,
|
||||
fixed::Fixed,
|
||||
gfx_api::{AcquireSync, BufferResv, BufferResvUser, ReleaseSync, SampleRect, SyncFile},
|
||||
|
|
@ -25,9 +26,15 @@ use {
|
|||
wl_buffer::WlBuffer,
|
||||
wl_callback::WlCallback,
|
||||
wl_seat::{
|
||||
text_input::TextInputConnection, wl_pointer::PendingScroll,
|
||||
zwp_pointer_constraints_v1::SeatConstraint, Dnd, NodeSeatState, SeatId,
|
||||
WlSeatGlobal,
|
||||
tablet::{
|
||||
PadButtonState, TabletPad, TabletPadGroup, TabletPadRing, TabletPadStrip,
|
||||
TabletRingEventSource, TabletStripEventSource, TabletTool, TabletToolChanges,
|
||||
ToolButtonState,
|
||||
},
|
||||
text_input::TextInputConnection,
|
||||
wl_pointer::PendingScroll,
|
||||
zwp_pointer_constraints_v1::SeatConstraint,
|
||||
Dnd, NodeSeatState, SeatId, WlSeatGlobal,
|
||||
},
|
||||
wl_surface::{
|
||||
commit_timeline::{ClearReason, CommitTimeline, CommitTimelineError},
|
||||
|
|
@ -262,7 +269,7 @@ pub struct WlSurface {
|
|||
pub presentation_feedback: RefCell<Vec<Rc<WpPresentationFeedback>>>,
|
||||
seat_state: NodeSeatState,
|
||||
toplevel: CloneCell<Option<Rc<dyn ToplevelNode>>>,
|
||||
cursors: SmallMap<SeatId, Rc<CursorSurface>, 1>,
|
||||
cursors: SmallMap<CursorUserId, Rc<CursorSurface>, 1>,
|
||||
dnd_icons: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>,
|
||||
pub tracker: Tracker<Self>,
|
||||
idle_inhibitors: SmallMap<ZwpIdleInhibitorV1Id, Rc<ZwpIdleInhibitorV1>, 1>,
|
||||
|
|
@ -655,13 +662,13 @@ impl WlSurface {
|
|||
|
||||
pub fn get_cursor(
|
||||
self: &Rc<Self>,
|
||||
seat: &Rc<WlSeatGlobal>,
|
||||
user: &Rc<CursorUser>,
|
||||
) -> Result<Rc<CursorSurface>, WlSurfaceError> {
|
||||
if let Some(cursor) = self.cursors.get(&seat.id()) {
|
||||
if let Some(cursor) = self.cursors.get(&user.id) {
|
||||
return Ok(cursor);
|
||||
}
|
||||
self.set_role(SurfaceRole::Cursor)?;
|
||||
let cursor = Rc::new(CursorSurface::new(seat, self));
|
||||
let cursor = Rc::new(CursorSurface::new(user, self));
|
||||
track!(self.client, cursor);
|
||||
cursor.handle_buffer_change();
|
||||
Ok(cursor)
|
||||
|
|
@ -1496,14 +1503,6 @@ impl Node for WlSurface {
|
|||
dnd.seat.dnd_surface_motion(self, dnd, time_usec, x, y);
|
||||
}
|
||||
|
||||
fn node_into_surface(self: Rc<Self>) -> Option<Rc<WlSurface>> {
|
||||
Some(self.clone())
|
||||
}
|
||||
|
||||
fn node_is_xwayland_surface(&self) -> bool {
|
||||
self.client.is_xwayland
|
||||
}
|
||||
|
||||
fn node_on_swipe_begin(&self, seat: &Rc<WlSeatGlobal>, time_usec: u64, finger_count: u32) {
|
||||
seat.swipe_begin_surface(self, time_usec, finger_count)
|
||||
}
|
||||
|
|
@ -1543,6 +1542,99 @@ impl Node for WlSurface {
|
|||
fn node_on_hold_end(&self, seat: &Rc<WlSeatGlobal>, time_usec: u64, cancelled: bool) {
|
||||
seat.hold_end_surface(self, time_usec, cancelled)
|
||||
}
|
||||
|
||||
fn node_on_tablet_pad_enter(&self, pad: &Rc<TabletPad>) {
|
||||
pad.surface_enter(self);
|
||||
}
|
||||
|
||||
fn node_on_tablet_pad_leave(&self, pad: &Rc<TabletPad>) {
|
||||
pad.surface_leave(self);
|
||||
}
|
||||
|
||||
fn node_on_tablet_pad_button(
|
||||
&self,
|
||||
pad: &Rc<TabletPad>,
|
||||
time_usec: u64,
|
||||
button: u32,
|
||||
state: PadButtonState,
|
||||
) {
|
||||
pad.surface_button(self, time_usec, button, state);
|
||||
}
|
||||
|
||||
fn node_on_tablet_pad_mode_switch(
|
||||
&self,
|
||||
pad: &Rc<TabletPad>,
|
||||
group: &Rc<TabletPadGroup>,
|
||||
time_usec: u64,
|
||||
mode: u32,
|
||||
) {
|
||||
pad.surface_mode_switch(self, group, time_usec, mode);
|
||||
}
|
||||
|
||||
fn node_on_tablet_pad_ring(
|
||||
&self,
|
||||
pad: &Rc<TabletPad>,
|
||||
ring: &Rc<TabletPadRing>,
|
||||
source: Option<TabletRingEventSource>,
|
||||
angle: Option<f64>,
|
||||
time_usec: u64,
|
||||
) {
|
||||
pad.surface_ring(self, ring, source, angle, time_usec);
|
||||
}
|
||||
|
||||
fn node_on_tablet_pad_strip(
|
||||
&self,
|
||||
pad: &Rc<TabletPad>,
|
||||
strip: &Rc<TabletPadStrip>,
|
||||
source: Option<TabletStripEventSource>,
|
||||
position: Option<f64>,
|
||||
time_usec: u64,
|
||||
) {
|
||||
pad.surface_strip(self, strip, source, position, time_usec);
|
||||
}
|
||||
|
||||
fn node_on_tablet_tool_leave(&self, tool: &Rc<TabletTool>, time_usec: u64) {
|
||||
tool.surface_leave(self, time_usec);
|
||||
}
|
||||
|
||||
fn node_on_tablet_tool_enter(
|
||||
self: Rc<Self>,
|
||||
tool: &Rc<TabletTool>,
|
||||
time_usec: u64,
|
||||
x: Fixed,
|
||||
y: Fixed,
|
||||
) {
|
||||
tool.surface_enter(&self, time_usec, x, y);
|
||||
}
|
||||
|
||||
fn node_on_tablet_tool_button(
|
||||
&self,
|
||||
tool: &Rc<TabletTool>,
|
||||
time_usec: u64,
|
||||
button: u32,
|
||||
state: ToolButtonState,
|
||||
) {
|
||||
tool.surface_button(self, time_usec, button, state);
|
||||
}
|
||||
|
||||
fn node_on_tablet_tool_apply_changes(
|
||||
self: Rc<Self>,
|
||||
tool: &Rc<TabletTool>,
|
||||
time_usec: u64,
|
||||
changes: Option<&TabletToolChanges>,
|
||||
x: Fixed,
|
||||
y: Fixed,
|
||||
) {
|
||||
tool.surface_apply_changes(&self, time_usec, changes, x, y);
|
||||
}
|
||||
|
||||
fn node_into_surface(self: Rc<Self>) -> Option<Rc<WlSurface>> {
|
||||
Some(self.clone())
|
||||
}
|
||||
|
||||
fn node_is_xwayland_surface(&self) -> bool {
|
||||
self.client.is_xwayland
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
use {
|
||||
crate::{
|
||||
cursor::Cursor,
|
||||
cursor_user::CursorUser,
|
||||
fixed::Fixed,
|
||||
ifs::{wl_seat::WlSeatGlobal, wl_surface::WlSurface},
|
||||
ifs::wl_surface::WlSurface,
|
||||
leaks::Tracker,
|
||||
rect::Rect,
|
||||
renderer::Renderer,
|
||||
|
|
@ -13,7 +14,7 @@ use {
|
|||
};
|
||||
|
||||
pub struct CursorSurface {
|
||||
seat: Rc<WlSeatGlobal>,
|
||||
user: Rc<CursorUser>,
|
||||
surface: Rc<WlSurface>,
|
||||
hotspot: Cell<(i32, i32)>,
|
||||
extents: Cell<Rect>,
|
||||
|
|
@ -21,9 +22,9 @@ pub struct CursorSurface {
|
|||
}
|
||||
|
||||
impl CursorSurface {
|
||||
pub fn new(seat: &Rc<WlSeatGlobal>, surface: &Rc<WlSurface>) -> Self {
|
||||
pub fn new(user: &Rc<CursorUser>, surface: &Rc<WlSurface>) -> Self {
|
||||
Self {
|
||||
seat: seat.clone(),
|
||||
user: user.clone(),
|
||||
surface: surface.clone(),
|
||||
hotspot: Cell::new((0, 0)),
|
||||
extents: Cell::new(Default::default()),
|
||||
|
|
@ -38,7 +39,7 @@ impl CursorSurface {
|
|||
}
|
||||
|
||||
pub fn handle_surface_destroy(&self) {
|
||||
self.seat.set_app_cursor(None);
|
||||
self.user.set(None);
|
||||
}
|
||||
|
||||
pub fn handle_buffer_change(&self) {
|
||||
|
|
@ -57,9 +58,7 @@ impl CursorSurface {
|
|||
}
|
||||
|
||||
pub fn update_hardware_cursor(&self) {
|
||||
if self.seat.hardware_cursor() {
|
||||
self.seat.update_hardware_cursor();
|
||||
}
|
||||
self.user.update_hardware_cursor();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -124,7 +123,7 @@ impl Cursor for CursorSurface {
|
|||
}
|
||||
|
||||
fn handle_set(self: Rc<Self>) {
|
||||
self.surface.cursors.insert(self.seat.id(), self.clone());
|
||||
self.surface.cursors.insert(self.user.id, self.clone());
|
||||
if self.surface.cursors.is_not_empty() {
|
||||
self.surface
|
||||
.set_visible(self.surface.client.state.root_visible());
|
||||
|
|
@ -132,7 +131,7 @@ impl Cursor for CursorSurface {
|
|||
}
|
||||
|
||||
fn handle_unset(&self) {
|
||||
self.surface.cursors.remove(&self.seat.id());
|
||||
self.surface.cursors.remove(&self.user.id);
|
||||
if self.surface.cursors.is_empty() {
|
||||
self.surface.set_visible(false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use {
|
|||
cursor::KnownCursor,
|
||||
fixed::Fixed,
|
||||
ifs::{
|
||||
wl_seat::{NodeSeatState, SeatId, WlSeatGlobal},
|
||||
wl_seat::{tablet::TabletTool, NodeSeatState, SeatId, WlSeatGlobal},
|
||||
wl_surface::{x_surface::XSurface, WlSurface, WlSurfaceError},
|
||||
},
|
||||
rect::Rect,
|
||||
|
|
@ -366,7 +366,17 @@ impl Node for Xwindow {
|
|||
|
||||
fn node_on_pointer_focus(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
// log::info!("wl-surface focus");
|
||||
seat.set_known_cursor(KnownCursor::Default);
|
||||
seat.pointer_cursor().set_known(KnownCursor::Default);
|
||||
}
|
||||
|
||||
fn node_on_tablet_tool_enter(
|
||||
self: Rc<Self>,
|
||||
tool: &Rc<TabletTool>,
|
||||
_time_usec: u64,
|
||||
_x: Fixed,
|
||||
_y: Fixed,
|
||||
) {
|
||||
tool.cursor().set_known(KnownCursor::Default)
|
||||
}
|
||||
|
||||
fn node_into_toplevel(self: Rc<Self>) -> Option<Rc<dyn ToplevelNode>> {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use {
|
|||
cursor::KnownCursor,
|
||||
fixed::Fixed,
|
||||
ifs::{
|
||||
wl_seat::{NodeSeatState, WlSeatGlobal},
|
||||
wl_seat::{tablet::TabletTool, NodeSeatState, WlSeatGlobal},
|
||||
wl_surface::xdg_surface::{XdgSurface, XdgSurfaceError, XdgSurfaceExt},
|
||||
xdg_positioner::{
|
||||
XdgPositioned, XdgPositioner, CA_FLIP_X, CA_FLIP_Y, CA_RESIZE_X, CA_RESIZE_Y,
|
||||
|
|
@ -344,7 +344,17 @@ impl Node for XdgPopup {
|
|||
|
||||
fn node_on_pointer_focus(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
// log::info!("xdg-popup focus");
|
||||
seat.set_known_cursor(KnownCursor::Default);
|
||||
seat.pointer_cursor().set_known(KnownCursor::Default);
|
||||
}
|
||||
|
||||
fn node_on_tablet_tool_enter(
|
||||
self: Rc<Self>,
|
||||
tool: &Rc<TabletTool>,
|
||||
_time_usec: u64,
|
||||
_x: Fixed,
|
||||
_y: Fixed,
|
||||
) {
|
||||
tool.cursor().set_known(KnownCursor::Default)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use {
|
|||
fixed::Fixed,
|
||||
ifs::{
|
||||
ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1,
|
||||
wl_seat::{NodeSeatState, SeatId, WlSeatGlobal},
|
||||
wl_seat::{tablet::TabletTool, NodeSeatState, SeatId, WlSeatGlobal},
|
||||
wl_surface::{
|
||||
xdg_surface::{
|
||||
xdg_toplevel::xdg_dialog_v1::XdgDialogV1, XdgSurface, XdgSurfaceError,
|
||||
|
|
@ -260,13 +260,7 @@ impl XdgToplevelRequestHandler for XdgToplevel {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn move_(&self, req: Move, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
let seat = self.xdg.surface.client.lookup(req.seat)?;
|
||||
if let Some(parent) = self.toplevel_data.parent.get() {
|
||||
if let Some(float) = parent.node_into_float() {
|
||||
seat.move_(&float);
|
||||
}
|
||||
}
|
||||
fn move_(&self, _req: Move, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -532,7 +526,17 @@ impl Node for XdgToplevel {
|
|||
|
||||
fn node_on_pointer_focus(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
// log::info!("xdg-toplevel focus");
|
||||
seat.set_known_cursor(KnownCursor::Default);
|
||||
seat.pointer_cursor().set_known(KnownCursor::Default);
|
||||
}
|
||||
|
||||
fn node_on_tablet_tool_enter(
|
||||
self: Rc<Self>,
|
||||
tool: &Rc<TabletTool>,
|
||||
_time_usec: u64,
|
||||
_x: Fixed,
|
||||
_y: Fixed,
|
||||
) {
|
||||
tool.cursor().set_known(KnownCursor::Default)
|
||||
}
|
||||
|
||||
fn node_into_toplevel(self: Rc<Self>) -> Option<Rc<dyn ToplevelNode>> {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use {
|
|||
crate::{
|
||||
client::{Client, ClientError},
|
||||
cursor::KnownCursor,
|
||||
ifs::wl_seat::WlSeatGlobal,
|
||||
ifs::wl_seat::{tablet::TabletToolOpt, WlSeatGlobal},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
wire::{wp_cursor_shape_device_v1::*, WpCursorShapeDeviceV1Id},
|
||||
|
|
@ -46,10 +46,15 @@ const ALL_SCROLL: u32 = 32;
|
|||
const ZOOM_IN: u32 = 33;
|
||||
const ZOOM_OUT: u32 = 34;
|
||||
|
||||
pub enum CursorShapeCursorUser {
|
||||
Seat(Rc<WlSeatGlobal>),
|
||||
TabletTool(Rc<TabletToolOpt>),
|
||||
}
|
||||
|
||||
pub struct WpCursorShapeDeviceV1 {
|
||||
pub id: WpCursorShapeDeviceV1Id,
|
||||
pub client: Rc<Client>,
|
||||
pub seat: Rc<WlSeatGlobal>,
|
||||
pub cursor_user: CursorShapeCursorUser,
|
||||
pub tracker: Tracker<Self>,
|
||||
pub version: Version,
|
||||
}
|
||||
|
|
@ -100,14 +105,24 @@ impl WpCursorShapeDeviceV1RequestHandler for WpCursorShapeDeviceV1 {
|
|||
ZOOM_OUT => KnownCursor::ZoomOut,
|
||||
_ => return Err(WpCursorShapeDeviceV1Error::UnknownShape(req.shape)),
|
||||
};
|
||||
let pointer_node = match self.seat.pointer_node() {
|
||||
Some(n) => n,
|
||||
_ => return Ok(()),
|
||||
let tablet_tool;
|
||||
let (node_client_id, user) = match &self.cursor_user {
|
||||
CursorShapeCursorUser::Seat(s) => match s.pointer_node() {
|
||||
Some(n) => (n.node_client_id(), s.pointer_cursor()),
|
||||
_ => return Ok(()),
|
||||
},
|
||||
CursorShapeCursorUser::TabletTool(t) => match t.get() {
|
||||
Some(t) => {
|
||||
tablet_tool = t;
|
||||
(tablet_tool.node().node_client_id(), tablet_tool.cursor())
|
||||
}
|
||||
_ => return Ok(()),
|
||||
},
|
||||
};
|
||||
if pointer_node.node_client_id() != Some(self.client.id) {
|
||||
if node_client_id != Some(self.client.id) {
|
||||
return Ok(());
|
||||
}
|
||||
self.seat.set_known_cursor(cursor);
|
||||
user.set_known(cursor);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@ use {
|
|||
crate::{
|
||||
client::{Client, ClientError},
|
||||
globals::{Global, GlobalName},
|
||||
ifs::wp_cursor_shape_device_v1::WpCursorShapeDeviceV1,
|
||||
ifs::wp_cursor_shape_device_v1::{CursorShapeCursorUser, WpCursorShapeDeviceV1},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
wire::{wp_cursor_shape_manager_v1::*, WpCursorShapeManagerV1Id},
|
||||
wire::{wp_cursor_shape_manager_v1::*, WpCursorShapeDeviceV1Id, WpCursorShapeManagerV1Id},
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
|
|
@ -63,6 +63,25 @@ pub struct WpCursorShapeManagerV1 {
|
|||
pub version: Version,
|
||||
}
|
||||
|
||||
impl WpCursorShapeManagerV1 {
|
||||
fn get(
|
||||
&self,
|
||||
id: WpCursorShapeDeviceV1Id,
|
||||
cursor_user: CursorShapeCursorUser,
|
||||
) -> Result<(), WpCursorShapeManagerV1Error> {
|
||||
let device = Rc::new(WpCursorShapeDeviceV1 {
|
||||
id,
|
||||
client: self.client.clone(),
|
||||
cursor_user,
|
||||
tracker: Default::default(),
|
||||
version: self.version,
|
||||
});
|
||||
track!(self.client, device);
|
||||
self.client.add_client_obj(&device)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl WpCursorShapeManagerV1RequestHandler for WpCursorShapeManagerV1 {
|
||||
type Error = WpCursorShapeManagerV1Error;
|
||||
|
||||
|
|
@ -73,24 +92,18 @@ impl WpCursorShapeManagerV1RequestHandler for WpCursorShapeManagerV1 {
|
|||
|
||||
fn get_pointer(&self, req: GetPointer, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
let pointer = self.client.lookup(req.pointer)?;
|
||||
let device = Rc::new(WpCursorShapeDeviceV1 {
|
||||
id: req.cursor_shape_device,
|
||||
client: self.client.clone(),
|
||||
seat: pointer.seat.global.clone(),
|
||||
tracker: Default::default(),
|
||||
version: self.version,
|
||||
});
|
||||
track!(self.client, device);
|
||||
self.client.add_client_obj(&device)?;
|
||||
Ok(())
|
||||
self.get(
|
||||
req.cursor_shape_device,
|
||||
CursorShapeCursorUser::Seat(pointer.seat.global.clone()),
|
||||
)
|
||||
}
|
||||
|
||||
fn get_tablet_tool_v2(
|
||||
&self,
|
||||
_req: GetTabletToolV2,
|
||||
_slf: &Rc<Self>,
|
||||
) -> Result<(), Self::Error> {
|
||||
Err(WpCursorShapeManagerV1Error::TabletToolNotSupported)
|
||||
fn get_tablet_tool_v2(&self, req: GetTabletToolV2, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
let tool = self.client.lookup(req.tablet_tool)?;
|
||||
self.get(
|
||||
req.cursor_shape_device,
|
||||
CursorShapeCursorUser::TabletTool(tool.tool.clone()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -107,7 +120,5 @@ simple_add_obj!(WpCursorShapeManagerV1);
|
|||
pub enum WpCursorShapeManagerV1Error {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
#[error("This compositor does not support tablet tools")]
|
||||
TabletToolNotSupported,
|
||||
}
|
||||
efrom!(WpCursorShapeManagerV1Error, ClientError);
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ impl XdgToplevelDragV1 {
|
|||
if self.source.data.was_used() {
|
||||
if let Some(tl) = self.toplevel.get() {
|
||||
let output = seat.get_output();
|
||||
let (x, y) = seat.position();
|
||||
let (x, y) = seat.pointer_cursor().position();
|
||||
tl.drag.take();
|
||||
tl.after_toplevel_drag(
|
||||
&output,
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ pub struct DefaultSetup {
|
|||
|
||||
impl DefaultSetup {
|
||||
pub fn move_to(&self, x: i32, y: i32) {
|
||||
let (ox, oy) = self.seat.position();
|
||||
let (ox, oy) = self.seat.pointer_cursor().position();
|
||||
let (nx, ny) = (Fixed::from_int(x), Fixed::from_int(y));
|
||||
let (dx, dy) = (nx - ox, ny - oy);
|
||||
self.mouse.rel(dx.to_f64(), dy.to_f64())
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
|||
client.sync().await;
|
||||
|
||||
tassert_eq!(
|
||||
ds.seat.get_desired_known_cursor(),
|
||||
ds.seat.pointer_cursor().desired_known_cursor(),
|
||||
Some(KnownCursor::ContextMenu)
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -21,9 +21,19 @@ use {
|
|||
libinput_device_config_tap_get_enabled, libinput_device_config_tap_get_finger_count,
|
||||
libinput_device_config_tap_set_drag_enabled,
|
||||
libinput_device_config_tap_set_drag_lock_enabled,
|
||||
libinput_device_config_tap_set_enabled, libinput_device_get_name,
|
||||
libinput_device_get_user_data, libinput_device_has_capability,
|
||||
libinput_device_set_user_data, libinput_device_unref, libinput_path_remove_device,
|
||||
libinput_device_config_tap_set_enabled, libinput_device_get_device_group,
|
||||
libinput_device_get_id_product, libinput_device_get_id_vendor,
|
||||
libinput_device_get_name, libinput_device_get_user_data, libinput_device_group,
|
||||
libinput_device_group_get_user_data, libinput_device_group_set_user_data,
|
||||
libinput_device_has_capability, libinput_device_set_user_data,
|
||||
libinput_device_tablet_pad_get_mode_group, libinput_device_tablet_pad_get_num_buttons,
|
||||
libinput_device_tablet_pad_get_num_mode_groups,
|
||||
libinput_device_tablet_pad_get_num_rings, libinput_device_tablet_pad_get_num_strips,
|
||||
libinput_device_unref, libinput_path_remove_device, libinput_tablet_pad_mode_group,
|
||||
libinput_tablet_pad_mode_group_get_index, libinput_tablet_pad_mode_group_get_mode,
|
||||
libinput_tablet_pad_mode_group_get_num_modes,
|
||||
libinput_tablet_pad_mode_group_has_button, libinput_tablet_pad_mode_group_has_ring,
|
||||
libinput_tablet_pad_mode_group_has_strip,
|
||||
},
|
||||
LibInput,
|
||||
},
|
||||
|
|
@ -36,6 +46,16 @@ pub struct LibInputDevice<'a> {
|
|||
pub(super) _phantom: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
pub struct LibInputDeviceGroup<'a> {
|
||||
pub(super) group: *mut libinput_device_group,
|
||||
pub(super) _phantom: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
pub struct LibInputTabletPadModeGroup<'a> {
|
||||
pub(super) group: *mut libinput_tablet_pad_mode_group,
|
||||
pub(super) _phantom: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
pub struct RegisteredDevice {
|
||||
pub(super) _li: Rc<LibInput>,
|
||||
pub(super) dev: *mut libinput_device,
|
||||
|
|
@ -191,6 +211,96 @@ impl<'a> LibInputDevice<'a> {
|
|||
pub fn has_natural_scrolling(&self) -> bool {
|
||||
unsafe { libinput_device_config_scroll_has_natural_scroll(self.dev) != 0 }
|
||||
}
|
||||
|
||||
pub fn device_group(&self) -> LibInputDeviceGroup<'_> {
|
||||
LibInputDeviceGroup {
|
||||
group: unsafe { libinput_device_get_device_group(self.dev) },
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn product(&self) -> u32 {
|
||||
unsafe { libinput_device_get_id_product(self.dev) as u32 }
|
||||
}
|
||||
|
||||
pub fn vendor(&self) -> u32 {
|
||||
unsafe { libinput_device_get_id_vendor(self.dev) as u32 }
|
||||
}
|
||||
|
||||
pub fn pad_num_buttons(&self) -> u32 {
|
||||
match unsafe { libinput_device_tablet_pad_get_num_buttons(self.dev) } {
|
||||
-1 => 0,
|
||||
n => n as u32,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pad_num_rings(&self) -> u32 {
|
||||
match unsafe { libinput_device_tablet_pad_get_num_rings(self.dev) } {
|
||||
-1 => 0,
|
||||
n => n as u32,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pad_num_strips(&self) -> u32 {
|
||||
match unsafe { libinput_device_tablet_pad_get_num_strips(self.dev) } {
|
||||
-1 => 0,
|
||||
n => n as u32,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pad_num_mode_groups(&self) -> u32 {
|
||||
match unsafe { libinput_device_tablet_pad_get_num_mode_groups(self.dev) } {
|
||||
-1 => 0,
|
||||
n => n as u32,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pad_mode_group(&self, group: u32) -> Option<LibInputTabletPadModeGroup<'_>> {
|
||||
let group = unsafe { libinput_device_tablet_pad_get_mode_group(self.dev, group as _) };
|
||||
if group.is_null() {
|
||||
return None;
|
||||
}
|
||||
Some(LibInputTabletPadModeGroup {
|
||||
group,
|
||||
_phantom: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> LibInputDeviceGroup<'a> {
|
||||
pub fn user_data(&self) -> usize {
|
||||
unsafe { libinput_device_group_get_user_data(self.group) }
|
||||
}
|
||||
|
||||
pub fn set_user_data(&self, user_data: usize) {
|
||||
unsafe { libinput_device_group_set_user_data(self.group, user_data) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> LibInputTabletPadModeGroup<'a> {
|
||||
pub fn index(&self) -> u32 {
|
||||
unsafe { libinput_tablet_pad_mode_group_get_index(self.group) as u32 }
|
||||
}
|
||||
|
||||
pub fn num_modes(&self) -> u32 {
|
||||
unsafe { libinput_tablet_pad_mode_group_get_num_modes(self.group) as u32 }
|
||||
}
|
||||
|
||||
pub fn mode(&self) -> u32 {
|
||||
unsafe { libinput_tablet_pad_mode_group_get_mode(self.group) as u32 }
|
||||
}
|
||||
|
||||
pub fn has_button(&self, button: u32) -> bool {
|
||||
unsafe { libinput_tablet_pad_mode_group_has_button(self.group, button as _) != 0 }
|
||||
}
|
||||
|
||||
pub fn has_ring(&self, ring: u32) -> bool {
|
||||
unsafe { libinput_tablet_pad_mode_group_has_ring(self.group, ring as _) != 0 }
|
||||
}
|
||||
|
||||
pub fn has_strip(&self, strip: u32) -> bool {
|
||||
unsafe { libinput_tablet_pad_mode_group_has_strip(self.group, strip as _) != 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl RegisteredDevice {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
use {
|
||||
crate::libinput::{
|
||||
consts::{ButtonState, EventType, KeyState, PointerAxis, Switch, SwitchState},
|
||||
device::LibInputDevice,
|
||||
consts::{
|
||||
ButtonState, EventType, KeyState, PointerAxis, Switch, SwitchState,
|
||||
TabletPadRingAxisSource, TabletPadStripAxisSource, TabletToolProximityState,
|
||||
TabletToolTipState, TabletToolType,
|
||||
},
|
||||
device::{LibInputDevice, LibInputTabletPadModeGroup},
|
||||
sys::{
|
||||
libinput_event, libinput_event_destroy, libinput_event_gesture,
|
||||
libinput_event_gesture_get_angle_delta, libinput_event_gesture_get_cancelled,
|
||||
|
|
@ -11,6 +15,7 @@ use {
|
|||
libinput_event_gesture_get_time_usec, libinput_event_get_device,
|
||||
libinput_event_get_gesture_event, libinput_event_get_keyboard_event,
|
||||
libinput_event_get_pointer_event, libinput_event_get_switch_event,
|
||||
libinput_event_get_tablet_pad_event, libinput_event_get_tablet_tool_event,
|
||||
libinput_event_get_type, libinput_event_keyboard, libinput_event_keyboard_get_key,
|
||||
libinput_event_keyboard_get_key_state, libinput_event_keyboard_get_time_usec,
|
||||
libinput_event_pointer, libinput_event_pointer_get_button,
|
||||
|
|
@ -20,7 +25,25 @@ use {
|
|||
libinput_event_pointer_get_scroll_value_v120, libinput_event_pointer_get_time_usec,
|
||||
libinput_event_pointer_has_axis, libinput_event_switch,
|
||||
libinput_event_switch_get_switch, libinput_event_switch_get_switch_state,
|
||||
libinput_event_switch_get_time_usec,
|
||||
libinput_event_switch_get_time_usec, libinput_event_tablet_pad,
|
||||
libinput_event_tablet_pad_get_button_number,
|
||||
libinput_event_tablet_pad_get_button_state, libinput_event_tablet_pad_get_mode,
|
||||
libinput_event_tablet_pad_get_mode_group, libinput_event_tablet_pad_get_ring_number,
|
||||
libinput_event_tablet_pad_get_ring_position, libinput_event_tablet_pad_get_ring_source,
|
||||
libinput_event_tablet_pad_get_strip_number,
|
||||
libinput_event_tablet_pad_get_strip_position,
|
||||
libinput_event_tablet_pad_get_strip_source, libinput_event_tablet_pad_get_time_usec,
|
||||
libinput_event_tablet_tool, libinput_event_tablet_tool_get_button,
|
||||
libinput_event_tablet_tool_get_button_state,
|
||||
libinput_event_tablet_tool_get_proximity_state,
|
||||
libinput_event_tablet_tool_get_time_usec, libinput_event_tablet_tool_get_tip_state,
|
||||
libinput_event_tablet_tool_get_tool,
|
||||
libinput_event_tablet_tool_get_wheel_delta_discrete,
|
||||
libinput_event_tablet_tool_get_x_transformed,
|
||||
libinput_event_tablet_tool_get_y_transformed, libinput_tablet_tool,
|
||||
libinput_tablet_tool_get_serial, libinput_tablet_tool_get_tool_id,
|
||||
libinput_tablet_tool_get_type, libinput_tablet_tool_get_user_data,
|
||||
libinput_tablet_tool_set_user_data,
|
||||
},
|
||||
},
|
||||
std::marker::PhantomData,
|
||||
|
|
@ -51,6 +74,21 @@ pub struct LibInputEventSwitch<'a> {
|
|||
pub(super) _phantom: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
pub struct LibInputEventTabletTool<'a> {
|
||||
pub(super) event: *mut libinput_event_tablet_tool,
|
||||
pub(super) _phantom: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
pub struct LibInputEventTabletPad<'a> {
|
||||
pub(super) event: *mut libinput_event_tablet_pad,
|
||||
pub(super) _phantom: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
pub struct LibInputTabletTool<'a> {
|
||||
pub(super) tool: *mut libinput_tablet_tool,
|
||||
pub(super) _phantom: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl<'a> Drop for LibInputEvent<'a> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
|
|
@ -59,6 +97,22 @@ impl<'a> Drop for LibInputEvent<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
macro_rules! converter {
|
||||
($name:ident, $out:ident, $f:ident) => {
|
||||
pub fn $name(&self) -> Option<$out> {
|
||||
let res = unsafe { $f(self.event) };
|
||||
if res.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some($out {
|
||||
event: res,
|
||||
_phantom: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl<'a> LibInputEvent<'a> {
|
||||
pub fn ty(&self) -> EventType {
|
||||
unsafe { EventType(libinput_event_get_type(self.event)) }
|
||||
|
|
@ -71,53 +125,36 @@ impl<'a> LibInputEvent<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn keyboard_event(&self) -> Option<LibInputEventKeyboard> {
|
||||
let res = unsafe { libinput_event_get_keyboard_event(self.event) };
|
||||
if res.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(LibInputEventKeyboard {
|
||||
event: res,
|
||||
_phantom: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pointer_event(&self) -> Option<LibInputEventPointer> {
|
||||
let res = unsafe { libinput_event_get_pointer_event(self.event) };
|
||||
if res.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(LibInputEventPointer {
|
||||
event: res,
|
||||
_phantom: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gesture_event(&self) -> Option<LibInputEventGesture> {
|
||||
let res = unsafe { libinput_event_get_gesture_event(self.event) };
|
||||
if res.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(LibInputEventGesture {
|
||||
event: res,
|
||||
_phantom: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn switch_event(&self) -> Option<LibInputEventSwitch> {
|
||||
let res = unsafe { libinput_event_get_switch_event(self.event) };
|
||||
if res.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(LibInputEventSwitch {
|
||||
event: res,
|
||||
_phantom: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
converter!(
|
||||
keyboard_event,
|
||||
LibInputEventKeyboard,
|
||||
libinput_event_get_keyboard_event
|
||||
);
|
||||
converter!(
|
||||
pointer_event,
|
||||
LibInputEventPointer,
|
||||
libinput_event_get_pointer_event
|
||||
);
|
||||
converter!(
|
||||
gesture_event,
|
||||
LibInputEventGesture,
|
||||
libinput_event_get_gesture_event
|
||||
);
|
||||
converter!(
|
||||
switch_event,
|
||||
LibInputEventSwitch,
|
||||
libinput_event_get_switch_event
|
||||
);
|
||||
converter!(
|
||||
tablet_tool_event,
|
||||
LibInputEventTabletTool,
|
||||
libinput_event_get_tablet_tool_event
|
||||
);
|
||||
converter!(
|
||||
tablet_pad_event,
|
||||
LibInputEventTabletPad,
|
||||
libinput_event_get_tablet_pad_event
|
||||
);
|
||||
}
|
||||
|
||||
impl<'a> LibInputEventKeyboard<'a> {
|
||||
|
|
@ -228,3 +265,205 @@ impl<'a> LibInputEventSwitch<'a> {
|
|||
unsafe { SwitchState(libinput_event_switch_get_switch_state(self.event)) }
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! has_changed {
|
||||
($name:ident, $f:ident) => {
|
||||
pub fn $name(&self) -> bool {
|
||||
unsafe { crate::libinput::sys::$f(self.event) != 0 }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! get_double {
|
||||
($name:ident, $f:ident) => {
|
||||
pub fn $name(&self) -> f64 {
|
||||
unsafe { crate::libinput::sys::$f(self.event) }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! has_capability {
|
||||
($name:ident, $f:ident) => {
|
||||
pub fn $name(&self) -> bool {
|
||||
unsafe { crate::libinput::sys::$f(self.tool) != 0 }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl<'a> LibInputTabletTool<'a> {
|
||||
pub fn user_data(&self) -> usize {
|
||||
unsafe { libinput_tablet_tool_get_user_data(self.tool) }
|
||||
}
|
||||
|
||||
pub fn set_user_data(&self, user_data: usize) {
|
||||
unsafe { libinput_tablet_tool_set_user_data(self.tool, user_data) }
|
||||
}
|
||||
|
||||
pub fn type_(&self) -> TabletToolType {
|
||||
unsafe { TabletToolType(libinput_tablet_tool_get_type(self.tool)) }
|
||||
}
|
||||
|
||||
pub fn tool_id(&self) -> u64 {
|
||||
unsafe { libinput_tablet_tool_get_tool_id(self.tool) }
|
||||
}
|
||||
|
||||
pub fn serial(&self) -> u64 {
|
||||
unsafe { libinput_tablet_tool_get_serial(self.tool) }
|
||||
}
|
||||
|
||||
has_capability!(has_pressure, libinput_tablet_tool_has_pressure);
|
||||
has_capability!(has_distance, libinput_tablet_tool_has_distance);
|
||||
has_capability!(has_tilt, libinput_tablet_tool_has_tilt);
|
||||
has_capability!(has_rotation, libinput_tablet_tool_has_rotation);
|
||||
has_capability!(has_slider, libinput_tablet_tool_has_slider);
|
||||
// has_capability!(has_size, libinput_tablet_tool_has_size);
|
||||
has_capability!(has_wheel, libinput_tablet_tool_has_wheel);
|
||||
}
|
||||
|
||||
impl<'a> LibInputEventTabletTool<'a> {
|
||||
pub fn tool(&self) -> LibInputTabletTool<'_> {
|
||||
LibInputTabletTool {
|
||||
tool: unsafe { libinput_event_tablet_tool_get_tool(self.event) },
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn time_usec(&self) -> u64 {
|
||||
unsafe { libinput_event_tablet_tool_get_time_usec(self.event) }
|
||||
}
|
||||
|
||||
has_changed!(x_has_changed, libinput_event_tablet_tool_x_has_changed);
|
||||
has_changed!(y_has_changed, libinput_event_tablet_tool_y_has_changed);
|
||||
has_changed!(
|
||||
pressure_has_changed,
|
||||
libinput_event_tablet_tool_pressure_has_changed
|
||||
);
|
||||
has_changed!(
|
||||
distance_has_changed,
|
||||
libinput_event_tablet_tool_distance_has_changed
|
||||
);
|
||||
has_changed!(
|
||||
tilt_x_has_changed,
|
||||
libinput_event_tablet_tool_tilt_x_has_changed
|
||||
);
|
||||
has_changed!(
|
||||
tilt_y_has_changed,
|
||||
libinput_event_tablet_tool_tilt_y_has_changed
|
||||
);
|
||||
has_changed!(
|
||||
rotation_has_changed,
|
||||
libinput_event_tablet_tool_rotation_has_changed
|
||||
);
|
||||
has_changed!(
|
||||
slider_has_changed,
|
||||
libinput_event_tablet_tool_slider_has_changed
|
||||
);
|
||||
// has_changed!(
|
||||
// size_major_has_changed,
|
||||
// libinput_event_tablet_tool_size_major_has_changed
|
||||
// );
|
||||
// has_changed!(
|
||||
// size_minor_has_changed,
|
||||
// libinput_event_tablet_tool_size_minor_has_changed
|
||||
// );
|
||||
has_changed!(
|
||||
wheel_has_changed,
|
||||
libinput_event_tablet_tool_wheel_has_changed
|
||||
);
|
||||
|
||||
// get_double!(x, libinput_event_tablet_tool_get_x);
|
||||
// get_double!(y, libinput_event_tablet_tool_get_y);
|
||||
get_double!(dx, libinput_event_tablet_tool_get_dx);
|
||||
get_double!(dy, libinput_event_tablet_tool_get_dy);
|
||||
get_double!(pressure, libinput_event_tablet_tool_get_pressure);
|
||||
get_double!(distance, libinput_event_tablet_tool_get_distance);
|
||||
get_double!(tilt_x, libinput_event_tablet_tool_get_tilt_x);
|
||||
get_double!(tilt_y, libinput_event_tablet_tool_get_tilt_y);
|
||||
get_double!(rotation, libinput_event_tablet_tool_get_rotation);
|
||||
get_double!(
|
||||
slider_position,
|
||||
libinput_event_tablet_tool_get_slider_position
|
||||
);
|
||||
// get_double!(size_major, libinput_event_tablet_tool_get_size_major);
|
||||
// get_double!(size_minor, libinput_event_tablet_tool_get_size_minor);
|
||||
get_double!(wheel_delta, libinput_event_tablet_tool_get_wheel_delta);
|
||||
|
||||
pub fn wheel_delta_discrete(&self) -> i32 {
|
||||
unsafe { libinput_event_tablet_tool_get_wheel_delta_discrete(self.event) as _ }
|
||||
}
|
||||
|
||||
pub fn x_transformed(&self, width: u32) -> f64 {
|
||||
unsafe { libinput_event_tablet_tool_get_x_transformed(self.event, width) }
|
||||
}
|
||||
|
||||
pub fn y_transformed(&self, width: u32) -> f64 {
|
||||
unsafe { libinput_event_tablet_tool_get_y_transformed(self.event, width) }
|
||||
}
|
||||
|
||||
pub fn proximity_state(&self) -> TabletToolProximityState {
|
||||
unsafe {
|
||||
TabletToolProximityState(libinput_event_tablet_tool_get_proximity_state(self.event))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tip_state(&self) -> TabletToolTipState {
|
||||
unsafe { TabletToolTipState(libinput_event_tablet_tool_get_tip_state(self.event)) }
|
||||
}
|
||||
|
||||
pub fn button(&self) -> u32 {
|
||||
unsafe { libinput_event_tablet_tool_get_button(self.event) }
|
||||
}
|
||||
|
||||
pub fn button_state(&self) -> ButtonState {
|
||||
unsafe { ButtonState(libinput_event_tablet_tool_get_button_state(self.event)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> LibInputEventTabletPad<'a> {
|
||||
pub fn time_usec(&self) -> u64 {
|
||||
unsafe { libinput_event_tablet_pad_get_time_usec(self.event) }
|
||||
}
|
||||
|
||||
pub fn ring_position(&self) -> f64 {
|
||||
unsafe { libinput_event_tablet_pad_get_ring_position(self.event) }
|
||||
}
|
||||
|
||||
pub fn ring_number(&self) -> u32 {
|
||||
unsafe { libinput_event_tablet_pad_get_ring_number(self.event) as u32 }
|
||||
}
|
||||
|
||||
pub fn ring_source(&self) -> TabletPadRingAxisSource {
|
||||
unsafe { TabletPadRingAxisSource(libinput_event_tablet_pad_get_ring_source(self.event)) }
|
||||
}
|
||||
|
||||
pub fn strip_position(&self) -> f64 {
|
||||
unsafe { libinput_event_tablet_pad_get_strip_position(self.event) }
|
||||
}
|
||||
|
||||
pub fn strip_number(&self) -> u32 {
|
||||
unsafe { libinput_event_tablet_pad_get_strip_number(self.event) as u32 }
|
||||
}
|
||||
|
||||
pub fn strip_source(&self) -> TabletPadStripAxisSource {
|
||||
unsafe { TabletPadStripAxisSource(libinput_event_tablet_pad_get_strip_source(self.event)) }
|
||||
}
|
||||
|
||||
pub fn button_number(&self) -> u32 {
|
||||
unsafe { libinput_event_tablet_pad_get_button_number(self.event) }
|
||||
}
|
||||
|
||||
pub fn button_state(&self) -> ButtonState {
|
||||
unsafe { ButtonState(libinput_event_tablet_pad_get_button_state(self.event)) }
|
||||
}
|
||||
|
||||
pub fn mode(&self) -> u32 {
|
||||
unsafe { libinput_event_tablet_pad_get_mode(self.event) as u32 }
|
||||
}
|
||||
|
||||
pub fn mode_group(&self) -> LibInputTabletPadModeGroup {
|
||||
LibInputTabletPadModeGroup {
|
||||
group: unsafe { libinput_event_tablet_pad_get_mode_group(self.event) },
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ pub struct libinput(u8);
|
|||
#[repr(transparent)]
|
||||
pub struct libinput_device(u8);
|
||||
#[repr(transparent)]
|
||||
pub struct libinput_device_group(u8);
|
||||
#[repr(transparent)]
|
||||
pub struct libinput_event(u8);
|
||||
#[repr(transparent)]
|
||||
pub struct libinput_event_keyboard(u8);
|
||||
|
|
@ -18,6 +20,16 @@ pub struct libinput_event_pointer(u8);
|
|||
pub struct libinput_event_gesture(u8);
|
||||
#[repr(transparent)]
|
||||
pub struct libinput_event_switch(u8);
|
||||
#[repr(transparent)]
|
||||
pub struct libinput_event_tablet_tool(u8);
|
||||
#[repr(transparent)]
|
||||
pub struct libinput_event_tablet_pad(u8);
|
||||
#[repr(transparent)]
|
||||
pub struct libinput_tablet_pad_mode_group(u8);
|
||||
#[repr(transparent)]
|
||||
pub struct libinput_tablet_tool(u8);
|
||||
// #[repr(transparent)]
|
||||
// pub struct libinput_tablet_pad(u8);
|
||||
|
||||
#[link(name = "input")]
|
||||
extern "C" {
|
||||
|
|
@ -166,6 +178,185 @@ extern "C" {
|
|||
event: *mut libinput_event_switch,
|
||||
) -> libinput_switch_state;
|
||||
pub fn libinput_event_switch_get_time_usec(event: *mut libinput_event_switch) -> u64;
|
||||
|
||||
pub fn libinput_device_get_device_group(
|
||||
device: *mut libinput_device,
|
||||
) -> *mut libinput_device_group;
|
||||
pub fn libinput_device_group_set_user_data(group: *mut libinput_device_group, user_data: usize);
|
||||
pub fn libinput_device_group_get_user_data(group: *mut libinput_device_group) -> usize;
|
||||
|
||||
pub fn libinput_device_get_id_product(device: *mut libinput_device) -> c::c_uint;
|
||||
pub fn libinput_device_get_id_vendor(device: *mut libinput_device) -> c::c_uint;
|
||||
|
||||
pub fn libinput_event_get_tablet_tool_event(
|
||||
event: *mut libinput_event,
|
||||
) -> *mut libinput_event_tablet_tool;
|
||||
pub fn libinput_event_get_tablet_pad_event(
|
||||
event: *mut libinput_event,
|
||||
) -> *mut libinput_event_tablet_pad;
|
||||
pub fn libinput_event_tablet_tool_get_tool(
|
||||
event: *mut libinput_event_tablet_tool,
|
||||
) -> *mut libinput_tablet_tool;
|
||||
pub fn libinput_event_tablet_pad_get_mode_group(
|
||||
event: *mut libinput_event_tablet_pad,
|
||||
) -> *mut libinput_tablet_pad_mode_group;
|
||||
pub fn libinput_event_tablet_tool_x_has_changed(
|
||||
event: *mut libinput_event_tablet_tool,
|
||||
) -> c::c_int;
|
||||
pub fn libinput_event_tablet_tool_y_has_changed(
|
||||
event: *mut libinput_event_tablet_tool,
|
||||
) -> c::c_int;
|
||||
pub fn libinput_event_tablet_tool_pressure_has_changed(
|
||||
event: *mut libinput_event_tablet_tool,
|
||||
) -> c::c_int;
|
||||
pub fn libinput_event_tablet_tool_distance_has_changed(
|
||||
event: *mut libinput_event_tablet_tool,
|
||||
) -> c::c_int;
|
||||
pub fn libinput_event_tablet_tool_tilt_x_has_changed(
|
||||
event: *mut libinput_event_tablet_tool,
|
||||
) -> c::c_int;
|
||||
pub fn libinput_event_tablet_tool_tilt_y_has_changed(
|
||||
event: *mut libinput_event_tablet_tool,
|
||||
) -> c::c_int;
|
||||
pub fn libinput_event_tablet_tool_rotation_has_changed(
|
||||
event: *mut libinput_event_tablet_tool,
|
||||
) -> c::c_int;
|
||||
pub fn libinput_event_tablet_tool_slider_has_changed(
|
||||
event: *mut libinput_event_tablet_tool,
|
||||
) -> c::c_int;
|
||||
// pub fn libinput_event_tablet_tool_size_major_has_changed(
|
||||
// event: *mut libinput_event_tablet_tool,
|
||||
// ) -> c::c_int;
|
||||
// pub fn libinput_event_tablet_tool_size_minor_has_changed(
|
||||
// event: *mut libinput_event_tablet_tool,
|
||||
// ) -> c::c_int;
|
||||
pub fn libinput_event_tablet_tool_wheel_has_changed(
|
||||
event: *mut libinput_event_tablet_tool,
|
||||
) -> c::c_int;
|
||||
// pub fn libinput_event_tablet_tool_get_x(event: *mut libinput_event_tablet_tool) -> f64;
|
||||
// pub fn libinput_event_tablet_tool_get_y(event: *mut libinput_event_tablet_tool) -> f64;
|
||||
pub fn libinput_event_tablet_tool_get_dx(event: *mut libinput_event_tablet_tool) -> f64;
|
||||
pub fn libinput_event_tablet_tool_get_dy(event: *mut libinput_event_tablet_tool) -> f64;
|
||||
pub fn libinput_event_tablet_tool_get_pressure(event: *mut libinput_event_tablet_tool) -> f64;
|
||||
pub fn libinput_event_tablet_tool_get_distance(event: *mut libinput_event_tablet_tool) -> f64;
|
||||
pub fn libinput_event_tablet_tool_get_tilt_x(event: *mut libinput_event_tablet_tool) -> f64;
|
||||
pub fn libinput_event_tablet_tool_get_tilt_y(event: *mut libinput_event_tablet_tool) -> f64;
|
||||
pub fn libinput_event_tablet_tool_get_rotation(event: *mut libinput_event_tablet_tool) -> f64;
|
||||
pub fn libinput_event_tablet_tool_get_slider_position(
|
||||
event: *mut libinput_event_tablet_tool,
|
||||
) -> f64;
|
||||
// pub fn libinput_event_tablet_tool_get_size_major(event: *mut libinput_event_tablet_tool)
|
||||
// -> f64;
|
||||
// pub fn libinput_event_tablet_tool_get_size_minor(event: *mut libinput_event_tablet_tool)
|
||||
// -> f64;
|
||||
pub fn libinput_event_tablet_tool_get_wheel_delta(
|
||||
event: *mut libinput_event_tablet_tool,
|
||||
) -> f64;
|
||||
pub fn libinput_event_tablet_tool_get_wheel_delta_discrete(
|
||||
event: *mut libinput_event_tablet_tool,
|
||||
) -> c::c_int;
|
||||
pub fn libinput_event_tablet_tool_get_x_transformed(
|
||||
event: *mut libinput_event_tablet_tool,
|
||||
width: u32,
|
||||
) -> f64;
|
||||
pub fn libinput_event_tablet_tool_get_y_transformed(
|
||||
event: *mut libinput_event_tablet_tool,
|
||||
width: u32,
|
||||
) -> f64;
|
||||
pub fn libinput_event_tablet_tool_get_proximity_state(
|
||||
event: *mut libinput_event_tablet_tool,
|
||||
) -> libinput_tablet_tool_proximity_state;
|
||||
pub fn libinput_event_tablet_tool_get_tip_state(
|
||||
event: *mut libinput_event_tablet_tool,
|
||||
) -> libinput_tablet_tool_tip_state;
|
||||
pub fn libinput_event_tablet_tool_get_button(event: *mut libinput_event_tablet_tool) -> u32;
|
||||
pub fn libinput_event_tablet_tool_get_button_state(
|
||||
event: *mut libinput_event_tablet_tool,
|
||||
) -> libinput_button_state;
|
||||
// pub fn libinput_event_tablet_tool_get_seat_button_count(
|
||||
// event: *mut libinput_event_tablet_tool,
|
||||
// ) -> u32;
|
||||
pub fn libinput_event_tablet_tool_get_time_usec(event: *mut libinput_event_tablet_tool) -> u64;
|
||||
pub fn libinput_tablet_tool_get_type(
|
||||
tool: *mut libinput_tablet_tool,
|
||||
) -> libinput_tablet_tool_type;
|
||||
pub fn libinput_tablet_tool_get_tool_id(tool: *mut libinput_tablet_tool) -> u64;
|
||||
pub fn libinput_tablet_tool_has_pressure(tool: *mut libinput_tablet_tool) -> c::c_int;
|
||||
pub fn libinput_tablet_tool_has_distance(tool: *mut libinput_tablet_tool) -> c::c_int;
|
||||
pub fn libinput_tablet_tool_has_tilt(tool: *mut libinput_tablet_tool) -> c::c_int;
|
||||
pub fn libinput_tablet_tool_has_rotation(tool: *mut libinput_tablet_tool) -> c::c_int;
|
||||
pub fn libinput_tablet_tool_has_slider(tool: *mut libinput_tablet_tool) -> c::c_int;
|
||||
// pub fn libinput_tablet_tool_has_size(tool: *mut libinput_tablet_tool) -> c::c_int;
|
||||
pub fn libinput_tablet_tool_has_wheel(tool: *mut libinput_tablet_tool) -> c::c_int;
|
||||
// pub fn libinput_tablet_tool_has_button(tool: *mut libinput_tablet_tool, code: u32) -> c::c_int;
|
||||
// pub fn libinput_tablet_tool_is_unique(tool: *mut libinput_tablet_tool) -> c::c_int;
|
||||
pub fn libinput_tablet_tool_get_serial(tool: *mut libinput_tablet_tool) -> u64;
|
||||
pub fn libinput_tablet_tool_get_user_data(tool: *mut libinput_tablet_tool) -> usize;
|
||||
pub fn libinput_tablet_tool_set_user_data(tool: *mut libinput_tablet_tool, user_data: usize);
|
||||
pub fn libinput_event_tablet_pad_get_ring_position(
|
||||
event: *mut libinput_event_tablet_pad,
|
||||
) -> f64;
|
||||
pub fn libinput_event_tablet_pad_get_ring_number(
|
||||
event: *mut libinput_event_tablet_pad,
|
||||
) -> c::c_uint;
|
||||
pub fn libinput_event_tablet_pad_get_ring_source(
|
||||
event: *mut libinput_event_tablet_pad,
|
||||
) -> libinput_tablet_pad_ring_axis_source;
|
||||
pub fn libinput_event_tablet_pad_get_strip_position(
|
||||
event: *mut libinput_event_tablet_pad,
|
||||
) -> f64;
|
||||
pub fn libinput_event_tablet_pad_get_strip_number(
|
||||
event: *mut libinput_event_tablet_pad,
|
||||
) -> c::c_uint;
|
||||
pub fn libinput_event_tablet_pad_get_strip_source(
|
||||
event: *mut libinput_event_tablet_pad,
|
||||
) -> libinput_tablet_pad_strip_axis_source;
|
||||
pub fn libinput_event_tablet_pad_get_button_number(
|
||||
event: *mut libinput_event_tablet_pad,
|
||||
) -> u32;
|
||||
pub fn libinput_event_tablet_pad_get_button_state(
|
||||
event: *mut libinput_event_tablet_pad,
|
||||
) -> libinput_button_state;
|
||||
// pub fn libinput_event_tablet_pad_get_key(event: *mut libinput_event_tablet_pad) -> u32;
|
||||
// pub fn libinput_event_tablet_pad_get_key_state(
|
||||
// event: *mut libinput_event_tablet_pad,
|
||||
// ) -> libinput_key_state;
|
||||
pub fn libinput_event_tablet_pad_get_mode(event: *mut libinput_event_tablet_pad) -> c::c_uint;
|
||||
pub fn libinput_event_tablet_pad_get_time_usec(event: *mut libinput_event_tablet_pad) -> u64;
|
||||
pub fn libinput_device_tablet_pad_get_mode_group(
|
||||
device: *mut libinput_device,
|
||||
index: c::c_uint,
|
||||
) -> *mut libinput_tablet_pad_mode_group;
|
||||
pub fn libinput_device_tablet_pad_get_num_mode_groups(device: *mut libinput_device)
|
||||
-> c::c_int;
|
||||
pub fn libinput_device_tablet_pad_get_num_buttons(device: *mut libinput_device) -> c::c_int;
|
||||
pub fn libinput_device_tablet_pad_get_num_rings(device: *mut libinput_device) -> c::c_int;
|
||||
pub fn libinput_device_tablet_pad_get_num_strips(device: *mut libinput_device) -> c::c_int;
|
||||
pub fn libinput_tablet_pad_mode_group_get_index(
|
||||
group: *mut libinput_tablet_pad_mode_group,
|
||||
) -> c::c_uint;
|
||||
pub fn libinput_tablet_pad_mode_group_get_num_modes(
|
||||
group: *mut libinput_tablet_pad_mode_group,
|
||||
) -> c::c_uint;
|
||||
pub fn libinput_tablet_pad_mode_group_get_mode(
|
||||
group: *mut libinput_tablet_pad_mode_group,
|
||||
) -> c::c_uint;
|
||||
pub fn libinput_tablet_pad_mode_group_has_button(
|
||||
group: *mut libinput_tablet_pad_mode_group,
|
||||
button: c::c_uint,
|
||||
) -> c::c_int;
|
||||
pub fn libinput_tablet_pad_mode_group_has_ring(
|
||||
group: *mut libinput_tablet_pad_mode_group,
|
||||
ring: c::c_uint,
|
||||
) -> c::c_int;
|
||||
pub fn libinput_tablet_pad_mode_group_has_strip(
|
||||
group: *mut libinput_tablet_pad_mode_group,
|
||||
strip: c::c_uint,
|
||||
) -> c::c_int;
|
||||
// pub fn libinput_tablet_pad_mode_group_button_is_toggle(
|
||||
// group: *mut libinput_tablet_pad_mode_group,
|
||||
// button: c::c_uint,
|
||||
// ) -> c::c_int;
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ mod clientmem;
|
|||
mod compositor;
|
||||
mod config;
|
||||
mod cursor;
|
||||
mod cursor_user;
|
||||
mod dbus;
|
||||
mod drm_feedback;
|
||||
mod edid;
|
||||
|
|
|
|||
100
src/state.rs
100
src/state.rs
|
|
@ -4,7 +4,8 @@ use {
|
|||
async_engine::{AsyncEngine, SpawnedFuture},
|
||||
backend::{
|
||||
Backend, BackendDrmDevice, BackendEvent, Connector, ConnectorId, ConnectorIds,
|
||||
DrmDeviceId, DrmDeviceIds, InputDevice, InputDeviceId, InputDeviceIds, MonitorInfo,
|
||||
DrmDeviceId, DrmDeviceIds, InputDevice, InputDeviceGroupIds, InputDeviceId,
|
||||
InputDeviceIds, MonitorInfo,
|
||||
},
|
||||
backends::dummy::DummyBackend,
|
||||
cli::RunArgs,
|
||||
|
|
@ -12,6 +13,7 @@ use {
|
|||
clientmem::ClientMemOffset,
|
||||
config::ConfigProxy,
|
||||
cursor::{Cursor, ServerCursors},
|
||||
cursor_user::{CursorUserGroup, CursorUserGroupId, CursorUserGroupIds, CursorUserIds},
|
||||
dbus::Dbus,
|
||||
drm_feedback::{DrmFeedback, DrmFeedbackIds},
|
||||
fixed::Fixed,
|
||||
|
|
@ -32,8 +34,11 @@ use {
|
|||
jay_seat_events::JaySeatEvents,
|
||||
jay_workspace_watcher::JayWorkspaceWatcher,
|
||||
wl_drm::WlDrmGlobal,
|
||||
wl_output::{OutputId, PersistentOutputState},
|
||||
wl_seat::{SeatIds, WlSeatGlobal},
|
||||
wl_output::{OutputGlobalOpt, OutputId, PersistentOutputState},
|
||||
wl_seat::{
|
||||
tablet::{TabletIds, TabletInit, TabletPadIds, TabletPadInit, TabletToolIds},
|
||||
SeatIds, WlSeatGlobal,
|
||||
},
|
||||
wl_surface::{
|
||||
wl_subsurface::SubsurfaceIds,
|
||||
zwp_idle_inhibitor_v1::{IdleInhibitorId, IdleInhibitorIds, ZwpIdleInhibitorV1},
|
||||
|
|
@ -185,6 +190,14 @@ pub struct State {
|
|||
pub explicit_sync_enabled: Cell<bool>,
|
||||
pub keyboard_state_ids: KeyboardStateIds,
|
||||
pub security_context_acceptors: SecurityContextAcceptors,
|
||||
pub cursor_user_group_ids: CursorUserGroupIds,
|
||||
pub cursor_user_ids: CursorUserIds,
|
||||
pub cursor_user_groups: CopyHashMap<CursorUserGroupId, Rc<CursorUserGroup>>,
|
||||
pub cursor_user_group_hardware_cursor: CloneCell<Option<Rc<CursorUserGroup>>>,
|
||||
pub input_device_group_ids: InputDeviceGroupIds,
|
||||
pub tablet_ids: TabletIds,
|
||||
pub tablet_tool_ids: TabletToolIds,
|
||||
pub tablet_pad_ids: TabletPadIds,
|
||||
}
|
||||
|
||||
// impl Drop for State {
|
||||
|
|
@ -256,6 +269,9 @@ 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 tablet_init: Option<Box<TabletInit>>,
|
||||
pub tablet_pad_init: Option<Box<TabletPadInit>>,
|
||||
}
|
||||
|
||||
pub struct ConnectorData {
|
||||
|
|
@ -460,9 +476,8 @@ impl State {
|
|||
UpdateTextTexturesVisitor.visit_display(&self.root);
|
||||
}
|
||||
|
||||
let seats = self.globals.seats.lock();
|
||||
for seat in seats.values() {
|
||||
seat.render_ctx_changed();
|
||||
for cursor_user_groups in self.cursor_user_groups.lock().values() {
|
||||
cursor_user_groups.render_ctx_changed();
|
||||
}
|
||||
|
||||
if let Some(ctx) = &ctx {
|
||||
|
|
@ -505,8 +520,8 @@ impl State {
|
|||
}
|
||||
};
|
||||
self.cursors.set(cursors);
|
||||
for seat in self.globals.seats.lock().values() {
|
||||
seat.reload_known_cursor();
|
||||
for cursor_user_group in self.cursor_user_groups.lock().values() {
|
||||
cursor_user_group.reload_known_cursor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -785,21 +800,13 @@ impl State {
|
|||
}
|
||||
|
||||
pub fn refresh_hardware_cursors(&self) {
|
||||
let seat = self
|
||||
.globals
|
||||
.seats
|
||||
.lock()
|
||||
.values()
|
||||
.find(|s| s.hardware_cursor())
|
||||
.cloned();
|
||||
let seat = match seat {
|
||||
Some(s) => s,
|
||||
_ => {
|
||||
self.disable_hardware_cursors();
|
||||
if let Some(g) = self.cursor_user_group_hardware_cursor.get() {
|
||||
if let Some(u) = g.active() {
|
||||
u.update_hardware_cursor();
|
||||
return;
|
||||
}
|
||||
};
|
||||
seat.update_hardware_cursor();
|
||||
}
|
||||
self.disable_hardware_cursors()
|
||||
}
|
||||
|
||||
pub fn for_each_seat_tester<F: Fn(&JaySeatEvents)>(&self, f: F) {
|
||||
|
|
@ -868,10 +875,10 @@ impl State {
|
|||
ReleaseSync::Implicit,
|
||||
);
|
||||
if render_hardware_cursors {
|
||||
for seat in self.globals.lock_seats().values() {
|
||||
if let Some(cursor) = seat.get_cursor() {
|
||||
let (mut x, mut y) = seat.get_position();
|
||||
if seat.hardware_cursor() {
|
||||
if let Some(cursor_user_group) = self.cursor_user_group_hardware_cursor.get() {
|
||||
if let Some(cursor_user) = cursor_user_group.active() {
|
||||
if let Some(cursor) = cursor_user.get() {
|
||||
let (mut x, mut y) = cursor_user.position();
|
||||
x = x + x_off - Fixed::from_int(position.x1());
|
||||
y = y + y_off - Fixed::from_int(position.y1());
|
||||
cursor.render(&mut renderer, x, y);
|
||||
|
|
@ -883,9 +890,9 @@ impl State {
|
|||
}
|
||||
|
||||
fn have_hardware_cursor(&self) -> bool {
|
||||
for seat in self.globals.lock_seats().values() {
|
||||
if seat.get_cursor().is_some() {
|
||||
if seat.hardware_cursor() {
|
||||
if let Some(group) = self.cursor_user_group_hardware_cursor.get() {
|
||||
if let Some(user) = group.active() {
|
||||
if user.get().is_some() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -995,6 +1002,43 @@ impl State {
|
|||
pub fn root_visible(&self) -> bool {
|
||||
!self.idle.backend_idle.get()
|
||||
}
|
||||
|
||||
pub fn find_closest_output(&self, mut x: i32, mut y: i32) -> (Rc<OutputNode>, i32, i32) {
|
||||
let mut optimal_dist = i32::MAX;
|
||||
let mut optimal_output = None;
|
||||
let outputs = self.root.outputs.lock();
|
||||
for output in outputs.values() {
|
||||
let pos = output.global.pos.get();
|
||||
let dist = pos.dist_squared(x, y);
|
||||
if dist == 0 {
|
||||
if pos.contains(x, y) {
|
||||
return (output.clone(), x, y);
|
||||
}
|
||||
}
|
||||
if dist < optimal_dist {
|
||||
optimal_dist = dist;
|
||||
optimal_output = Some(output.clone());
|
||||
}
|
||||
}
|
||||
if let Some(output) = optimal_output {
|
||||
let pos = output.global.pos.get();
|
||||
if pos.is_empty() {
|
||||
return (output, pos.x1(), pos.y1());
|
||||
}
|
||||
if x < pos.x1() {
|
||||
x = pos.x1();
|
||||
} else if x >= pos.x2() {
|
||||
x = pos.x2() - 1;
|
||||
}
|
||||
if y < pos.y1() {
|
||||
y = pos.y1();
|
||||
} else if y >= pos.y2() {
|
||||
y = pos.y2() - 1;
|
||||
}
|
||||
return (output, x, y);
|
||||
}
|
||||
(self.dummy_output.get().unwrap(), 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
|
|
|||
|
|
@ -185,12 +185,8 @@ impl ConnectorHandler {
|
|||
global.opt.global.set(Some(global.clone()));
|
||||
let mut ws_to_move = VecDeque::new();
|
||||
if self.state.root.outputs.len() == 1 {
|
||||
let seats = self.state.globals.seats.lock();
|
||||
let pos = global.pos.get();
|
||||
let x = (pos.x1() + pos.x2()) / 2;
|
||||
let y = (pos.y1() + pos.y2()) / 2;
|
||||
for seat in seats.values() {
|
||||
seat.set_position(x, y);
|
||||
for seat in self.state.globals.seats.lock().values() {
|
||||
seat.cursor_group().first_output_connected(&on);
|
||||
}
|
||||
let dummy = self.state.dummy_output.get().unwrap();
|
||||
for ws in dummy.workspaces.iter() {
|
||||
|
|
@ -287,12 +283,8 @@ impl ConnectorHandler {
|
|||
};
|
||||
move_ws_to_output(&ws, &target, config);
|
||||
}
|
||||
let seats = self.state.globals.seats.lock();
|
||||
for seat in seats.values() {
|
||||
if seat.get_output().id == on.id {
|
||||
let tpos = target.global.pos.get();
|
||||
seat.set_position((tpos.x1() + tpos.x2()) / 2, (tpos.y1() + tpos.y2()) / 2);
|
||||
}
|
||||
for seat in self.state.globals.seats.lock().values() {
|
||||
seat.cursor_group().output_disconnected(&on, &target);
|
||||
}
|
||||
self.state
|
||||
.remove_output_scale(on.global.persistent.scale.get());
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ pub fn handle(state: &Rc<State>, dev: Rc<dyn InputDevice>) {
|
|||
devnode: props.devnode,
|
||||
keymap: Default::default(),
|
||||
xkb_state: Default::default(),
|
||||
output: Default::default(),
|
||||
tablet_init: dev.tablet_info(),
|
||||
tablet_pad_init: dev.tablet_pad_info(),
|
||||
});
|
||||
let ae = Rc::new(AsyncEvent::default());
|
||||
let oh = DeviceHandler {
|
||||
|
|
|
|||
|
|
@ -122,3 +122,7 @@ impl Add<Duration> for Time {
|
|||
pub fn now_usec() -> u64 {
|
||||
Time::now_unchecked().usec()
|
||||
}
|
||||
|
||||
pub fn usec_to_msec(usec: u64) -> u32 {
|
||||
(usec / 1000) as u32
|
||||
}
|
||||
|
|
|
|||
121
src/tree.rs
121
src/tree.rs
|
|
@ -4,7 +4,15 @@ use {
|
|||
client::{Client, ClientId},
|
||||
fixed::Fixed,
|
||||
ifs::{
|
||||
wl_seat::{wl_pointer::PendingScroll, Dnd, NodeSeatState, WlSeatGlobal},
|
||||
wl_seat::{
|
||||
tablet::{
|
||||
PadButtonState, TabletPad, TabletPadGroup, TabletPadRing, TabletPadStrip,
|
||||
TabletRingEventSource, TabletStripEventSource, TabletTool, TabletToolChanges,
|
||||
ToolButtonState,
|
||||
},
|
||||
wl_pointer::PendingScroll,
|
||||
Dnd, NodeSeatState, WlSeatGlobal,
|
||||
},
|
||||
wl_surface::WlSurface,
|
||||
},
|
||||
rect::Rect,
|
||||
|
|
@ -344,8 +352,119 @@ pub trait Node: 'static {
|
|||
let _ = cancelled;
|
||||
}
|
||||
|
||||
fn node_on_tablet_pad_enter(&self, pad: &Rc<TabletPad>) {
|
||||
let _ = pad;
|
||||
}
|
||||
|
||||
fn node_on_tablet_pad_leave(&self, pad: &Rc<TabletPad>) {
|
||||
let _ = pad;
|
||||
}
|
||||
|
||||
fn node_on_tablet_pad_button(
|
||||
&self,
|
||||
pad: &Rc<TabletPad>,
|
||||
time_usec: u64,
|
||||
button: u32,
|
||||
state: PadButtonState,
|
||||
) {
|
||||
let _ = pad;
|
||||
let _ = time_usec;
|
||||
let _ = button;
|
||||
let _ = state;
|
||||
}
|
||||
|
||||
fn node_on_tablet_pad_mode_switch(
|
||||
&self,
|
||||
pad: &Rc<TabletPad>,
|
||||
group: &Rc<TabletPadGroup>,
|
||||
time_usec: u64,
|
||||
mode: u32,
|
||||
) {
|
||||
let _ = pad;
|
||||
let _ = group;
|
||||
let _ = time_usec;
|
||||
let _ = mode;
|
||||
}
|
||||
|
||||
fn node_on_tablet_pad_ring(
|
||||
&self,
|
||||
pad: &Rc<TabletPad>,
|
||||
ring: &Rc<TabletPadRing>,
|
||||
source: Option<TabletRingEventSource>,
|
||||
angle: Option<f64>,
|
||||
time_usec: u64,
|
||||
) {
|
||||
let _ = pad;
|
||||
let _ = time_usec;
|
||||
let _ = ring;
|
||||
let _ = source;
|
||||
let _ = angle;
|
||||
}
|
||||
|
||||
fn node_on_tablet_pad_strip(
|
||||
&self,
|
||||
pad: &Rc<TabletPad>,
|
||||
strip: &Rc<TabletPadStrip>,
|
||||
source: Option<TabletStripEventSource>,
|
||||
position: Option<f64>,
|
||||
time_usec: u64,
|
||||
) {
|
||||
let _ = pad;
|
||||
let _ = time_usec;
|
||||
let _ = strip;
|
||||
let _ = source;
|
||||
let _ = position;
|
||||
}
|
||||
|
||||
fn node_on_tablet_tool_leave(&self, tool: &Rc<TabletTool>, time_usec: u64) {
|
||||
let _ = tool;
|
||||
let _ = time_usec;
|
||||
}
|
||||
|
||||
fn node_on_tablet_tool_enter(
|
||||
self: Rc<Self>,
|
||||
tool: &Rc<TabletTool>,
|
||||
time_usec: u64,
|
||||
x: Fixed,
|
||||
y: Fixed,
|
||||
) {
|
||||
let _ = tool;
|
||||
let _ = time_usec;
|
||||
let _ = x;
|
||||
let _ = y;
|
||||
}
|
||||
|
||||
fn node_on_tablet_tool_button(
|
||||
&self,
|
||||
tool: &Rc<TabletTool>,
|
||||
time_usec: u64,
|
||||
button: u32,
|
||||
state: ToolButtonState,
|
||||
) {
|
||||
let _ = tool;
|
||||
let _ = time_usec;
|
||||
let _ = button;
|
||||
let _ = state;
|
||||
}
|
||||
|
||||
fn node_on_tablet_tool_apply_changes(
|
||||
self: Rc<Self>,
|
||||
tool: &Rc<TabletTool>,
|
||||
time_usec: u64,
|
||||
changes: Option<&TabletToolChanges>,
|
||||
x: Fixed,
|
||||
y: Fixed,
|
||||
) {
|
||||
let _ = tool;
|
||||
let _ = time_usec;
|
||||
let _ = changes;
|
||||
let _ = x;
|
||||
let _ = y;
|
||||
}
|
||||
|
||||
// TYPE CONVERTERS
|
||||
|
||||
#[cfg_attr(not(feature = "it"), allow(dead_code))]
|
||||
fn node_into_float(self: Rc<Self>) -> Option<Rc<FloatNode>> {
|
||||
None
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,13 @@ use {
|
|||
crate::{
|
||||
backend::KeyState,
|
||||
cursor::KnownCursor,
|
||||
cursor_user::CursorUser,
|
||||
fixed::Fixed,
|
||||
ifs::wl_seat::{
|
||||
collect_kb_foci, collect_kb_foci2, wl_pointer::PendingScroll, NodeSeatState, SeatId,
|
||||
WlSeatGlobal, BTN_LEFT,
|
||||
collect_kb_foci, collect_kb_foci2,
|
||||
tablet::{TabletTool, TabletToolChanges, TabletToolId},
|
||||
wl_pointer::PendingScroll,
|
||||
NodeSeatState, SeatId, WlSeatGlobal, BTN_LEFT,
|
||||
},
|
||||
rect::Rect,
|
||||
renderer::Renderer,
|
||||
|
|
@ -112,7 +115,7 @@ pub struct ContainerNode {
|
|||
focus_history: LinkedList<NodeRef<ContainerChild>>,
|
||||
child_nodes: RefCell<AHashMap<NodeId, LinkedNode<ContainerChild>>>,
|
||||
workspace: CloneCell<Rc<WorkspaceNode>>,
|
||||
seats: RefCell<AHashMap<SeatId, SeatState>>,
|
||||
cursors: RefCell<AHashMap<CursorType, CursorState>>,
|
||||
state: Rc<State>,
|
||||
pub render_data: RefCell<ContainerRenderData>,
|
||||
scroller: Scroller,
|
||||
|
|
@ -141,7 +144,13 @@ pub struct ContainerChild {
|
|||
factor: Cell<f64>,
|
||||
}
|
||||
|
||||
struct SeatState {
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
enum CursorType {
|
||||
Seat(SeatId),
|
||||
TabletTool(TabletToolId),
|
||||
}
|
||||
|
||||
struct CursorState {
|
||||
cursor: KnownCursor,
|
||||
target: bool,
|
||||
x: i32,
|
||||
|
|
@ -210,7 +219,7 @@ impl ContainerNode {
|
|||
focus_history: Default::default(),
|
||||
child_nodes: RefCell::new(child_nodes),
|
||||
workspace: CloneCell::new(workspace.clone()),
|
||||
seats: RefCell::new(Default::default()),
|
||||
cursors: RefCell::new(Default::default()),
|
||||
state: state.clone(),
|
||||
render_data: Default::default(),
|
||||
scroller: Default::default(),
|
||||
|
|
@ -328,7 +337,7 @@ impl ContainerNode {
|
|||
}
|
||||
|
||||
fn cancel_seat_ops(&self) {
|
||||
let mut seats = self.seats.borrow_mut();
|
||||
let mut seats = self.cursors.borrow_mut();
|
||||
for seat in seats.values_mut() {
|
||||
seat.op = None;
|
||||
}
|
||||
|
|
@ -514,12 +523,21 @@ impl ContainerNode {
|
|||
);
|
||||
}
|
||||
|
||||
fn pointer_move(self: &Rc<Self>, seat: &Rc<WlSeatGlobal>, mut x: i32, mut y: i32) {
|
||||
fn pointer_move(
|
||||
self: &Rc<Self>,
|
||||
id: CursorType,
|
||||
cursor: &CursorUser,
|
||||
x: Fixed,
|
||||
y: Fixed,
|
||||
target: bool,
|
||||
) {
|
||||
let mut x = x.round_down();
|
||||
let mut y = y.round_down();
|
||||
let title_height = self.state.theme.sizes.title_height.get();
|
||||
let mut seats = self.seats.borrow_mut();
|
||||
let seat_state = seats.entry(seat.id()).or_insert_with(|| SeatState {
|
||||
let mut seats = self.cursors.borrow_mut();
|
||||
let seat_state = seats.entry(id).or_insert_with(|| CursorState {
|
||||
cursor: KnownCursor::Default,
|
||||
target: false,
|
||||
target,
|
||||
x,
|
||||
y,
|
||||
op: None,
|
||||
|
|
@ -601,7 +619,7 @@ impl ContainerNode {
|
|||
};
|
||||
if new_cursor != mem::replace(&mut seat_state.cursor, new_cursor) {
|
||||
if seat_state.target {
|
||||
seat.set_known_cursor(new_cursor);
|
||||
cursor.set_known(new_cursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1036,6 +1054,80 @@ impl ContainerNode {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn button(
|
||||
self: Rc<Self>,
|
||||
id: CursorType,
|
||||
seat: &Rc<WlSeatGlobal>,
|
||||
time_usec: u64,
|
||||
pressed: bool,
|
||||
) {
|
||||
let mut seat_datas = self.cursors.borrow_mut();
|
||||
let seat_data = match seat_datas.get_mut(&id) {
|
||||
Some(s) => s,
|
||||
_ => return,
|
||||
};
|
||||
if seat_data.op.is_none() {
|
||||
if !pressed {
|
||||
return;
|
||||
}
|
||||
let (kind, child) = 'res: {
|
||||
let mono = self.mono_child.is_some();
|
||||
for child in self.children.iter() {
|
||||
let rect = child.title_rect.get();
|
||||
if rect.contains(seat_data.x, seat_data.y) {
|
||||
self.activate_child(&child);
|
||||
child
|
||||
.node
|
||||
.clone()
|
||||
.node_do_focus(seat, Direction::Unspecified);
|
||||
break 'res (SeatOpKind::Move, child);
|
||||
} else if !mono {
|
||||
if self.split.get() == ContainerSplit::Horizontal {
|
||||
if seat_data.x < rect.x1() {
|
||||
break 'res (
|
||||
SeatOpKind::Resize {
|
||||
dist_left: seat_data.x
|
||||
- child.prev().unwrap().body.get().x2(),
|
||||
dist_right: child.body.get().x1() - seat_data.x,
|
||||
},
|
||||
child,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if seat_data.y < rect.y1() {
|
||||
break 'res (
|
||||
SeatOpKind::Resize {
|
||||
dist_left: seat_data.y
|
||||
- child.prev().unwrap().body.get().y2(),
|
||||
dist_right: child.body.get().y1() - seat_data.y,
|
||||
},
|
||||
child,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
};
|
||||
if seat_data
|
||||
.double_click_state
|
||||
.click(&self.state, time_usec, seat_data.x, seat_data.y)
|
||||
&& kind == SeatOpKind::Move
|
||||
{
|
||||
drop(seat_datas);
|
||||
seat.set_tl_floating(child.node.clone(), true);
|
||||
return;
|
||||
}
|
||||
seat_data.op = Some(SeatOp { child, kind })
|
||||
} else if !pressed {
|
||||
let op = seat_data.op.take().unwrap();
|
||||
drop(seat_datas);
|
||||
if op.kind == SeatOpKind::Move {
|
||||
// todo
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SeatOp {
|
||||
|
|
@ -1194,76 +1286,14 @@ impl Node for ContainerNode {
|
|||
if button != BTN_LEFT {
|
||||
return;
|
||||
}
|
||||
let mut seat_datas = self.seats.borrow_mut();
|
||||
let seat_data = match seat_datas.get_mut(&seat.id()) {
|
||||
Some(s) => s,
|
||||
_ => return,
|
||||
};
|
||||
if seat_data.op.is_none() {
|
||||
if state != KeyState::Pressed {
|
||||
return;
|
||||
}
|
||||
let (kind, child) = 'res: {
|
||||
let mono = self.mono_child.is_some();
|
||||
for child in self.children.iter() {
|
||||
let rect = child.title_rect.get();
|
||||
if rect.contains(seat_data.x, seat_data.y) {
|
||||
self.activate_child(&child);
|
||||
child
|
||||
.node
|
||||
.clone()
|
||||
.node_do_focus(seat, Direction::Unspecified);
|
||||
break 'res (SeatOpKind::Move, child);
|
||||
} else if !mono {
|
||||
if self.split.get() == ContainerSplit::Horizontal {
|
||||
if seat_data.x < rect.x1() {
|
||||
break 'res (
|
||||
SeatOpKind::Resize {
|
||||
dist_left: seat_data.x
|
||||
- child.prev().unwrap().body.get().x2(),
|
||||
dist_right: child.body.get().x1() - seat_data.x,
|
||||
},
|
||||
child,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if seat_data.y < rect.y1() {
|
||||
break 'res (
|
||||
SeatOpKind::Resize {
|
||||
dist_left: seat_data.y
|
||||
- child.prev().unwrap().body.get().y2(),
|
||||
dist_right: child.body.get().y1() - seat_data.y,
|
||||
},
|
||||
child,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
};
|
||||
if seat_data
|
||||
.double_click_state
|
||||
.click(&self.state, time_usec, seat_data.x, seat_data.y)
|
||||
&& kind == SeatOpKind::Move
|
||||
{
|
||||
drop(seat_datas);
|
||||
seat.set_tl_floating(child.node.clone(), true);
|
||||
return;
|
||||
}
|
||||
seat_data.op = Some(SeatOp { child, kind })
|
||||
} else if state == KeyState::Released {
|
||||
let op = seat_data.op.take().unwrap();
|
||||
drop(seat_datas);
|
||||
if op.kind == SeatOpKind::Move {
|
||||
// todo
|
||||
}
|
||||
}
|
||||
let id = CursorType::Seat(seat.id());
|
||||
self.button(id, seat, time_usec, state == KeyState::Pressed);
|
||||
}
|
||||
|
||||
fn node_on_axis_event(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, event: &PendingScroll) {
|
||||
let mut seat_datas = self.seats.borrow_mut();
|
||||
let seat_data = match seat_datas.get_mut(&seat.id()) {
|
||||
let mut seat_datas = self.cursors.borrow_mut();
|
||||
let id = CursorType::Seat(seat.id());
|
||||
let seat_data = match seat_datas.get_mut(&id) {
|
||||
Some(s) => s,
|
||||
_ => return,
|
||||
};
|
||||
|
|
@ -1299,29 +1329,76 @@ impl Node for ContainerNode {
|
|||
|
||||
fn node_on_pointer_enter(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, x: Fixed, y: Fixed) {
|
||||
// log::info!("node_on_pointer_enter");
|
||||
self.pointer_move(seat, x.round_down(), y.round_down());
|
||||
self.pointer_move(
|
||||
CursorType::Seat(seat.id()),
|
||||
seat.pointer_cursor(),
|
||||
x,
|
||||
y,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
fn node_on_pointer_unfocus(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
// log::info!("unfocus");
|
||||
let mut seats = self.seats.borrow_mut();
|
||||
if let Some(seat_state) = seats.get_mut(&seat.id()) {
|
||||
let mut seats = self.cursors.borrow_mut();
|
||||
let id = CursorType::Seat(seat.id());
|
||||
if let Some(seat_state) = seats.get_mut(&id) {
|
||||
seat_state.target = false;
|
||||
}
|
||||
}
|
||||
|
||||
fn node_on_pointer_focus(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
// log::info!("container focus");
|
||||
let mut seats = self.seats.borrow_mut();
|
||||
if let Some(seat_state) = seats.get_mut(&seat.id()) {
|
||||
let mut seats = self.cursors.borrow_mut();
|
||||
let id = CursorType::Seat(seat.id());
|
||||
if let Some(seat_state) = seats.get_mut(&id) {
|
||||
seat_state.target = true;
|
||||
seat.set_known_cursor(seat_state.cursor);
|
||||
seat.pointer_cursor().set_known(seat_state.cursor);
|
||||
}
|
||||
}
|
||||
|
||||
fn node_on_pointer_motion(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, x: Fixed, y: Fixed) {
|
||||
// log::info!("node_on_pointer_motion");
|
||||
self.pointer_move(seat, x.round_down(), y.round_down());
|
||||
self.pointer_move(
|
||||
CursorType::Seat(seat.id()),
|
||||
seat.pointer_cursor(),
|
||||
x,
|
||||
y,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
fn node_on_tablet_tool_leave(&self, tool: &Rc<TabletTool>, _time_usec: u64) {
|
||||
let id = CursorType::TabletTool(tool.id);
|
||||
self.cursors.borrow_mut().remove(&id);
|
||||
}
|
||||
|
||||
fn node_on_tablet_tool_enter(
|
||||
self: Rc<Self>,
|
||||
tool: &Rc<TabletTool>,
|
||||
_time_usec: u64,
|
||||
x: Fixed,
|
||||
y: Fixed,
|
||||
) {
|
||||
tool.cursor().set_known(KnownCursor::Default);
|
||||
self.pointer_move(CursorType::TabletTool(tool.id), tool.cursor(), x, y, true);
|
||||
}
|
||||
|
||||
fn node_on_tablet_tool_apply_changes(
|
||||
self: Rc<Self>,
|
||||
tool: &Rc<TabletTool>,
|
||||
time_usec: u64,
|
||||
changes: Option<&TabletToolChanges>,
|
||||
x: Fixed,
|
||||
y: Fixed,
|
||||
) {
|
||||
let id = CursorType::TabletTool(tool.id);
|
||||
self.pointer_move(id, tool.cursor(), x, y, false);
|
||||
if let Some(changes) = changes {
|
||||
if let Some(pressed) = changes.down {
|
||||
self.button(id, tool.seat(), time_usec, pressed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn node_into_container(self: Rc<Self>) -> Option<Rc<ContainerNode>> {
|
||||
|
|
@ -1332,13 +1409,13 @@ impl Node for ContainerNode {
|
|||
Some(self)
|
||||
}
|
||||
|
||||
fn node_is_container(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn node_into_toplevel(self: Rc<Self>) -> Option<Rc<dyn ToplevelNode>> {
|
||||
Some(self)
|
||||
}
|
||||
|
||||
fn node_is_container(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl ContainingNode for ContainerNode {
|
||||
|
|
@ -1534,7 +1611,7 @@ impl ToplevelNodeBase for ContainerNode {
|
|||
}
|
||||
|
||||
fn tl_destroy_impl(&self) {
|
||||
mem::take(self.seats.borrow_mut().deref_mut());
|
||||
mem::take(self.cursors.borrow_mut().deref_mut());
|
||||
let mut cn = self.child_nodes.borrow_mut();
|
||||
for (_, n) in cn.drain() {
|
||||
n.node.tl_destroy();
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ use {
|
|||
crate::{
|
||||
backend::ConnectorId,
|
||||
cursor::KnownCursor,
|
||||
ifs::wl_seat::{NodeSeatState, WlSeatGlobal},
|
||||
fixed::Fixed,
|
||||
ifs::wl_seat::{tablet::TabletTool, NodeSeatState, WlSeatGlobal},
|
||||
rect::Rect,
|
||||
renderer::Renderer,
|
||||
state::State,
|
||||
|
|
@ -140,6 +141,16 @@ impl Node for DisplayNode {
|
|||
|
||||
fn node_on_pointer_focus(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
// log::info!("display focus");
|
||||
seat.set_known_cursor(KnownCursor::Default);
|
||||
seat.pointer_cursor().set_known(KnownCursor::Default);
|
||||
}
|
||||
|
||||
fn node_on_tablet_tool_enter(
|
||||
self: Rc<Self>,
|
||||
tool: &Rc<TabletTool>,
|
||||
_time_usec: u64,
|
||||
_x: Fixed,
|
||||
_y: Fixed,
|
||||
) {
|
||||
tool.cursor().set_known(KnownCursor::Default)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,12 @@ use {
|
|||
crate::{
|
||||
backend::KeyState,
|
||||
cursor::KnownCursor,
|
||||
cursor_user::CursorUser,
|
||||
fixed::Fixed,
|
||||
ifs::wl_seat::{NodeSeatState, SeatId, WlSeatGlobal, BTN_LEFT},
|
||||
ifs::wl_seat::{
|
||||
tablet::{TabletTool, TabletToolChanges, TabletToolId},
|
||||
NodeSeatState, SeatId, WlSeatGlobal, BTN_LEFT,
|
||||
},
|
||||
rect::Rect,
|
||||
renderer::Renderer,
|
||||
scale::Scale,
|
||||
|
|
@ -44,11 +48,17 @@ pub struct FloatNode {
|
|||
pub render_titles_scheduled: Cell<bool>,
|
||||
pub title: RefCell<String>,
|
||||
pub title_textures: CopyHashMap<Scale, TextTexture>,
|
||||
seats: RefCell<AHashMap<SeatId, SeatState>>,
|
||||
cursors: RefCell<AHashMap<CursorType, CursorState>>,
|
||||
pub attention_requested: Cell<bool>,
|
||||
}
|
||||
|
||||
struct SeatState {
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
enum CursorType {
|
||||
Seat(SeatId),
|
||||
TabletTool(TabletToolId),
|
||||
}
|
||||
|
||||
struct CursorState {
|
||||
cursor: KnownCursor,
|
||||
target: bool,
|
||||
x: i32,
|
||||
|
|
@ -113,7 +123,7 @@ impl FloatNode {
|
|||
render_titles_scheduled: Cell::new(false),
|
||||
title: Default::default(),
|
||||
title_textures: Default::default(),
|
||||
seats: Default::default(),
|
||||
cursors: Default::default(),
|
||||
attention_requested: Cell::new(false),
|
||||
});
|
||||
floater.pull_child_properties();
|
||||
|
|
@ -214,14 +224,23 @@ impl FloatNode {
|
|||
}
|
||||
}
|
||||
|
||||
fn pointer_move(self: &Rc<Self>, seat: &Rc<WlSeatGlobal>, x: i32, y: i32) {
|
||||
fn pointer_move(
|
||||
self: &Rc<Self>,
|
||||
id: CursorType,
|
||||
cursor: &CursorUser,
|
||||
x: Fixed,
|
||||
y: Fixed,
|
||||
target: bool,
|
||||
) {
|
||||
let x = x.round_down();
|
||||
let y = y.round_down();
|
||||
let theme = &self.state.theme;
|
||||
let bw = theme.sizes.border_width.get();
|
||||
let th = theme.sizes.title_height.get();
|
||||
let mut seats = self.seats.borrow_mut();
|
||||
let seat_state = seats.entry(seat.id()).or_insert_with(|| SeatState {
|
||||
let mut seats = self.cursors.borrow_mut();
|
||||
let seat_state = seats.entry(id).or_insert_with(|| CursorState {
|
||||
cursor: KnownCursor::Default,
|
||||
target: false,
|
||||
target,
|
||||
x,
|
||||
y,
|
||||
op_type: OpType::Move,
|
||||
|
|
@ -289,6 +308,7 @@ impl FloatNode {
|
|||
}
|
||||
}
|
||||
self.position.set(Rect::new(x1, y1, x2, y2).unwrap());
|
||||
self.state.damage();
|
||||
self.schedule_layout();
|
||||
return;
|
||||
}
|
||||
|
|
@ -334,7 +354,7 @@ impl FloatNode {
|
|||
seat_state.op_type = op_type;
|
||||
if new_cursor != mem::replace(&mut seat_state.cursor, new_cursor) {
|
||||
if seat_state.target {
|
||||
seat.set_known_cursor(new_cursor);
|
||||
cursor.set_known(new_cursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -398,6 +418,77 @@ impl FloatNode {
|
|||
self.state.tree_changed();
|
||||
}
|
||||
}
|
||||
|
||||
fn button(
|
||||
self: Rc<Self>,
|
||||
id: CursorType,
|
||||
cursor: &CursorUser,
|
||||
seat: &Rc<WlSeatGlobal>,
|
||||
time_usec: u64,
|
||||
pressed: bool,
|
||||
) {
|
||||
let mut cursors = self.cursors.borrow_mut();
|
||||
let cursor_data = match cursors.get_mut(&id) {
|
||||
Some(s) => s,
|
||||
_ => return,
|
||||
};
|
||||
if !cursor_data.op_active {
|
||||
if !pressed {
|
||||
return;
|
||||
}
|
||||
if cursor_data.op_type == OpType::Move {
|
||||
if let Some(tl) = self.child.get() {
|
||||
tl.node_do_focus(seat, Direction::Unspecified);
|
||||
}
|
||||
}
|
||||
if cursor_data.double_click_state.click(
|
||||
&self.state,
|
||||
time_usec,
|
||||
cursor_data.x,
|
||||
cursor_data.y,
|
||||
) && cursor_data.op_type == OpType::Move
|
||||
{
|
||||
if let Some(tl) = self.child.get() {
|
||||
drop(cursors);
|
||||
seat.set_tl_floating(tl, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
cursor_data.op_active = true;
|
||||
let pos = self.position.get();
|
||||
match cursor_data.op_type {
|
||||
OpType::Move => {
|
||||
self.restack();
|
||||
cursor_data.dist_hor = cursor_data.x;
|
||||
cursor_data.dist_ver = cursor_data.y;
|
||||
}
|
||||
OpType::ResizeLeft => cursor_data.dist_hor = cursor_data.x,
|
||||
OpType::ResizeTop => cursor_data.dist_ver = cursor_data.y,
|
||||
OpType::ResizeRight => cursor_data.dist_hor = pos.width() - cursor_data.x,
|
||||
OpType::ResizeBottom => cursor_data.dist_ver = pos.height() - cursor_data.y,
|
||||
OpType::ResizeTopLeft => {
|
||||
cursor_data.dist_hor = cursor_data.x;
|
||||
cursor_data.dist_ver = cursor_data.y;
|
||||
}
|
||||
OpType::ResizeTopRight => {
|
||||
cursor_data.dist_hor = pos.width() - cursor_data.x;
|
||||
cursor_data.dist_ver = cursor_data.y;
|
||||
}
|
||||
OpType::ResizeBottomLeft => {
|
||||
cursor_data.dist_hor = cursor_data.x;
|
||||
cursor_data.dist_ver = pos.height() - cursor_data.y;
|
||||
}
|
||||
OpType::ResizeBottomRight => {
|
||||
cursor_data.dist_hor = pos.width() - cursor_data.x;
|
||||
cursor_data.dist_ver = pos.height() - cursor_data.y;
|
||||
}
|
||||
}
|
||||
} else if !pressed {
|
||||
cursor_data.op_active = false;
|
||||
let ws = cursor.output().ensure_workspace();
|
||||
self.set_workspace(&ws);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for FloatNode {
|
||||
|
|
@ -487,89 +578,89 @@ impl Node for FloatNode {
|
|||
if button != BTN_LEFT {
|
||||
return;
|
||||
}
|
||||
let mut seat_datas = self.seats.borrow_mut();
|
||||
let seat_data = match seat_datas.get_mut(&seat.id()) {
|
||||
Some(s) => s,
|
||||
_ => return,
|
||||
};
|
||||
if !seat_data.op_active {
|
||||
if state != KeyState::Pressed {
|
||||
return;
|
||||
}
|
||||
if seat_data.op_type == OpType::Move {
|
||||
if let Some(tl) = self.child.get() {
|
||||
tl.node_do_focus(seat, Direction::Unspecified);
|
||||
}
|
||||
}
|
||||
if seat_data
|
||||
.double_click_state
|
||||
.click(&self.state, time_usec, seat_data.x, seat_data.y)
|
||||
&& seat_data.op_type == OpType::Move
|
||||
{
|
||||
if let Some(tl) = self.child.get() {
|
||||
drop(seat_datas);
|
||||
seat.set_tl_floating(tl, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
seat_data.op_active = true;
|
||||
let pos = self.position.get();
|
||||
match seat_data.op_type {
|
||||
OpType::Move => {
|
||||
self.restack();
|
||||
seat_data.dist_hor = seat_data.x;
|
||||
seat_data.dist_ver = seat_data.y;
|
||||
}
|
||||
OpType::ResizeLeft => seat_data.dist_hor = seat_data.x,
|
||||
OpType::ResizeTop => seat_data.dist_ver = seat_data.y,
|
||||
OpType::ResizeRight => seat_data.dist_hor = pos.width() - seat_data.x,
|
||||
OpType::ResizeBottom => seat_data.dist_ver = pos.height() - seat_data.y,
|
||||
OpType::ResizeTopLeft => {
|
||||
seat_data.dist_hor = seat_data.x;
|
||||
seat_data.dist_ver = seat_data.y;
|
||||
}
|
||||
OpType::ResizeTopRight => {
|
||||
seat_data.dist_hor = pos.width() - seat_data.x;
|
||||
seat_data.dist_ver = seat_data.y;
|
||||
}
|
||||
OpType::ResizeBottomLeft => {
|
||||
seat_data.dist_hor = seat_data.x;
|
||||
seat_data.dist_ver = pos.height() - seat_data.y;
|
||||
}
|
||||
OpType::ResizeBottomRight => {
|
||||
seat_data.dist_hor = pos.width() - seat_data.x;
|
||||
seat_data.dist_ver = pos.height() - seat_data.y;
|
||||
}
|
||||
}
|
||||
} else if state == KeyState::Released {
|
||||
seat_data.op_active = false;
|
||||
let ws = seat.get_output().ensure_workspace();
|
||||
self.set_workspace(&ws);
|
||||
}
|
||||
self.button(
|
||||
CursorType::Seat(seat.id()),
|
||||
seat.pointer_cursor(),
|
||||
seat,
|
||||
time_usec,
|
||||
state == KeyState::Pressed,
|
||||
);
|
||||
}
|
||||
|
||||
fn node_on_pointer_enter(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, x: Fixed, y: Fixed) {
|
||||
self.pointer_move(seat, x.round_down(), y.round_down());
|
||||
self.pointer_move(
|
||||
CursorType::Seat(seat.id()),
|
||||
seat.pointer_cursor(),
|
||||
x,
|
||||
y,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
fn node_on_pointer_unfocus(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
let mut seats = self.seats.borrow_mut();
|
||||
if let Some(seat_state) = seats.get_mut(&seat.id()) {
|
||||
let mut cursors = self.cursors.borrow_mut();
|
||||
let id = CursorType::Seat(seat.id());
|
||||
if let Some(seat_state) = cursors.get_mut(&id) {
|
||||
seat_state.target = false;
|
||||
}
|
||||
}
|
||||
|
||||
fn node_on_pointer_focus(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
// log::info!("float focus");
|
||||
let mut seats = self.seats.borrow_mut();
|
||||
if let Some(seat_state) = seats.get_mut(&seat.id()) {
|
||||
let mut cursors = self.cursors.borrow_mut();
|
||||
let id = CursorType::Seat(seat.id());
|
||||
if let Some(seat_state) = cursors.get_mut(&id) {
|
||||
seat_state.target = true;
|
||||
seat.set_known_cursor(seat_state.cursor);
|
||||
seat.pointer_cursor().set_known(seat_state.cursor);
|
||||
}
|
||||
}
|
||||
|
||||
fn node_on_pointer_motion(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, x: Fixed, y: Fixed) {
|
||||
self.pointer_move(seat, x.round_down(), y.round_down());
|
||||
self.pointer_move(
|
||||
CursorType::Seat(seat.id()),
|
||||
seat.pointer_cursor(),
|
||||
x,
|
||||
y,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
fn node_on_tablet_tool_leave(&self, tool: &Rc<TabletTool>, _time_usec: u64) {
|
||||
let id = CursorType::TabletTool(tool.id);
|
||||
self.cursors.borrow_mut().remove(&id);
|
||||
}
|
||||
|
||||
fn node_on_tablet_tool_enter(
|
||||
self: Rc<Self>,
|
||||
tool: &Rc<TabletTool>,
|
||||
_time_usec: u64,
|
||||
x: Fixed,
|
||||
y: Fixed,
|
||||
) {
|
||||
tool.cursor().set_known(KnownCursor::Default);
|
||||
self.pointer_move(CursorType::TabletTool(tool.id), tool.cursor(), x, y, true);
|
||||
}
|
||||
|
||||
fn node_on_tablet_tool_apply_changes(
|
||||
self: Rc<Self>,
|
||||
tool: &Rc<TabletTool>,
|
||||
time_usec: u64,
|
||||
changes: Option<&TabletToolChanges>,
|
||||
x: Fixed,
|
||||
y: Fixed,
|
||||
) {
|
||||
self.pointer_move(CursorType::TabletTool(tool.id), tool.cursor(), x, y, false);
|
||||
if let Some(changes) = changes {
|
||||
if let Some(pressed) = changes.down {
|
||||
self.button(
|
||||
CursorType::TabletTool(tool.id),
|
||||
tool.cursor(),
|
||||
tool.seat(),
|
||||
time_usec,
|
||||
pressed,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn node_into_float(self: Rc<Self>) -> Option<Rc<FloatNode>> {
|
||||
|
|
|
|||
|
|
@ -11,8 +11,10 @@ use {
|
|||
wl_buffer::WlBufferStorage,
|
||||
wl_output::WlOutputGlobal,
|
||||
wl_seat::{
|
||||
collect_kb_foci2, wl_pointer::PendingScroll, NodeSeatState, SeatId, WlSeatGlobal,
|
||||
BTN_LEFT,
|
||||
collect_kb_foci2,
|
||||
tablet::{TabletTool, TabletToolChanges, TabletToolId},
|
||||
wl_pointer::PendingScroll,
|
||||
NodeSeatState, SeatId, WlSeatGlobal, BTN_LEFT,
|
||||
},
|
||||
wl_surface::{
|
||||
ext_session_lock_surface_v1::ExtSessionLockSurfaceV1,
|
||||
|
|
@ -63,7 +65,7 @@ pub struct OutputNode {
|
|||
pub is_dummy: bool,
|
||||
pub status: CloneCell<Rc<String>>,
|
||||
pub scroll: Scroller,
|
||||
pub pointer_positions: CopyHashMap<SeatId, (i32, i32)>,
|
||||
pub pointer_positions: CopyHashMap<PointerType, (i32, i32)>,
|
||||
pub lock_surface: CloneCell<Option<Rc<ExtSessionLockSurfaceV1>>>,
|
||||
pub hardware_cursor: CloneCell<Option<Rc<dyn HardwareCursor>>>,
|
||||
pub hardware_cursor_needs_render: Cell<bool>,
|
||||
|
|
@ -72,6 +74,12 @@ pub struct OutputNode {
|
|||
pub screencopies: CopyHashMap<(ClientId, ZwlrScreencopyFrameV1Id), Rc<ZwlrScreencopyFrameV1>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub enum PointerType {
|
||||
Seat(SeatId),
|
||||
TabletTool(TabletToolId),
|
||||
}
|
||||
|
||||
pub async fn output_render_data(state: Rc<State>) {
|
||||
loop {
|
||||
let container = state.pending_output_render_data.pop().await;
|
||||
|
|
@ -553,6 +561,10 @@ impl OutputNode {
|
|||
}
|
||||
}
|
||||
self.global.send_mode();
|
||||
for seat in self.state.globals.seats.lock().values() {
|
||||
seat.cursor_group().output_pos_changed(self)
|
||||
}
|
||||
self.state.tree_changed();
|
||||
}
|
||||
|
||||
pub fn find_layer_surface_at(
|
||||
|
|
@ -589,8 +601,9 @@ impl OutputNode {
|
|||
self.schedule_update_render_data();
|
||||
}
|
||||
|
||||
fn pointer_move(self: &Rc<Self>, seat: &Rc<WlSeatGlobal>, x: i32, y: i32) {
|
||||
self.pointer_positions.set(seat.id(), (x, y));
|
||||
fn pointer_move(self: &Rc<Self>, id: PointerType, x: Fixed, y: Fixed) {
|
||||
self.pointer_positions
|
||||
.set(id, (x.round_down(), y.round_down()));
|
||||
}
|
||||
|
||||
pub fn has_fullscreen(&self) -> bool {
|
||||
|
|
@ -637,6 +650,29 @@ impl OutputNode {
|
|||
set_layer_visible!(self.layers[2], visible);
|
||||
set_layer_visible!(self.layers[3], visible);
|
||||
}
|
||||
|
||||
fn button(self: Rc<Self>, id: PointerType) {
|
||||
let (x, y) = match self.pointer_positions.get(&id) {
|
||||
Some(p) => p,
|
||||
_ => return,
|
||||
};
|
||||
if y >= self.state.theme.sizes.title_height.get() {
|
||||
return;
|
||||
}
|
||||
let ws = 'ws: {
|
||||
let rd = self.render_data.borrow_mut();
|
||||
for title in &rd.titles {
|
||||
if x >= title.x1 && x < title.x2 {
|
||||
break 'ws title.ws.clone();
|
||||
}
|
||||
}
|
||||
return;
|
||||
};
|
||||
self.show_workspace(&ws);
|
||||
ws.flush_jay_workspaces();
|
||||
self.schedule_update_render_data();
|
||||
self.state.tree_changed();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OutputTitle {
|
||||
|
|
@ -840,26 +876,7 @@ impl Node for OutputNode {
|
|||
if state != KeyState::Pressed || button != BTN_LEFT {
|
||||
return;
|
||||
}
|
||||
let (x, y) = match self.pointer_positions.get(&seat.id()) {
|
||||
Some(p) => p,
|
||||
_ => return,
|
||||
};
|
||||
if y >= self.state.theme.sizes.title_height.get() {
|
||||
return;
|
||||
}
|
||||
let ws = 'ws: {
|
||||
let rd = self.render_data.borrow_mut();
|
||||
for title in &rd.titles {
|
||||
if x >= title.x1 && x < title.x2 {
|
||||
break 'ws title.ws.clone();
|
||||
}
|
||||
}
|
||||
return;
|
||||
};
|
||||
self.show_workspace(&ws);
|
||||
ws.flush_jay_workspaces();
|
||||
self.schedule_update_render_data();
|
||||
self.state.tree_changed();
|
||||
self.button(PointerType::Seat(seat.id()));
|
||||
}
|
||||
|
||||
fn node_on_axis_event(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, event: &PendingScroll) {
|
||||
|
|
@ -901,16 +918,49 @@ impl Node for OutputNode {
|
|||
}
|
||||
|
||||
fn node_on_pointer_enter(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, x: Fixed, y: Fixed) {
|
||||
self.pointer_move(seat, x.round_down(), y.round_down());
|
||||
self.pointer_move(PointerType::Seat(seat.id()), x, y);
|
||||
}
|
||||
|
||||
fn node_on_pointer_focus(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
// log::info!("output focus");
|
||||
seat.set_known_cursor(KnownCursor::Default);
|
||||
seat.pointer_cursor().set_known(KnownCursor::Default);
|
||||
}
|
||||
|
||||
fn node_on_pointer_motion(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, x: Fixed, y: Fixed) {
|
||||
self.pointer_move(seat, x.round_down(), y.round_down());
|
||||
self.pointer_move(PointerType::Seat(seat.id()), x, y);
|
||||
}
|
||||
|
||||
fn node_on_tablet_tool_leave(&self, tool: &Rc<TabletTool>, _time_usec: u64) {
|
||||
self.pointer_positions
|
||||
.remove(&PointerType::TabletTool(tool.id));
|
||||
}
|
||||
|
||||
fn node_on_tablet_tool_enter(
|
||||
self: Rc<Self>,
|
||||
tool: &Rc<TabletTool>,
|
||||
_time_usec: u64,
|
||||
x: Fixed,
|
||||
y: Fixed,
|
||||
) {
|
||||
tool.cursor().set_known(KnownCursor::Default);
|
||||
self.pointer_move(PointerType::TabletTool(tool.id), x, y);
|
||||
}
|
||||
|
||||
fn node_on_tablet_tool_apply_changes(
|
||||
self: Rc<Self>,
|
||||
tool: &Rc<TabletTool>,
|
||||
_time_usec: u64,
|
||||
changes: Option<&TabletToolChanges>,
|
||||
x: Fixed,
|
||||
y: Fixed,
|
||||
) {
|
||||
let id = PointerType::TabletTool(tool.id);
|
||||
self.pointer_move(id, x, y);
|
||||
if let Some(changes) = changes {
|
||||
if changes.down == Some(true) {
|
||||
self.button(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ impl Node for PlaceholderNode {
|
|||
}
|
||||
|
||||
fn node_on_pointer_enter(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, _x: Fixed, _y: Fixed) {
|
||||
seat.set_known_cursor(KnownCursor::Default);
|
||||
seat.pointer_cursor().set_known(KnownCursor::Default);
|
||||
seat.enter_toplevel(self.clone());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@ use {
|
|||
crate::{
|
||||
client::ClientId,
|
||||
cursor::KnownCursor,
|
||||
fixed::Fixed,
|
||||
ifs::{
|
||||
jay_workspace::JayWorkspace,
|
||||
wl_output::OutputId,
|
||||
wl_seat::{NodeSeatState, WlSeatGlobal},
|
||||
wl_seat::{tablet::TabletTool, NodeSeatState, WlSeatGlobal},
|
||||
wl_surface::WlSurface,
|
||||
},
|
||||
rect::Rect,
|
||||
|
|
@ -274,7 +275,17 @@ impl Node for WorkspaceNode {
|
|||
|
||||
fn node_on_pointer_focus(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
// log::info!("workspace focus");
|
||||
seat.set_known_cursor(KnownCursor::Default);
|
||||
seat.pointer_cursor().set_known(KnownCursor::Default);
|
||||
}
|
||||
|
||||
fn node_on_tablet_tool_enter(
|
||||
self: Rc<Self>,
|
||||
tool: &Rc<TabletTool>,
|
||||
_time_usec: u64,
|
||||
_x: Fixed,
|
||||
_y: Fixed,
|
||||
) {
|
||||
tool.cursor().set_known(KnownCursor::Default)
|
||||
}
|
||||
|
||||
fn node_into_workspace(self: Rc<Self>) -> Option<Rc<WorkspaceNode>> {
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ pub mod opaque_cell;
|
|||
pub mod option_ext;
|
||||
pub mod oserror;
|
||||
pub mod page_size;
|
||||
pub mod pending_serial;
|
||||
pub mod process_name;
|
||||
pub mod ptr_ext;
|
||||
pub mod queue;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,11 @@ use {
|
|||
utils::copyhashmap::{CopyHashMap, Locked},
|
||||
},
|
||||
ahash::AHashMap,
|
||||
std::{cell::RefCell, collections::hash_map::Entry, rc::Rc},
|
||||
std::{
|
||||
cell::{Ref, RefCell},
|
||||
collections::hash_map::Entry,
|
||||
rc::Rc,
|
||||
},
|
||||
};
|
||||
|
||||
pub struct Bindings<P> {
|
||||
|
|
@ -84,4 +88,8 @@ impl<P: Object> PerClientBindings<P> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn borrow(&self) -> Ref<AHashMap<ClientId, AHashMap<ObjectId, Rc<P>>>> {
|
||||
self.bindings.borrow()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ impl<K: Eq + Hash, V> CopyHashMap<K, V> {
|
|||
unsafe { self.map.get().deref().contains_key(k) }
|
||||
}
|
||||
|
||||
pub fn lock(&self) -> Locked<K, V> {
|
||||
pub fn lock(&self) -> Locked<'_, K, V> {
|
||||
Locked {
|
||||
source: self,
|
||||
map: self.clear(),
|
||||
|
|
|
|||
19
src/utils/pending_serial.rs
Normal file
19
src/utils/pending_serial.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
use crate::client::Client;
|
||||
|
||||
pub struct PendingSerial<'a> {
|
||||
serial: Option<u32>,
|
||||
client: &'a Client,
|
||||
}
|
||||
|
||||
impl<'a> PendingSerial<'a> {
|
||||
pub fn new(client: &'a Client) -> Self {
|
||||
Self {
|
||||
serial: None,
|
||||
client,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&mut self) -> u32 {
|
||||
*self.serial.get_or_insert_with(|| self.client.next_serial())
|
||||
}
|
||||
}
|
||||
|
|
@ -246,6 +246,7 @@ pub struct Input {
|
|||
pub transform_matrix: Option<[[f64; 2]; 2]>,
|
||||
pub keymap: Option<ConfigKeymap>,
|
||||
pub switch_actions: AHashMap<SwitchEvent, Action>,
|
||||
pub output: Option<Option<OutputMatch>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ impl ActionParser<'_> {
|
|||
.extract(val("input"))?
|
||||
.parse_map(&mut InputParser {
|
||||
cx: self.0,
|
||||
tag_ok: false,
|
||||
is_inputs_array: false,
|
||||
})
|
||||
.map_spanned_err(ActionParserError::ConfigureInput)?;
|
||||
Ok(Action::ConfigureInput { input })
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use {
|
|||
action::ActionParser,
|
||||
input_match::{InputMatchParser, InputMatchParserError},
|
||||
keymap::KeymapParser,
|
||||
output_match::OutputMatchParser,
|
||||
},
|
||||
Input,
|
||||
},
|
||||
|
|
@ -43,7 +44,7 @@ pub enum InputParserError {
|
|||
|
||||
pub struct InputParser<'a> {
|
||||
pub cx: &'a Context<'a>,
|
||||
pub tag_ok: bool,
|
||||
pub is_inputs_array: bool,
|
||||
}
|
||||
|
||||
impl<'a> Parser for InputParser<'a> {
|
||||
|
|
@ -77,6 +78,8 @@ impl<'a> Parser for InputParser<'a> {
|
|||
on_lid_closed_val,
|
||||
on_converted_to_laptop_val,
|
||||
on_converted_to_tablet_val,
|
||||
output_val,
|
||||
remove_mapping,
|
||||
),
|
||||
) = ext.extract((
|
||||
(
|
||||
|
|
@ -98,6 +101,8 @@ impl<'a> Parser for InputParser<'a> {
|
|||
opt(val("on-lid-closed")),
|
||||
opt(val("on-converted-to-laptop")),
|
||||
opt(val("on-converted-to-tablet")),
|
||||
opt(val("output")),
|
||||
recover(opt(bol("remove-mapping"))),
|
||||
),
|
||||
))?;
|
||||
let accel_profile = match accel_profile {
|
||||
|
|
@ -122,7 +127,7 @@ impl<'a> Parser for InputParser<'a> {
|
|||
},
|
||||
};
|
||||
if let Some(tag) = tag {
|
||||
if self.tag_ok {
|
||||
if self.is_inputs_array {
|
||||
self.cx.used.borrow_mut().defined_inputs.insert(tag.into());
|
||||
} else {
|
||||
log::warn!(
|
||||
|
|
@ -147,7 +152,7 @@ impl<'a> Parser for InputParser<'a> {
|
|||
let mut switch_actions = AHashMap::new();
|
||||
let mut parse_action = |val: Option<Spanned<&Value>>, name, event| {
|
||||
if let Some(val) = val {
|
||||
if !self.tag_ok {
|
||||
if !self.is_inputs_array {
|
||||
log::warn!(
|
||||
"{name} has no effect in this position: {}",
|
||||
self.cx.error3(val.span)
|
||||
|
|
@ -176,6 +181,39 @@ impl<'a> Parser for InputParser<'a> {
|
|||
"on-converted-to-tablet",
|
||||
SwitchEvent::ConvertedToTablet,
|
||||
);
|
||||
let mut output = None;
|
||||
if let Some(val) = output_val {
|
||||
match val.parse(&mut OutputMatchParser(self.cx)) {
|
||||
Ok(v) => output = Some(Some(v)),
|
||||
Err(e) => {
|
||||
log::warn!("Could not parse output: {}", self.cx.error(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(val) = remove_mapping {
|
||||
if self.is_inputs_array {
|
||||
log::warn!(
|
||||
"`remove-mapping` has no effect in this position: {}",
|
||||
self.cx.error3(val.span)
|
||||
);
|
||||
} else if !val.value {
|
||||
log::warn!(
|
||||
"`remove-mapping = false` has no effect: {}",
|
||||
self.cx.error3(val.span)
|
||||
);
|
||||
} else if let Some(output) = output_val {
|
||||
log::warn!(
|
||||
"Ignoring `remove-mapping = true` due to conflicting `output` field: {}",
|
||||
self.cx.error3(val.span)
|
||||
);
|
||||
log::info!(
|
||||
"`output` field defined here: {}",
|
||||
self.cx.error3(output.span)
|
||||
);
|
||||
} else {
|
||||
output = Some(None);
|
||||
}
|
||||
}
|
||||
Ok(Input {
|
||||
tag: tag.despan_into(),
|
||||
match_: match_val.parse_map(&mut InputMatchParser(self.cx))?,
|
||||
|
|
@ -190,6 +228,7 @@ impl<'a> Parser for InputParser<'a> {
|
|||
transform_matrix,
|
||||
keymap,
|
||||
switch_actions,
|
||||
output,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -206,7 +245,7 @@ impl<'a> Parser for InputsParser<'a> {
|
|||
for el in array {
|
||||
match el.parse(&mut InputParser {
|
||||
cx: self.0,
|
||||
tag_ok: true,
|
||||
is_inputs_array: true,
|
||||
}) {
|
||||
Ok(o) => res.push(o),
|
||||
Err(e) => {
|
||||
|
|
@ -228,7 +267,7 @@ impl<'a> Parser for InputsParser<'a> {
|
|||
);
|
||||
InputParser {
|
||||
cx: self.0,
|
||||
tag_ok: true,
|
||||
is_inputs_array: true,
|
||||
}
|
||||
.parse_table(span, table)
|
||||
.map(|v| vec![v])
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ use {
|
|||
exec::{set_env, unset_env, Command},
|
||||
get_workspace,
|
||||
input::{
|
||||
capability::CAP_SWITCH, get_seat, input_devices, on_new_input_device,
|
||||
FocusFollowsMouseMode, InputDevice, Seat, SwitchEvent,
|
||||
capability::CAP_SWITCH, get_seat, input_devices, on_input_device_removed,
|
||||
on_new_input_device, FocusFollowsMouseMode, InputDevice, Seat, SwitchEvent,
|
||||
},
|
||||
is_reload,
|
||||
keyboard::{Keymap, ModifiedKeySym},
|
||||
|
|
@ -28,9 +28,9 @@ use {
|
|||
switch_to_vt,
|
||||
theme::{reset_colors, reset_font, reset_sizes, set_font},
|
||||
video::{
|
||||
connectors, drm_devices, on_connector_connected, on_graphics_initialized,
|
||||
on_new_connector, on_new_drm_device, set_direct_scanout_enabled, set_gfx_api,
|
||||
Connector, DrmDevice,
|
||||
connectors, drm_devices, on_connector_connected, on_connector_disconnected,
|
||||
on_graphics_initialized, on_new_connector, on_new_drm_device,
|
||||
set_direct_scanout_enabled, set_gfx_api, Connector, DrmDevice,
|
||||
},
|
||||
},
|
||||
std::{cell::RefCell, io::ErrorKind, path::PathBuf, rc::Rc},
|
||||
|
|
@ -422,6 +422,17 @@ impl Input {
|
|||
c.set_keymap(km);
|
||||
}
|
||||
}
|
||||
if let Some(output) = &self.output {
|
||||
if let Some(output) = output {
|
||||
for connector in connectors() {
|
||||
if output.matches(connector, state) {
|
||||
c.set_connector(connector);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
c.remove_mapping();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -550,6 +561,10 @@ struct State {
|
|||
input_devices: AHashMap<String, InputMatch>,
|
||||
persistent: Rc<PersistentState>,
|
||||
keymaps: AHashMap<String, Keymap>,
|
||||
|
||||
io_maps: Vec<(InputMatch, OutputMatch)>,
|
||||
io_inputs: RefCell<AHashMap<InputDevice, Vec<bool>>>,
|
||||
io_outputs: RefCell<AHashMap<Connector, Vec<bool>>>,
|
||||
}
|
||||
|
||||
impl Drop for State {
|
||||
|
|
@ -701,6 +716,60 @@ impl State {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn add_io_output(&self, c: Connector) {
|
||||
let mappings: Vec<_> = self
|
||||
.io_maps
|
||||
.iter()
|
||||
.map(|(_, output)| output.matches(c, self))
|
||||
.collect();
|
||||
if mappings.len() > 0 {
|
||||
self.io_outputs.borrow_mut().insert(c, mappings);
|
||||
}
|
||||
}
|
||||
|
||||
fn add_io_input(&self, d: InputDevice) {
|
||||
let mappings: Vec<_> = self
|
||||
.io_maps
|
||||
.iter()
|
||||
.map(|(input, _)| input.matches(d, self))
|
||||
.collect();
|
||||
if mappings.len() > 0 {
|
||||
self.io_inputs.borrow_mut().insert(d, mappings);
|
||||
}
|
||||
}
|
||||
|
||||
fn map_input_to_output(&self, d: InputDevice) {
|
||||
let input_mappings = &*self.io_inputs.borrow();
|
||||
let Some(input_matches) = input_mappings.get(&d) else {
|
||||
return;
|
||||
};
|
||||
for (idx, &input_is_match) in input_matches.iter().enumerate() {
|
||||
if input_is_match {
|
||||
for (&c, output_maps) in &*self.io_outputs.borrow() {
|
||||
if output_maps.get(idx) == Some(&true) {
|
||||
d.set_connector(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn map_output_to_input(&self, c: Connector) {
|
||||
let output_mappings = &*self.io_outputs.borrow();
|
||||
let Some(output_matches) = output_mappings.get(&c) else {
|
||||
return;
|
||||
};
|
||||
for (idx, &output_is_match) in output_matches.iter().enumerate() {
|
||||
if output_is_match {
|
||||
for (&d, input_matches) in &*self.io_inputs.borrow() {
|
||||
if input_matches.get(idx) == Some(&true) {
|
||||
d.set_connector(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Hash)]
|
||||
|
|
@ -763,6 +832,7 @@ fn load_config(initial_load: bool, persistent: &Rc<PersistentState>) {
|
|||
}
|
||||
}
|
||||
let mut input_devices = AHashMap::new();
|
||||
let mut io_maps = vec![];
|
||||
for input in &config.inputs {
|
||||
if let Some(tag) = &input.tag {
|
||||
let prev = input_devices.insert(tag.clone(), input.match_.clone());
|
||||
|
|
@ -770,6 +840,9 @@ fn load_config(initial_load: bool, persistent: &Rc<PersistentState>) {
|
|||
log::warn!("Duplicate input tag {tag}");
|
||||
}
|
||||
}
|
||||
if let Some(Some(output)) = &input.output {
|
||||
io_maps.push((input.match_.clone(), output.clone()));
|
||||
}
|
||||
}
|
||||
let mut named_drm_device = AHashMap::new();
|
||||
for drm_device in &config.drm_devices {
|
||||
|
|
@ -786,6 +859,9 @@ fn load_config(initial_load: bool, persistent: &Rc<PersistentState>) {
|
|||
input_devices,
|
||||
persistent: persistent.clone(),
|
||||
keymaps,
|
||||
io_maps,
|
||||
io_inputs: Default::default(),
|
||||
io_outputs: Default::default(),
|
||||
});
|
||||
state.set_status(&config.status);
|
||||
let mut switch_actions = vec![];
|
||||
|
|
@ -827,6 +903,8 @@ fn load_config(initial_load: bool, persistent: &Rc<PersistentState>) {
|
|||
on_connector_connected({
|
||||
let state = state.clone();
|
||||
move |c| {
|
||||
state.add_io_output(c);
|
||||
state.map_output_to_input(c);
|
||||
let id = OutputId {
|
||||
manufacturer: c.manufacturer(),
|
||||
model: c.model(),
|
||||
|
|
@ -841,6 +919,12 @@ fn load_config(initial_load: bool, persistent: &Rc<PersistentState>) {
|
|||
}
|
||||
}
|
||||
});
|
||||
on_connector_disconnected({
|
||||
let state = state.clone();
|
||||
move |c| {
|
||||
state.io_outputs.borrow_mut().remove(&c);
|
||||
}
|
||||
});
|
||||
set_default_workspace_capture(config.workspace_capture);
|
||||
for (k, v) in config.env {
|
||||
set_env(&k, &v);
|
||||
|
|
@ -896,6 +980,7 @@ fn load_config(initial_load: bool, persistent: &Rc<PersistentState>) {
|
|||
let state = state.clone();
|
||||
let switch_actions = switch_actions.clone();
|
||||
move |c| {
|
||||
state.add_io_input(c);
|
||||
for input in &config.inputs {
|
||||
if input.match_.matches(c, &state) {
|
||||
input.apply(c, &state);
|
||||
|
|
@ -904,7 +989,18 @@ fn load_config(initial_load: bool, persistent: &Rc<PersistentState>) {
|
|||
state.handle_switch_device(c, &switch_actions);
|
||||
}
|
||||
});
|
||||
on_input_device_removed({
|
||||
let state = state.clone();
|
||||
move |c| {
|
||||
state.io_inputs.borrow_mut().remove(&c);
|
||||
}
|
||||
});
|
||||
for c in connectors() {
|
||||
state.add_io_output(c);
|
||||
}
|
||||
for c in jay_config::input::input_devices() {
|
||||
state.add_io_input(c);
|
||||
state.map_input_to_output(c);
|
||||
state.handle_switch_device(c, &switch_actions);
|
||||
}
|
||||
persistent
|
||||
|
|
|
|||
|
|
@ -841,6 +841,14 @@
|
|||
"on-converted-to-tablet": {
|
||||
"description": "An action to execute when the convertible device is converted to a tablet.\n\nThis should only be used in the top-level inputs array.\n",
|
||||
"$ref": "#/$defs/Action"
|
||||
},
|
||||
"output": {
|
||||
"description": "Maps this input device to an output.\n\nThis is used to map touch screen and graphics tablets to outputs.\n\n- Example:\n\n ```toml\n [[inputs]]\n match.name = \"Wacom Bamboo Comic 2FG Pen\"\n output.connector = \"DP-1\"\n ```\n",
|
||||
"$ref": "#/$defs/OutputMatch"
|
||||
},
|
||||
"remove-mapping": {
|
||||
"type": "boolean",
|
||||
"description": "Removes the mapping of from this device to an output.\n\nThis should only be used within `configure-input` actions.\n\n- Example:\n\n ```toml\n [shortcuts]\n alt-x = { type = \"configure-input\", input = { match.tag = \"wacom\", remove-mapping = true } }\n\n [[inputs]]\n tag = \"wacom\"\n match.name = \"Wacom Bamboo Comic 2FG Pen\"\n output.connector = \"DP-1\"\n ```\n"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
|
|
|||
|
|
@ -1685,6 +1685,42 @@ The table has the following fields:
|
|||
|
||||
The value of this field should be a [Action](#types-Action).
|
||||
|
||||
- `output` (optional):
|
||||
|
||||
Maps this input device to an output.
|
||||
|
||||
This is used to map touch screen and graphics tablets to outputs.
|
||||
|
||||
- Example:
|
||||
|
||||
```toml
|
||||
[[inputs]]
|
||||
match.name = "Wacom Bamboo Comic 2FG Pen"
|
||||
output.connector = "DP-1"
|
||||
```
|
||||
|
||||
The value of this field should be a [OutputMatch](#types-OutputMatch).
|
||||
|
||||
- `remove-mapping` (optional):
|
||||
|
||||
Removes the mapping of from this device to an output.
|
||||
|
||||
This should only be used within `configure-input` actions.
|
||||
|
||||
- Example:
|
||||
|
||||
```toml
|
||||
[shortcuts]
|
||||
alt-x = { type = "configure-input", input = { match.tag = "wacom", remove-mapping = true } }
|
||||
|
||||
[[inputs]]
|
||||
tag = "wacom"
|
||||
match.name = "Wacom Bamboo Comic 2FG Pen"
|
||||
output.connector = "DP-1"
|
||||
```
|
||||
|
||||
The value of this field should be a boolean.
|
||||
|
||||
|
||||
<a name="types-InputMatch"></a>
|
||||
### `InputMatch`
|
||||
|
|
|
|||
|
|
@ -1285,6 +1285,40 @@ Input:
|
|||
An action to execute when the convertible device is converted to a tablet.
|
||||
|
||||
This should only be used in the top-level inputs array.
|
||||
output:
|
||||
ref: OutputMatch
|
||||
required: false
|
||||
description: |
|
||||
Maps this input device to an output.
|
||||
|
||||
This is used to map touch screen and graphics tablets to outputs.
|
||||
|
||||
- Example:
|
||||
|
||||
```toml
|
||||
[[inputs]]
|
||||
match.name = "Wacom Bamboo Comic 2FG Pen"
|
||||
output.connector = "DP-1"
|
||||
```
|
||||
remove-mapping:
|
||||
kind: boolean
|
||||
required: false
|
||||
description: |
|
||||
Removes the mapping of from this device to an output.
|
||||
|
||||
This should only be used within `configure-input` actions.
|
||||
|
||||
- Example:
|
||||
|
||||
```toml
|
||||
[shortcuts]
|
||||
alt-x = { type = "configure-input", input = { match.tag = "wacom", remove-mapping = true } }
|
||||
|
||||
[[inputs]]
|
||||
tag = "wacom"
|
||||
match.name = "Wacom Bamboo Comic 2FG Pen"
|
||||
output.connector = "DP-1"
|
||||
```
|
||||
|
||||
|
||||
AccelProfile:
|
||||
|
|
|
|||
|
|
@ -109,6 +109,11 @@ request get_device_keymap {
|
|||
id: u32,
|
||||
}
|
||||
|
||||
request map_to_output {
|
||||
id: u32,
|
||||
output: optstr,
|
||||
}
|
||||
|
||||
# events
|
||||
|
||||
event seat {
|
||||
|
|
@ -148,3 +153,8 @@ event keymap {
|
|||
keymap: fd,
|
||||
keymap_len: u32,
|
||||
}
|
||||
|
||||
event input_device_output {
|
||||
id: u32,
|
||||
output: str,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -132,3 +132,110 @@ event switch_event {
|
|||
input_device: u32,
|
||||
event: u32,
|
||||
}
|
||||
|
||||
event tablet_tool_proximity_in {
|
||||
}
|
||||
|
||||
event tablet_tool_proximity_out {
|
||||
}
|
||||
|
||||
event tablet_tool_down {
|
||||
}
|
||||
|
||||
event tablet_tool_up {
|
||||
}
|
||||
|
||||
event tablet_tool_motion {
|
||||
x: fixed,
|
||||
y: fixed,
|
||||
}
|
||||
|
||||
event tablet_tool_pressure {
|
||||
pressure: pod(f64),
|
||||
}
|
||||
|
||||
event tablet_tool_distance {
|
||||
distance: pod(f64),
|
||||
}
|
||||
|
||||
event tablet_tool_tilt {
|
||||
tilt_x: pod(f64),
|
||||
tilt_y: pod(f64),
|
||||
}
|
||||
|
||||
event tablet_tool_rotation {
|
||||
degrees: pod(f64),
|
||||
}
|
||||
|
||||
event tablet_tool_slider {
|
||||
position: pod(f64),
|
||||
}
|
||||
|
||||
event tablet_tool_wheel {
|
||||
degrees: pod(f64),
|
||||
clicks: i32,
|
||||
}
|
||||
|
||||
event tablet_tool_button {
|
||||
button: u32,
|
||||
state: u32,
|
||||
}
|
||||
|
||||
event tablet_tool_frame {
|
||||
seat: u32,
|
||||
time_usec: pod(u64),
|
||||
input_device: u32,
|
||||
tool: u32,
|
||||
}
|
||||
|
||||
event tablet_pad_mode_switch {
|
||||
seat: u32,
|
||||
time_usec: pod(u64),
|
||||
input_device: u32,
|
||||
group: u32,
|
||||
mode: u32,
|
||||
}
|
||||
|
||||
event tablet_pad_button {
|
||||
seat: u32,
|
||||
time_usec: pod(u64),
|
||||
input_device: u32,
|
||||
button: u32,
|
||||
state: u32,
|
||||
}
|
||||
|
||||
event tablet_pad_strip_source {
|
||||
source: u32,
|
||||
}
|
||||
|
||||
event tablet_pad_strip_position {
|
||||
position: pod(f64),
|
||||
}
|
||||
|
||||
event tablet_pad_strip_stop {
|
||||
}
|
||||
|
||||
event tablet_pad_strip_frame {
|
||||
seat: u32,
|
||||
time_usec: pod(u64),
|
||||
input_device: u32,
|
||||
strip: u32,
|
||||
}
|
||||
|
||||
event tablet_pad_ring_source {
|
||||
source: u32,
|
||||
}
|
||||
|
||||
event tablet_pad_ring_angle {
|
||||
degrees: pod(f64),
|
||||
}
|
||||
|
||||
event tablet_pad_ring_stop {
|
||||
}
|
||||
|
||||
event tablet_pad_ring_frame {
|
||||
seat: u32,
|
||||
time_usec: pod(u64),
|
||||
input_device: u32,
|
||||
ring: u32,
|
||||
}
|
||||
|
|
|
|||
7
wire/zwp_tablet_manager_v2.txt
Normal file
7
wire/zwp_tablet_manager_v2.txt
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
request get_tablet_seat {
|
||||
tablet_seat: id(zwp_tablet_seat_v2),
|
||||
seat: id(wl_seat),
|
||||
}
|
||||
|
||||
request destroy {
|
||||
}
|
||||
27
wire/zwp_tablet_pad_group_v2.txt
Normal file
27
wire/zwp_tablet_pad_group_v2.txt
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
request destroy {
|
||||
}
|
||||
|
||||
event buttons {
|
||||
buttons: array(u32),
|
||||
}
|
||||
|
||||
event ring {
|
||||
ring: id(zwp_tablet_pad_ring_v2),
|
||||
}
|
||||
|
||||
event strip {
|
||||
strip: id(zwp_tablet_pad_strip_v2),
|
||||
}
|
||||
|
||||
event modes {
|
||||
modes: u32,
|
||||
}
|
||||
|
||||
event done {
|
||||
}
|
||||
|
||||
event mode_switch {
|
||||
time: u32,
|
||||
serial: u32,
|
||||
mode: u32,
|
||||
}
|
||||
22
wire/zwp_tablet_pad_ring_v2.txt
Normal file
22
wire/zwp_tablet_pad_ring_v2.txt
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
request set_feedback {
|
||||
description: str,
|
||||
serial: u32,
|
||||
}
|
||||
|
||||
request destroy {
|
||||
}
|
||||
|
||||
event source {
|
||||
source: u32,
|
||||
}
|
||||
|
||||
event angle {
|
||||
degrees: fixed,
|
||||
}
|
||||
|
||||
event stop {
|
||||
}
|
||||
|
||||
event frame {
|
||||
time: u32,
|
||||
}
|
||||
22
wire/zwp_tablet_pad_strip_v2.txt
Normal file
22
wire/zwp_tablet_pad_strip_v2.txt
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
request set_feedback {
|
||||
description: str,
|
||||
serial: u32,
|
||||
}
|
||||
|
||||
request destroy {
|
||||
}
|
||||
|
||||
event source {
|
||||
source: u32,
|
||||
}
|
||||
|
||||
event position {
|
||||
position: u32,
|
||||
}
|
||||
|
||||
event stop {
|
||||
}
|
||||
|
||||
event frame {
|
||||
time: u32,
|
||||
}
|
||||
43
wire/zwp_tablet_pad_v2.txt
Normal file
43
wire/zwp_tablet_pad_v2.txt
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
request set_feedback {
|
||||
button: u32,
|
||||
description: str,
|
||||
serial: u32,
|
||||
}
|
||||
|
||||
request destroy {
|
||||
}
|
||||
|
||||
event group {
|
||||
pad_group: id(zwp_tablet_pad_group_v2),
|
||||
}
|
||||
|
||||
event path {
|
||||
path: str,
|
||||
}
|
||||
|
||||
event buttons {
|
||||
buttons: u32,
|
||||
}
|
||||
|
||||
event done {
|
||||
}
|
||||
|
||||
event button {
|
||||
time: u32,
|
||||
button: u32,
|
||||
state: u32,
|
||||
}
|
||||
|
||||
event enter {
|
||||
serial: u32,
|
||||
tablet: id(zwp_tablet_v2),
|
||||
surface: id(wl_surface),
|
||||
}
|
||||
|
||||
event leave {
|
||||
serial: u32,
|
||||
surface: id(wl_surface),
|
||||
}
|
||||
|
||||
event removed {
|
||||
}
|
||||
14
wire/zwp_tablet_seat_v2.txt
Normal file
14
wire/zwp_tablet_seat_v2.txt
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
request destroy {
|
||||
}
|
||||
|
||||
event tablet_added {
|
||||
id: id(zwp_tablet_v2),
|
||||
}
|
||||
|
||||
event tool_added {
|
||||
id: id(zwp_tablet_tool_v2),
|
||||
}
|
||||
|
||||
event pad_added {
|
||||
id: id(zwp_tablet_pad_v2),
|
||||
}
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
# requests
|
||||
|
||||
request set_cursor {
|
||||
serial: u32,
|
||||
surface: id(wl_surface),
|
||||
|
|
@ -9,3 +7,84 @@ request set_cursor {
|
|||
|
||||
request destroy {
|
||||
}
|
||||
|
||||
event type {
|
||||
tool_type: u32,
|
||||
}
|
||||
|
||||
event hardware_serial {
|
||||
hardware_serial_hi: u32,
|
||||
hardware_serial_lo: u32,
|
||||
}
|
||||
|
||||
event hardware_id_wacom {
|
||||
hardware_id_hi: u32,
|
||||
hardware_id_lo: u32,
|
||||
}
|
||||
|
||||
event capability {
|
||||
capability: u32,
|
||||
}
|
||||
|
||||
event done {
|
||||
}
|
||||
|
||||
event removed {
|
||||
}
|
||||
|
||||
event proximity_in {
|
||||
serial: u32,
|
||||
tablet: id(zwp_tablet_v2),
|
||||
surface: id(wl_surface),
|
||||
}
|
||||
|
||||
event proximity_out {
|
||||
}
|
||||
|
||||
event down {
|
||||
serial: u32,
|
||||
}
|
||||
|
||||
event up {
|
||||
}
|
||||
|
||||
event motion {
|
||||
x: fixed,
|
||||
y: fixed,
|
||||
}
|
||||
|
||||
event pressure {
|
||||
pressure: u32,
|
||||
}
|
||||
|
||||
event distance {
|
||||
distance: u32,
|
||||
}
|
||||
|
||||
event tilt {
|
||||
tilt_x: fixed,
|
||||
tilt_y: fixed,
|
||||
}
|
||||
|
||||
event rotation {
|
||||
degrees: fixed,
|
||||
}
|
||||
|
||||
event slider {
|
||||
position: i32,
|
||||
}
|
||||
|
||||
event wheel {
|
||||
degrees: fixed,
|
||||
clicks: i32,
|
||||
}
|
||||
|
||||
event button {
|
||||
serial: u32,
|
||||
button: u32,
|
||||
state: u32,
|
||||
}
|
||||
|
||||
event frame {
|
||||
time: u32,
|
||||
}
|
||||
|
|
|
|||
21
wire/zwp_tablet_v2.txt
Normal file
21
wire/zwp_tablet_v2.txt
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
request destroy {
|
||||
}
|
||||
|
||||
event name {
|
||||
name: str,
|
||||
}
|
||||
|
||||
event id {
|
||||
vid: u32,
|
||||
pid: u32,
|
||||
}
|
||||
|
||||
event path {
|
||||
path: str,
|
||||
}
|
||||
|
||||
event done {
|
||||
}
|
||||
|
||||
event removed {
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue