Code Monkey home page Code Monkey logo

rat's Introduction

Rat 🐀

keyboard. driven. mouse.

Installation

Important

Rat only works on Linux with X11 and only with a single display.

Download the pre-built binary from the GitHub Releases page and add it to your $PATH.

Tip

For easy access define a keyboard shortcut to launch Rat.

Keybindings

Rat grabs the keyboard during execution, such that only the following keys are active.

Modes

The following keys change the mode Rat is working in. Rat starts in normal mode.

Key Mode
q Exit Rat.
s Scroll mode.
ESCAPE Return to normal mode.

Directions

The following keys set the direction. The action taken depends on the current mode.

  • In normal mode, the pointer is moved.
  • In scroll mode, the page is scrolled.
Key Direction
h / LEFT Left
y Left + 30° Up
u Up + 30° Left
k / UP Up
i Up + 30° Right
o Right + 30° Up
l / RIGHT Right
. Right + 30° Down
, Down + 30° Right
DOWN / j Down
m Down + 30° Left
n Left + 30° Down

Buttons

The following keys act as the three mouse buttons.

Key Button
SPACE Left
x Middle
r Right

Alternatives

Note

Since Rat is still in early development, here's a list of more mature alternatives. If you know of a program that would fit this list, feel free to reach out ❤️

Name Platform Description
warpd X11 Wayland macOS A modal keyboard driven interface for mouse manipulation.
keynav X11 Control the mouse with the keyboard.
keynavish Windows Control the mouse with the keyboard, on Windows.
TPMouse Windows A virtual trackball for Windows, via vim-like homerow controls.
AhkCoordGrid Windows AutoHotkey code for Windows overlay grid allowing you to emulate mouse click at different points on the screen using keyboard shortcuts.
Mouseable Windows Control the mouse via the keyboard.
win-vind Windows You can operate Windows with key bindings like Vim.
Scoot macOS Your friendly cursor teleportation and actuation tool.
Shortcat macOS Manipulate macOS masterfully, minus the mouse.
vimac macOS Stop using your clunky trackpad/mouse now.
Homerow macOS Keyboard shortcuts for every button in macOS.
Superkey macOS Simple and powerful keyboard enhancement on macOS.

Troubleshooting

Dependencies

Make sure the X11 Testing -- Record extension library is installed.

sudo apt install libxtst6 

Logs

The log outputs of Rat can be found in $HOME/.local/share/rat.log.

Contributing

Thank you for considering to contribute ❤️!

Feedback

If you run into problems or have feedback, feel free to open a GitHub issue.

Development

If you want to work on the code yourself, see DEVELOPMENT.md for further documentation.

Tip

For ideas on what to work on, have a look at the GitHub issues.

rat's People

Contributors

david-haerer avatar

Watchers

 avatar  avatar

rat's Issues

Unable to click on some UI elements

Problem

I am unable to perform a click on some UI elements:

  • The nm-applet icon in the systray.
  • Right click in Sublime Merge.
  • Click on ZSH shell in Wezterm, however it does work Helix in Wezterm.

Otherwise it works as expected.

Environment

  • Debian
  • X11
  • Qtile

Code

The following Xlib libraries are imported

const c = @cImport({
    @cInclude("X11/Xlib.h");
    @cInclude("X11/extensions/XTest.h");
});

The keyboard is grabbed in the following way

const display = try c.XOpenDisplay(null);
const window = c.DefaultRootWindow(display);
_ = c.XSelectInput(display, window, c.KeyPressMask | c.KeyReleaseMask);
c.XGrabKeyboard(display, window, c.False, c.GrabModeAsync, c.GrabModeAsync, c.CurrentTime);

The click is performed with the following two functions

const left_button = 1;
_ = c.XTestFakeButtonEvent(display, left_button, c.True, c.CurrentTime);
_ = c.XTestFakeButtonEvent(display, left_button, c.False, c.CurrentTime);

Research

Debugging

Failed Attempt 1

Running XSync after every XTest call did not resolve the issue.

Failed Attempt 2

var child: Window = undefined;
var root_x: c_int = undefined;
var root_y: c_int = undefined;
var win_x: c_int = undefined;
var win_y: c_int = undefined;
var mask: c_uint = undefined;
if (c.XQueryPointer(self.display, self.root, &self.root, &child, &root_x, &root_y, &win_x, &win_y, &mask) == 0) {
    std.log.warn("The pointer is on a different screen!", .{});
    return;
}
std.log.debug("root={}", .{self.root});
std.log.debug("child={}", .{child});
std.log.debug("root_x={}, root_y={}", .{ root_x, root_y });
std.log.debug("win_x={}, win_y={}", .{ win_x, win_y });
std.log.debug("mask={}", .{mask});
_ = c.XTestFakeMotionEvent(self.display, 0, win_x, win_y, c.CurrentTime);
_ = c.XSetInputFocus(self.display, child, c.RevertToParent, c.CurrentTime);
_ = c.XTestFakeButtonEvent(self.display, @intFromEnum(button), c.True, c.CurrentTime);
_ = c.XTestFakeButtonEvent(self.display, @intFromEnum(button), c.False, c.CurrentTime);
_ = c.XSync(self.display, c.False);
  1. Get the pointer position.
  2. Add an absolute motion event to that position.
  3. Focus the window under the pointer.
  4. Press the button.
  5. Release the button.

Run XSync after every step.

This did not resolve the issue.

Failed Attempt 3

Using XSendEvent also does not work.

var child: Window = undefined;
var root_x: c_int = undefined;
var root_y: c_int = undefined;
var win_x: c_int = undefined;
var win_y: c_int = undefined;
var mask: c_uint = undefined;
if (c.XQueryPointer(self.display, self.root, &self.root, &child, &root_x, &root_y, &win_x, &win_y, &mask) == 0) {
    std.log.warn("The pointer is on a different screen!", .{});
    return;
}
_ = c.XSync(self.display, c.False);
var event: c.XEvent = undefined;
event.xbutton.type = c.ButtonPress;
event.xbutton.display = self.display;
event.xbutton.window = child;
event.xbutton.root = self.root;
event.xbutton.subwindow = c.None;
event.xbutton.time = c.CurrentTime;
event.xbutton.x = win_x;
event.xbutton.y = win_y;
event.xbutton.x_root = root_x;
event.xbutton.y_root = root_y;
event.xbutton.state = c.Button1Mask;
event.xbutton.button = c.Button1;
event.xbutton.same_screen = c.True;
_ = c.XSendEvent(self.display, child, c.False, c.ButtonPressMask, &event);
event.xbutton.type = c.ButtonRelease;
_ = c.XSendEvent(self.display, child, c.False, c.ButtonReleaseMask, &event);

Using c.SubstructureRedirectMask in addition also does not work.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.