From 119b623640254f425a89a3f13ce5cbe633ead9c2 Mon Sep 17 00:00:00 2001 From: Tom Smeding Date: Thu, 1 Feb 2024 14:09:26 +0100 Subject: Support for keycode input --- main.cpp | 84 ++++++++++++++++++++++++++++++++++++++++----------------------- xutil.cpp | 5 +++- xutil.h | 1 + 3 files changed, 58 insertions(+), 32 deletions(-) diff --git a/main.cpp b/main.cpp index d597629..a3679ab 100644 --- a/main.cpp +++ b/main.cpp @@ -12,24 +12,31 @@ namespace { void usage(const char *argv0) { std::cerr << "Usage:\n" + << " " << argv0 << " --which\n" << " " << argv0 << " \n" - << " " << argv0 << " [ ]*\n" + << " " << argv0 << " [ | s: | c: ]*\n" << "\n" - << "Keys are specified with the X11 keysym name or ID (in hexadecimal with \"0x\").\n" - << "Both are given by xev(1).\n" + << "Keys are specified with the X11 keysym name, its keysym ID (prefixed with 's:'),\n" + << "or its keycode (prefixed with 'c:'). The keycode is typically non-portable\n" + << "because it depends on the particular keyboard hardware.\n" + << "xev(1) prints the keysym name and the keysym ID (in hexadecimal).\n" + << "Keysym IDs and keycodes can be given in decimal and in hexadecimal (with 0x).\n" << "Examples:\n" - << "- KP_1 and 0xffb1 are valid key values for the '1' key on the numpad.\n" - << "- XF86Launch7 and 0x1008ff47 are valid key values for F16 on an Apple keyboard.\n" + << "- KP_1 and s:0xffb1 are valid key values for the '1' key on the numpad.\n" + << "- XF86Launch7 and s:0x1008ff47 are valid key values for F16 on an Apple keyboard.\n" << "The values can also be found in /usr/include/xkbcommon/xkbcommon-keysyms.h .\n" << "\n" << "If a single key is given, the program grabs that key and exits when the key is\n" << "pressed. If multiple keys are given, the output string corresponding to the\n" << "pressed key will be printed before exiting.\n" + << "\n" + << "In --which mode, the program will grab the entire keyboard, wait until the first\n" + << "pressed key, and print its keycode for use with 'c:'.\n" ; } - std::vector> parse_watchlist(int argc, char **argv) { - std::vector> watchlist; + std::vector> parse_watchlist(Display *dpy, int argc, char **argv) { + std::vector> watchlist; if (argc <= 1) { usage(argv[0]); @@ -42,29 +49,40 @@ namespace { exit(0); } - std::optional msym; + std::optional mcode; - if (argv[i][0] == '0' && argv[i][1] == 'x') { - char *endp; - unsigned code = strtol(argv[i] + 2, &endp, 16); - if (argv[i][2] == '\0' || *endp != '\0') { - std::cerr << "Invalid hexadecimal number in '" << argv[i] << "'\n"; - exit(1); + if ((argv[i][0] == 's' || argv[i][0] == 'c') && argv[i][1] == ':') { + unsigned number; + if (argv[i][2] == '0' && argv[i][3] == 'x') { + char *endp; + number = strtol(argv[i] + 2, &endp, 16); + if (argv[i][2] == '\0' || *endp != '\0') { + std::cerr << "Invalid hexadecimal number in '" << argv[i] << "'\n"; + exit(1); + } + } else { + char *endp; + number = strtol(argv[i] + 2, &endp, 10); + if (argv[i][2] == '\0' || *endp != '\0') { + std::cerr << "Invalid decimal number in '" << argv[i] << "'\n"; + exit(1); + } } - msym = x::Keysym{code}; + if (argv[i][0] == 's') mcode = x::Keysym{number}.toCode(dpy); + else mcode = x::Keycode{number}; } - if (!msym) { + if (!mcode) { for (size_t j = 0; j < keysym_table.size(); j++) { if (strcmp(argv[i], keysym_table[j].first) == 0) { - msym = keysym_table[j].second; + mcode = keysym_table[j].second.toCode(dpy); break; } } } - if (!msym) { - std::cerr << "Unknown keysym '" << argv[i] << "'\n"; + if (!mcode) { + std::cerr << "Unknown key name '" << argv[i] << "'\n"; exit(1); } @@ -72,14 +90,14 @@ namespace { if (i == 1 && argc == 2) { // empty output } else if (i >= argc - 1) { - std::cerr << "Missing output for keysym '" << argv[i] << "'\n"; + std::cerr << "Missing output for key '" << argv[i] << "'\n"; exit(1); } else { output = argv[i+1]; i++; // skip output } - watchlist.emplace_back(*msym, std::move(output)); + watchlist.emplace_back(*mcode, std::move(output)); } return watchlist; @@ -87,22 +105,26 @@ namespace { } int main(int argc, char **argv) { - std::vector> watchlist = parse_watchlist(argc, argv); - auto dpypair = x::XOpenDisplayRAII(nullptr); Display *dpy = dpypair.first; + if (argc >= 2 && strcmp(argv[1], "--which") == 0) { + x::globalKeyboardGrab(dpy, [](const XKeyEvent &ev) -> bool { + std::cout << "c:" << ev.keycode << std::endl; + return true; + }); + return 0; + } + + std::vector> watchlist = parse_watchlist(dpy, argc, argv); + Window root = DefaultRootWindow(dpy); std::vector>> cleaners; cleaners.reserve(watchlist.size()); - std::vector keycodes; - keycodes.reserve(watchlist.size()); for (const auto &p : watchlist) { - x::Keycode code = p.first.toCode(dpy); - keycodes.push_back(code); - cleaners.push_back(x::XGrabKeyRAII(dpy, code, AnyModifier, root)); + cleaners.push_back(x::XGrabKeyRAII(dpy, p.first, AnyModifier, root)); } XSelectInput(dpy, root, KeyPressMask); @@ -110,9 +132,9 @@ int main(int argc, char **argv) { XEvent ev; XNextEvent(dpy, &ev); if (ev.type != KeyPress) continue; - for (size_t i = 0; i < watchlist.size(); i++) { - if (ev.xkey.keycode == (unsigned int)keycodes[i]) { - if (watchlist[i].second.size() > 0) std::cout << watchlist[i].second << std::endl; + for (const auto &p : watchlist) { + if (ev.xkey.keycode == (unsigned int)p.first) { + if (p.second.size() > 0) std::cout << p.second << std::endl; return 0; } } diff --git a/xutil.cpp b/xutil.cpp index 7adceaa..2651dba 100644 --- a/xutil.cpp +++ b/xutil.cpp @@ -49,8 +49,11 @@ namespace x { } void globalKeyWatch(Display *dpy, Keysym keysym, std::function callback) { + globalKeyWatch(dpy, keysym.toCode(dpy), callback); + } + + void globalKeyWatch(Display *dpy, Keycode keycode, std::function callback) { const Window root = DefaultRootWindow(dpy); - const Keycode keycode = keysym.toCode(dpy); auto guard = XGrabKeyRAII(dpy, keycode, AnyModifier, root); diff --git a/xutil.h b/xutil.h index 609d20c..e067edd 100644 --- a/xutil.h +++ b/xutil.h @@ -72,6 +72,7 @@ namespace x { // the callback on every hit of that key. Ungrabs and returns when the // callback returns true. void globalKeyWatch(Display *dpy, Keysym keysym, std::function callback); + void globalKeyWatch(Display *dpy, Keycode keycode, std::function callback); // Grab the whole keyboard globally, and run the callback on every keyboard // key event. Ungrabs and returns when the callback returns true. -- cgit v1.2.3-70-g09d2