main.cpp 6.0 KB

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