A scripting language that allows complex key remapping on Linux.

Overview

Map2

License: MIT

A scripting language that allows complex key remapping on Linux, written in Rust.

All of the functionality related to interacting with graphical elements such as getting the active window information is currently only supported on X11. Wayland support is planned but probably won't be added for some time.

For details check the documentation.

Examples

Map2 is a very flexible scripting language that can utilize logic control statements, arithmetic and provides lots of useful built-in functions.

// change the 'a' key to 'b'
a::b;

// map the 'c' key to a code block
c::{
  // output text to standard output
  print("hello world");
  
  // type the text using the virtual keyboard
  send("some text{enter}");
};

// define variables and lambda functions
let sum = |argument1, argument2|{
  return argument1 + argument2;
};

let my_sum = sum(1, 2);
print("the sum of 1 and 2 is: " + my_sum);

// do something when the active window changes
on_window_change(||{
  if(active_window_class() == "firefox"){
    print("firefox is now the active window");
    
    // map 'F1' to ctrl+'t' (open new browser tab)
    f1::^t;
  }else{
    print("firefox is not the active window");
    
    // map 'F1' back
    f1::f1;
  }
});

For more examples check the examples directory.

Getting started

tldr:

  • find the input device file descriptors you want to grab
  • write them into a file (i.e. devices.list)
  • write a script file for remapping the keys
  • run $ map2 -d devices.list script_name.m2

In order to execute a script simply pass it as an argument to the map2 command provided by this package.

$ map2 script-name.m2

By default a script is able to output events though a virtual output device, but in order for a script to intercept input events from physical devices it is necessary to define which devices should be grabbed (all events will pass through the script).

To describe which devices should be grabbed it is necessary to provide a list of file descriptor paths, regular expressions are also supported.

For example, in order to grab a keyboard and Logitech mouse, one might specify the device list as such:

devices.list:

/dev/input/by-id/usb-Logitech_G700s.*-event-kbd
/dev/input/by-path/pci-0000:03:00.0-usb-0:9:1.0-event-kbd

In order to find out which file descriptor corresponds to which physical device one should examine /dev/input/by-id/ and /dev/input/by-path/. After defining the device list we can test it using a short script.

example.m2:

// maps 'a' to 'b'
a::b;

And finally execute the script which should successfully remap the keys.

$ map2 -d devices.list example.m2

Each device can only be grabbed once. Attempting to run several scripts that attempt to grab the same device simultaneously will produce warnings and the device will not be grabbed.

Install

Arch Linux

Install stable version:

$ yay -S map2

Install latest version:

$ yay -S map2-git

Build local package from cloned source:

$ makepkg -si

Other distributions

Install precompiled binary:

  • download the latest release from the releases page
  • unpack the archive
  • copy the files to the respective folders

if there's an error with system libraries being too old, try building from source instead.

Build local package from cloned source:

  • $ cargo build --release
  • copy the compiled binary somewhere in your PATH (i.e. $ cp target/release/map2 /usr/bin)

Documentation

Mappings

Basic mappings simply map one key to a different one.

a::b; // when 'a' is pressed, 'b' will be pressed instead

Under the hood the above expression is a shorthand for the following code:

{a down}::{b down};
{a repeat}::{b repeat};
{a up}::{b up};

Keys can also be remapped to key sequences, meaning that pressing the key will result in the key sequence being typed.

a::"hello world"; // maps a to "hello world"

Also see Key sequences.

Complex mappings can be used by putting a code block on the right side of the mapping expression.

a::{
  sleep(200);
  print("hi");
  send("b");
};

Modifier flags

Modifier flags can be added to key mappings in order to press down the key with the given modifier. Multiple flags can be used at once.
The following modifier flags can be used:

  • ^ - control
  • + - shift
  • ! - alt
  • # - meta

Flags can be used on both sides of a mapping expression:

!a::b; // maps 'alt+a' to 'b'
a::+b; // maps 'a' to 'shift+b'
#!^a::+b; // maps 'meta+alt+ctrl+a' to 'shift+b'

Key symbols

