main.cpp 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. #include <cstring>
  2. #include <cassert>
  3. #include <cstdio>
  4. #include <map>
  5. #include <X11/Xlib.h>
  6. #define XK_MISCELLANY
  7. #define XK_LATIN1
  8. #include <X11/keysymdef.h>
  9. extern "C" {
  10. #include <xdo.h>
  11. }
  12. struct KeyboardMapping {
  13. const char* keyseq;
  14. int window_index;
  15. static const int ALL_WINDOWS = -1;
  16. KeyboardMapping(const char* keyseq, int window_index)
  17. : keyseq(keyseq), window_index(window_index)
  18. {
  19. }
  20. };
  21. const std::map<KeySym, const KeyboardMapping> kbd_map = {
  22. {XK_w, KeyboardMapping("Up", 0)},
  23. {XK_a, KeyboardMapping("Left", 0)},
  24. {XK_s, KeyboardMapping("Down", 0)},
  25. {XK_d, KeyboardMapping("Right", 0)},
  26. {XK_Up, KeyboardMapping("Up", 1)},
  27. {XK_Left, KeyboardMapping("Left", 1)},
  28. {XK_Down, KeyboardMapping("Down", 1)},
  29. {XK_Right, KeyboardMapping("Right", 1)},
  30. {XK_Control_L, KeyboardMapping("Control_L", 0)},
  31. {XK_Control_R, KeyboardMapping("Control_R", 1)},
  32. {XK_space, KeyboardMapping("Control_R", KeyboardMapping::ALL_WINDOWS)},
  33. {XK_Delete, KeyboardMapping("Delete", 1)},
  34. {XK_v, KeyboardMapping("Delete", 0)},
  35. };
  36. const useconds_t KEY_DELAY = 12000; // microseconds
  37. void search_windows_by_name(const xdo_t* xdo, const char* name_regex, Window** windowlist_ret, unsigned int* nwindows_ret) {
  38. xdo_search_t search;
  39. memset(&search, 0, sizeof(xdo_search_t));
  40. search.max_depth = -1;
  41. search.require = xdo_search::SEARCH_ALL;
  42. search.searchmask |= SEARCH_NAME;
  43. search.winname = name_regex;
  44. xdo_search_windows(xdo, &search, windowlist_ret, nwindows_ret);
  45. }
  46. class ToontownKeyboardManager {
  47. Display* display;
  48. int screen;
  49. Window main_window;
  50. xdo_t* xdo;
  51. void search_toontown_windows(Window** windowlist_ret, unsigned int* nwindows_ret) {
  52. search_windows_by_name(xdo, "^Toontown", windowlist_ret, nwindows_ret);
  53. }
  54. void send_keyseq(const KeyboardMapping* mapping, int type) {
  55. Window* windows;
  56. unsigned int nwindows;
  57. search_toontown_windows(&windows, &nwindows);
  58. for(unsigned int i=0; i<nwindows; i++) {
  59. if(mapping->window_index >= nwindows
  60. || mapping->window_index == KeyboardMapping::ALL_WINDOWS
  61. || mapping->window_index == i) {
  62. // file:///usr/share/doc/libxdo-dev/html/xdo_8h.html
  63. // http://www.cl.cam.ac.uk/~mgk25/ucs/keysymdef.h
  64. if(type == KeyPress) {
  65. xdo_send_keysequence_window_down(xdo, windows[i], mapping->keyseq, KEY_DELAY);
  66. } else if(type == KeyRelease) {
  67. xdo_send_keysequence_window_up(xdo, windows[i], mapping->keyseq, KEY_DELAY);
  68. }
  69. }
  70. }
  71. if(type == KeyPress) {
  72. printf("> '%s' down\n", mapping->keyseq);
  73. } else if(type == KeyRelease) {
  74. printf("> '%s' up\n", mapping->keyseq);
  75. }
  76. }
  77. bool handle_key(XKeyEvent* key_event) {
  78. // https://tronche.com/gui/x/xlib/utilities/keyboard/XLookupKeysym.html
  79. KeySym key_symbol = XLookupKeysym(key_event, 0);
  80. if(key_event->state == ControlMask && key_symbol == XK_c) {
  81. return false; // Ctrl-c
  82. } else {
  83. auto kbd_map_it = kbd_map.find(key_symbol);
  84. if(kbd_map_it != kbd_map.end()) {
  85. send_keyseq(&kbd_map_it->second, key_event->type);
  86. } else if(key_event->type == KeyPress) {
  87. printf(
  88. "no mapping for '%s' (%#lx)\n",
  89. XKeysymToString(key_symbol),
  90. key_symbol
  91. );
  92. }
  93. return true;
  94. }
  95. }
  96. static Bool key_autorepeat_predicate(Display* display, XEvent* event, XPointer arg) {
  97. XKeyEvent* release_event = (XKeyEvent*)arg;
  98. return event->type == KeyPress
  99. && event->xkey.keycode == release_event->keycode
  100. && event->xkey.time == release_event->time;
  101. }
  102. bool key_event(XKeyEvent* key_event) {
  103. // https://tronche.com/gui/x/xlib/events/keyboard-pointer/keyboard-pointer.html#XKeyEvent
  104. XEvent repeat_event;
  105. if(!XCheckIfEvent(display, &repeat_event, key_autorepeat_predicate, (XPointer)key_event)) {
  106. return handle_key(key_event);
  107. } else {
  108. // ignore auto release event
  109. // xcheckifevent() removed the associated auto press event
  110. return true;
  111. }
  112. }
  113. bool window_event(XEvent* event) {
  114. if(event->type == KeyPress || event->type == KeyRelease) {
  115. return key_event(&event->xkey);
  116. } else {
  117. printf("window event %d", event->type);
  118. return true;
  119. }
  120. }
  121. public:
  122. ToontownKeyboardManager() {
  123. display = XOpenDisplay(NULL);
  124. assert(display);
  125. screen = XDefaultScreen(display);
  126. xdo = xdo_new(NULL);
  127. assert(xdo);
  128. // https://tronche.com/gui/x/xlib/window/XCreateWindow.html
  129. main_window = XCreateSimpleWindow(
  130. display,
  131. RootWindow(display, screen), // parent
  132. 0, // x
  133. 0, // y
  134. 50, // width
  135. 50, // height
  136. 1, // border width
  137. 0, // border
  138. 0 // background
  139. );
  140. XSelectInput(display, main_window, KeyPressMask | KeyReleaseMask);
  141. XMapWindow(display, main_window);
  142. }
  143. void loop() {
  144. XEvent event;
  145. do {
  146. XNextEvent(display, &event);
  147. } while(window_event(&event));
  148. }
  149. ~ToontownKeyboardManager() {
  150. XCloseDisplay(display);
  151. }
  152. };
  153. int main() {
  154. // http://stackoverflow.com/questions/2100654/ignore-auto-repeat-in-x11-applications
  155. // XAutoRepeatOn(display);
  156. ToontownKeyboardManager app;
  157. app.loop();
  158. return 0;
  159. }