#include #include #include #include #include #include #include "xutil.h" #include "keysym_table.h" namespace { void usage(const char *argv0) { std::cerr << "Usage:\n" << " " << argv0 << " --which\n" << " " << argv0 << " \n" << " " << argv0 << " [ | s: | c: ]*\n" << "\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 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(Display *dpy, int argc, char **argv) { std::vector> watchlist; if (argc <= 1) { usage(argv[0]); exit(1); } for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { usage(argv[0]); exit(0); } std::optional mcode; 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); } } if (argv[i][0] == 's') mcode = x::Keysym{number}.toCode(dpy); else mcode = x::Keycode{number}; } if (!mcode) { for (size_t j = 0; j < keysym_table.size(); j++) { if (strcmp(argv[i], keysym_table[j].first) == 0) { mcode = keysym_table[j].second.toCode(dpy); break; } } } if (!mcode) { std::cerr << "Unknown key name '" << argv[i] << "'\n"; exit(1); } std::string output; if (i == 1 && argc == 2) { // empty output } else if (i >= argc - 1) { std::cerr << "Missing output for key '" << argv[i] << "'\n"; exit(1); } else { output = argv[i+1]; i++; // skip output } watchlist.emplace_back(*mcode, std::move(output)); } return watchlist; } } int main(int argc, char **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()); for (const auto &p : watchlist) { cleaners.push_back(x::XGrabKeyRAII(dpy, p.first, AnyModifier, root)); } XSelectInput(dpy, root, KeyPressMask); while (true) { XEvent ev; XNextEvent(dpy, &ev); if (ev.type != KeyPress) continue; 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; } } } }