To descript keys in key mappings and sequences it is possible to either use literal key symbols (such as 'a') or any valid input event as defined in input-event-codes.h (such as 'KEY_A'). In the case of special keys such as 'left mouse button', input events need to be used (i.e. BTN_MOUSE).

Non-US keyboard layouts: Alternative keys that require modifiers (such as '@', '%', etc.) probably won't work and get mapped to different keys. To fix this use the modifier flags and the original key instead. (i.e. on the US layout the '@' symbol requires pressing shift+'2', meaning that +2 should be used in simple mappings and {shift down}2{shift up} should be used in key sequences.)

Variables

Variables can be initialized using the let keyword. Assigning a value to a non-existent variable produces an error.

let foo = 33;

Variables are dynamically typed, meaning the their type can change at runtime.

let foo = 33;
foo = "hello";

Control statements

The flow of execution can be controlled using control statements.

If statement

If statements can check whether a certain condition is satisfied and execute code conditionally.

let a = 3;

if (a == 1){
  print("a is 1");
}else if (a == 3){
  print("a is 3");
}else{
  print("a is not 1 or 3");
}

For loop

For loops are useful when a code block should be run several times.

for(let i=0; i<10; i = i+1){
  print(i);
}

Key sequences

Key sequences represent multiple keys with a specific ordering. They can be used on the right side of a mapping expression in order to type the sequence when a key is pressed.

a::"hello world";

Complex keys are also permitted.

a::"hello{enter}world{shift down}1{shift up}";

Functions

All functions are either built-in functions provided by the runtime itself or user defined functions.

List of built-in functions

print(value)

Print the value to the standard output.

print(33);

print("hello");

print(||{});

map_key(trigger, callback)

Maps a key to a callback at runtime, meaning expressions can be used as parameters. This is useful when the trigger key or callback need to be dynamically evaluated.

map_key("a", ||{
  send("b");
});

sleep(duration)

Pauses the execution for a certain duration. This does not block other mappings and tasks.

sleep(1000); // sleep for 1 second

on_window_change(callback)

Registers a callback that is called whenever the active window changes.

on_window_change(||{
  print("hello");
});

active_window_class()

Gets the class name of the currently active window or Void.

if(active_window_class() == "firefox"){
  print("firefox!");
}

number_to_char(number: Number)

Converts a number to the corresponding character.

let char = num_to_char(97);
print(char); // output: 'a'

char_to_number(char: String)

Converts a character to the corresponding number.

let number = char_to_number("a");
print(number); // output: '97'

exit(exit_code?: Number)

Terminates the application with the specified exit code. If no exit code is provided it defaults to '0'.

exit(); // exits the application and indicates success
exit(1); // exits the application and indicates an error

execute(command: String, ...arguments: String[]): String | Void

Executes the given command with the provided arguments.
The standard output of the command is returned as a string if the command succeeds. If the command fails, Void is returned.

let message = execute("echo", "hello", "world");
let now = execute("date");

Comments

Code inside of comments is not evaluated and will be ignored. There exist two types of comments: line comments and in-line comments.

// this is a line comment
print(/* this is an in-line comment */ "hello");

Feature roadmap

  • more built-ins
  • escaped characters in strings and key sequences
  • update documentation and refactor code
  • better tests to avoid regressions
  • pre-packaged binaries for various distros
  • mouse events
  • Wayland support (someday)

Contributing

If you want to report bugs, add suggestions or help out with development please check the issues page and open an issue if it doesn't exist yet.

License

MIT

Authors

