mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-21 02:01:55 +07:00
Add Mouse{Left,Right,Middle,Back,Forward} binds
This commit is contained in:
committed by
Ivan Molodetskikh
parent
b16d7abb35
commit
4e33f45522
+16
-1
@@ -1124,6 +1124,11 @@ pub struct Key {
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
||||
pub enum Trigger {
|
||||
Keysym(Keysym),
|
||||
MouseLeft,
|
||||
MouseRight,
|
||||
MouseMiddle,
|
||||
MouseBack,
|
||||
MouseForward,
|
||||
WheelScrollDown,
|
||||
WheelScrollUp,
|
||||
WheelScrollLeft,
|
||||
@@ -2924,7 +2929,17 @@ impl FromStr for Key {
|
||||
}
|
||||
}
|
||||
|
||||
let trigger = if key.eq_ignore_ascii_case("WheelScrollDown") {
|
||||
let trigger = if key.eq_ignore_ascii_case("MouseLeft") {
|
||||
Trigger::MouseLeft
|
||||
} else if key.eq_ignore_ascii_case("MouseRight") {
|
||||
Trigger::MouseRight
|
||||
} else if key.eq_ignore_ascii_case("MouseMiddle") {
|
||||
Trigger::MouseMiddle
|
||||
} else if key.eq_ignore_ascii_case("MouseBack") {
|
||||
Trigger::MouseBack
|
||||
} else if key.eq_ignore_ascii_case("MouseForward") {
|
||||
Trigger::MouseForward
|
||||
} else if key.eq_ignore_ascii_case("WheelScrollDown") {
|
||||
Trigger::WheelScrollDown
|
||||
} else if key.eq_ignore_ascii_case("WheelScrollUp") {
|
||||
Trigger::WheelScrollUp
|
||||
|
||||
+55
-12
@@ -1656,11 +1656,43 @@ impl State {
|
||||
|
||||
let serial = SERIAL_COUNTER.next_serial();
|
||||
|
||||
let button = event.button_code();
|
||||
let button = event.button();
|
||||
|
||||
let button_code = event.button_code();
|
||||
|
||||
let button_state = event.state();
|
||||
|
||||
// Ignore release events for mouse clicks that triggered a bind.
|
||||
if self.niri.suppressed_buttons.remove(&button_code) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ButtonState::Pressed == button_state {
|
||||
let mods = self.niri.seat.get_keyboard().unwrap().modifier_state();
|
||||
let modifiers = modifiers_from_state(mods);
|
||||
|
||||
if self.niri.mods_with_mouse_binds.contains(&modifiers) {
|
||||
let comp_mod = self.backend.mod_key();
|
||||
|
||||
if let Some(bind) = match button {
|
||||
Some(MouseButton::Left) => Some(Trigger::MouseLeft),
|
||||
Some(MouseButton::Right) => Some(Trigger::MouseRight),
|
||||
Some(MouseButton::Middle) => Some(Trigger::MouseMiddle),
|
||||
Some(MouseButton::Back) => Some(Trigger::MouseBack),
|
||||
Some(MouseButton::Forward) => Some(Trigger::MouseForward),
|
||||
_ => None,
|
||||
}
|
||||
.and_then(|trigger| {
|
||||
let config = self.niri.config.borrow();
|
||||
let bindings = &config.binds;
|
||||
find_configured_bind(bindings, comp_mod, trigger, mods)
|
||||
}) {
|
||||
self.niri.suppressed_buttons.insert(button_code);
|
||||
self.handle_bind(bind.clone());
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
// We received an event for the regular pointer, so show it now.
|
||||
self.niri.pointer_hidden = false;
|
||||
self.niri.tablet_cursor_location = None;
|
||||
@@ -1669,8 +1701,7 @@ impl State {
|
||||
let window = mapped.window.clone();
|
||||
|
||||
// Check if we need to start an interactive move.
|
||||
if event.button() == Some(MouseButton::Left) && !pointer.is_grabbed() {
|
||||
let mods = self.niri.seat.get_keyboard().unwrap().modifier_state();
|
||||
if button == Some(MouseButton::Left) && !pointer.is_grabbed() {
|
||||
let mod_down = match self.backend.mod_key() {
|
||||
CompositorMod::Super => mods.logo,
|
||||
CompositorMod::Alt => mods.alt,
|
||||
@@ -1689,7 +1720,7 @@ impl State {
|
||||
) {
|
||||
let start_data = PointerGrabStartData {
|
||||
focus: None,
|
||||
button: event.button_code(),
|
||||
button: button_code,
|
||||
location,
|
||||
};
|
||||
let grab = MoveGrab::new(start_data, window.clone());
|
||||
@@ -1701,8 +1732,7 @@ impl State {
|
||||
}
|
||||
}
|
||||
// Check if we need to start an interactive resize.
|
||||
else if event.button() == Some(MouseButton::Right) && !pointer.is_grabbed() {
|
||||
let mods = self.niri.seat.get_keyboard().unwrap().modifier_state();
|
||||
else if button == Some(MouseButton::Right) && !pointer.is_grabbed() {
|
||||
let mod_down = match self.backend.mod_key() {
|
||||
CompositorMod::Super => mods.logo,
|
||||
CompositorMod::Alt => mods.alt,
|
||||
@@ -1762,7 +1792,7 @@ impl State {
|
||||
{
|
||||
let start_data = PointerGrabStartData {
|
||||
focus: None,
|
||||
button: event.button_code(),
|
||||
button: button_code,
|
||||
location,
|
||||
};
|
||||
let grab = ResizeGrab::new(start_data, window.clone());
|
||||
@@ -1786,8 +1816,7 @@ impl State {
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
|
||||
if event.button() == Some(MouseButton::Middle) && !pointer.is_grabbed() {
|
||||
let mods = self.niri.seat.get_keyboard().unwrap().modifier_state();
|
||||
if button == Some(MouseButton::Middle) && !pointer.is_grabbed() {
|
||||
let mod_down = match self.backend.mod_key() {
|
||||
CompositorMod::Super => mods.logo,
|
||||
CompositorMod::Alt => mods.alt,
|
||||
@@ -1797,7 +1826,7 @@ impl State {
|
||||
let location = pointer.current_location();
|
||||
let start_data = PointerGrabStartData {
|
||||
focus: None,
|
||||
button: event.button_code(),
|
||||
button: button_code,
|
||||
location,
|
||||
};
|
||||
let grab = SpatialMovementGrab::new(start_data, output);
|
||||
@@ -1817,7 +1846,7 @@ impl State {
|
||||
self.niri.focus_layer_surface_if_on_demand(layer_under);
|
||||
}
|
||||
|
||||
if let Some(button) = event.button() {
|
||||
if let Some(button) = button {
|
||||
let pos = pointer.current_location();
|
||||
if let Some((output, _)) = self.niri.output_under(pos) {
|
||||
let output = output.clone();
|
||||
@@ -1845,7 +1874,7 @@ impl State {
|
||||
pointer.button(
|
||||
self,
|
||||
&ButtonEvent {
|
||||
button,
|
||||
button: button_code,
|
||||
state: button_state,
|
||||
serial,
|
||||
time: event.time_msec(),
|
||||
@@ -3110,6 +3139,20 @@ pub fn mods_with_binds(
|
||||
rv
|
||||
}
|
||||
|
||||
pub fn mods_with_mouse_binds(comp_mod: CompositorMod, binds: &Binds) -> HashSet<Modifiers> {
|
||||
mods_with_binds(
|
||||
comp_mod,
|
||||
binds,
|
||||
&[
|
||||
Trigger::MouseLeft,
|
||||
Trigger::MouseRight,
|
||||
Trigger::MouseMiddle,
|
||||
Trigger::MouseBack,
|
||||
Trigger::MouseForward,
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
pub fn mods_with_wheel_binds(comp_mod: CompositorMod, binds: &Binds) -> HashSet<Modifiers> {
|
||||
mods_with_binds(
|
||||
comp_mod,
|
||||
|
||||
+10
-1
@@ -116,7 +116,8 @@ use crate::frame_clock::FrameClock;
|
||||
use crate::handlers::{configure_lock_surface, XDG_ACTIVATION_TOKEN_TIMEOUT};
|
||||
use crate::input::scroll_tracker::ScrollTracker;
|
||||
use crate::input::{
|
||||
apply_libinput_settings, mods_with_finger_scroll_binds, mods_with_wheel_binds, TabletData,
|
||||
apply_libinput_settings, mods_with_finger_scroll_binds, mods_with_mouse_binds,
|
||||
mods_with_wheel_binds, TabletData,
|
||||
};
|
||||
use crate::ipc::server::IpcServer;
|
||||
use crate::layer::mapped::LayerSurfaceRenderElement;
|
||||
@@ -277,6 +278,8 @@ pub struct Niri {
|
||||
pub seat: Seat<State>,
|
||||
/// Scancodes of the keys to suppress.
|
||||
pub suppressed_keys: HashSet<Keycode>,
|
||||
/// Button codes of the mouse buttons to suppress.
|
||||
pub suppressed_buttons: HashSet<u32>,
|
||||
pub bind_cooldown_timers: HashMap<Key, RegistrationToken>,
|
||||
pub bind_repeat_timer: Option<RegistrationToken>,
|
||||
pub keyboard_focus: KeyboardFocus,
|
||||
@@ -313,6 +316,7 @@ pub struct Niri {
|
||||
pub gesture_swipe_3f_cumulative: Option<(f64, f64)>,
|
||||
pub vertical_wheel_tracker: ScrollTracker,
|
||||
pub horizontal_wheel_tracker: ScrollTracker,
|
||||
pub mods_with_mouse_binds: HashSet<Modifiers>,
|
||||
pub mods_with_wheel_binds: HashSet<Modifiers>,
|
||||
pub vertical_finger_scroll_tracker: ScrollTracker,
|
||||
pub horizontal_finger_scroll_tracker: ScrollTracker,
|
||||
@@ -1142,6 +1146,8 @@ impl State {
|
||||
|
||||
if config.binds != old_config.binds {
|
||||
self.niri.hotkey_overlay.on_hotkey_config_updated();
|
||||
self.niri.mods_with_mouse_binds =
|
||||
mods_with_mouse_binds(self.backend.mod_key(), &config.binds);
|
||||
self.niri.mods_with_wheel_binds =
|
||||
mods_with_wheel_binds(self.backend.mod_key(), &config.binds);
|
||||
self.niri.mods_with_finger_scroll_binds =
|
||||
@@ -1846,6 +1852,7 @@ impl Niri {
|
||||
let cursor_manager =
|
||||
CursorManager::new(&config_.cursor.xcursor_theme, config_.cursor.xcursor_size);
|
||||
|
||||
let mods_with_mouse_binds = mods_with_mouse_binds(backend.mod_key(), &config_.binds);
|
||||
let mods_with_wheel_binds = mods_with_wheel_binds(backend.mod_key(), &config_.binds);
|
||||
let mods_with_finger_scroll_binds =
|
||||
mods_with_finger_scroll_binds(backend.mod_key(), &config_.binds);
|
||||
@@ -1998,6 +2005,7 @@ impl Niri {
|
||||
popups: PopupManager::default(),
|
||||
popup_grab: None,
|
||||
suppressed_keys: HashSet::new(),
|
||||
suppressed_buttons: HashSet::new(),
|
||||
bind_cooldown_timers: HashMap::new(),
|
||||
bind_repeat_timer: Option::default(),
|
||||
presentation_state,
|
||||
@@ -2025,6 +2033,7 @@ impl Niri {
|
||||
gesture_swipe_3f_cumulative: None,
|
||||
vertical_wheel_tracker: ScrollTracker::new(120),
|
||||
horizontal_wheel_tracker: ScrollTracker::new(120),
|
||||
mods_with_mouse_binds,
|
||||
mods_with_wheel_binds,
|
||||
|
||||
// 10 is copied from Clutter: DISCRETE_SCROLL_STEP.
|
||||
|
||||
@@ -415,6 +415,11 @@ fn key_name(comp_mod: CompositorMod, key: &Key) -> String {
|
||||
|
||||
let pretty = match key.trigger {
|
||||
Trigger::Keysym(keysym) => prettify_keysym_name(&keysym_get_name(keysym)),
|
||||
Trigger::MouseLeft => String::from("Mouse Left"),
|
||||
Trigger::MouseRight => String::from("Mouse Right"),
|
||||
Trigger::MouseMiddle => String::from("Mouse Middle"),
|
||||
Trigger::MouseBack => String::from("Mouse Back"),
|
||||
Trigger::MouseForward => String::from("Mouse Forward"),
|
||||
Trigger::WheelScrollDown => String::from("Wheel Scroll Down"),
|
||||
Trigger::WheelScrollUp => String::from("Wheel Scroll Up"),
|
||||
Trigger::WheelScrollLeft => String::from("Wheel Scroll Left"),
|
||||
|
||||
@@ -99,6 +99,26 @@ binds {
|
||||
Both mouse wheel and touchpad scroll binds will prevent applications from receiving any scroll events when their modifiers are held down.
|
||||
For example, if you have a `Mod+WheelScrollDown` bind, then while holding `Mod`, all mouse wheel scrolling will be consumed by niri.
|
||||
|
||||
### Mouse Click Bindings
|
||||
|
||||
<sup>Since: next release</sup>
|
||||
|
||||
You can bind mouse clicks using the following syntax.
|
||||
|
||||
```kdl
|
||||
binds {
|
||||
Mod+MouseLeft { close-window; }
|
||||
Mod+MouseRight { close-window; }
|
||||
Mod+MouseMiddle { close-window; }
|
||||
Mod+MouseForward { close-window; }
|
||||
Mod+MouseBack { close-window; }
|
||||
}
|
||||
```
|
||||
|
||||
Mouse clicks operate on the window that was focused at the time of the click, not the window you're clicking.
|
||||
|
||||
Note that binding `Mod+MouseLeft` or `Mod+MouseRight` will override the corresponding gesture (moving or resizing the window).
|
||||
|
||||
### Actions
|
||||
|
||||
Every action that you can bind is also available for programmatic invocation via `niri msg action`.
|
||||
|
||||
Reference in New Issue
Block a user