Initial commit

master
eater 4 years ago
commit 6f0d10fa2f
Signed by: eater
GPG Key ID: AD2560A0F84F0759

2
.gitignore vendored

@ -0,0 +1,2 @@
/target
**/*.rs.bk

79
Cargo.lock generated

@ -0,0 +1,79 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "autocfg"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cc"
version = "1.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
version = "0.2.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "maplit"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "misclick"
version = "0.1.0"
dependencies = [
"maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rlua 0.16.3 (registry+https://github.com/rust-lang/crates.io-index)",
"x11 2.18.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-traits"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "pkg-config"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rlua"
version = "0.16.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "x11"
version = "2.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
]
[metadata]
"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
"checksum cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)" = "f52a465a666ca3d838ebbf08b241383421412fe7ebb463527bba275526d89f76"
"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558"
"checksum maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
"checksum num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c81ffc11c212fa327657cb19dd85eb7419e163b5b076bede2bdb5c974c07e4"
"checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
"checksum rlua 0.16.3 (registry+https://github.com/rust-lang/crates.io-index)" = "62fc0e980c94fe9ef795b1bb3874649c8c6e9bb67d3b90d48380ba24c69c23ea"
"checksum x11 2.18.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39697e3123f715483d311b5826e254b6f3cfebdd83cf7ef3358f579c3d68e235"

@ -0,0 +1,13 @@
[package]
name = "misclick"
version = "0.1.0"
authors = ["eater <=@eater.me>"]
edition = "2018"
[dependencies]
rlua = "0.16.3"
maplit = "1.0.2"
[dependencies.x11]
version = "2.18.1"
features = ["xlib", "xtest"]

@ -0,0 +1,2 @@
bind(k.x, m.n(12))
sync(m.left, k.n(132))

@ -0,0 +1,7 @@
use std::collections::HashMap;
use crate::types::{Bind, Handler};
#[derive(Default, Debug)]
pub struct Config {
pub binds: HashMap<Bind, Handler>
}

@ -0,0 +1,39 @@
use rlua::{Lua, Context, Error};
use crate::maps::init_lua;
use crate::types::{Bind, Action, Handler};
use crate::config::Config;
use std::default::Default;
pub fn load_config(lua_config: &str) -> Result<Config, Error> {
let lua = Lua::new();
let bind: Result<_, Error> = lua.context(|c: Context| {
init_lua(c)?;
let g = c.globals();
let mut config = Config::default();
c.scope::<_, Result<_, Error>>(|scope| {
g.set("sync", scope.create_function_mut(|_: Context, args: (Bind, Bind, Option<bool>)| {
println!("config -> sync({:?}, {:?}, {:?})", args.0, args.1, args.2);
config
.binds
.insert(args.0, Handler {
action: Action::Sync(args.1),
passthrough: args.2.unwrap_or(false),
});
Ok(())
})?)?;
c.load(lua_config).exec()?;
Ok(())
})?;
Ok(config)
});
Ok(bind?)
}

@ -0,0 +1,190 @@
mod types;
mod config;
mod maps;
mod lua_config;
extern crate x11;
use x11::xlib;
use x11::xtest;
use std::mem;
use std::ffi::{CStr, CString};
use std::ptr::null;
use x11::xlib::{AnyModifier, ButtonPress, ButtonRelease, ButtonReleaseMask, ButtonPressMask, GrabModeAsync, RevertToNone, BadCursor, XEvent, xError, XAnyEvent, xEvent, Display, XKeyEvent, InputFocus, KeyPress, KeyRelease, KeyPressMask, KeyReleaseMask, XButtonEvent, CurrentTime, Button1Mask, Button2Mask, Window};
use std::io::Cursor;
use x11::xinput2::XINotifyDetailNone;
use crate::types::{Bind, Action, Handler};
use std::os::raw::c_long;
use std::collections::HashMap;
use crate::types::Bind::{Key, Mouse};
struct XInfo {
root: u64,
display: *mut Display,
}
struct Subscription {
on: Bind,
handler: Handler,
}
impl Subscription {
pub fn subscribe(&self, xinfo: &XInfo) {
unsafe {
match self.on {
Bind::Key(key) => {
xlib::XGrabKey(xinfo.display, key as i32, AnyModifier, xinfo.root, 0, GrabModeAsync, GrabModeAsync);
}
Bind::Mouse(button) => {
xlib::XGrabButton(xinfo.display, button, AnyModifier, xinfo.root, 0, (ButtonReleaseMask | ButtonPressMask) as u32, GrabModeAsync, GrabModeAsync, xinfo.root, 0);
}
_ => {}
}
}
}
pub fn unsubscribe(&self, xinfo: &XInfo) {
unsafe {
match self.on {
Bind::Key(key) => {
xlib::XUngrabKey(xinfo.display, key as i32, AnyModifier, xinfo.root);
}
Bind::Mouse(button) => {
xlib::XUngrabButton(xinfo.display, button, AnyModifier, xinfo.root);
}
_ => {}
}
}
}
pub fn handle(&self, pressed: bool, xinfo: &XInfo, coords: Target) {
println!("Handling trigger={:?} pressed={:?} action={:?}", self.on, pressed, self.handler.action);
match &self.handler.action {
Action::Sync(bind) => {
match bind {
Bind::Key(key) => {
unsafe {
xtest::XTestFakeKeyEvent(xinfo.display, *key, pressed as i32, CurrentTime);
}
}
Bind::Mouse(key) => {
unsafe {
xtest::XTestFakeButtonEvent(xinfo.display, *key, pressed as i32, CurrentTime);
}
}
_ => {}
}
}
_ => {}
}
}
}
fn main() {
let config = lua_config::load_config("\
# sync up keys with others
# tables defined in maps.rs
# manual key codes can be given via k.n() or m.n()
sync(k.n(52), m.left)\
\
").unwrap();
let display = unsafe { xlib::XOpenDisplay(null()) };
let root = unsafe { xlib::XDefaultRootWindow(display) };
unsafe { xtest::XTestGrabControl(display, true as i32) };
let xinfo = XInfo { display, root };
let mut subs: HashMap<Bind, Subscription> = HashMap::new();
for (bind, handler) in config.binds.iter() {
let sub = Subscription { handler: (*handler).clone(), on: (*bind).clone() };
sub.subscribe(&xinfo);
subs.insert((*bind).clone(), sub);
}
let mut event: xlib::XEvent = unsafe { mem::MaybeUninit::uninit().assume_init() };
loop {
unsafe { xlib::XNextEvent(display, &mut event); }
match event.get_type() {
xlib::KeyPress => {
let key = Key(unsafe { event.key.keycode });
if let Some(sub) = subs.get(&key) {
sub.handle(true, &xinfo, unsafe { event.key.to_coords() });
}
}
xlib::KeyRelease => {
let key = Key(unsafe { event.key.keycode });
if let Some(sub) = subs.get(&key) {
sub.handle(false, &xinfo, unsafe { event.key.to_coords() });
}
}
xlib::ButtonPress => {
let key = Mouse(unsafe { event.button.button });
if let Some(sub) = subs.get(&key) {
sub.handle(true, &xinfo, unsafe { event.button.to_coords() });
}
}
xlib::ButtonRelease => {
let key = Mouse(unsafe { event.button.button });
if let Some(sub) = subs.get(&key) {
sub.handle(false, &xinfo, unsafe { event.button.to_coords() });
}
}
_ => {}
}
}
}
struct Target {
x: i32,
y: i32,
x_root: i32,
y_root: i32,
window: Window,
subwindow: Window,
}
trait ToCoords {
fn to_coords(&self) -> Target;
}
impl ToCoords for XKeyEvent {
fn to_coords(&self) -> Target {
Target {
x: self.x,
y: self.y,
x_root: self.x_root,
y_root: self.y_root,
window: self.window,
subwindow: self.subwindow,
}
}
}
impl ToCoords for XButtonEvent {
fn to_coords(&self) -> Target {
Target {
x: self.x,
y: self.y,
x_root: self.x_root,
y_root: self.y_root,
window: self.window,
subwindow: self.subwindow,
}
}
}

@ -0,0 +1,32 @@
use rlua::{Result, Context};
use crate::types::Bind;
pub fn init_lua(lua: Context) -> Result<()> {
create_button_map(lua)?;
Ok(())
}
fn create_button_map(lua: Context) -> Result<()> {
let globals = lua.globals();
let mouse_table = lua.create_table()?;
mouse_table.set("n", lua.create_function(|_, n: u32| Ok(Bind::Mouse(n)))?)?;
mouse_table.set("left", Bind::Mouse(1))?;
mouse_table.set("middle", Bind::Mouse(2))?;
mouse_table.set("right", Bind::Mouse(3))?;
mouse_table.set("scroll_up", Bind::Mouse(4))?;
mouse_table.set("scroll_down", Bind::Mouse(5))?;
globals.set("mouse", mouse_table.clone())?;
globals.set("m", mouse_table)?;
let key_table = lua.create_table()?;
key_table.set("n", lua.create_function(|_, n: u32| Ok(Bind::Key(n)))?)?;
globals.set("key", key_table.clone())?;
globals.set("k", key_table.clone())?;
return Ok(());
}

@ -0,0 +1,90 @@
use rlua::{FromLua, Context, Value, Error, ToLua};
use std::convert::TryFrom;
#[derive(Hash, Ord, PartialOrd, Eq, PartialEq, Debug, Clone)]
pub enum Bind {
Mouse(u32),
Key(u32),
Ping(String),
}
#[derive(Debug, Clone)]
pub struct Handler {
pub passthrough: bool,
pub action: Action,
}
#[derive(Debug, Clone)]
pub enum Action {
Sync(Bind),
Press(Bind),
Release(Bind),
Sleep(u32),
List(Vec<Action>),
}
impl<'lua> FromLua<'lua> for Bind {
fn from_lua(lua_value: Value<'lua>, _: Context<'lua>) -> Result<Self, Error> {
if let Value::Integer(n) = lua_value {
return Ok(Bind::Key(u32::try_from(n).map_err(|e| Error::external(e))?));
}
if let Value::String(c) = lua_value {
return Ok(Bind::Ping(String::from(c.to_str()?)));
}
if let Value::Table(table) = lua_value {
let typ: String = table.get("type")?;
let bind = match typ.as_str() {
"mouse" => Bind::Mouse(table.get("code")?),
"key" => Bind::Key(table.get("code")?),
"ping" => Bind::Ping(table.get("code")?),
_ => return Err(Error::RuntimeError("Type should be one of mouse, key or ping".to_string()))
};
return Ok(bind);
}
Err(Error::RuntimeError("Can't create Bind from non-table".to_string()))
}
}
impl<'lua> ToLua<'lua> for Bind {
fn to_lua(self, lua: Context<'lua>) -> Result<Value<'lua>, Error> {
let table = lua.create_table()?;
table.set("type", match self {
Bind::Key(_) => "key",
Bind::Ping(_) => "ping",
Bind::Mouse(_) => "mouse",
})?;
match self {
Bind::Key(n) => table.set("code", n)?,
Bind::Mouse(n) => table.set("code", n)?,
Bind::Ping(c) => table.set("code", c)?,
}
Ok(table.to_lua(lua)?)
}
}
impl<'lua> FromLua<'lua> for Action {
fn from_lua(lua_value: Value<'lua>, _: Context<'lua>) -> Result<Self, Error> {
let table = if let Value::Table(table) = lua_value {
table
} else {
return Err(Error::RuntimeError("Action should be a table".to_string()));
};
let res = match table.get::<&str, String>("type")?.as_str() {
"sync" => Action::Sync(table.get("bind")?),
"press" => Action::Press(table.get("bind")?),
"release" => Action::Release(table.get("bind")?),
"sleep" => Action::Sleep(table.get("time")?),
"list" => Action::List(table.get("items")?),
_ => return Err(Error::RuntimeError("Action type is invalid, should be one of sync, release, press, sleep, list".to_string()))
};
Ok(res)
}
}
Loading…
Cancel
Save