#include #include #include #include "xutil.h" namespace x { Keycode::Keycode() : code{0} {} Keycode::Keycode(unsigned int code) : code{code} {} Keycode::operator unsigned int() const { return code; } bool Keycode::operator==(Keycode other) const { return code == other.code; } Keysym::Keysym() : sym{0} {} Keysym::Keysym(unsigned int sym) : sym{sym} {} Keysym::operator unsigned int() const { return sym; } Keycode Keysym::toCode(Display *dpy) const { return XKeysymToKeycode(dpy, sym); } bool Keysym::operator==(Keysym other) const { return sym == other.sym; } void bel(Display *dpy) { XkbBell(dpy, None, 100, None); } using UponExitF = UponExit>; UponExitF XGrabKeyRAII(Display *dpy, Keycode code, int modifier, Window win) { XGrabKey(dpy, (unsigned int)code, modifier, win, False, GrabModeAsync, GrabModeAsync); return UponExitF{[dpy, code, modifier, win]() { XUngrabKey(dpy, (unsigned int)code, modifier, win); XSync(dpy, False); }}; } UponExitF XGrabKeyboardRAII(Display *dpy, Window win) { int ret = XGrabKeyboard(dpy, win, False, GrabModeAsync, GrabModeAsync, CurrentTime); if (ret == AlreadyGrabbed) { XUngrabKeyboard(dpy, CurrentTime); XSync(dpy, False); throw std::runtime_error("Cannot grab keyboard: already grabbed"); } return UponExitF{[dpy]() { XUngrabKeyboard(dpy, CurrentTime); XSync(dpy, False); }}; } std::pair XOpenDisplayRAII(const char *name) { Display *dpy = XOpenDisplay(name); if (dpy == nullptr) { std::cerr << "Cannot open X display" << std::endl; exit(1); } return std::make_pair(dpy, UponExitF{[dpy]() { XCloseDisplay(dpy); }}); } void globalKeyWatch(Display *dpy, Keysym keysym, std::function callback) { const Window root = DefaultRootWindow(dpy); const Keycode keycode = keysym.toCode(dpy); auto guard = XGrabKeyRAII(dpy, keycode, AnyModifier, root); XSelectInput(dpy, root, KeyPressMask); while (true) { XEvent ev; XNextEvent(dpy, &ev); if (ev.type == KeyPress && ev.xkey.keycode == (unsigned int)keycode) { if (callback(ev.xkey)) return; } } } void globalKeyboardGrab(Display *dpy, std::function callback) { const Window root = DefaultRootWindow(dpy); try { auto guard = XGrabKeyboardRAII(dpy, root); while (true) { XEvent ev; XNextEvent(dpy, &ev); if (ev.type == KeyPress) { if (callback(ev.xkey)) return; } } } catch (std::exception &e) { std::cerr << e.what() << std::endl; } } }