Comments
  • Doesn't work

    Doesn't work

    cat q.m2:

    a::b;
    

    RUST_BACKTRACE=1 ./target/release/map2 q.m2:

    thread 'tokio-runtime-worker' panicked at 'called `Result::unwrap()` on an `Err` value: Permission denied (os error 13)', /home/pp/git/map2/src/device/virtual_input_device.rs:110:76
    stack backtrace:
       0: rust_begin_unwind
                 at /build/rustc-n7HJ8w/rustc-1.47.0+dfsg1+llvm/library/std/src/panicking.rs:475
       1: core::panicking::panic_fmt
                 at /build/rustc-n7HJ8w/rustc-1.47.0+dfsg1+llvm/library/core/src/panicking.rs:85
       2: core::result::unwrap_failed
                 at /build/rustc-n7HJ8w/rustc-1.47.0+dfsg1+llvm/library/core/src/result.rs:1220
       3: <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll
       4: tokio::runtime::task::core::Core<T,S>::poll
       5: <std::panic::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once
       6: tokio::runtime::task::harness::Harness<T,S>::poll
       7: std::thread::local::LocalKey<T>::with
       8: tokio::runtime::thread_pool::worker::Context::run_task
       9: tokio::runtime::thread_pool::worker::Context::run
      10: tokio::macros::scoped_tls::ScopedKey<T>::set
      11: tokio::runtime::thread_pool::worker::run
      12: tokio::runtime::task::core::Core<T,S>::poll
      13: <std::panic::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once
      14: tokio::runtime::task::harness::Harness<T,S>::poll
      15: tokio::runtime::blocking::pool::Inner::run
    note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
    
    opened by pepa65 9
  • can't get it to work - keep getting device busy errors

    can't get it to work - keep getting device busy errors

    issue: i can't get it to work at all; seems like it doesn't like the file descriptors. I saw issue #3 but it wasn't any help. system: Fedora 34 Cinnamon (and yes, I tried it with SELinux disabled too) kernel: 5.15.12-100 install type: tried both manual install from releases (/usr/bin/map2 with chmod 755) and building with cargo. map2 version: 1.0.6 for releases page / source for cargo (map2 --version gives map2 1.0 in both cases) keyboard/mouse: logitech k400 wireless htpc keyboard via logitech receiver script: just trying to get the simple example.m2 from the README working for now: a::b; file descriptors: honestly, I was a bit confused about what is supposed to go in here. will give full details below.


    File descriptors

    This part was the most confusing for me. The README only says:

    To describe which devices should be grabbed it is necessary to provide a list of file descriptor paths, regular expressions are also supported. ... In order to find out which file descriptor corresponds to which physical device one should examine /dev/input/by-id/ and /dev/input/by-path/

    But doesn't actually describe how to identify what the correct file descriptor is (e.g. not sure if it's supposed to be obvious or it is purely a trial-and-error process).

    Anyway, I had:

    $ ls -acl /dev/input/by-id /dev/input/by-path | grep -Pvi 'Headset|spkr'
    /dev/input/by-id:
    total 0
    lrwxrwxrwx. 1 root root 9 Jan  8 19:58 usb-Logitech_USB_Receiver-if02-event-mouse -> ../event4
    lrwxrwxrwx. 1 root root 9 Jan  8 19:58 usb-Logitech_USB_Receiver-if02-mouse -> ../mouse1
    lrwxrwxrwx. 1 root root 9 Jan  8 19:58 usb-MOSART_Semi._2.4G_Wireless_Mouse-event-if00 -> ../event7
    lrwxrwxrwx. 1 root root 9 Jan  8 19:58 usb-MOSART_Semi._2.4G_Wireless_Mouse-event-mouse -> ../event6
    lrwxrwxrwx. 1 root root 9 Jan  8 19:58 usb-MOSART_Semi._2.4G_Wireless_Mouse-mouse -> ../mouse0
     
    /dev/input/by-path:
    total 0
    lrwxrwxrwx. 1 root root 9 Jan  8 19:58 pci-0000:00:12.0-usb-0:2:1.3-event -> ../event3
    lrwxrwxrwx. 1 root root 9 Jan  8 19:58 pci-0000:00:13.0-usb-0:4:1.2-event-mouse -> ../event4
    lrwxrwxrwx. 1 root root 9 Jan  8 19:58 pci-0000:00:13.0-usb-0:4:1.2-mouse -> ../mouse1
    lrwxrwxrwx. 1 root root 9 Jan  8 19:58 pci-0000:00:13.2-usb-0:5.2:1.0-event -> ../event7
    lrwxrwxrwx. 1 root root 9 Jan  8 19:58 pci-0000:00:13.2-usb-0:5.2:1.0-event-mouse -> ../event6
    lrwxrwxrwx. 1 root root 9 Jan  8 19:58 pci-0000:00:13.2-usb-0:5.2:1.0-mouse -> ../mouse0
    

    Since the example in the README showed 2 entries, I assume that I also need 2 entries: one from /dev/input/by-id and one from /dev/input/by-path. I also made the assumption that I wanted one of the "usb-Logitech_USB_Receiver" ones and that I should probably pair similar events (e.g. the file under /dev/input/by-id that points to ../mouse1 and the file under /dev/input/by-path that points to ../mouse1)... but all guesswork.

    Since I had 2 candidates, I made 2 files and figured I just try one and if it didn't work, then try the other.

    devices-mouse1.list
    /dev/input/by-id/usb-Logitech_USB_Receiver-if02-mouse
    /dev/input/by-path/pci-0000:00:13.0-usb-0:4:1.2-mouse
    
    devices-event4.list
    /dev/input/by-id/usb-Logitech_USB_Receiver-if02-event-mouse
    /dev/input/by-path/pci-0000:00:13.0-usb-0:4:1.2-event-mouse
    

    Errors

    Attempting first as regular user then with sudo, using first the precompiled version from releases page then with version built from source with caro.

    $ cd /tmp/map2
    $ ls -acl
    total 1.4M
    drwxr-x---. 3 fd fd   60 Jan  9 19:37 map2-1.0.6-x86_64/
    -rw-rw----. 1 fd fd  120 Jan  9 19:02 devices-event4.list
    -rw-rw----. 1 fd fd  108 Jan  9 19:02 devices-mouse1.list
    -rw-rw----. 1 fd fd   25 Jan  9 19:01 example.m2
    -rw-r--r--. 1 fd fd 1.4M Jan  9 19:38 map2-1.0.6-x86_64.tar.gz
     
    $ sudo -i
    # setenforce 0
    # getenforce
    Permissive
     
    # cd /tmp/map2/map2-1.0.6-x86_64
    # find . -type f
    ./usr/bin/map2
    ./usr/share/man/man1/map2.1.gz
    # cp -a -t /usr/bin ./usr/bin/map2
    # cp -a -t /usr/share/man/man1 ./usr/share/man/man1/map2.1.gz
    # chmod 755 /usr/bin/map2
    # exit
     
    $ map2 --version
    map2 1.0
     
    $ map2 -d devices-event4.list example.m2 
    thread 'tokio-runtime-worker' panicked at 'failed to open fd '/dev/input/by-path/pci-0000:00:13.0-usb-0:4:1.2-event-mouse': Os { code: 13, kind: PermissionDenied, message: "Permission denied" }', /home/shiro/project/map2/src/device/virtual_input_device.rs:88:10
    note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
     
     $ map2 -d devices-mouse1.list example.m2 
    thread 'tokio-runtime-worker' panicked at 'failed to open fd '/dev/input/by-path/pci-0000:00:13.0-usb-0:4:1.2-mouse': Os { code: 13, kind: PermissionDenied, message: "Permission denied" }', /home/shiro/project/map2/src/device/virtual_input_device.rs:88:10
    note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
     
    $ sudo map2 -d devices-event4.list example.m2 
    failed to grab device '/dev/input/by-id/usb-Logitech_USB_Receiver-if02-event-mouse': Device or resource busy (os error 16)
     
    $ sudo map2 -d devices-mouse1.list example.m2 
    thread 'tokio-runtime-worker' panicked at 'failed to open fd '/dev/input/by-path/pci-0000:00:13.0-usb-0:4:1.2-mouse': Os { code: 25, kind: Other, message: "Inappropriate ioctl for device" }', /home/shiro/project/map2/src/device/virtual_input_device.rs:91:56
    note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
    

    From this, I think that my devices-event4.list is the correct file descriptor to use for my logitech k400 but still can't get it to work.

    I tried the same thing with the cargo build but got the same error message.

    # dnf install -y ncurses ncurses-base ncurses-static ncurses-libs ncurses-devel ncurses-c++-libs \
    	libevdev libevdev-devel xorg-x11-drv-evdev xorg-x11-drv-evdev-devel libevdev-utils
    # exit
     
    $ mkdir /tmp/map2
    $ git clone https://github.com/shiro/map2 map2_src
    $ cd map2_src/
    $ cargo build --release
    $ cd target/release/
    $ ./map2 --version
    map2 1.0
      
    $ ./map2 -d ../../../devices-mouse1.list ../../../example.m2
    thread 'tokio-runtime-worker' panicked at 'failed to open fd '/dev/input/by-path/pci-0000:00:13.0-usb-0:4:1.2-mouse': Os { code: 13, kind: PermissionDenied, message: "Permission denied" }', /tmp/map2/map2/src/device/virtual_input_device.rs:88:10
    note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
     
    $ sudo ./map2 -d ../../../devices-mouse1.list ../../../example.m2
    thread 'tokio-runtime-worker' panicked at 'failed to open fd '/dev/input/by-path/pci-0000:00:13.0-usb-0:4:1.2-mouse': Os { code: 25, kind: Uncategorized, message: "Inappropriate ioctl for device" }', /tmp/map2/map2/src/device/virtual_input_device.rs:91:56
    note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
    
    $ ./map2 -d ../../../devices-event4.list ../../../example.m2
    thread 'tokio-runtime-worker' panicked at 'failed to open fd '/dev/input/by-path/pci-0000:00:13.0-usb-0:4:1.2-event-mouse': Os { code: 13, kind: PermissionDenied, message: "Permission denied" }', /tmp/map2/map2/src/device/virtual_input_device.rs:88:10
    note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
     
    $ sudo ./map2 -d ../../../devices-event4.list ../../../example.m2
    failed to grab device '/dev/input/by-id/usb-Logitech_USB_Receiver-if02-event-mouse': Device or resource busy (os error 16)
    

    I've closed several processes like Solaar that I thought might potentially be responsible but no luck..

    Any ideas?

    opened by zpangwin 5
  • Doesn't build

    Doesn't build

    Traceback on cargo build --release:

       Compiling map2 v0.1.0 (/home/pp/git/map2)
    error[E0554]: `#![feature]` may not be used on the stable release channel
     --> src/lib.rs:1:1
      |
    1 | #![feature(type_ascription)]
      | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    
    error[E0554]: `#![feature]` may not be used on the stable release channel
     --> src/lib.rs:2:1
      |
    2 | #![feature(async_closure)]
      | ^^^^^^^^^^^^^^^^^^^^^^^^^^
    
    error[E0554]: `#![feature]` may not be used on the stable release channel
     --> src/lib.rs:3:1
      |
    3 | #![feature(unboxed_closures)]
      | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    
    error[E0554]: `#![feature]` may not be used on the stable release channel
     --> src/lib.rs:4:1
      |
    4 | #![feature(trait_alias)]
    
      | ^^^^^^^^^^^^^^^^^^^^^^^^
    
    error[E0554]: `#![feature]` may not be used on the stable release channel
     --> src/lib.rs:5:1
      |
    5 | #![feature(label_break_value)]
      | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    
    error[E0554]: `#![feature]` may not be used on the stable release channel
     --> src/lib.rs:6:1
      |
    6 | #![feature(destructuring_assignment)]
      | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    
    error[E0554]: `#![feature]` may not be used on the stable release channel
     --> src/lib.rs:7:1
      |
    7 | #![feature(seek_convenience)]
      | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    
    warning: unused imports: `parse_key_action_with_mods`, `parse_key_sequence`
     --> src/runtime/evaluation.rs:7:30
      |
    7 | use crate::parsing::parser::{parse_key_sequence, parse_key_action_with_mods};
      |                              ^^^^^^^^^^^^^^^^^^  ^^^^^^^^^^^^^^^^^^^^^^^^^^
      |
      = note: `#[warn(unused_imports)]` on by default
    
    warning: unused import: `evdev_rs::enums::int_to_ev_key`
     --> src/runtime/evaluation.rs:8:5
      |
    8 | use evdev_rs::enums::int_to_ev_key;
      |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    
    warning: unused import: `evaluation::*`
     --> src/runtime/mod.rs:4:5
      |
    4 | use evaluation::*;
      |     ^^^^^^^^^^^^^
    
    warning: unused imports: `VerboseError`, `context`
      --> src/parsing/mod.rs:12:18
       |
    12 | use nom::error::{context, VerboseError, ParseError};
       |                  ^^^^^^^  ^^^^^^^^^^^^
    
    warning: unused import: `crate::runtime::evaluation::*`
      --> src/parsing/mod.rs:36:5
       |
    36 | use crate::runtime::evaluation::*;
       |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    
    warning: unused import: `nom::error::convert_error`
     --> src/parsing/parser.rs:2:5
      |
    2 | use nom::error::convert_error;
      |     ^^^^^^^^^^^^^^^^^^^^^^^^^
    
    warning: unused import: `InputIter`
     --> src/parsing/custom_combinators.rs:1:40
      |
    1 | use nom::{Compare, CompareResult, Err, InputIter, InputLength, InputTake, Parser, Slice, Offset};
      |                                        ^^^^^^^^^
    
    warning: unused import: `nom::combinator::map_res`
     --> src/parsing/key_action.rs:2:5
    
      |
    2 | use nom::combinator::map_res;
      |     ^^^^^^^^^^^^^^^^^^^^^^^^
    
    warning: unused import: `nom::combinator::consumed`
     --> src/parsing/variable.rs:2:5
      |
    2 | use nom::combinator::consumed;
      |     ^^^^^^^^^^^^^^^^^^^^^^^^^
    
    warning: unnecessary parentheses around `if` condition
      --> src/parsing/error.rs:36:19
       |
    36 |         } else if (other.input.input_len() > self.input.input_len()) {
       |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove these parentheses
       |
       = note: `#[warn(unused_parens)]` on by default
    
    warning: unused import: `tokio::sync::oneshot::Sender`
      --> src/device/virtual_input_device.rs:11:5
       |
    11 | use tokio::sync::oneshot::Sender;
       |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    
    warning: unused import: `crate::device::virt_device`
      --> src/device/virtual_input_device.rs:15:5
       |
    15 | use crate::device::virt_device;
       |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
    
    warning: variable does not need to be mutable
       --> src/runtime/builtin_functions.rs:172:17
        |
    
    172 |             let mut child_process = cmd.output().await
        |                 ----^^^^^^^^^^^^^
        |                 |
        |                 help: remove this `mut`
        |
        = note: `#[warn(unused_mut)]` on by default
    
    warning: unreachable expression
       --> src/device/virtual_input_device.rs:141:13
        |
    124 | /             loop {
    125 | |                 match watch_rx.recv() {
    126 | |                     Ok(event) => {
    127 | |                         use FsWatchEvent::*;
    ...   |
    139 | |                 }
    140 | |             }
        | |_____________- any code following this expression is unreachable
    141 |               Ok(())
        |               ^^^^^^ unreachable expression
        |
        = note: `#[warn(unreachable_code)]` on by default
    
    warning: unreachable expression
       --> src/device/virtual_input_device.rs:177:9
        |
    159 | /         loop {
    160 | |             let fs_event = fs_event_rx.recv().await.unwrap();
    161 | |             match fs_event {
    162 | |                 FsWatchEvent::ADD(path) => {
    ...   |
    175 | |             }
    176 | |         }
    
        | |_________- any code following this expression is unreachable
    177 |           Ok::<(), anyhow::Error>(())
        |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^ unreachable expression
    
    warning: unreachable expression
      --> src/device/virtual_output_device.rs:24:9
       |
    16 | /         loop {
    17 | |             let msg = reader_rx.recv().await;
    18 | |             let ev: InputEvent = match msg {
    19 | |                 Some(v) => v,
    ...  |
    22 | |             input_device.write_event(&ev)?;
    23 | |         }
       | |_________- any code following this expression is unreachable
    24 |           Ok(())
       |           ^^^^^^ unreachable expression
    
    warning: unused import: `futures::StreamExt`
     --> src/parsing/mod.rs:6:5
      |
    6 | use futures::StreamExt;
      |     ^^^^^^^^^^^^^^^^^^
    
    error: aborting due to 7 previous errors; 17 warnings emitted
    
    For more information about this error, try `rustc --explain E0554`.
    error: could not compile `map2`.
    
    To learn more, run the command again with --verbose.
    warning: build failed, waiting for other jobs to finish...
    error: build failed
    
    opened by pepa65 5
  • Release binaries for older machines

    Release binaries for older machines

    Looks very interesting, but it suffers from the common "requires a very recent GLIBC" issue. If you could release binaries that are made on an older machine, it would not affect people with a newer GLIBC, but it would extend your reach. I am on the latest Ubuntu-family LTS (20.04), but last month I was still running 18.04 and I ran into this issue a lot... Debug output:

    map2: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.33' not found (required by map2)
    map2: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.32' not found (required by map2)
    
    opened by pepa65 3
  • Adding udev rule to access /dev/uinput as user in Arch Linux

    Adding udev rule to access /dev/uinput as user in Arch Linux

    Since I found a way to add the needed udev rule on my system, I figured I'd share it here.

    Create file in /etc/udev/rules.d/, for example 01-uinput-permission.rules, with the following content:

    SUBSYSTEM=="misc", KERNEL=="uinput", MODE="0660", GROUP="uinput"
    

    Create /etc/modules-load.d/uinput.conf with:

    uinput
    

    (trick found here)

    and

    reboot
    

    NOTE: I also happened to create the uinput usergroup, and adding my user to it beforehand, which could've helped as well.

    opened by arilebedey 1
  • [Feature request] allow execution of shell scripts

    [Feature request] allow execution of shell scripts

    with this one simple change, this program can become a viable alternative to sxhkd. to compare, sxhkd can do what this program can, by utilizing xdotool

    opened by nojusr 1
  • Failing to properly remap capslock to meta/super key

    Failing to properly remap capslock to meta/super key

    I've been trying to remap CAPSLOCK to Super_L via your method,

    {capslock down}::{
      caps_down = true;
      key_pressed = false;
      send("{ctrl down}");
    };
    
    {capslock up}::{
      caps_down = false;
      send("{ctrl up}");
      if (key_pressed == false){
        send("{esc}");
      }
    };
    

    where I tried replacing ctrl with win, super, hyper, Super_L, and 0xffeb. No success.

    Would there be a way to achieve this by default, or should I be looking into modifying the source code itself?

    opened by arilebedey 2
  • Does map2 support hotstringing?

    Does map2 support hotstringing?

    By hotstrings I mean what AutoHotkey means, i.e. being able to type abbreviations and the script would replace it with the full form (like FBI -> Federal Bureau of Investigations).

    Does map2 support that? If so - could you, please, provide an example?

    opened by Drugoy 11
  • I can't figure out where this code is failing.

    I can't figure out where this code is failing.

    // ================= // APASTRON KEYBINDS // ================= sleep(2000); // sleep 2s print("the active window class is: " + active_window_class()); // Main File on_window_change(||{ if(active_window_class() == "steam_app_1264850"){ print("Apastron mappings loaded: " + active_window_class()); // maps 'esdf' to 'wasd' e::w; s::a; d::s; f::d; c::KEY_CAPSLOCK; v::BTN_THUMB; BTN_1::z; BTN_2::BTN_BACK; BTN_3::BTN_FORWARD; a::KEY_LEFTSHIFT; g::b; }else{ print("Apastron mappings reverted: " + active_window_class()); // maps 'esdf' back to 'esdf' e::e; s::s; d::d; f::f; c::c; v::v; BTN_1::BTN_1; BTN_2::BTN_2; BTN_3::BTN_3;
    a::a; g::g; }else if({send_modifier("{shift end}")}){ print("exiting apastron.m2 "); execute("exit(0)"); } });

    opened by HarmonBlues 13
Releases(1.0.6)
Owner
Matt
Just another white rabbit, move along.
Matt
A metamacro toolkit for writing complex macros.

Big Mac This crate contains the branching_parser! metamacro, which can be used to create complex macros with few lines of code. To use the macro, call

null 1 Nov 14, 2021
This crate provides a convenient macro that allows you to generate type wrappers that promise to always uphold arbitrary invariants that you specified.

prae This crate provides a convenient macro that allows you to generate type wrappers that promise to always uphold arbitrary invariants that you spec

null 96 Dec 4, 2022
📦 Crate Protocol allows anyone to create, manage, and trade a tokenized basket of assets, which we refer to as a Crate.

?? Crate Protocol Crate Protocol allows anyone to create, manage, and trade a tokenized basket of assets, which we refer to as a Crate. A Crate is alw

Crate Protocol 63 Oct 31, 2022
Tiny crate that allows to wait for a stop signal across multiple threads

Tiny crate that allows to wait for a stop signal across multiple threads. Helpful mostly in server applications that run indefinitely and need a signal for graceful shutdowns.

Dominik Nakamura 5 Dec 16, 2022
CFD is a tool that allows you to check one or more domains to see if they are protected by CloudFlare or not.

CFD is a tool that allows you to check one or more domains to see if they are protected by CloudFlare or not. The check is carried out based on five criteria: 3 headers in the HTTP response, IP, and SSL certificate issuer. The check result can be displayed on the screen or saved to a file.

Airat Galiullin 13 Apr 7, 2023
A little tribute to the Dango Daikazoku from Clannad (by Key, KyoAni, et al)

dango A little tribute to the Dango Daikazoku from Clannad (by Key, KyoAni, et al) Try it with your friends at http://ernestwong.nz/dango-tribute/serv

Ernest Wong 19 Nov 21, 2022
Dynamic key remapper for X11 and Wayland

???????????? ⌨️ xremap is a key remapper for Linux. Unlike xmodmap, it supports app-specific remapping and Wayland. Concept Fast - Xremap is written i

Takashi Kokubun 643 Jan 8, 2023
69-key split mechanical keyboard (PCB, case, firmware)

ErgoNICE An open source 69-key column-staggered split mechanical keyboard with a rotary knob, extra connectors, a 3D printed case with "floating key"

null 12 Oct 1, 2022
tr-lang is a language that aims to bring programming language syntax closer to Turkish.

tr-lang Made with ❤️ in ???? tr-lang is a language that aims to bring programming language syntax closer to Turkish. tr-lang is a stack based language

Kerem Göksu 10 Apr 2, 2022
A language server implementation for the WGSL shading language

wgsl-analyzer wgsl-analyzer is a language server plugin for the WGSL Shading language. It comes with a VS Code plugin located in ./editors/code, but d

null 155 Jan 2, 2023
Linux Advanced keyboard customizer

A multi-mode, rule-based keyboard customizer Features Key Remapping Execute command Install

秋葉 3 Apr 4, 2022
A shiny new package manager written in rust explicitly for gemlock/linux and it's distributions

Gem A shiny new package manager written in rust explicitly for gemlock/linux and it's distributions. List of content How to setup Systems Ubuntu Arch

Gemlock 1 Feb 22, 2022
Building a better screen reader for the Linux desktop, one step at a time.

Building a better screen reader for the Linux desktop, one step at a time.

Odilia 44 Dec 31, 2022
The SATySFi Language Server

[WIP] SATySFi Language Server This repository is work-in-progress yet. Features Kind Function Done codeAction Add the definition of an undefined comma

monaqa 50 Dec 24, 2022
scraps of a potential language

seaslug small, beautiful, knowable, DOESN'T EXIST YET LOL non-turing complete, verified terminating code placed into well-defined interfaces similar t

Tyler Neely 34 Nov 1, 2022
A language server for lua written in rust

lua-analyzer lua-analyzer is a lsp server for lua. This is mostly for me to learn the lsp protocol and language analysis so suggestions are helpful. T

null 61 Dec 11, 2022
Uindex is a data store, for data that can be parsed as sentences in some context-free language.

Uindex - Universal index Uindex is a data store, for data that can be parsed as sentences in some context-free language.

Enrique Pérez Arnaud 3 Jul 20, 2021
A simple programming language for everyone.

Slang A simple programming language for everyone, made with Rust. State In very early stages. Plan is to create a byte-code compiler and make that exe

Slang, Inc. 11 Jul 1, 2022
A programming language. Better mantra pending.

Dusk Dusk is a programming language I've been working on on and off for the past while now. It's still very much in its infancy (... a quick look thro

Kaylynn Morgan 14 Oct 24, 2022