logo

qmk_firmware

custom branch of QMK firmware git clone https://anongit.hacktivis.me/git/qmk_firmware.git

pointing_device.c (19509B)


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