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.

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