123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- #include <cstring>
- #include <cassert>
- #include <cstdio>
- #include <map>
- #include <X11/Xlib.h>
- #define XK_MISCELLANY
- #define XK_LATIN1
- #include <X11/keysymdef.h>
- extern "C" {
- #include <xdo.h>
- }
- struct KeyboardMapping {
- const char* keyseq;
- int window_index;
- static const int ALL_WINDOWS = -1;
- KeyboardMapping(const char* keyseq, int window_index)
- : keyseq(keyseq), window_index(window_index)
- {
- }
- };
- const std::map<KeySym, const KeyboardMapping> kbd_map = {
- // 0
- {XK_w, KeyboardMapping("Up", 0)},
- {XK_a, KeyboardMapping("Left", 0)},
- {XK_s, KeyboardMapping("Down", 0)},
- {XK_d, KeyboardMapping("Right", 0)},
- {XK_Control_L, KeyboardMapping("Control_L", 0)},
- {XK_v, KeyboardMapping("Delete", 0)},
- // 1
- {XK_Up, KeyboardMapping("Up", 1)},
- {XK_Left, KeyboardMapping("Left", 1)},
- {XK_Down, KeyboardMapping("Down", 1)},
- {XK_Right, KeyboardMapping("Right", 1)},
- {XK_Control_R, KeyboardMapping("Control_R", 1)},
- {XK_Delete, KeyboardMapping("Delete", 1)},
- // 2
- {XK_i, KeyboardMapping("Up", 2)},
- {XK_j, KeyboardMapping("Left", 2)},
- {XK_k, KeyboardMapping("Down", 2)},
- {XK_l, KeyboardMapping("Right", 2)},
- {XK_n, KeyboardMapping("Control_L", 2)},
- {XK_semicolon, KeyboardMapping("Delete", 2)},
- // all
- {XK_space, KeyboardMapping("Control_R", KeyboardMapping::ALL_WINDOWS)},
- };
- const useconds_t KEY_DELAY = 12000; // microseconds
- void search_windows_by_name(const xdo_t* xdo, const char* name_regex, Window** windowlist_ret, unsigned int* nwindows_ret) {
- xdo_search_t search;
- memset(&search, 0, sizeof(xdo_search_t));
- search.max_depth = -1;
- search.require = xdo_search::SEARCH_ALL;
- search.searchmask |= SEARCH_NAME;
- search.winname = name_regex;
- xdo_search_windows(xdo, &search, windowlist_ret, nwindows_ret);
- }
- class ToontownKeyboardManager {
- Display* display;
- int screen;
- Window main_window;
- xdo_t* xdo;
- void search_toontown_windows(Window** windowlist_ret, unsigned int* nwindows_ret) {
- search_windows_by_name(xdo, "^Toontown", windowlist_ret, nwindows_ret);
- }
- void send_keyseq(const KeyboardMapping* mapping, int type) {
- Window* windows;
- unsigned int nwindows;
- search_toontown_windows(&windows, &nwindows);
- for(unsigned int i=0; i<nwindows; i++) {
- if(mapping->window_index >= nwindows
- || mapping->window_index == KeyboardMapping::ALL_WINDOWS
- || mapping->window_index == i) {
- // file:///usr/share/doc/libxdo-dev/html/xdo_8h.html
- // http://www.cl.cam.ac.uk/~mgk25/ucs/keysymdef.h
- if(type == KeyPress) {
- xdo_send_keysequence_window_down(xdo, windows[i], mapping->keyseq, KEY_DELAY);
- } else if(type == KeyRelease) {
- xdo_send_keysequence_window_up(xdo, windows[i], mapping->keyseq, KEY_DELAY);
- }
- }
- }
- if(type == KeyPress) {
- printf("> '%s' down\n", mapping->keyseq);
- } else if(type == KeyRelease) {
- printf("> '%s' up\n", mapping->keyseq);
- }
- }
- bool handle_key(XKeyEvent* key_event) {
- // https://tronche.com/gui/x/xlib/utilities/keyboard/XLookupKeysym.html
- KeySym key_symbol = XLookupKeysym(key_event, 0);
- if(key_event->state == ControlMask && key_symbol == XK_c) {
- return false; // Ctrl-c
- } else {
- auto kbd_map_it = kbd_map.find(key_symbol);
- if(kbd_map_it != kbd_map.end()) {
- send_keyseq(&kbd_map_it->second, key_event->type);
- } else if(key_event->type == KeyPress) {
- printf(
- "no mapping for '%s' (%#lx)\n",
- XKeysymToString(key_symbol),
- key_symbol
- );
- }
- return true;
- }
- }
- static Bool key_autorepeat_predicate(Display* display, XEvent* event, XPointer arg) {
- XKeyEvent* release_event = (XKeyEvent*)arg;
- return event->type == KeyPress
- && event->xkey.keycode == release_event->keycode
- && event->xkey.time == release_event->time;
- }
- bool key_event(XKeyEvent* key_event) {
- // https://tronche.com/gui/x/xlib/events/keyboard-pointer/keyboard-pointer.html#XKeyEvent
- XEvent repeat_event;
- if(!XCheckIfEvent(display, &repeat_event, key_autorepeat_predicate, (XPointer)key_event)) {
- return handle_key(key_event);
- } else {
- // ignore auto release event
- // xcheckifevent() removed the associated auto press event
- return true;
- }
- }
- bool window_event(XEvent* event) {
- if(event->type == KeyPress || event->type == KeyRelease) {
- return key_event(&event->xkey);
- } else {
- printf("window event %d", event->type);
- return true;
- }
- }
- public:
- ToontownKeyboardManager() {
- display = XOpenDisplay(NULL);
- assert(display);
- screen = XDefaultScreen(display);
- xdo = xdo_new(NULL);
- assert(xdo);
- // https://tronche.com/gui/x/xlib/window/XCreateWindow.html
- main_window = XCreateSimpleWindow(
- display,
- RootWindow(display, screen), // parent
- 0, // x
- 0, // y
- 50, // width
- 50, // height
- 1, // border width
- 0, // border
- 0 // background
- );
- XSelectInput(display, main_window, KeyPressMask | KeyReleaseMask);
- XMapWindow(display, main_window);
- }
- void loop() {
- XEvent event;
- do {
- XNextEvent(display, &event);
- } while(window_event(&event));
- }
- ~ToontownKeyboardManager() {
- XCloseDisplay(display);
- }
- };
- int main() {
- // http://stackoverflow.com/questions/2100654/ignore-auto-repeat-in-x11-applications
- // XAutoRepeatOn(display);
- ToontownKeyboardManager app;
- app.loop();
- return 0;
- }
|