smallinput.h 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. /**
  2. @file smallinput.h
  3. Small API for getting keyboard/mouse input, with possiblity to record it and
  4. play back.
  5. The Linux Input API requires root pirivileges (sudo).
  6. by Milsolav "drummyfish" Ciz, released under CC0 1.0 (public domain)
  7. */
  8. #ifndef _SMALLINPUT_H
  9. #define _SMALLINPUT_H
  10. #include <stdint.h>
  11. #include <stdio.h>
  12. #define SMALLINPUT_MODE_NORMAL 0 ///< Only handle input.
  13. #define SMALLINPUT_MODE_RECORD 1 ///< Handle input and record it.
  14. #define SMALLINPUT_MODE_PLAY 2 ///< Play back recorded input.
  15. #define SMALLINPUT_KEY_NONE 0
  16. #define SMALLINPUT_SPACE ' '
  17. #define SMALLINPUT_BACKSPACE 8
  18. #define SMALLINPUT_TAB 9
  19. #define SMALLINPUT_RETURN 13
  20. #define SMALLINPUT_SHIFT 14
  21. #define SMALLINPUT_ESCAPE 27
  22. #define SMALLINPUT_DELETE 127
  23. #define SMALLINPUT_ARROW_UP 128
  24. #define SMALLINPUT_ARROW_RIGHT 129
  25. #define SMALLINPUT_ARROW_DOWN 130
  26. #define SMALLINPUT_ARROW_LEFT 131
  27. #define SMALLINPUT_F1 132
  28. #define SMALLINPUT_F2 133
  29. #define SMALLINPUT_F3 134
  30. #define SMALLINPUT_F4 135
  31. #define SMALLINPUT_F5 136
  32. #define SMALLINPUT_F6 137
  33. #define SMALLINPUT_F7 138
  34. #define SMALLINPUT_F8 139
  35. #define SMALLINPUT_F9 140
  36. #define SMALLINPUT_F10 141
  37. #define SMALLINPUT_F11 142
  38. #define SMALLINPUT_F12 143
  39. #define SMALLINPUT_CTRL 144
  40. #define SMALLINPUT_MOUSE_L 253
  41. #define SMALLINPUT_MOUSE_M 254
  42. #define SMALLINPUT_MOUSE_R 255
  43. #define SMALLINPUT_RECORD_KEY_DOWN 1 ///< Mouse down event, followed by code.
  44. #define SMALLINPUT_RECORD_KEY_UP 2 ///< Moue up event, followed by code.
  45. #define SMALLINPUT_RECORD_MOUSE_X 3 ///< Mouse x move, followed by s32 value.
  46. #define SMALLINPUT_RECORD_MOUSE_Y 4 ///< Mouse y move, followed by s32 value.
  47. #define SMALLINPUT_RECORD_END 255 ///< Record end, followed by 4 more same values.
  48. uint8_t input_keyStates[256];
  49. int32_t input_mousePosition[2];
  50. uint8_t input_keyStatesPrevious[256];
  51. int32_t input_mousePositionPrevious[2];
  52. uint8_t input_mode;
  53. uint32_t input_frame = 0;
  54. uint8_t *input_recordData;
  55. uint32_t input_recordPosition;
  56. uint32_t input_recordSize;
  57. #if 1 // TODO: add other options for input handling (SDL, xinput, ...)
  58. /*
  59. This is using Linux Input Subsystem API. Defines can be found in
  60. include/uapi/linux/input-event-codes.h.
  61. */
  62. #include <fcntl.h>
  63. #include <linux/input.h>
  64. #include <sys/time.h>
  65. #include <unistd.h>
  66. typedef struct
  67. {
  68. struct timeval time;
  69. uint16_t type;
  70. uint16_t code;
  71. int32_t value;
  72. } LinuxInputEvent;
  73. #ifdef DEV_KEYBOARD
  74. #define INPUT_KEYBOARD_FILE DEV_KEYBOARD
  75. #else
  76. #define INPUT_KEYBOARD_FILE "/dev/input/event0"
  77. #endif
  78. #ifdef DEV_MOUSE
  79. #define INPUT_MOUSE_FILE DEV_MOUSE
  80. #else
  81. #define INPUT_MOUSE_FILE "/dev/input/event1"
  82. #endif
  83. int input_keyboardFile = 0;
  84. int input_mouseFile = 0;
  85. /**
  86. Maps this library's key codes to linux input key codes.
  87. */
  88. static const int input_linuxCodes[256] =
  89. {
  90. #define no KEY_RESERVED
  91. no,no,no,no,no,no,no,no,KEY_BACKSPACE,KEY_TAB,no,no,no,KEY_ENTER,KEY_LEFTSHIFT,no,
  92. no,no,no,no,no,no,no,no,no,no,no,KEY_ESC,no,no,no,no,
  93. KEY_SPACE,no,no,no,no,no,no,no,no,no,no,no,KEY_COMMA,no,KEY_DOT,no,
  94. KEY_0,KEY_1,KEY_2,KEY_3,KEY_4,KEY_5,KEY_6,KEY_7,KEY_8,KEY_9,no,KEY_SEMICOLON,no,KEY_EQUAL,no,KEY_QUESTION,
  95. no,KEY_A,KEY_B,KEY_C,KEY_D,KEY_E,KEY_F,KEY_G,KEY_H,KEY_I,KEY_J,KEY_K,KEY_L,KEY_M,KEY_N,KEY_O,
  96. KEY_P,KEY_Q,KEY_R,KEY_S,KEY_T,KEY_U,KEY_V,KEY_W,KEY_X,KEY_Y,KEY_Z,no,no,no,no,no,
  97. no,KEY_A,KEY_B,KEY_C,KEY_D,KEY_E,KEY_F,KEY_G,KEY_H,KEY_I,KEY_J,KEY_K,KEY_L,KEY_M,KEY_N,KEY_O,
  98. KEY_P,KEY_Q,KEY_R,KEY_S,KEY_T,KEY_U,KEY_V,KEY_W,KEY_X,KEY_Y,KEY_Z,no,no,no,no,KEY_DELETE,
  99. KEY_UP,KEY_RIGHT,KEY_DOWN,KEY_LEFT,KEY_F1,KEY_F2,KEY_F3,KEY_F4,KEY_F5,KEY_F6,KEY_F7,KEY_F8,KEY_F9,KEY_F10,KEY_F11,KEY_F12,
  100. KEY_LEFTCTRL,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,
  101. no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,
  102. no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,
  103. no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,
  104. no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,
  105. no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,
  106. no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no
  107. #undef no
  108. };
  109. #endif
  110. void input_recordU8(uint8_t value)
  111. {
  112. if (input_recordPosition < (input_recordSize - 1))
  113. {
  114. input_recordData[input_recordPosition] = value;
  115. input_recordPosition++;
  116. }
  117. }
  118. void input_recordU32(uint32_t value)
  119. {
  120. for (uint8_t i = 0; i < 4; ++i)
  121. {
  122. input_recordU8(value % 256);
  123. value /= 256;
  124. }
  125. }
  126. uint32_t input_readU32(uint8_t *data)
  127. {
  128. uint32_t result = 0;
  129. for (uint8_t i = 0; i < 4; ++i)
  130. result = result * 256 + data[3 - i];
  131. return result;
  132. }
  133. /**
  134. Initializes the library with given mode (SMALLINPUT_MODE_*).
  135. */
  136. uint8_t input_init(uint8_t mode, uint8_t *recordData, uint32_t recordDataLength)
  137. {
  138. input_mode = mode;
  139. input_mousePosition[0] = 0;
  140. input_mousePosition[1] = 0;
  141. input_mousePositionPrevious[0] = 0;
  142. input_mousePositionPrevious[1] = 0;
  143. input_frame = 0;
  144. input_recordData = recordData;
  145. input_recordPosition = 0;
  146. input_recordSize = recordDataLength;
  147. for (int16_t i = 0; i < 256; ++i)
  148. {
  149. input_keyStates[i] = 0;
  150. input_keyStatesPrevious[i] = 0;
  151. }
  152. uint8_t result = 1;
  153. if (input_mode != SMALLINPUT_MODE_PLAY)
  154. {
  155. input_keyboardFile = open(INPUT_KEYBOARD_FILE, O_RDONLY);
  156. result = input_keyboardFile >= 0;
  157. if (result)
  158. {
  159. fcntl(input_keyboardFile, F_SETFL, O_NONBLOCK);
  160. input_mouseFile = open(INPUT_MOUSE_FILE, O_RDONLY);
  161. result = input_mouseFile >= 0;
  162. if (result)
  163. fcntl(input_mouseFile, F_SETFL, O_NONBLOCK);
  164. }
  165. if (!result)
  166. puts("could not open device file (are you root?)");
  167. }
  168. return result;
  169. }
  170. void input_end(void)
  171. {
  172. if (input_mode == SMALLINPUT_MODE_RECORD)
  173. for (uint8_t i = 0; i < 5; ++i)
  174. input_recordU8(SMALLINPUT_RECORD_END);
  175. close(input_keyboardFile);
  176. close(input_mouseFile);
  177. }
  178. /**
  179. Should be called once every main loop iteration to retrieve current input
  180. state.
  181. */
  182. void input_update(void)
  183. {
  184. LinuxInputEvent event;
  185. if (input_mode == SMALLINPUT_MODE_PLAY)
  186. {
  187. while (input_recordPosition < input_recordSize)
  188. {
  189. uint32_t nextFrame = input_readU32(input_recordData + input_recordPosition);
  190. if (input_frame >= nextFrame)
  191. {
  192. input_recordPosition += 4;
  193. uint8_t rec = input_recordData[input_recordPosition];
  194. switch (rec)
  195. {
  196. case SMALLINPUT_RECORD_KEY_DOWN:
  197. case SMALLINPUT_RECORD_KEY_UP:
  198. input_recordPosition++;
  199. input_keyStates[input_recordData[input_recordPosition]] = rec == SMALLINPUT_RECORD_KEY_DOWN;
  200. input_recordPosition++;
  201. break;
  202. case SMALLINPUT_RECORD_MOUSE_X:
  203. case SMALLINPUT_RECORD_MOUSE_Y:
  204. input_recordPosition++;
  205. input_mousePosition[rec == SMALLINPUT_RECORD_MOUSE_Y] =
  206. input_readU32(input_recordData + input_recordPosition);
  207. input_recordPosition += 4;
  208. break;
  209. case SMALLINPUT_RECORD_END:
  210. input_recordPosition = input_recordSize;
  211. break;
  212. default: /*printf("corrupt record\n");*/ break;
  213. }
  214. }
  215. else
  216. break;
  217. }
  218. }
  219. else
  220. {
  221. while (1) // keyboard
  222. {
  223. if (read(input_keyboardFile, &event, sizeof(event)) <= 0)
  224. break;
  225. if (event.type == EV_KEY && (event.value == 1 || event.value == 0))
  226. for (uint16_t i = 0; i < 256; ++i)
  227. if (event.code == input_linuxCodes[i])
  228. {
  229. input_keyStates[i] = event.value;
  230. break;
  231. }
  232. }
  233. while (1) // mouse
  234. {
  235. if (read(input_mouseFile, &event, sizeof(event)) <= 0)
  236. break;
  237. if (event.type == EV_REL)
  238. input_mousePosition[event.code % 2] += event.value;
  239. else if (event.type == EV_KEY)
  240. {
  241. input_keyStates[
  242. event.code == BTN_LEFT ? SMALLINPUT_MOUSE_L :
  243. (event.code == BTN_RIGHT ? SMALLINPUT_MOUSE_R : SMALLINPUT_MOUSE_M)]
  244. = event.value;
  245. }
  246. }
  247. }
  248. for (uint16_t i = 0; i < 256; ++i)
  249. if (input_keyStates[i] && input_keyStates[i] < 255)
  250. input_keyStates[i]++;
  251. if (input_mode == SMALLINPUT_MODE_RECORD)
  252. {
  253. for (uint8_t i = 0; i < 2; ++i) // record mouse events
  254. if (input_mousePositionPrevious[i] != input_mousePosition[i])
  255. {
  256. input_recordU32(input_frame + 1);
  257. input_recordU8((i == 0) ? SMALLINPUT_RECORD_MOUSE_X : SMALLINPUT_RECORD_MOUSE_Y);
  258. input_recordU32(input_mousePosition[i]);
  259. input_mousePositionPrevious[i] = input_mousePosition[i];
  260. }
  261. for (uint16_t i = 0; i < 256; ++i) // record key events
  262. {
  263. uint8_t a = input_keyStates[i] > 0;
  264. uint8_t b = input_keyStatesPrevious[i] > 0;
  265. if (a != b)
  266. {
  267. input_recordU32(input_frame + 1);
  268. input_recordU8(a ? SMALLINPUT_RECORD_KEY_DOWN : SMALLINPUT_RECORD_KEY_UP);
  269. input_recordU8(i);
  270. input_keyStatesPrevious[i] = input_keyStates[i];
  271. }
  272. }
  273. }
  274. input_frame++;
  275. }
  276. /**
  277. Returns the number of input frames for which given key has been pressed (> 1:
  278. key is pressed, == 1: key was just pressed, == 0: key is not pressed).
  279. */
  280. static inline uint8_t input_getKey(uint8_t key)
  281. {
  282. if (key >= 'a' && key <= 'z')
  283. key = 'A' + (key - 'a');
  284. return input_keyStates[key];
  285. }
  286. /**
  287. Gets the mouse position.
  288. */
  289. static inline void input_getMousePos(int32_t *x, int32_t *y)
  290. {
  291. *x = input_mousePosition[0];
  292. *y = input_mousePosition[1];
  293. }
  294. static inline void input_setMousePos(int32_t x, int32_t y)
  295. {
  296. input_mousePosition[0] = x;
  297. input_mousePosition[1] = y;
  298. }
  299. /**
  300. Prints the current input state.
  301. */
  302. void input_print()
  303. {
  304. printf("frame: %d\nmouse pos: %d %d",input_frame,input_mousePosition[0],input_mousePosition[1]);
  305. for (uint16_t i = 0; i < 256; ++i)
  306. {
  307. if (i % 8 == 0)
  308. putchar('\n');
  309. char c = (i > ' ' && i <= 126) ? i : '?';
  310. uint8_t n = input_getKey(i);
  311. printf("%s",n ? " [" : " ");
  312. printf("%03d (\'%c\'): %03d",i,c,input_getKey(i));
  313. printf("%s",n ? "] " : " ");
  314. }
  315. putchar('\n');
  316. }
  317. void input_printRecord()
  318. {
  319. for (uint32_t i = 0; i < input_recordPosition; ++i)
  320. {
  321. if (i % 32 == 0)
  322. putchar('\n');
  323. printf("%d,",input_recordData[i]);
  324. }
  325. }
  326. uint32_t input_hash()
  327. {
  328. uint32_t result = 0;
  329. for (uint16_t i = 0; i < 256; ++i)
  330. result += input_getKey(i) * (i + 1);
  331. int32_t x, y;
  332. input_getMousePos(&x,&y);
  333. result += x + y << 16;
  334. return result;
  335. }
  336. #endif // guard