Browse Source

[Core] Split support for pointing devices. (#15304)

* Draft implementation

* formatting

* fix combined buttons

* remove pimoroni throttle

* sync pointing on a throttle loop with checksum

* no longer used

* doh

Co-authored-by: Drashna Jaelre <drashna@live.com>

* switch pimoroni to a cpi equivalent

* add cpi support

* allow user modification of seperate mouse reports

* a little tidy up

* add *_RIGHT defines.

* docs

* doxygen comments

* basic changelog

* clean up pimoroni

* small doc fixes

* Update docs/feature_pointing_device.md

Co-authored-by: Drashna Jaelre <drashna@live.com>

* performance tweak if side has usb

* Don't run init funtions on wrong side

* renamed some variables for consistency

* fix pimoroni typos

* Clamp instead of OR

* Promote combined values to uint16_t

* Update pointing_device.c

Co-authored-by: Drashna Jaelre <drashna@live.com>
Co-authored-by: Nick Brassel <nick@tzarc.org>
Dasky 3 years ago
parent
commit
7f7364c559

+ 13 - 0
docs/ChangeLog/20220226/PR15304.md

@@ -0,0 +1,13 @@
+### Split Common core now supports Pointing Devices ([#15304](https://github.com/qmk/qmk_firmware/pull/15304))
+
+Pointing devices can now be shared across a split keyboard with support for a single pointing device or a pointing device on each side.
+
+This feature can be enabled with `#define SPLIT_POINTING_ENABLE` and one of the following options:
+
+| Setting                   | Description                        |
+|---------------------------|------------------------------------|
+|`POINTING_DEVICE_LEFT`     | Pointing device on the left side   |
+|`POINTING_DEVICE_RIGHT`    | Pointing device on the right side  |
+|`POINTING_DEVICE_COMBINED` | Pointing device on both sides      |
+
+See the [Pointing Device](../feature_pointing_device.md) documentation for further configuration options.

+ 105 - 11
docs/feature_pointing_device.md

@@ -127,11 +127,10 @@ The Pimoroni Trackball module is a I2C based breakout board with an RGB enable t
 | Setting                             | Description                                                                        | Default |
 | Setting                             | Description                                                                        | Default |
 |-------------------------------------|------------------------------------------------------------------------------------|---------|
 |-------------------------------------|------------------------------------------------------------------------------------|---------|
 |`PIMORONI_TRACKBALL_ADDRESS`         | (Required) Sets the I2C Address for the Pimoroni Trackball.                        | `0x0A`  |
 |`PIMORONI_TRACKBALL_ADDRESS`         | (Required) Sets the I2C Address for the Pimoroni Trackball.                        | `0x0A`  |
-|`PIMORONI_TRACKBALL_TIMEOUT`         | (Optional) The timeout for i2c communication with the trackpad in milliseconds.    | `100`   |
-|`PIMORONI_TRACKBALL_INTERVAL_MS`     | (Optional) The update/read interval for the sensor in milliseconds.                | `8`     |
+|`PIMORONI_TRACKBALL_TIMEOUT`         | (Optional) The timeout for i2c communication with the trackball in milliseconds.   | `100`   |
 |`PIMORONI_TRACKBALL_SCALE`           | (Optional) The multiplier used to generate reports from the sensor.                | `5`     |
 |`PIMORONI_TRACKBALL_SCALE`           | (Optional) The multiplier used to generate reports from the sensor.                | `5`     |
 |`PIMORONI_TRACKBALL_DEBOUNCE_CYCLES` | (Optional) The number of scan cycles used for debouncing on the ball press.        | `20`    |
 |`PIMORONI_TRACKBALL_DEBOUNCE_CYCLES` | (Optional) The number of scan cycles used for debouncing on the ball press.        | `20`    |
-|`PIMORONI_TRACKBALL_ERROR_COUNT`     | (Optional) Specifies the number of read/write errors until the sensor is disabled. | `10`  |
+|`PIMORONI_TRACKBALL_ERROR_COUNT`     | (Optional) Specifies the number of read/write errors until the sensor is disabled. | `10`    |
 
 
 ### PMW 3360 Sensor
 ### PMW 3360 Sensor
 
 
@@ -171,14 +170,35 @@ void           pointing_device_driver_set_cpi(uint16_t cpi) {}
 
 
 ## Common Configuration
 ## Common Configuration
 
 
-| Setting                       | Description                                                           | Default       |
-|-------------------------------|-----------------------------------------------------------------------|---------------|
-|`POINTING_DEVICE_ROTATION_90`  | (Optional) Rotates the X and Y data by  90 degrees.                   | _not defined_ |
-|`POINTING_DEVICE_ROTATION_180` | (Optional) Rotates the X and Y data by 180 degrees.                   | _not defined_ |
-|`POINTING_DEVICE_ROTATION_270` | (Optional) Rotates the X and Y data by 270 degrees.                   | _not defined_ |
-|`POINTING_DEVICE_INVERT_X`     | (Optional) Inverts the X axis report.                                 | _not defined_ |
-|`POINTING_DEVICE_INVERT_Y`     | (Optional) Inverts the Y axis report.                                 | _not defined_ |
-|`POINTING_DEVICE_MOTION_PIN`   | (Optional) If supported, will only read from sensor if pin is active. | _not defined_ |
+| Setting                          | Description                                                           | Default           |
+|----------------------------------|-----------------------------------------------------------------------|-------------------|
+|`POINTING_DEVICE_ROTATION_90`     | (Optional) Rotates the X and Y data by  90 degrees.                   | _not defined_     |
+|`POINTING_DEVICE_ROTATION_180`    | (Optional) Rotates the X and Y data by 180 degrees.                   | _not defined_     |
+|`POINTING_DEVICE_ROTATION_270`    | (Optional) Rotates the X and Y data by 270 degrees.                   | _not defined_     |
+|`POINTING_DEVICE_INVERT_X`        | (Optional) Inverts the X axis report.                                 | _not defined_     |
+|`POINTING_DEVICE_INVERT_Y`        | (Optional) Inverts the Y axis report.                                 | _not defined_     |
+|`POINTING_DEVICE_MOTION_PIN`      | (Optional) If supported, will only read from sensor if pin is active. | _not defined_     |
+|`POINTING_DEVICE_TASK_THROTTLE_MS`      | (Optional) Limits the frequency that the sensor is polled for motion. | _not defined_     |
+
+!> When using `SPLIT_POINTING_ENABLE` the `POINTING_DEVICE_MOTION_PIN` functionality is not supported and would recommend `POINTING_DEVICE_TASK_THROTTLE_MS` be set to `1`. Increasing this value will increase transport performance at the cost of possible mouse responsiveness.
+
+
+## Split Keyboard Configuration
+
+The following configuration options are only available when using `SPLIT_POINTING_ENABLE` see [data sync options](feature_split_keyboard.md?id=data-sync-options). The rotation and invert `*_RIGHT` options are only used with `POINTING_DEVICE_COMBINED`. If using `POINTING_DEVICE_LEFT` or `POINTING_DEVICE_RIGHT` use the common configuration above to configure your pointing device.
+
+| Setting                                | Description                                                           | Default       |
+|----------------------------------------|-----------------------------------------------------------------------|---------------|
+|`POINTING_DEVICE_LEFT`                  | Pointing device on the left side (Required - pick one only)           | _not defined_ |
+|`POINTING_DEVICE_RIGHT`                 | Pointing device on the right side (Required - pick one only)          | _not defined_ |
+|`POINTING_DEVICE_COMBINED`              | Pointing device on both sides (Required - pick one only)              | _not defined_ |
+|`POINTING_DEVICE_ROTATION_90_RIGHT`     | (Optional) Rotates the X and Y data by  90 degrees.                   | _not defined_ |
+|`POINTING_DEVICE_ROTATION_180_RIGHT`    | (Optional) Rotates the X and Y data by 180 degrees.                   | _not defined_ |
+|`POINTING_DEVICE_ROTATION_270_RIGHT`    | (Optional) Rotates the X and Y data by 270 degrees.                   | _not defined_ |
+|`POINTING_DEVICE_INVERT_X_RIGHT`        | (Optional) Inverts the X axis report.                                 | _not defined_ |
+|`POINTING_DEVICE_INVERT_Y_RIGHT`        | (Optional) Inverts the Y axis report.                                 | _not defined_ |
+
+!> If there is a `_RIGHT` configuration option or callback, the [common configuration](feature_pointing_device.md?id=common-configuration) option will work for the left. For correct left/right detection you should setup a [handedness option](feature_split_keyboard?id=setting-handedness), `EE_HANDS` is usually a good option for an existing board that doesn't do handedness by hardware.
 
 
 
 
 ## Callbacks and Functions 
 ## Callbacks and Functions 
@@ -196,6 +216,21 @@ void           pointing_device_driver_set_cpi(uint16_t cpi) {}
 | `pointing_device_set_report(mouse_report)`                 | Sets the mouse report to the assigned `mouse_report_t` data structured passed to the function.                | 
 | `pointing_device_set_report(mouse_report)`                 | Sets the mouse report to the assigned `mouse_report_t` data structured passed to the function.                | 
 | `pointing_device_send(void)`                               | Sends the current mouse report to the host system.  Function can be replaced.                                 | 
 | `pointing_device_send(void)`                               | Sends the current mouse report to the host system.  Function can be replaced.                                 | 
 | `has_mouse_report_changed(old, new)`                       | Compares the old and new `mouse_report_t` data and returns true only if it has changed.                       |
 | `has_mouse_report_changed(old, new)`                       | Compares the old and new `mouse_report_t` data and returns true only if it has changed.                       |
+| `pointing_device_adjust_by_defines(mouse_report)`          | Applies rotations and invert configurations to a raw mouse report.                                             |
+
+
+## Split Keyboard Callbacks and Functions
+
+The combined functions below are only available when using `SPLIT_POINTING_ENABLE` and `POINTING_DEVICE_COMBINED`. The 2 callbacks `pointing_device_task_combined_*` replace the single sided equivalents above. See the [combined pointing devices example](feature_pointing_device.md?id=combined-pointing-devices)
+
+| Function                                                        | Description                                                                                                              |
+|-----------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------|
+| `pointing_device_set_shared_report(mouse_report)`               | Sets the shared mouse report to the assigned `mouse_report_t` data structured passed to the function.                    |
+| `pointing_device_set_cpi_on_side(bool, uint16_t)`               | Sets the CPI/DPI of one side, if supported. Passing `true` will set the left and `false` the right`                      |
+| `pointing_device_combine_reports(left_report, right_report)`    | Returns a combined mouse_report of left_report and right_report (as a `mouse_report_t` data structure)                   |
+| `pointing_device_task_combined_kb(left_report, right_report)`   | Callback, so keyboard code can intercept and modify the data. Returns a combined mouse report.                           |
+| `pointing_device_task_combined_user(left_report, right_report)` | Callback, so user code can intercept and modify. Returns a combined mouse report using `pointing_device_combine_reports` |
+| `pointing_device_adjust_by_defines_right(mouse_report)`         | Applies right side rotations and invert configurations to a raw mouse report.                                            |
 
 
 
 
 # Manipulating Mouse Reports
 # Manipulating Mouse Reports
@@ -242,3 +277,62 @@ case MS_SPECIAL:
 ```
 ```
 
 
 Recall that the mouse report is set to zero (except the buttons) whenever it is sent, so the scrolling would only occur once in each case.
 Recall that the mouse report is set to zero (except the buttons) whenever it is sent, so the scrolling would only occur once in each case.
+
+## Split Examples
+
+The following examples make use the `SPLIT_POINTING_ENABLE` functionality and show how to manipulate the mouse report for a scrolling mode.
+
+### Single Pointing Device
+
+The following example will work with either `POINTING_DEVICE_LEFT` or `POINTING_DEVICE_RIGHT` and enables scrolling mode while on a particular layer.
+
+```c
+
+static bool scrolling_mode = false;
+
+layer_state_t layer_state_set_user(layer_state_t state) {
+    switch (get_highest_layer(state)) {
+        case _RAISE:  // If we're on the _RAISE layer enable scrolling mode
+            scrolling_mode = true;
+            pointing_device_set_cpi(2000);
+            break;
+        default:
+            if (scrolling_mode) {  // check if we were scrolling before and set disable if so
+                scrolling_mode = false;
+                pointing_device_set_cpi(8000);
+            }
+            break;
+    }
+    return state;
+}
+
+report_mouse_t pointing_device_task_user(report_mouse_t mouse_report) {
+    if (scrolling_mode) {
+        mouse_report.h = mouse_report.x;
+        mouse_report.v = mouse_report.y;
+        mouse_report.x = 0;
+        mouse_report.y = 0;
+    }
+    return mouse_report;
+}
+
+```
+
+### Combined Pointing Devices
+
+The following example requires `POINTING_DEVICE_COMBINED` and sets the left side pointing device to scroll only.
+
+```c
+void keyboard_post_init_user(void) {
+    pointing_device_set_cpi_on_side(true, 1000); //Set cpi on left side to a low value for slower scrolling.
+    pointing_device_set_cpi_on_side(false, 8000); //Set cpi on right side to a reasonable value for mousing.
+}
+
+report_mouse_t pointing_device_task_combined_user(report_mouse_t left_report, report_mouse_t right_report) {
+    left_report.h = left_report.x;
+    left_report.v = left_report.y;
+    left_report.x = 0;
+    left_report.y = 0;
+    return pointing_device_combine_reports(left_report, right_report);
+}
+```

+ 8 - 0
docs/feature_split_keyboard.md

@@ -266,6 +266,14 @@ This enables transmitting the current OLED on/off status to the slave side of th
 
 
 This enables transmitting the current ST7565 on/off status to the slave side of the split keyboard. The purpose of this feature is to support state (on/off state only) syncing.
 This enables transmitting the current ST7565 on/off status to the slave side of the split keyboard. The purpose of this feature is to support state (on/off state only) syncing.
 
 
+```c
+#define SPLIT_POINTING_ENABLE
+```
+
+This enables transmitting the pointing device status to the master side of the split keyboard. The purpose of this feature is to enable use pointing devices on the slave side. 
+
+!> There is additional required configuration for `SPLIT_POINTING_ENABLE` outlined in the [pointing device documentation](feature_pointing_device.md?id=split-keyboard-configuration).
+
 ### Custom data sync between sides :id=custom-data-sync
 ### Custom data sync between sides :id=custom-data-sync
 
 
 QMK's split transport allows for arbitrary data transactions at both the keyboard and user levels. This is modelled on a remote procedure call, with the master invoking a function on the slave side, with the ability to send data from master to slave, process it slave side, and send data back from slave to master.
 QMK's split transport allows for arbitrary data transactions at both the keyboard and user levels. This is modelled on a remote procedure call, with the master invoking a function on the slave side, with the ability to send data from master to slave, process it slave side, and send data back from slave to master.

+ 19 - 3
drivers/sensors/pimoroni_trackball.c

@@ -33,8 +33,24 @@
 
 
 static uint16_t precision = 128;
 static uint16_t precision = 128;
 
 
-float pimoroni_trackball_get_precision(void) { return ((float)precision / 128); }
-void  pimoroni_trackball_set_precision(float floatprecision) { precision = (floatprecision * 128); }
+uint16_t pimoroni_trackball_get_cpi(void) { return (precision * 125); }
+/**
+ * @brief Sets the scaling value for pimoroni trackball
+ *
+ * Sets a scaling value for pimoroni trackball to allow runtime adjustment. This isn't used by the sensor and is an
+ * approximation so the functions are consistent across drivers.
+ *
+ * NOTE: This rounds down to the nearest number divisable by 125 that's a positive integer, values below 125 are clamped to 125.
+ *
+ * @param cpi uint16_t
+ */
+void pimoroni_trackball_set_cpi(uint16_t cpi) {
+    if (cpi < 249) {
+        precision = 1;
+    } else {
+        precision = (cpi - (cpi % 125)) / 125;
+    }
+}
 
 
 void pimoroni_trackball_set_rgbw(uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
 void pimoroni_trackball_set_rgbw(uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
     uint8_t                              data[4] = {r, g, b, w};
     uint8_t                              data[4] = {r, g, b, w};
@@ -60,7 +76,7 @@ i2c_status_t read_pimoroni_trackball(pimoroni_data_t* data) {
     return status;
     return status;
 }
 }
 
 
-__attribute__((weak)) void pimironi_trackball_device_init(void) {
+__attribute__((weak)) void pimoroni_trackball_device_init(void) {
     i2c_init();
     i2c_init();
     pimoroni_trackball_set_rgbw(0x00, 0x00, 0x00, 0x00);
     pimoroni_trackball_set_rgbw(0x00, 0x00, 0x00, 0x00);
 }
 }

+ 3 - 6
drivers/sensors/pimoroni_trackball.h

@@ -23,9 +23,6 @@
 #ifndef PIMORONI_TRACKBALL_ADDRESS
 #ifndef PIMORONI_TRACKBALL_ADDRESS
 #    define PIMORONI_TRACKBALL_ADDRESS 0x0A
 #    define PIMORONI_TRACKBALL_ADDRESS 0x0A
 #endif
 #endif
-#ifndef PIMORONI_TRACKBALL_INTERVAL_MS
-#    define PIMORONI_TRACKBALL_INTERVAL_MS 8
-#endif
 #ifndef PIMORONI_TRACKBALL_SCALE
 #ifndef PIMORONI_TRACKBALL_SCALE
 #    define PIMORONI_TRACKBALL_SCALE 5
 #    define PIMORONI_TRACKBALL_SCALE 5
 #endif
 #endif
@@ -52,10 +49,10 @@ typedef struct {
     uint8_t click;
     uint8_t click;
 } pimoroni_data_t;
 } pimoroni_data_t;
 
 
-void         pimironi_trackball_device_init(void);
+void         pimoroni_trackball_device_init(void);
 void         pimoroni_trackball_set_rgbw(uint8_t red, uint8_t green, uint8_t blue, uint8_t white);
 void         pimoroni_trackball_set_rgbw(uint8_t red, uint8_t green, uint8_t blue, uint8_t white);
 int16_t      pimoroni_trackball_get_offsets(uint8_t negative_dir, uint8_t positive_dir, uint8_t scale);
 int16_t      pimoroni_trackball_get_offsets(uint8_t negative_dir, uint8_t positive_dir, uint8_t scale);
 void         pimoroni_trackball_adapt_values(int8_t* mouse, int16_t* offset);
 void         pimoroni_trackball_adapt_values(int8_t* mouse, int16_t* offset);
-float        pimoroni_trackball_get_precision(void);
-void         pimoroni_trackball_set_precision(float precision);
+uint16_t     pimoroni_trackball_get_cpi(void);
+void         pimoroni_trackball_set_cpi(uint16_t cpi);
 i2c_status_t read_pimoroni_trackball(pimoroni_data_t* data);
 i2c_status_t read_pimoroni_trackball(pimoroni_data_t* data);

+ 356 - 33
quantum/pointing_device.c

@@ -18,24 +18,105 @@
 
 
 #include "pointing_device.h"
 #include "pointing_device.h"
 #include <string.h>
 #include <string.h>
+#include "timer.h"
 #ifdef MOUSEKEY_ENABLE
 #ifdef MOUSEKEY_ENABLE
 #    include "mousekey.h"
 #    include "mousekey.h"
 #endif
 #endif
 #if (defined(POINTING_DEVICE_ROTATION_90) + defined(POINTING_DEVICE_ROTATION_180) + defined(POINTING_DEVICE_ROTATION_270)) > 1
 #if (defined(POINTING_DEVICE_ROTATION_90) + defined(POINTING_DEVICE_ROTATION_180) + defined(POINTING_DEVICE_ROTATION_270)) > 1
 #    error More than one rotation selected.  This is not supported.
 #    error More than one rotation selected.  This is not supported.
 #endif
 #endif
+#if defined(SPLIT_POINTING_ENABLE)
+#    include "transactions.h"
+#    include "keyboard.h"
 
 
-static report_mouse_t mouseReport = {};
+report_mouse_t shared_mouse_report = {};
+uint16_t       shared_cpi          = 0;
+
+/**
+ * @brief Sets the shared mouse report used be pointing device task
+ *
+ * NOTE : Only available when using SPLIT_POINTING_ENABLE
+ *
+ * @param[in] new_mouse_report report_mouse_t
+ */
+void pointing_device_set_shared_report(report_mouse_t new_mouse_report) { shared_mouse_report = new_mouse_report; }
+
+/**
+ * @brief Gets current pointing device CPI if supported
+ *
+ * Gets current cpi of the shared report and returns it as uint16_t
+ *
+ * NOTE : Only available when using SPLIT_POINTING_ENABLE
+ *
+ * @return cpi value as uint16_t
+ */
+uint16_t pointing_device_get_shared_cpi(void) { return shared_cpi; }
+
+#    if defined(POINTING_DEVICE_LEFT)
+#        define POINTING_DEVICE_THIS_SIDE is_keyboard_left()
+#    elif defined(POINTING_DEVICE_RIGHT)
+#        define POINTING_DEVICE_THIS_SIDE !is_keyboard_left()
+#    elif defined(POINTING_DEVICE_COMBINED)
+#        define POINTING_DEVICE_THIS_SIDE true
+#    endif
+
+#endif  // defined(SPLIT_POINTING_ENABLE)
+
+static report_mouse_t local_mouse_report = {};
 
 
 extern const pointing_device_driver_t pointing_device_driver;
 extern const pointing_device_driver_t pointing_device_driver;
 
 
+/**
+ * @brief Compares 2 mouse reports for difference and returns result
+ *
+ * @param[in] new report_mouse_t
+ * @param[in] old report_mouse_t
+ * @return bool result
+ */
 __attribute__((weak)) bool has_mouse_report_changed(report_mouse_t new, report_mouse_t old) { return memcmp(&new, &old, sizeof(new)); }
 __attribute__((weak)) bool has_mouse_report_changed(report_mouse_t new, report_mouse_t old) { return memcmp(&new, &old, sizeof(new)); }
 
 
-__attribute__((weak)) void           pointing_device_init_kb(void) {}
-__attribute__((weak)) void           pointing_device_init_user(void) {}
+/**
+ * @brief Keyboard level code pointing device initialisation
+ *
+ */
+__attribute__((weak)) void pointing_device_init_kb(void) {}
+
+/**
+ * @brief User level code pointing device initialisation
+ *
+ */
+__attribute__((weak)) void pointing_device_init_user(void) {}
+
+/**
+ * @brief Weak function allowing for keyboard level mouse report modification
+ *
+ * Takes report_mouse_t struct allowing modification at keyboard level then returns report_mouse_t.
+ *
+ * @param[in] mouse_report report_mouse_t
+ * @return report_mouse_t
+ */
 __attribute__((weak)) report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report) { return pointing_device_task_user(mouse_report); }
 __attribute__((weak)) report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report) { return pointing_device_task_user(mouse_report); }
+
+/**
+ * @brief Weak function allowing for user level mouse report modification
+ *
+ * Takes report_mouse_t struct allowing modification at user level then returns report_mouse_t.
+ *
+ * @param[in] mouse_report report_mouse_t
+ * @return report_mouse_t
+ */
 __attribute__((weak)) report_mouse_t pointing_device_task_user(report_mouse_t mouse_report) { return mouse_report; }
 __attribute__((weak)) report_mouse_t pointing_device_task_user(report_mouse_t mouse_report) { return mouse_report; }
 
 
+/**
+ * @brief Handles pointing device buttons
+ *
+ * Returns modified button bitmask using bool pressed and selected pointing_device_buttons_t button in uint8_t buttons bitmask.
+ *
+ * @param buttons[in] uint8_t bitmask
+ * @param pressed[in] bool
+ * @param button[in] pointing_device_buttons_t value
+ * @return Modified uint8_t bitmask buttons
+ */
 __attribute__((weak)) uint8_t pointing_device_handle_buttons(uint8_t buttons, bool pressed, pointing_device_buttons_t button) {
 __attribute__((weak)) uint8_t pointing_device_handle_buttons(uint8_t buttons, bool pressed, pointing_device_buttons_t button) {
     if (pressed) {
     if (pressed) {
         buttons |= 1 << (button);
         buttons |= 1 << (button);
@@ -45,7 +126,17 @@ __attribute__((weak)) uint8_t pointing_device_handle_buttons(uint8_t buttons, bo
     return buttons;
     return buttons;
 }
 }
 
 
+/**
+ * @brief Initialises pointing device
+ *
+ * Initialises pointing device, perform driver init and optional keyboard/user level code.
+ */
 __attribute__((weak)) void pointing_device_init(void) {
 __attribute__((weak)) void pointing_device_init(void) {
+#if defined(SPLIT_POINTING_ENABLE)
+    if (!(POINTING_DEVICE_THIS_SIDE)) {
+        return;
+    }
+#endif
     pointing_device_driver.init();
     pointing_device_driver.init();
 #ifdef POINTING_DEVICE_MOTION_PIN
 #ifdef POINTING_DEVICE_MOTION_PIN
     setPinInputHigh(POINTING_DEVICE_MOTION_PIN);
     setPinInputHigh(POINTING_DEVICE_MOTION_PIN);
@@ -54,67 +145,299 @@ __attribute__((weak)) void pointing_device_init(void) {
     pointing_device_init_user();
     pointing_device_init_user();
 }
 }
 
 
+/**
+ * @brief Sends processed mouse report to host
+ *
+ * This sends the mouse report generated by pointing_device_task if changed since the last report. Once send zeros mouse report except buttons.
+ *
+ */
 __attribute__((weak)) void pointing_device_send(void) {
 __attribute__((weak)) void pointing_device_send(void) {
     static report_mouse_t old_report = {};
     static report_mouse_t old_report = {};
 
 
     // If you need to do other things, like debugging, this is the place to do it.
     // If you need to do other things, like debugging, this is the place to do it.
-    if (has_mouse_report_changed(mouseReport, old_report)) {
-        host_mouse_send(&mouseReport);
+    if (has_mouse_report_changed(local_mouse_report, old_report)) {
+        host_mouse_send(&local_mouse_report);
     }
     }
     // send it and 0 it out except for buttons, so those stay until they are explicity over-ridden using update_pointing_device
     // send it and 0 it out except for buttons, so those stay until they are explicity over-ridden using update_pointing_device
-    mouseReport.x = 0;
-    mouseReport.y = 0;
-    mouseReport.v = 0;
-    mouseReport.h = 0;
+    local_mouse_report.x = 0;
+    local_mouse_report.y = 0;
+    local_mouse_report.v = 0;
+    local_mouse_report.h = 0;
 
 
-    memcpy(&old_report, &mouseReport, sizeof(mouseReport));
+    memcpy(&old_report, &local_mouse_report, sizeof(local_mouse_report));
 }
 }
 
 
-__attribute__((weak)) void pointing_device_task(void) {
-    // Gather report info
-#ifdef POINTING_DEVICE_MOTION_PIN
-    if (!readPin(POINTING_DEVICE_MOTION_PIN))
-#endif
-        mouseReport = pointing_device_driver.get_report(mouseReport);
-
-        // Support rotation of the sensor data
+/**
+ * @brief Adjust mouse report by any optional common pointing configuration defines
+ *
+ * This applies rotation or inversion to the mouse report as selected by the pointing device common configuration defines.
+ *
+ * @param mouse_report[in] takes a report_mouse_t to be adjusted
+ * @return report_mouse_t with adjusted values
+ */
+report_mouse_t pointing_device_adjust_by_defines(report_mouse_t mouse_report) {
+    // Support rotation of the sensor data
 #if defined(POINTING_DEVICE_ROTATION_90) || defined(POINTING_DEVICE_ROTATION_180) || defined(POINTING_DEVICE_ROTATION_270)
 #if defined(POINTING_DEVICE_ROTATION_90) || defined(POINTING_DEVICE_ROTATION_180) || defined(POINTING_DEVICE_ROTATION_270)
-    int8_t x = mouseReport.x, y = mouseReport.y;
+    int8_t x = mouse_report.x, y = mouse_report.y;
 #    if defined(POINTING_DEVICE_ROTATION_90)
 #    if defined(POINTING_DEVICE_ROTATION_90)
-    mouseReport.x = y;
-    mouseReport.y = -x;
+    mouse_report.x = y;
+    mouse_report.y = -x;
 #    elif defined(POINTING_DEVICE_ROTATION_180)
 #    elif defined(POINTING_DEVICE_ROTATION_180)
-    mouseReport.x = -x;
-    mouseReport.y = -y;
+    mouse_report.x = -x;
+    mouse_report.y = -y;
 #    elif defined(POINTING_DEVICE_ROTATION_270)
 #    elif defined(POINTING_DEVICE_ROTATION_270)
-    mouseReport.x = -y;
-    mouseReport.y = x;
+    mouse_report.x = -y;
+    mouse_report.y = x;
 #    else
 #    else
 #        error "How the heck did you get here?!"
 #        error "How the heck did you get here?!"
 #    endif
 #    endif
 #endif
 #endif
     // Support Inverting the X and Y Axises
     // Support Inverting the X and Y Axises
 #if defined(POINTING_DEVICE_INVERT_X)
 #if defined(POINTING_DEVICE_INVERT_X)
-    mouseReport.x = -mouseReport.x;
+    mouse_report.x = -mouse_report.x;
 #endif
 #endif
 #if defined(POINTING_DEVICE_INVERT_Y)
 #if defined(POINTING_DEVICE_INVERT_Y)
-    mouseReport.y = -mouseReport.y;
+    mouse_report.y = -mouse_report.y;
+#endif
+    return mouse_report;
+}
+
+/**
+ * @brief Retrieves and processes pointing device data.
+ *
+ * This function is part of the keyboard loop and retrieves the mouse report from the pointing device driver.
+ * It applies any optional configuration e.g. rotation or axis inversion and then initiates a send.
+ *
+ */
+__attribute__((weak)) void pointing_device_task(void) {
+#if defined(SPLIT_POINTING_ENABLE)
+    // Don't poll the target side pointing device.
+    if (!is_keyboard_master()) {
+        return;
+    };
+#endif
+
+#if defined(POINTING_DEVICE_TASK_THROTTLE_MS)
+    static uint32_t last_exec = 0;
+    if (timer_elapsed32(last_exec) < POINTING_DEVICE_TASK_THROTTLE_MS) {
+        return;
+    }
+    last_exec = timer_read32();
+#else
+#    if defined(SPLIT_POINTING_ENABLE)
+#        pragma message("It's recommended you enable a throttle when sharing pointing devices.")
+#    endif
+#endif
+
+    // Gather report info
+#ifdef POINTING_DEVICE_MOTION_PIN
+#    if defined(SPLIT_POINTING_ENABLE)
+#        error POINTING_DEVICE_MOTION_PIN not supported when sharing the pointing device report between sides.
+#    endif
+    if (!readPin(POINTING_DEVICE_MOTION_PIN))
 #endif
 #endif
 
 
+#if defined(SPLIT_POINTING_ENABLE)
+#    if defined(POINTING_DEVICE_COMBINED)
+        static uint8_t old_buttons = 0;
+    local_mouse_report.buttons = old_buttons;
+    local_mouse_report         = pointing_device_driver.get_report(local_mouse_report);
+    old_buttons                = local_mouse_report.buttons;
+#    elif defined(POINTING_DEVICE_LEFT) || defined(POINTING_DEVICE_RIGHT)
+        local_mouse_report = POINTING_DEVICE_THIS_SIDE ? pointing_device_driver.get_report(local_mouse_report) : shared_mouse_report;
+#    else
+#        error "You need to define the side(s) the pointing device is on. POINTING_DEVICE_COMBINED / POINTING_DEVICE_LEFT / POINTING_DEVICE_RIGHT"
+#    endif
+#else
+    local_mouse_report = pointing_device_driver.get_report(local_mouse_report);
+#endif  // defined(SPLIT_POINTING_ENABLE)
+
     // allow kb to intercept and modify report
     // allow kb to intercept and modify report
-    mouseReport = pointing_device_task_kb(mouseReport);
+#if defined(SPLIT_POINTING_ENABLE) && defined(POINTING_DEVICE_COMBINED)
+    if (is_keyboard_left()) {
+        local_mouse_report  = pointing_device_adjust_by_defines(local_mouse_report);
+        shared_mouse_report = pointing_device_adjust_by_defines_right(shared_mouse_report);
+    } else {
+        local_mouse_report  = pointing_device_adjust_by_defines_right(local_mouse_report);
+        shared_mouse_report = pointing_device_adjust_by_defines(shared_mouse_report);
+    }
+    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);
+#else
+    local_mouse_report = pointing_device_adjust_by_defines(local_mouse_report);
+    local_mouse_report = pointing_device_task_kb(local_mouse_report);
+#endif
     // combine with mouse report to ensure that the combined is sent correctly
     // combine with mouse report to ensure that the combined is sent correctly
 #ifdef MOUSEKEY_ENABLE
 #ifdef MOUSEKEY_ENABLE
     report_mouse_t mousekey_report = mousekey_get_report();
     report_mouse_t mousekey_report = mousekey_get_report();
-    mouseReport.buttons            = mouseReport.buttons | mousekey_report.buttons;
+    local_mouse_report.buttons     = local_mouse_report.buttons | mousekey_report.buttons;
 #endif
 #endif
     pointing_device_send();
     pointing_device_send();
 }
 }
 
 
-report_mouse_t pointing_device_get_report(void) { return mouseReport; }
+/**
+ * @brief Gets current mouse report used by pointing device task
+ *
+ * @return report_mouse_t
+ */
+report_mouse_t pointing_device_get_report(void) { return local_mouse_report; }
+
+/**
+ * @brief Sets mouse report used be pointing device task
+ *
+ * @param[in] new_mouse_report
+ */
+void pointing_device_set_report(report_mouse_t new_mouse_report) { local_mouse_report = new_mouse_report; }
+
+/**
+ * @brief Gets current pointing device CPI if supported
+ *
+ * Gets current cpi from pointing device driver if supported and returns it as uint16_t
+ *
+ * @return cpi value as uint16_t
+ */
+uint16_t pointing_device_get_cpi(void) {
+#if defined(SPLIT_POINTING_ENABLE)
+    return POINTING_DEVICE_THIS_SIDE ? pointing_device_driver.get_cpi() : shared_cpi;
+#else
+    return pointing_device_driver.get_cpi();
+#endif
+}
 
 
-void pointing_device_set_report(report_mouse_t newMouseReport) { mouseReport = newMouseReport; }
+/**
+ * @brief Set pointing device CPI if supported
+ *
+ * Takes a uint16_t value to set pointing device cpi if supported by driver.
+ *
+ * @param[in] cpi uint16_t value.
+ */
+void pointing_device_set_cpi(uint16_t cpi) {
+#if defined(SPLIT_POINTING_ENABLE)
+    if (POINTING_DEVICE_THIS_SIDE) {
+        pointing_device_driver.set_cpi(cpi);
+    } else {
+        shared_cpi = cpi;
+    }
+#else
+    pointing_device_driver.set_cpi(cpi);
+#endif
+}
 
 
-uint16_t pointing_device_get_cpi(void) { return pointing_device_driver.get_cpi(); }
+#if defined(SPLIT_POINTING_ENABLE) && defined(POINTING_DEVICE_COMBINED)
+/**
+ * @brief Set pointing device CPI if supported
+ *
+ * Takes a bool and uint16_t and allows setting cpi for a single side when using 2 pointing devices with a split keyboard.
+ *
+ * NOTE: Only available when using SPLIT_POINTING_ENABLE and POINTING_DEVICE_COMBINED
+ *
+ * @param[in] left true = left, false = right.
+ * @param[in] cpi uint16_t value.
+ */
+void pointing_device_set_cpi_on_side(bool left, uint16_t cpi) {
+    bool local = (is_keyboard_left() & left) ? true : false;
+    if (local) {
+        pointing_device_driver.set_cpi(cpi);
+    } else {
+        shared_cpi = cpi;
+    }
+}
 
 
-void pointing_device_set_cpi(uint16_t cpi) { pointing_device_driver.set_cpi(cpi); }
+/**
+ * @brief clamps int16_t to int8_t
+ *
+ * @param[in] int16_t value
+ * @return int8_t clamped value
+ */
+static inline int8_t pointing_device_movement_clamp(int16_t value) {
+    if (value < INT8_MIN) {
+        return INT8_MIN;
+    } else if (value > INT8_MAX) {
+        return INT8_MAX;
+    } else {
+        return value;
+    }
+}
+
+/**
+ * @brief combines 2 mouse reports and returns 2
+ *
+ * Combines 2 report_mouse_t structs, clamping movement values to int8_t and ignores report_id then returns the resulting report_mouse_t struct.
+ *
+ * NOTE: Only available when using SPLIT_POINTING_ENABLE and POINTING_DEVICE_COMBINED
+ *
+ * @param[in] left_report left report_mouse_t
+ * @param[in] right_report right report_mouse_t
+ * @return combined report_mouse_t of left_report and right_report
+ */
+report_mouse_t pointing_device_combine_reports(report_mouse_t left_report, report_mouse_t right_report) {
+    left_report.x = pointing_device_movement_clamp((int16_t)left_report.x + right_report.x);
+    left_report.y = pointing_device_movement_clamp((int16_t)left_report.y + right_report.y);
+    left_report.h = pointing_device_movement_clamp((int16_t)left_report.h + right_report.h);
+    left_report.v = pointing_device_movement_clamp((int16_t)left_report.v + right_report.v);
+    left_report.buttons |= right_report.buttons;
+    return left_report;
+}
+
+/**
+ * @brief Adjust mouse report by any optional right pointing configuration defines
+ *
+ * This applies rotation or inversion to the mouse report as selected by the pointing device common configuration defines.
+ *
+ * NOTE: Only available when using SPLIT_POINTING_ENABLE and POINTING_DEVICE_COMBINED
+ *
+ * @param[in] mouse_report report_mouse_t to be adjusted
+ * @return report_mouse_t with adjusted values
+ */
+report_mouse_t pointing_device_adjust_by_defines_right(report_mouse_t mouse_report) {
+    // Support rotation of the sensor data
+#    if defined(POINTING_DEVICE_ROTATION_90_RIGHT) || defined(POINTING_DEVICE_ROTATION_RIGHT) || defined(POINTING_DEVICE_ROTATION_RIGHT)
+    int8_t x = mouse_report.x, y = mouse_report.y;
+#        if defined(POINTING_DEVICE_ROTATION_90_RIGHT)
+    mouse_report.x = y;
+    mouse_report.y = -x;
+#        elif defined(POINTING_DEVICE_ROTATION_180_RIGHT)
+    mouse_report.x = -x;
+    mouse_report.y = -y;
+#        elif defined(POINTING_DEVICE_ROTATION_270_RIGHT)
+    mouse_report.x = -y;
+    mouse_report.y = x;
+#        else
+#            error "How the heck did you get here?!"
+#        endif
+#    endif
+    // Support Inverting the X and Y Axises
+#    if defined(POINTING_DEVICE_INVERT_X_RIGHT)
+    mouse_report.x = -mouse_report.x;
+#    endif
+#    if defined(POINTING_DEVICE_INVERT_Y_RIGHT)
+    mouse_report.y = -mouse_report.y;
+#    endif
+    return mouse_report;
+}
+
+/**
+ * @brief Weak function allowing for keyboard level mouse report modification
+ *
+ * Takes 2 report_mouse_t structs allowing individual modification of sides at keyboard level then returns pointing_device_task_combined_user.
+ *
+ * NOTE: Only available when using SPLIT_POINTING_ENABLE and POINTING_DEVICE_COMBINED
+ *
+ * @param[in] left_report report_mouse_t
+ * @param[in] right_report report_mouse_t
+ * @return pointing_device_task_combined_user(left_report, right_report) by default
+ */
+__attribute__((weak)) report_mouse_t pointing_device_task_combined_kb(report_mouse_t left_report, report_mouse_t right_report) { return pointing_device_task_combined_user(left_report, right_report); }
+
+/**
+ * @brief Weak function allowing for user level mouse report modification
+ *
+ * Takes 2 report_mouse_t structs allowing individual modification of sides at user level then returns pointing_device_combine_reports.
+ *
+ * NOTE: Only available when using SPLIT_POINTING_ENABLE and POINTING_DEVICE_COMBINED
+ *
+ * @param[in] left_report report_mouse_t
+ * @param[in] right_report report_mouse_t
+ * @return pointing_device_combine_reports(left_report, right_report) by default
+ */
+__attribute__((weak)) report_mouse_t pointing_device_task_combined_user(report_mouse_t left_report, report_mouse_t right_report) { return pointing_device_combine_reports(left_report, right_report); }
+#endif

+ 13 - 0
quantum/pointing_device.h

@@ -86,3 +86,16 @@ void           pointing_device_init_user(void);
 report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report);
 report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report);
 report_mouse_t pointing_device_task_user(report_mouse_t mouse_report);
 report_mouse_t pointing_device_task_user(report_mouse_t mouse_report);
 uint8_t        pointing_device_handle_buttons(uint8_t buttons, bool pressed, pointing_device_buttons_t button);
 uint8_t        pointing_device_handle_buttons(uint8_t buttons, bool pressed, pointing_device_buttons_t button);
+report_mouse_t pointing_device_adjust_by_defines(report_mouse_t mouse_report);
+
+#if defined(SPLIT_POINTING_ENABLE)
+void     pointing_device_set_shared_report(report_mouse_t report);
+uint16_t pointing_device_get_shared_cpi(void);
+#    if defined(POINTING_DEVICE_COMBINED)
+void           pointing_device_set_cpi_on_side(bool left, uint16_t cpi);
+report_mouse_t pointing_device_combine_reports(report_mouse_t left_report, report_mouse_t right_report);
+report_mouse_t pointing_device_task_combined_kb(report_mouse_t left_report, report_mouse_t right_report);
+report_mouse_t pointing_device_task_combined_user(report_mouse_t left_report, report_mouse_t right_report);
+report_mouse_t pointing_device_adjust_by_defines_right(report_mouse_t mouse_report);
+#    endif //defined(POINTING_DEVICE_COMBINED)
+#endif //defined(SPLIT_POINTING_ENABLE)

+ 11 - 13
quantum/pointing_device_drivers.c

@@ -165,14 +165,13 @@ const pointing_device_driver_t pointing_device_driver = {
 // clang-format on
 // clang-format on
 
 
 #elif defined(POINTING_DEVICE_DRIVER_pimoroni_trackball)
 #elif defined(POINTING_DEVICE_DRIVER_pimoroni_trackball)
-report_mouse_t pimorono_trackball_get_report(report_mouse_t mouse_report) {
-    static fast_timer_t throttle      = 0;
-    static uint16_t     debounce      = 0;
-    static uint8_t      error_count   = 0;
-    pimoroni_data_t     pimoroni_data = {0};
-    static int16_t      x_offset = 0, y_offset = 0;
-
-    if (error_count < PIMORONI_TRACKBALL_ERROR_COUNT && timer_elapsed_fast(throttle) >= PIMORONI_TRACKBALL_INTERVAL_MS) {
+report_mouse_t pimoroni_trackball_get_report(report_mouse_t mouse_report) {
+    static uint16_t debounce      = 0;
+    static uint8_t  error_count   = 0;
+    pimoroni_data_t pimoroni_data = {0};
+    static int16_t  x_offset = 0, y_offset = 0;
+
+    if (error_count < PIMORONI_TRACKBALL_ERROR_COUNT) {
         i2c_status_t status = read_pimoroni_trackball(&pimoroni_data);
         i2c_status_t status = read_pimoroni_trackball(&pimoroni_data);
 
 
         if (status == I2C_STATUS_SUCCESS) {
         if (status == I2C_STATUS_SUCCESS) {
@@ -195,17 +194,16 @@ report_mouse_t pimorono_trackball_get_report(report_mouse_t mouse_report) {
         } else {
         } else {
             error_count++;
             error_count++;
         }
         }
-        throttle = timer_read_fast();
     }
     }
     return mouse_report;
     return mouse_report;
 }
 }
 
 
 // clang-format off
 // clang-format off
 const pointing_device_driver_t pointing_device_driver = {
 const pointing_device_driver_t pointing_device_driver = {
-    .init       = pimironi_trackball_device_init,
-    .get_report = pimorono_trackball_get_report,
-    .set_cpi    = NULL,
-    .get_cpi    = NULL
+    .init       = pimoroni_trackball_device_init,
+    .get_report = pimoroni_trackball_get_report,
+    .set_cpi    = pimoroni_trackball_set_cpi,
+    .get_cpi    = pimoroni_trackball_get_cpi
 };
 };
 // clang-format on
 // clang-format on
 #elif defined(POINTING_DEVICE_DRIVER_pmw3360)
 #elif defined(POINTING_DEVICE_DRIVER_pmw3360)

+ 6 - 0
quantum/split_common/transaction_id_define.h

@@ -78,6 +78,12 @@ enum serial_transaction_id {
     PUT_ST7565,
     PUT_ST7565,
 #endif  // defined(ST7565_ENABLE) && defined(SPLIT_ST7565_ENABLE)
 #endif  // defined(ST7565_ENABLE) && defined(SPLIT_ST7565_ENABLE)
 
 
+#if defined(POINTING_DEVICE_ENABLE) && defined(SPLIT_POINTING_ENABLE)
+    GET_POINTING_CHECKSUM,
+    GET_POINTING_DATA,
+    PUT_POINTING_CPI,
+#endif  // defined(POINTING_DEVICE_ENABLE) && defined(SPLIT_POINTING_ENABLE)
+
 #if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
 #if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
     PUT_RPC_INFO,
     PUT_RPC_INFO,
     PUT_RPC_REQ_DATA,
     PUT_RPC_REQ_DATA,

+ 79 - 0
quantum/split_common/transactions.c

@@ -578,6 +578,82 @@ static void st7565_handlers_slave(matrix_row_t master_matrix[], matrix_row_t sla
 
 
 #endif  // defined(ST7565_ENABLE) && defined(SPLIT_ST7565_ENABLE)
 #endif  // defined(ST7565_ENABLE) && defined(SPLIT_ST7565_ENABLE)
 
 
+////////////////////////////////////////////////////
+// POINTING
+
+#if defined(POINTING_DEVICE_ENABLE) && defined(SPLIT_POINTING_ENABLE)
+
+static bool pointing_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+#    if defined(POINTING_DEVICE_LEFT)
+    if (is_keyboard_left()) {
+        return true;
+    }
+#    elif defined(POINTING_DEVICE_RIGHT)
+    if (!is_keyboard_left()) {
+        return true;
+    }
+#    endif
+    static uint32_t last_update = 0;
+    static uint16_t last_cpi    = 0;
+    report_mouse_t  temp_state;
+    uint16_t        temp_cpi;
+    bool            okay = read_if_checksum_mismatch(GET_POINTING_CHECKSUM, GET_POINTING_DATA, &last_update, &temp_state, &split_shmem->pointing.report, sizeof(temp_state));
+    if (okay) pointing_device_set_shared_report(temp_state);
+    temp_cpi = pointing_device_get_shared_cpi();
+    if (temp_cpi && memcmp(&last_cpi, &temp_cpi, sizeof(temp_cpi)) != 0) {
+        memcpy(&split_shmem->pointing.cpi, &temp_cpi, sizeof(temp_cpi));
+        okay = transport_write(PUT_POINTING_CPI, &split_shmem->pointing.cpi, sizeof(split_shmem->pointing.cpi));
+        if (okay) {
+            last_cpi = temp_cpi;
+        }
+    }
+    return okay;
+}
+
+extern const pointing_device_driver_t pointing_device_driver;
+
+static void pointing_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+#    if defined(POINTING_DEVICE_LEFT)
+    if (!is_keyboard_left()) {
+        return;
+    }
+#    elif defined(POINTING_DEVICE_RIGHT)
+    if (is_keyboard_left()) {
+        return;
+    }
+#    endif
+    report_mouse_t temp_report;
+    uint16_t       temp_cpi;
+#    ifdef POINTING_DEVICE_TASK_THROTTLE_MS
+    static uint32_t last_exec = 0;
+    if (timer_elapsed32(last_exec) < POINTING_DEVICE_TASK_THROTTLE_MS) {
+        return;
+    }
+    last_exec = timer_read32();
+#    endif
+    temp_cpi = pointing_device_driver.get_cpi();
+    if (split_shmem->pointing.cpi && memcmp(&split_shmem->pointing.cpi, &temp_cpi, sizeof(temp_cpi)) != 0) {
+        pointing_device_driver.set_cpi(split_shmem->pointing.cpi);
+    }
+    memset(&temp_report, 0, sizeof(temp_report));
+    temp_report = pointing_device_driver.get_report(temp_report);
+    memcpy(&split_shmem->pointing.report, &temp_report, sizeof(temp_report));
+    // Now update the checksum given that the pointing has been written to
+    split_shmem->pointing.checksum = crc8(&temp_report, sizeof(temp_report));
+}
+
+#    define TRANSACTIONS_POINTING_MASTER() TRANSACTION_HANDLER_MASTER(pointing)
+#    define TRANSACTIONS_POINTING_SLAVE() TRANSACTION_HANDLER_SLAVE(pointing)
+#    define TRANSACTIONS_POINTING_REGISTRATIONS [GET_POINTING_CHECKSUM] = trans_target2initiator_initializer(pointing.checksum), [GET_POINTING_DATA] = trans_target2initiator_initializer(pointing.report), [PUT_POINTING_CPI] = trans_initiator2target_initializer(pointing.cpi),
+
+#else  // defined(POINTING_DEVICE_ENABLE) && defined(SPLIT_POINTING_ENABLE)
+
+#    define TRANSACTIONS_POINTING_MASTER()
+#    define TRANSACTIONS_POINTING_SLAVE()
+#    define TRANSACTIONS_POINTING_REGISTRATIONS
+
+#endif  // defined(POINTING_DEVICE_ENABLE) && defined(SPLIT_POINTING_ENABLE)
+
 ////////////////////////////////////////////////////
 ////////////////////////////////////////////////////
 
 
 uint8_t                  dummy;
 uint8_t                  dummy;
@@ -604,6 +680,7 @@ split_transaction_desc_t split_transaction_table[NUM_TOTAL_TRANSACTIONS] = {
     TRANSACTIONS_WPM_REGISTRATIONS
     TRANSACTIONS_WPM_REGISTRATIONS
     TRANSACTIONS_OLED_REGISTRATIONS
     TRANSACTIONS_OLED_REGISTRATIONS
     TRANSACTIONS_ST7565_REGISTRATIONS
     TRANSACTIONS_ST7565_REGISTRATIONS
+    TRANSACTIONS_POINTING_REGISTRATIONS
 // clang-format on
 // clang-format on
 
 
 #if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
 #if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
@@ -629,6 +706,7 @@ bool transactions_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix
     TRANSACTIONS_WPM_MASTER();
     TRANSACTIONS_WPM_MASTER();
     TRANSACTIONS_OLED_MASTER();
     TRANSACTIONS_OLED_MASTER();
     TRANSACTIONS_ST7565_MASTER();
     TRANSACTIONS_ST7565_MASTER();
+    TRANSACTIONS_POINTING_MASTER();
     return true;
     return true;
 }
 }
 
 
@@ -647,6 +725,7 @@ void transactions_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[
     TRANSACTIONS_WPM_SLAVE();
     TRANSACTIONS_WPM_SLAVE();
     TRANSACTIONS_OLED_SLAVE();
     TRANSACTIONS_OLED_SLAVE();
     TRANSACTIONS_ST7565_SLAVE();
     TRANSACTIONS_ST7565_SLAVE();
+    TRANSACTIONS_POINTING_SLAVE();
 }
 }
 
 
 #if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
 #if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)

+ 13 - 0
quantum/split_common/transport.h

@@ -106,6 +106,15 @@ typedef struct _split_mods_sync_t {
 } split_mods_sync_t;
 } split_mods_sync_t;
 #endif  // SPLIT_MODS_ENABLE
 #endif  // SPLIT_MODS_ENABLE
 
 
+#if defined(POINTING_DEVICE_ENABLE) && defined(SPLIT_POINTING_ENABLE)
+#    include "pointing_device.h"
+typedef struct _split_slave_pointing_sync_t {
+    uint8_t        checksum;
+    report_mouse_t report;
+    uint16_t       cpi;
+} split_slave_pointing_sync_t;
+#endif  // defined(POINTING_DEVICE_ENABLE) && defined(SPLIT_POINTING_ENABLE)
+
 #if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
 #if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
 typedef struct _rpc_sync_info_t {
 typedef struct _rpc_sync_info_t {
     int8_t  transaction_id;
     int8_t  transaction_id;
@@ -173,6 +182,10 @@ typedef struct _split_shared_memory_t {
     uint8_t current_st7565_state;
     uint8_t current_st7565_state;
 #endif  // ST7565_ENABLE(OLED_ENABLE) && defined(SPLIT_ST7565_ENABLE)
 #endif  // ST7565_ENABLE(OLED_ENABLE) && defined(SPLIT_ST7565_ENABLE)
 
 
+#if defined(POINTING_DEVICE_ENABLE) && defined(SPLIT_POINTING_ENABLE)
+    split_slave_pointing_sync_t pointing;
+#endif  // defined(POINTING_DEVICE_ENABLE) && defined(SPLIT_POINTING_ENABLE)
+
 #if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
 #if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
     rpc_sync_info_t rpc_info;
     rpc_sync_info_t rpc_info;
     uint8_t         rpc_m2s_buffer[RPC_M2S_BUFFER_SIZE];
     uint8_t         rpc_m2s_buffer[RPC_M2S_BUFFER_SIZE];