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