Skip to content

Commit 5673677

Browse files
committed
Support pointer actions
1 parent e9c86d5 commit 5673677

File tree

5 files changed

+151
-9
lines changed

5 files changed

+151
-9
lines changed

src/config.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ pub struct Config {
4848

4949
input_text: InputText,
5050
list_items: ListItems,
51+
mouse: Mouse,
5152
}
5253

5354
impl Config {
@@ -111,6 +112,13 @@ struct Icon {
111112
fallback_icon_path: Option<PathBuf>,
112113
}
113114

115+
#[derive(Defaults, Deserialize)]
116+
#[serde(default)]
117+
struct Mouse {
118+
launch_on_middle: bool,
119+
wheel_scroll_multiplier: f64,
120+
}
121+
114122
fn default_config_path() -> Result<Option<PathBuf>> {
115123
let file = xdg::BaseDirectories::with_prefix(crate::prog_name!())
116124
.context("failed to get xdg dirs")?

src/config/params.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::desktop::IconConfig;
1010
use crate::draw::{BgParams, InputTextParams, ListParams};
1111
use crate::font::{Font, FontBackend, InnerFont};
1212
use crate::icon::Icon;
13-
use crate::window::Params as WindowParams;
13+
use crate::window::{Params as WindowParams, PointerParams};
1414

1515
macro_rules! select_conf {
1616
($config:ident, $inner:ident, $field:ident) => {
@@ -123,6 +123,15 @@ impl<'a> From<&'a Config> for Option<IconConfig> {
123123
}
124124
}
125125

126+
impl<'a> From<&'a Config> for PointerParams {
127+
fn from(config: &'a Config) -> Self {
128+
Self {
129+
launch_on_middle: config.mouse.launch_on_middle,
130+
wheel_scroll_multiplier: config.mouse.wheel_scroll_multiplier,
131+
}
132+
}
133+
}
134+
126135
fn default_font() -> Font {
127136
std::thread_local! {
128137
static DEFAULT_FONT: Lazy<Font> = Lazy::new(|| Rc::new(InnerFont::default()));

src/window.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use anyhow::Context;
22
use sctk::{
3-
delegate_compositor, delegate_keyboard, delegate_layer, delegate_output, delegate_registry,
4-
delegate_seat, delegate_shm, delegate_xdg_shell, delegate_xdg_window,
3+
delegate_compositor, delegate_keyboard, delegate_layer, delegate_output, delegate_pointer,
4+
delegate_registry, delegate_seat, delegate_shm, delegate_xdg_shell, delegate_xdg_window,
55
output::OutputState,
66
reexports::client::{
7-
protocol::{wl_keyboard::WlKeyboard, wl_surface::WlSurface},
7+
protocol::{wl_keyboard::WlKeyboard, wl_pointer::WlPointer, wl_surface::WlSurface},
88
*,
99
},
1010
reexports::{
@@ -25,11 +25,13 @@ use sctk::{
2525
};
2626

2727
use crate::state::State;
28+
pub use pointer::Params as PointerParams;
2829

2930
mod compositor;
3031
mod keyboard;
3132
mod layer_shell;
3233
mod output;
34+
mod pointer;
3335
mod registry;
3436
mod seat;
3537
mod shm;
@@ -60,15 +62,21 @@ pub struct Window {
6062
height: u32,
6163
scale: u16,
6264

63-
keyboard: Option<WlKeyboard>,
65+
input: InputSource,
6466
key_modifiers: sctk::seat::keyboard::Modifiers,
67+
wheel_scroll_pending: f64,
6568

6669
loop_handle: LoopHandle<'static, Window>,
6770
exit: bool,
6871

6972
error: Option<anyhow::Error>,
7073
}
7174

75+
struct InputSource {
76+
keyboard: Option<WlKeyboard>,
77+
pointer: Option<WlPointer>,
78+
}
79+
7280
enum RenderSurface {
7381
Xdg(xdg_win::Window),
7482
LayerShell(wlr_layer::LayerSurface),
@@ -159,8 +167,12 @@ impl Window {
159167
width,
160168
height,
161169
scale,
162-
keyboard: None,
170+
input: InputSource {
171+
keyboard: None,
172+
pointer: None,
173+
},
163174
key_modifiers: Default::default(),
175+
wheel_scroll_pending: 0.0,
164176
loop_handle: event_loop.handle(),
165177
exit: false,
166178
error: None,
@@ -276,3 +288,4 @@ delegate_xdg_shell!(Window);
276288
delegate_layer!(Window);
277289
delegate_xdg_window!(Window);
278290
delegate_registry!(Window);
291+
delegate_pointer!(Window);

src/window/pointer.rs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
use sctk::reexports::client::protocol::wl_pointer::AxisSource;
2+
use sctk::reexports::client::{protocol, Connection, QueueHandle};
3+
use sctk::seat::keyboard::Modifiers;
4+
use sctk::seat::pointer::{PointerEvent, PointerEventKind, PointerHandler, *};
5+
6+
use super::Window;
7+
8+
// According to https://wayland.freedesktop.org/libinput/doc/1.19.0/wheel-api.html
9+
// wheel typically has this angle per step.
10+
// This actually should be configured and auto-detected (from udev probably?) but
11+
// for now it should work for most cases and could be tuned via config.
12+
const SCROLL_PER_STEP: f64 = 15.0;
13+
14+
pub struct Params {
15+
pub launch_on_middle: bool,
16+
pub wheel_scroll_multiplier: f64,
17+
}
18+
19+
impl PointerHandler for Window {
20+
fn pointer_frame(
21+
&mut self,
22+
_conn: &Connection,
23+
_qh: &QueueHandle<Self>,
24+
_pointer: &protocol::wl_pointer::WlPointer,
25+
events: &[PointerEvent],
26+
) {
27+
let mut changed = false;
28+
let config = self.config.param::<Params>();
29+
30+
for event in events {
31+
// Ignore events for other surfaces
32+
if event.surface != *self.surface {
33+
continue;
34+
}
35+
36+
match event.kind {
37+
// TODO: implement precise clicks on items
38+
// PointerEventKind::Release {
39+
// button: BTN_LEFT, ..
40+
// } => ..,
41+
PointerEventKind::Release {
42+
button: BTN_MIDDLE, ..
43+
} if config.launch_on_middle => {
44+
let with_fork = matches!(self.key_modifiers, Modifiers { ctrl: true, .. });
45+
if let Err(err) = self.state.eval_input(with_fork) {
46+
self.error = Some(err);
47+
}
48+
}
49+
PointerEventKind::Release {
50+
button: BTN_RIGHT, ..
51+
} => self.exit = true,
52+
PointerEventKind::Release {
53+
button: BTN_BACK, ..
54+
} => self.state.prev_subitem(),
55+
PointerEventKind::Release {
56+
button: BTN_FORWARD,
57+
..
58+
} => self.state.next_subitem(),
59+
PointerEventKind::Axis {
60+
vertical:
61+
AxisScroll {
62+
absolute,
63+
discrete: _,
64+
// XXX: handle this one?
65+
stop: _,
66+
},
67+
source:
68+
Some(AxisSource::Wheel)
69+
| Some(AxisSource::Finger)
70+
| Some(AxisSource::Continuous),
71+
time: _,
72+
horizontal: _,
73+
} => {
74+
self.wheel_scroll_pending += absolute;
75+
}
76+
PointerEventKind::Enter { .. }
77+
| PointerEventKind::Leave { .. }
78+
| PointerEventKind::Motion { .. }
79+
| PointerEventKind::Press { .. }
80+
| PointerEventKind::Release { .. }
81+
| PointerEventKind::Axis { .. } => continue,
82+
}
83+
changed = true;
84+
}
85+
86+
if changed {
87+
let scroll_per_step = SCROLL_PER_STEP
88+
* if config.wheel_scroll_multiplier > 0.0 {
89+
config.wheel_scroll_multiplier
90+
} else {
91+
1.0
92+
};
93+
let wheel_steps = (self.wheel_scroll_pending / scroll_per_step) as i32;
94+
if wheel_steps != 0 {
95+
self.wheel_scroll_pending -= f64::from(wheel_steps) * scroll_per_step;
96+
}
97+
let is_wheel_down = wheel_steps > 0;
98+
for _ in 0..wheel_steps.abs() {
99+
if is_wheel_down {
100+
self.state.next_item();
101+
} else {
102+
self.state.prev_item();
103+
}
104+
}
105+
}
106+
}
107+
}

src/window/seat.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ impl SeatHandler for Window {
1818
capability: Capability,
1919
) {
2020
match capability {
21-
Capability::Keyboard if self.keyboard.is_none() => {
21+
Capability::Keyboard if self.input.keyboard.is_none() => {
2222
let wl_keyboard = match self.seat_state.get_keyboard_with_repeat(
2323
qh,
2424
&seat,
@@ -32,7 +32,12 @@ impl SeatHandler for Window {
3232
return;
3333
}
3434
};
35-
self.keyboard = Some(wl_keyboard);
35+
self.input.keyboard = Some(wl_keyboard);
36+
}
37+
Capability::Pointer if self.input.pointer.is_none() => {
38+
if let Ok(p) = self.seat_state.get_pointer(qh, &seat) {
39+
self.input.pointer = Some(p);
40+
}
3641
}
3742
_ => {}
3843
}
@@ -46,7 +51,7 @@ impl SeatHandler for Window {
4651
capability: Capability,
4752
) {
4853
if let Capability::Keyboard = capability {
49-
if let Some(k) = self.keyboard.take() {
54+
if let Some(k) = self.input.keyboard.take() {
5055
k.release();
5156
}
5257
}

0 commit comments

Comments
 (0)