Skip to content

Commit

Permalink
[Core] Split support for pointing devices. (#15304)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
3 people authored Dec 27, 2021
1 parent 76a6732 commit 7f7364c
Show file tree
Hide file tree
Showing 11 changed files with 626 additions and 66 deletions.
13 changes: 13 additions & 0 deletions docs/ChangeLog/20220226/PR15304.md
Original file line number Diff line number Diff line change
@@ -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.
116 changes: 105 additions & 11 deletions docs/feature_pointing_device.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,10 @@ The Pimoroni Trackball module is a I2C based breakout board with an RGB enable t
| Setting | Description | Default |
|-------------------------------------|------------------------------------------------------------------------------------|---------|
|`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_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

Expand Down Expand Up @@ -171,14 +170,35 @@ void pointing_device_driver_set_cpi(uint16_t cpi) {}
## 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
Expand All @@ -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_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. |
| `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
Expand Down Expand Up @@ -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.

## 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 changes: 8 additions & 0 deletions docs/feature_split_keyboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
```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
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.
Expand Down
22 changes: 19 additions & 3 deletions drivers/sensors/pimoroni_trackball.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,24 @@

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) {
uint8_t data[4] = {r, g, b, w};
Expand All @@ -60,7 +76,7 @@ i2c_status_t read_pimoroni_trackball(pimoroni_data_t* data) {
return status;
}

__attribute__((weak)) void pimironi_trackball_device_init(void) {
__attribute__((weak)) void pimoroni_trackball_device_init(void) {
i2c_init();
pimoroni_trackball_set_rgbw(0x00, 0x00, 0x00, 0x00);
}
Expand Down
9 changes: 3 additions & 6 deletions drivers/sensors/pimoroni_trackball.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@
#ifndef PIMORONI_TRACKBALL_ADDRESS
# define PIMORONI_TRACKBALL_ADDRESS 0x0A
#endif
#ifndef PIMORONI_TRACKBALL_INTERVAL_MS
# define PIMORONI_TRACKBALL_INTERVAL_MS 8
#endif
#ifndef PIMORONI_TRACKBALL_SCALE
# define PIMORONI_TRACKBALL_SCALE 5
#endif
Expand All @@ -52,10 +49,10 @@ typedef struct {
uint8_t click;
} 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);
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);
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);
Loading

0 comments on commit 7f7364c

Please sign in to comment.