Skip to content

Instantly share code, notes, and snippets.

@abeisgoat
Last active June 17, 2020 22:02
Show Gist options
  • Save abeisgoat/85f17a373614ea889fb11f9dd24dc0f2 to your computer and use it in GitHub Desktop.
Save abeisgoat/85f17a373614ea889fb11f9dd24dc0f2 to your computer and use it in GitHub Desktop.
#!/usr/bin/env node
const { spawn } = require("child_process");
const util = require("util");
const exec = util.promisify(require("child_process").exec);
const { writeFileSync } = require("fs");
const chalk = require("chalk");
const COMMAND_PREFIX = ";;";
const COMMANDS = {
help: () => {
console.log("This is the help info");
},
macro: () => {
console.log("Recording... (Press Escape to End)");
},
hello_world: () => {
console.log("Hi Eric!");
}
};
//https://en.wikipedia.org/wiki/Modifier_key
const modifiers = {
SHIFT: {
names: ["Shift_R", "Shift_L"],
active: false
},
CTRL: {
names: ["Control_R", "Control_L"],
active: false
}
};
let human_readable_history = [];
const max_human_readable_history_length =
Math.max(...Object.keys(COMMANDS).map((cmd) => cmd.length)) +
COMMAND_PREFIX.length;
function setModifier(name, value) {
for (modifierName in modifiers) {
const modifier = modifiers[modifierName];
if (modifier.names.indexOf(name) >= 0) {
modifier.active = value;
return true;
}
}
}
function onLiteralKeyEvent(keyboard_map, key_bundle) {
switch (key_bundle.action) {
case "press":
if (!setModifier(key_bundle.name, true)) {
const mapping = keyboard_map[key_bundle.code];
human_readable_history.push(modifiers.SHIFT.active ? mapping.shift : mapping.default)
}
const input = toUTF8(human_readable_history);
for (commandName of Object.keys(COMMANDS)) {
if (input.endsWith(COMMAND_PREFIX + commandName)) {
COMMANDS[commandName]();
break;
}
}
break;
case "release":
setModifier(key_bundle.name, false);
}
human_readable_history = human_readable_history.slice(-max_human_readable_history_length);
}
function toUTF8(arr) {
return Buffer.from(arr).toString("utf8");
}
(async () => {
writeFileSync(
"/tmp/xinput_capture",
`#!/usr/bin/env bash
xinput list |
grep -Po 'id=\\K\\d+(?=.*slave\\s*keyboard)' |
xargs -P0 -n1 xinput test |
awk 'BEGIN{while (("xmodmap -pke" | getline) > 0) k[$2]=$4}
{print $0 k[$NF]; fflush("/dev/stdout")}'`
);
//http://wiki.linuxquestions.org/wiki/List_of_Keysyms_Recognised_by_Xmodmap
// https://www.cl.cam.ac.uk/~mgk25/ucs/keysymdef.h
const keyboard_map = (await exec("xmodmap -pk")).stdout
.split("\n")
.map((row) => row.split(/[\W]/).filter((c) => c))
.reduce((map, row) => {
row = [...row, "", "", "", "", "", "", ""];
map[row[0]] = {default: row[1], shift: row[3]};
return map;
}, {});
console.log(`${chalk.green('✓')} Loaded ${Object.keys(keyboard_map).length} key map`);
const xinput_capture = spawn("bash", ["/tmp/xinput_capture"]);
xinput_capture.stdout.on("data", (data) =>
data
.toString()
.trim()
.split(/\W/)
.filter((d) => d && d !== "key")
.join(",")
.match(/([^,]+,[^,]+,[^,]+),?/g)
.map((match) => {
const [action, code, name] = match.split(",");
return { action, code, name };
})
.forEach(onLiteralKeyEvent.bind(null, keyboard_map))
);
console.log(`${chalk.green('✓')} Listening for keystrokes...`);
})();
{
"name": "keyro",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"bin": "./index.js",
"dependencies": {
"chalk": "^4.1.0"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment