Created
July 28, 2016 19:16
-
-
Save daniellandau/7679741bf8bbc5c345591593ca05e9f6 to your computer and use it in GitHub Desktop.
Gnome Shell extension code for grabbing key events from X
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const GLib = imports.gi.GLib; | |
const Gio = imports.gi.Gio; | |
let initialized = false; | |
let keymap = {}, xinputPids = [], xinputs = []; | |
function trackKeys(callback, out_reader, res) { | |
if (!initialized) initialize(); | |
const [out, length] = out_reader.read_upto_finish(res); | |
if (length > 0) { | |
if (/key *press *(\d+)/.test(out)) { | |
callback((keymap[RegExp.$1] || "unknown key") + ';' + new Date()); | |
} | |
out_reader.read_upto_async("", 0, 0, null, trackKeys.bind(null, callback), ""); | |
} | |
} | |
let nonAsciiWordRegex = /(grave|acute|circumflex|tilde|diaeresis|ring|E|cedilla|caron|ogonek|breve|abovedot|stroke|doubleacute|macron|slash)/; | |
let nonLatinLanguageRegex = /(hebrew|thai)/; | |
function initialize() { | |
const [ret, stdout, stderr, stat] = GLib.spawn_sync(null, ['/usr/bin/xmodmap', '-pke'], null, 0, null); | |
(stdout+'').split('\n').forEach((line) => { | |
if (/keycode *(\d+) = (\w+)/.test(line)) { | |
const scancode = RegExp.$1, | |
symbol = RegExp.$2; | |
let type; | |
if (symbol.startsWith("XF86")) type = 'media'; | |
else if (/^F\d+$/.test(symbol)) type = 'function'; | |
// TODO make this list more complete and inclusive of non-western-europian languages | |
// (current generated by hand from /usr/include/X11/keysymdef.h) | |
else if (/^\w$/.test(symbol.replace(nonAsciiWordRegex, '')) || nonLatinLanguageRegex.test(line)) type = 'alphanum'; | |
else type = symbol; | |
keymap[scancode] = type; | |
} | |
}); | |
initialized = true; | |
} | |
const xinput = { | |
poll: pollXinputs, | |
respawn: respawnXinputs, | |
spawn: spawnXinputs, | |
kill: killXinputs | |
}; | |
function pollXinputs(callback) { | |
// TODO, don't compare lengths, compare actual contents | |
if (xinputs.length !== listXinputs().length) { | |
respawnXinputs(callback); | |
} | |
} | |
function respawnXinputs(callback) { | |
killXinputs(); | |
spawnXinputs(callback); | |
} | |
function spawnXinputs(callback) { | |
// TODO handle xinput not existing | |
xinputs = listXinputs(); | |
xinputs.forEach((xinput) => { | |
const [res, pid, in_fd, out_fd, err_fd] = GLib.spawn_async_with_pipes(null, ['/usr/bin/xinput', 'test', xinput.id], null, 0, null); | |
xinputPids.push(pid); | |
const out_reader = new Gio.DataInputStream({ | |
base_stream: new Gio.UnixInputStream({fd: out_fd}) | |
}); | |
out_reader.read_upto_async( | |
"", 0, 0, null, | |
trackKeys.bind(null, (line) => callback(line + ';' + xinput.name + ';' + xinput.id)), ''); | |
}); | |
} | |
function killXinputs() { | |
xinputPids.forEach((xinputPid) => { | |
GLib.spawn_close_pid(xinputPid); | |
// TODO: is there a safer way to kill xinput? | |
}); | |
GLib.spawn_command_line_sync('killall xinput'); | |
xinputPids = []; | |
} | |
function listXinputs() { | |
const [ret, stdout, stderr, stat] = GLib.spawn_sync(null, ['/usr/bin/xinput', 'list'], null, 0, null); | |
return (stdout+'') | |
.split('\n') | |
.map((line) => { | |
if (/↳ (.*)\tid=(\d+)/.test(line)) { | |
return { | |
'name': RegExp.$1.trim(), | |
'id': RegExp.$2 | |
}; | |
} else { | |
return null; | |
} | |
}) | |
.filter((x) => !!x); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment