Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ Released binaries are not codesigned with a Developer ID, so after every upgrade
|-----|--------|
| Space | Click |
| Enter | Double click |
| `m` | Triple click |
| Delete | Right click |
| Escape | Close overlay |
| Backspace | Undo last step |
Expand Down Expand Up @@ -114,7 +115,7 @@ An alternative grid mode that recursively subdivides. Instead of two-key combos

- Enter bisect mode: press `b` from normal mode, or launch with `--bisect`
- Each hint key splits the current region into a smaller grid (2x2 by default)
- Space / Enter / Delete act (click / double-click / right-click) at the center of the current region and exit
- Space / Enter / `m` / Delete act (click / double-click / triple-click / right-click) at the center of the current region and exit
- Backspace pops back up one level
- Subdivision stops automatically once a cell would fall below `min_cell_size` pixels — the region is highlighted and you can act or back out
- Press `n` to switch back to normal mode
Expand All @@ -126,7 +127,7 @@ An alternative mode for direct keyboard-driven cursor movement. Instead of selec
- Enter free mode: press `v` from normal or bisect mode, or launch with `--free` (starts at mouse position) or `--free-center` (starts at screen center)
- Move the cursor with `h` / `j` / `k` / `l` (left / down / up / right)
- Press `=` to increase speed and `-` to decrease it; the current speed is shown in the top-left corner
- Space / Enter / Delete act (click / double-click / right-click) and exit
- Space / Enter / `m` / Delete act (click / double-click / triple-click / right-click) and exit
- Backspace returns to the previous mode; Escape closes the overlay
- Key repeat is supported: hold a movement key to move continuously

Expand All @@ -138,7 +139,7 @@ An alternative mode for direct keyboard-driven cursor movement. Instead of selec

Record multi-step mouse sequences for replay.

**Record:** Press `` ` `` to start recording. Navigate and act normally (Space to click, Enter to double-click, Tab to hover-only, `/` to drag). Press `` ` `` again to stop. You'll be prompted for an optional bind key and a name.
**Record:** Press `` ` `` to start recording. Navigate and act normally (Space to click, Enter to double-click, `m` to triple-click, Tab to hover-only, `/` to drag). Press `` ` `` again to stop. You'll be prompted for an optional bind key and a name.

**Replay:** Press `@` then the bind key. Or press Tab to search by name, then Enter to select.

Expand Down Expand Up @@ -178,6 +179,7 @@ bisect = "b"
free_mode = "v"
click = "space"
double_click = "enter"
triple_click = "m"
close = "escape"
undo = "backspace"
right_click = "delete"
Expand Down
16 changes: 16 additions & 0 deletions src/backend/macos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,22 @@ impl Backend for MacosBackend {
Ok(())
}

fn triple_click(&mut self, x: u32, y: u32) -> Result<()> {
self.hide_overlay();
self.focus_target_app();
for count in 1..=3 {
self.click_at(
K_CGEVENT_LEFT_MOUSE_DOWN,
K_CGEVENT_LEFT_MOUSE_UP,
K_CGMOUSE_BUTTON_LEFT,
x,
y,
count,
);
}
Ok(())
}

fn right_click(&mut self, x: u32, y: u32) -> Result<()> {
self.hide_overlay();
self.focus_target_app();
Expand Down
4 changes: 4 additions & 0 deletions src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub enum KeyEvent {
Char(char),
Click,
DoubleClick,
TripleClick,
RightClick,
Close,
Undo,
Expand Down Expand Up @@ -43,6 +44,9 @@ pub trait Backend {
/// Tear down the overlay, double click at (x, y), then return.
fn double_click(&mut self, x: u32, y: u32) -> Result<()>;

/// Tear down the overlay, triple click at (x, y), then return.
fn triple_click(&mut self, x: u32, y: u32) -> Result<()>;

/// Tear down the overlay, right click at (x, y), then return.
fn right_click(&mut self, x: u32, y: u32) -> Result<()>;

Expand Down
26 changes: 26 additions & 0 deletions src/backend/wayland.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,32 @@ impl Backend for WaylandBackend {
Ok(())
}

fn triple_click(&mut self, x: u32, y: u32) -> Result<()> {
self.teardown_surface()?;

if let Some(vp) = &self.state.vp {
vp.motion_absolute(timestamp(), x, y, self.state.screen_w, self.state.screen_h);
vp.frame();
}
self.eq
.roundtrip(&mut self.state)
.context("roundtrip after motion")?;

if let Some(vp) = &self.state.vp {
for _ in 0..3 {
let ts = timestamp();
vp.button(ts, BTN_LEFT, wl_pointer::ButtonState::Pressed);
vp.frame();
vp.button(ts, BTN_LEFT, wl_pointer::ButtonState::Released);
vp.frame();
}
}
self.eq
.roundtrip(&mut self.state)
.context("roundtrip after click")?;
Ok(())
}

/// Destroy the overlay so the compositor removes it from the surface stack,
/// then re-send motion to trigger a focus update, then right click.
fn right_click(&mut self, x: u32, y: u32) -> Result<()> {
Expand Down
8 changes: 8 additions & 0 deletions src/backend/x11.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,14 @@ impl Backend for X11Backend {
self.fake_button_click(BTN_LEFT)
}

fn triple_click(&mut self, x: u32, y: u32) -> Result<()> {
self.teardown()?;
self.warp_and_sync(x, y)?;
self.fake_button_click(BTN_LEFT)?;
self.fake_button_click(BTN_LEFT)?;
self.fake_button_click(BTN_LEFT)
}

fn right_click(&mut self, x: u32, y: u32) -> Result<()> {
self.teardown()?;
self.warp_and_sync(x, y)?;
Expand Down
5 changes: 5 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ pub struct KeyBindings {
pub free_mode: Key,
pub click: Key,
pub double_click: Key,
pub triple_click: Key,
pub close: Key,
pub undo: Key,
pub macro_menu: Key,
Expand All @@ -232,6 +233,7 @@ impl Default for KeyBindings {
free_mode: Key::Char('v'),
click: Key::Space,
double_click: Key::Enter,
triple_click: Key::Char('m'),
close: Key::Escape,
undo: Key::Backspace,
macro_menu: Key::Tab,
Expand Down Expand Up @@ -264,6 +266,9 @@ impl KeyBindings {
if key == self.double_click {
return Some(KeyEvent::DoubleClick);
}
if key == self.triple_click {
return Some(KeyEvent::TripleClick);
}
if key == self.close {
return Some(KeyEvent::Close);
}
Expand Down
1 change: 1 addition & 0 deletions src/macro_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub enum MacroActionKind {
Move(String),
Click(String),
DoubleClick(String),
TripleClick(String),
RightClick(String),
Drag(String, String),
}
Expand Down
4 changes: 4 additions & 0 deletions src/mode/bisect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ pub(super) fn handle_key<B: Backend>(
backend.double_click(cx, cy)?;
Ok(ModeTransition::Exit)
}
KeyEvent::TripleClick => {
backend.triple_click(cx, cy)?;
Ok(ModeTransition::Exit)
}
KeyEvent::RightClick => {
backend.right_click(cx, cy)?;
Ok(ModeTransition::Exit)
Expand Down
4 changes: 4 additions & 0 deletions src/mode/free.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ pub(super) fn handle_key<B: Backend>(
backend.double_click(x, y)?;
Ok(ModeTransition::Exit)
}
KeyEvent::TripleClick => {
backend.triple_click(x, y)?;
Ok(ModeTransition::Exit)
}
KeyEvent::RightClick => {
backend.right_click(x, y)?;
Ok(ModeTransition::Exit)
Expand Down
5 changes: 5 additions & 0 deletions src/mode/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,11 @@ pub(super) fn replay_macro(
backend.double_click(x, y)?;
}
}
MacroActionKind::TripleClick(keys) => {
if let Some((x, y)) = keys_to_pos(keys, w, h) {
backend.triple_click(x, y)?;
}
}
MacroActionKind::RightClick(keys) => {
if let Some((x, y)) = keys_to_pos(keys, w, h) {
backend.right_click(x, y)?;
Expand Down
6 changes: 6 additions & 0 deletions src/mode/normal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ pub(super) fn handle_key<B: Backend>(
}
Ok(ModeTransition::Exit)
}
KeyEvent::TripleClick if drag_origin.is_none() => {
if let Some((x, y)) = target {
backend.triple_click(x, y)?;
}
Ok(ModeTransition::Exit)
}
KeyEvent::RightClick if drag_origin.is_none() => {
if let Some((x, y)) = target {
backend.right_click(x, y)?;
Expand Down
9 changes: 8 additions & 1 deletion src/mode/recording.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ pub(super) fn handle_key<B: Backend>(
InputState::Ready { .. } => Ok(ModeTransition::Stay),
}
}
KeyEvent::Click | KeyEvent::DoubleClick | KeyEvent::RightClick
KeyEvent::Click | KeyEvent::DoubleClick | KeyEvent::TripleClick | KeyEvent::RightClick
if target.is_some() && drag_origin.is_none() =>
{
let (x, y) = target.unwrap();
Expand All @@ -154,6 +154,13 @@ pub(super) fn handle_key<B: Backend>(
wait_ms,
));
}
KeyEvent::TripleClick => {
backend.triple_click(x, y)?;
new_actions.push(MacroAction::new(
MacroActionKind::TripleClick(current_keys),
wait_ms,
));
}
KeyEvent::RightClick => {
backend.right_click(x, y)?;
new_actions.push(MacroAction::new(
Expand Down