You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

504 lines
17 KiB

  1. /* Copyright 2017 Joshua Broekhuijsen <snipeye+qmk@gmail.com>
  2. * Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
  3. * Copyright 2021 Dasky (@daskygit)
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include "pointing_device.h"
  19. #include <string.h>
  20. #include "timer.h"
  21. #include "gpio.h"
  22. #ifdef MOUSEKEY_ENABLE
  23. # include "mousekey.h"
  24. #endif
  25. #if (defined(POINTING_DEVICE_ROTATION_90) + defined(POINTING_DEVICE_ROTATION_180) + defined(POINTING_DEVICE_ROTATION_270)) > 1
  26. # error More than one rotation selected. This is not supported.
  27. #endif
  28. #if defined(POINTING_DEVICE_LEFT) || defined(POINTING_DEVICE_RIGHT) || defined(POINTING_DEVICE_COMBINED)
  29. # ifndef SPLIT_POINTING_ENABLE
  30. # error "Using POINTING_DEVICE_LEFT or POINTING_DEVICE_RIGHT or POINTING_DEVICE_COMBINED, then SPLIT_POINTING_ENABLE is required but has not been defined"
  31. # endif
  32. #endif
  33. #if defined(SPLIT_POINTING_ENABLE)
  34. # include "transactions.h"
  35. # include "keyboard.h"
  36. report_mouse_t shared_mouse_report = {};
  37. uint16_t shared_cpi = 0;
  38. /**
  39. * @brief Sets the shared mouse report used be pointing device task
  40. *
  41. * NOTE : Only available when using SPLIT_POINTING_ENABLE
  42. *
  43. * @param[in] new_mouse_report report_mouse_t
  44. */
  45. void pointing_device_set_shared_report(report_mouse_t new_mouse_report) {
  46. shared_mouse_report = new_mouse_report;
  47. }
  48. /**
  49. * @brief Gets current pointing device CPI if supported
  50. *
  51. * Gets current cpi of the shared report and returns it as uint16_t
  52. *
  53. * NOTE : Only available when using SPLIT_POINTING_ENABLE
  54. *
  55. * @return cpi value as uint16_t
  56. */
  57. uint16_t pointing_device_get_shared_cpi(void) {
  58. return shared_cpi;
  59. }
  60. # if defined(POINTING_DEVICE_LEFT)
  61. # define POINTING_DEVICE_THIS_SIDE is_keyboard_left()
  62. # elif defined(POINTING_DEVICE_RIGHT)
  63. # define POINTING_DEVICE_THIS_SIDE !is_keyboard_left()
  64. # elif defined(POINTING_DEVICE_COMBINED)
  65. # define POINTING_DEVICE_THIS_SIDE true
  66. # endif
  67. #endif // defined(SPLIT_POINTING_ENABLE)
  68. static report_mouse_t local_mouse_report = {};
  69. static bool pointing_device_force_send = false;
  70. extern const pointing_device_driver_t pointing_device_driver;
  71. /**
  72. * @brief Keyboard level code pointing device initialisation
  73. *
  74. */
  75. __attribute__((weak)) void pointing_device_init_kb(void) {}
  76. /**
  77. * @brief User level code pointing device initialisation
  78. *
  79. */
  80. __attribute__((weak)) void pointing_device_init_user(void) {}
  81. /**
  82. * @brief Weak function allowing for keyboard level mouse report modification
  83. *
  84. * Takes report_mouse_t struct allowing modification at keyboard level then returns report_mouse_t.
  85. *
  86. * @param[in] mouse_report report_mouse_t
  87. * @return report_mouse_t
  88. */
  89. __attribute__((weak)) report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report) {
  90. return pointing_device_task_user(mouse_report);
  91. }
  92. /**
  93. * @brief Weak function allowing for user level mouse report modification
  94. *
  95. * Takes report_mouse_t struct allowing modification at user level then returns report_mouse_t.
  96. *
  97. * @param[in] mouse_report report_mouse_t
  98. * @return report_mouse_t
  99. */
  100. __attribute__((weak)) report_mouse_t pointing_device_task_user(report_mouse_t mouse_report) {
  101. return mouse_report;
  102. }
  103. /**
  104. * @brief Handles pointing device buttons
  105. *
  106. * Returns modified button bitmask using bool pressed and selected pointing_device_buttons_t button in uint8_t buttons bitmask.
  107. *
  108. * @param buttons[in] uint8_t bitmask
  109. * @param pressed[in] bool
  110. * @param button[in] pointing_device_buttons_t value
  111. * @return Modified uint8_t bitmask buttons
  112. */
  113. __attribute__((weak)) uint8_t pointing_device_handle_buttons(uint8_t buttons, bool pressed, pointing_device_buttons_t button) {
  114. if (pressed) {
  115. buttons |= 1 << (button);
  116. } else {
  117. buttons &= ~(1 << (button));
  118. }
  119. return buttons;
  120. }
  121. /**
  122. * @brief Initialises pointing device
  123. *
  124. * Initialises pointing device, perform driver init and optional keyboard/user level code.
  125. */
  126. __attribute__((weak)) void pointing_device_init(void) {
  127. #if defined(SPLIT_POINTING_ENABLE)
  128. if ((POINTING_DEVICE_THIS_SIDE))
  129. #endif
  130. {
  131. pointing_device_driver.init();
  132. #ifdef POINTING_DEVICE_MOTION_PIN
  133. # ifdef POINTING_DEVICE_MOTION_PIN_ACTIVE_LOW
  134. gpio_set_pin_input_high(POINTING_DEVICE_MOTION_PIN);
  135. # else
  136. gpio_set_pin_input(POINTING_DEVICE_MOTION_PIN);
  137. # endif
  138. #endif
  139. }
  140. pointing_device_init_kb();
  141. pointing_device_init_user();
  142. }
  143. /**
  144. * @brief Sends processed mouse report to host
  145. *
  146. * This sends the mouse report generated by pointing_device_task if changed since the last report. Once send zeros mouse report except buttons.
  147. *
  148. */
  149. __attribute__((weak)) bool pointing_device_send(void) {
  150. static report_mouse_t old_report = {};
  151. bool should_send_report = has_mouse_report_changed(&local_mouse_report, &old_report);
  152. if (should_send_report) {
  153. host_mouse_send(&local_mouse_report);
  154. }
  155. // send it and 0 it out except for buttons, so those stay until they are explicity over-ridden using update_pointing_device
  156. uint8_t buttons = local_mouse_report.buttons;
  157. memset(&local_mouse_report, 0, sizeof(local_mouse_report));
  158. local_mouse_report.buttons = buttons;
  159. memcpy(&old_report, &local_mouse_report, sizeof(local_mouse_report));
  160. return should_send_report || buttons;
  161. }
  162. /**
  163. * @brief Adjust mouse report by any optional common pointing configuration defines
  164. *
  165. * This applies rotation or inversion to the mouse report as selected by the pointing device common configuration defines.
  166. *
  167. * @param mouse_report[in] takes a report_mouse_t to be adjusted
  168. * @return report_mouse_t with adjusted values
  169. */
  170. report_mouse_t pointing_device_adjust_by_defines(report_mouse_t mouse_report) {
  171. // Support rotation of the sensor data
  172. #if defined(POINTING_DEVICE_ROTATION_90) || defined(POINTING_DEVICE_ROTATION_180) || defined(POINTING_DEVICE_ROTATION_270)
  173. mouse_xy_report_t x = mouse_report.x;
  174. mouse_xy_report_t y = mouse_report.y;
  175. # if defined(POINTING_DEVICE_ROTATION_90)
  176. mouse_report.x = y;
  177. mouse_report.y = -x;
  178. # elif defined(POINTING_DEVICE_ROTATION_180)
  179. mouse_report.x = -x;
  180. mouse_report.y = -y;
  181. # elif defined(POINTING_DEVICE_ROTATION_270)
  182. mouse_report.x = -y;
  183. mouse_report.y = x;
  184. # else
  185. # error "How the heck did you get here?!"
  186. # endif
  187. #endif
  188. // Support Inverting the X and Y Axises
  189. #if defined(POINTING_DEVICE_INVERT_X)
  190. mouse_report.x = -mouse_report.x;
  191. #endif
  192. #if defined(POINTING_DEVICE_INVERT_Y)
  193. mouse_report.y = -mouse_report.y;
  194. #endif
  195. return mouse_report;
  196. }
  197. /**
  198. * @brief Retrieves and processes pointing device data.
  199. *
  200. * This function is part of the keyboard loop and retrieves the mouse report from the pointing device driver.
  201. * It applies any optional configuration e.g. rotation or axis inversion and then initiates a send.
  202. *
  203. */
  204. __attribute__((weak)) bool pointing_device_task(void) {
  205. #if defined(SPLIT_POINTING_ENABLE)
  206. // Don't poll the target side pointing device.
  207. if (!is_keyboard_master()) {
  208. return false;
  209. };
  210. #endif
  211. #if (POINTING_DEVICE_TASK_THROTTLE_MS > 0)
  212. static uint32_t last_exec = 0;
  213. if (timer_elapsed32(last_exec) < POINTING_DEVICE_TASK_THROTTLE_MS) {
  214. return false;
  215. }
  216. last_exec = timer_read32();
  217. #endif
  218. // Gather report info
  219. #ifdef POINTING_DEVICE_MOTION_PIN
  220. # if defined(SPLIT_POINTING_ENABLE)
  221. # error POINTING_DEVICE_MOTION_PIN not supported when sharing the pointing device report between sides.
  222. # endif
  223. # ifdef POINTING_DEVICE_MOTION_PIN_ACTIVE_LOW
  224. if (!gpio_read_pin(POINTING_DEVICE_MOTION_PIN))
  225. # else
  226. if (gpio_read_pin(POINTING_DEVICE_MOTION_PIN))
  227. # endif
  228. {
  229. #endif
  230. #if defined(SPLIT_POINTING_ENABLE)
  231. # if defined(POINTING_DEVICE_COMBINED)
  232. static uint8_t old_buttons = 0;
  233. local_mouse_report.buttons = old_buttons;
  234. local_mouse_report = pointing_device_driver.get_report(local_mouse_report);
  235. old_buttons = local_mouse_report.buttons;
  236. # elif defined(POINTING_DEVICE_LEFT) || defined(POINTING_DEVICE_RIGHT)
  237. local_mouse_report = POINTING_DEVICE_THIS_SIDE ? pointing_device_driver.get_report(local_mouse_report) : shared_mouse_report;
  238. # else
  239. # error "You need to define the side(s) the pointing device is on. POINTING_DEVICE_COMBINED / POINTING_DEVICE_LEFT / POINTING_DEVICE_RIGHT"
  240. # endif
  241. #else
  242. local_mouse_report = pointing_device_driver.get_report(local_mouse_report);
  243. #endif // defined(SPLIT_POINTING_ENABLE)
  244. #ifdef POINTING_DEVICE_MOTION_PIN
  245. }
  246. #endif
  247. // allow kb to intercept and modify report
  248. #if defined(SPLIT_POINTING_ENABLE) && defined(POINTING_DEVICE_COMBINED)
  249. if (is_keyboard_left()) {
  250. local_mouse_report = pointing_device_adjust_by_defines(local_mouse_report);
  251. shared_mouse_report = pointing_device_adjust_by_defines_right(shared_mouse_report);
  252. } else {
  253. local_mouse_report = pointing_device_adjust_by_defines_right(local_mouse_report);
  254. shared_mouse_report = pointing_device_adjust_by_defines(shared_mouse_report);
  255. }
  256. local_mouse_report = is_keyboard_left() ? pointing_device_task_combined_kb(local_mouse_report, shared_mouse_report) : pointing_device_task_combined_kb(shared_mouse_report, local_mouse_report);
  257. #else
  258. local_mouse_report = pointing_device_adjust_by_defines(local_mouse_report);
  259. local_mouse_report = pointing_device_task_kb(local_mouse_report);
  260. #endif
  261. // automatic mouse layer function
  262. #ifdef POINTING_DEVICE_AUTO_MOUSE_ENABLE
  263. pointing_device_task_auto_mouse(local_mouse_report);
  264. #endif
  265. // combine with mouse report to ensure that the combined is sent correctly
  266. #ifdef MOUSEKEY_ENABLE
  267. report_mouse_t mousekey_report = mousekey_get_report();
  268. local_mouse_report.buttons = local_mouse_report.buttons | mousekey_report.buttons;
  269. #endif
  270. const bool send_report = pointing_device_send() || pointing_device_force_send;
  271. pointing_device_force_send = false;
  272. return send_report;
  273. }
  274. /**
  275. * @brief Gets current mouse report used by pointing device task
  276. *
  277. * @return report_mouse_t
  278. */
  279. report_mouse_t pointing_device_get_report(void) {
  280. return local_mouse_report;
  281. }
  282. /**
  283. * @brief Sets mouse report used be pointing device task
  284. *
  285. * @param[in] mouse_report
  286. */
  287. void pointing_device_set_report(report_mouse_t mouse_report) {
  288. pointing_device_force_send = has_mouse_report_changed(&local_mouse_report, &mouse_report);
  289. memcpy(&local_mouse_report, &mouse_report, sizeof(local_mouse_report));
  290. }
  291. /**
  292. * @brief Gets current pointing device CPI if supported
  293. *
  294. * Gets current cpi from pointing device driver if supported and returns it as uint16_t
  295. *
  296. * @return cpi value as uint16_t
  297. */
  298. uint16_t pointing_device_get_cpi(void) {
  299. #if defined(SPLIT_POINTING_ENABLE)
  300. return POINTING_DEVICE_THIS_SIDE ? pointing_device_driver.get_cpi() : shared_cpi;
  301. #else
  302. return pointing_device_driver.get_cpi();
  303. #endif
  304. }
  305. /**
  306. * @brief Set pointing device CPI if supported
  307. *
  308. * Takes a uint16_t value to set pointing device cpi if supported by driver.
  309. *
  310. * @param[in] cpi uint16_t value.
  311. */
  312. void pointing_device_set_cpi(uint16_t cpi) {
  313. #if defined(SPLIT_POINTING_ENABLE)
  314. if (POINTING_DEVICE_THIS_SIDE) {
  315. pointing_device_driver.set_cpi(cpi);
  316. } else {
  317. shared_cpi = cpi;
  318. }
  319. #else
  320. pointing_device_driver.set_cpi(cpi);
  321. #endif
  322. }
  323. #if defined(SPLIT_POINTING_ENABLE) && defined(POINTING_DEVICE_COMBINED)
  324. /**
  325. * @brief Set pointing device CPI if supported
  326. *
  327. * Takes a bool and uint16_t and allows setting cpi for a single side when using 2 pointing devices with a split keyboard.
  328. *
  329. * NOTE: Only available when using SPLIT_POINTING_ENABLE and POINTING_DEVICE_COMBINED
  330. *
  331. * @param[in] left true = left, false = right.
  332. * @param[in] cpi uint16_t value.
  333. */
  334. void pointing_device_set_cpi_on_side(bool left, uint16_t cpi) {
  335. bool local = (is_keyboard_left() & left) ? true : false;
  336. if (local) {
  337. pointing_device_driver.set_cpi(cpi);
  338. } else {
  339. shared_cpi = cpi;
  340. }
  341. }
  342. /**
  343. * @brief clamps int16_t to int8_t
  344. *
  345. * @param[in] int16_t value
  346. * @return int8_t clamped value
  347. */
  348. static inline int8_t pointing_device_hv_clamp(int16_t value) {
  349. if (value < INT8_MIN) {
  350. return INT8_MIN;
  351. } else if (value > INT8_MAX) {
  352. return INT8_MAX;
  353. } else {
  354. return value;
  355. }
  356. }
  357. /**
  358. * @brief clamps int16_t to int8_t
  359. *
  360. * @param[in] clamp_range_t value
  361. * @return mouse_xy_report_t clamped value
  362. */
  363. static inline mouse_xy_report_t pointing_device_xy_clamp(clamp_range_t value) {
  364. if (value < XY_REPORT_MIN) {
  365. return XY_REPORT_MIN;
  366. } else if (value > XY_REPORT_MAX) {
  367. return XY_REPORT_MAX;
  368. } else {
  369. return value;
  370. }
  371. }
  372. /**
  373. * @brief combines 2 mouse reports and returns 2
  374. *
  375. * Combines 2 report_mouse_t structs, clamping movement values to int8_t and ignores report_id then returns the resulting report_mouse_t struct.
  376. *
  377. * NOTE: Only available when using SPLIT_POINTING_ENABLE and POINTING_DEVICE_COMBINED
  378. *
  379. * @param[in] left_report left report_mouse_t
  380. * @param[in] right_report right report_mouse_t
  381. * @return combined report_mouse_t of left_report and right_report
  382. */
  383. report_mouse_t pointing_device_combine_reports(report_mouse_t left_report, report_mouse_t right_report) {
  384. left_report.x = pointing_device_xy_clamp((clamp_range_t)left_report.x + right_report.x);
  385. left_report.y = pointing_device_xy_clamp((clamp_range_t)left_report.y + right_report.y);
  386. left_report.h = pointing_device_hv_clamp((int16_t)left_report.h + right_report.h);
  387. left_report.v = pointing_device_hv_clamp((int16_t)left_report.v + right_report.v);
  388. left_report.buttons |= right_report.buttons;
  389. return left_report;
  390. }
  391. /**
  392. * @brief Adjust mouse report by any optional right pointing configuration defines
  393. *
  394. * This applies rotation or inversion to the mouse report as selected by the pointing device common configuration defines.
  395. *
  396. * NOTE: Only available when using SPLIT_POINTING_ENABLE and POINTING_DEVICE_COMBINED
  397. *
  398. * @param[in] mouse_report report_mouse_t to be adjusted
  399. * @return report_mouse_t with adjusted values
  400. */
  401. report_mouse_t pointing_device_adjust_by_defines_right(report_mouse_t mouse_report) {
  402. // Support rotation of the sensor data
  403. # if defined(POINTING_DEVICE_ROTATION_90_RIGHT) || defined(POINTING_DEVICE_ROTATION_180_RIGHT) || defined(POINTING_DEVICE_ROTATION_270_RIGHT)
  404. mouse_xy_report_t x = mouse_report.x;
  405. mouse_xy_report_t y = mouse_report.y;
  406. # if defined(POINTING_DEVICE_ROTATION_90_RIGHT)
  407. mouse_report.x = y;
  408. mouse_report.y = -x;
  409. # elif defined(POINTING_DEVICE_ROTATION_180_RIGHT)
  410. mouse_report.x = -x;
  411. mouse_report.y = -y;
  412. # elif defined(POINTING_DEVICE_ROTATION_270_RIGHT)
  413. mouse_report.x = -y;
  414. mouse_report.y = x;
  415. # else
  416. # error "How the heck did you get here?!"
  417. # endif
  418. # endif
  419. // Support Inverting the X and Y Axises
  420. # if defined(POINTING_DEVICE_INVERT_X_RIGHT)
  421. mouse_report.x = -mouse_report.x;
  422. # endif
  423. # if defined(POINTING_DEVICE_INVERT_Y_RIGHT)
  424. mouse_report.y = -mouse_report.y;
  425. # endif
  426. return mouse_report;
  427. }
  428. /**
  429. * @brief Weak function allowing for keyboard level mouse report modification
  430. *
  431. * Takes 2 report_mouse_t structs allowing individual modification of sides at keyboard level then returns pointing_device_task_combined_user.
  432. *
  433. * NOTE: Only available when using SPLIT_POINTING_ENABLE and POINTING_DEVICE_COMBINED
  434. *
  435. * @param[in] left_report report_mouse_t
  436. * @param[in] right_report report_mouse_t
  437. * @return pointing_device_task_combined_user(left_report, right_report) by default
  438. */
  439. __attribute__((weak)) report_mouse_t pointing_device_task_combined_kb(report_mouse_t left_report, report_mouse_t right_report) {
  440. return pointing_device_task_combined_user(left_report, right_report);
  441. }
  442. /**
  443. * @brief Weak function allowing for user level mouse report modification
  444. *
  445. * Takes 2 report_mouse_t structs allowing individual modification of sides at user level then returns pointing_device_combine_reports.
  446. *
  447. * NOTE: Only available when using SPLIT_POINTING_ENABLE and POINTING_DEVICE_COMBINED
  448. *
  449. * @param[in] left_report report_mouse_t
  450. * @param[in] right_report report_mouse_t
  451. * @return pointing_device_combine_reports(left_report, right_report) by default
  452. */
  453. __attribute__((weak)) report_mouse_t pointing_device_task_combined_user(report_mouse_t left_report, report_mouse_t right_report) {
  454. return pointing_device_combine_reports(left_report, right_report);
  455. }
  456. #endif
  457. __attribute__((weak)) void pointing_device_keycode_handler(uint16_t keycode, bool pressed) {
  458. if IS_MOUSEKEY_BUTTON (keycode) {
  459. local_mouse_report.buttons = pointing_device_handle_buttons(local_mouse_report.buttons, pressed, keycode - KC_MS_BTN1);
  460. pointing_device_send();
  461. }
  462. }