Skip to content

Add per-layer-pointing-mode#728

Open
Raymond8196 wants to merge 9 commits intoHaoboGu:mainfrom
Raymond8196:feat/pointing-mode
Open

Add per-layer-pointing-mode#728
Raymond8196 wants to merge 9 commits intoHaoboGu:mainfrom
Raymond8196:feat/pointing-mode

Conversation

@Raymond8196
Copy link
Copy Markdown
Contributor

Summary

Add per-layer pointing device modes to RMK, allowing trackballs to behave differently on different layers (e.g., scroll on layer 1, precision mode on layer 2).

Key Features

Per-Layer Pointing Modes:

  • Cursor: Normal trackball mode (default)
  • Scroll: Convert motion to scroll wheel events
  • Sniper: Reduced sensitivity for precision control

Usage:

processor.set_layer_mode(1, PointingMode::Scroll(ScrollConfig::default()));
processor.set_layer_mode(2, PointingMode::Sniper(SniperConfig::default()));

Configure MO(1) and MO(2) keys in keymap. Fully Vial compatible.

Technical Details:
- Sub-pixel motion accumulation prevents precision loss
- Configurable sensitivity divisors for each mode
- Memory overhead: ~17 bytes (4-layer keyboard)
- Backward compatible: default behavior unchanged

Test Plan

- 23 unit tests for mode switching, accumulation, and edge cases
- All existing tests pass, zero clippy warnings
- Tested with PMW33xx/PMW3610 examples
- Verified Vial compatibility

Related Issues

Addresses #703

Add support for per-layer pointing device modes to PointingProcessor.
Users can now configure different layers to have different trackball
behaviors, enabling scroll and precision modes via layer switching.

Changes:
- Add PointingMode enum (Cursor/Scroll/Sniper) with configs
- Add MotionAccumulator for sub-pixel delta tracking
- Subscribe to LayerChangeEvent to receive layer changes
- Add set_layer_mode() and with_layer_modes() builder methods
- Refactor on_pointing_event() to support mode dispatch
- Add 9 unit tests for accumulator and mode switching

Usage example (in processor initialization):
  processor.set_layer_mode(1, PointingMode::Scroll(ScrollConfig::default()));
  processor.set_layer_mode(2, PointingMode::Sniper(SniperConfig::default()));

Then configure MO(1) and MO(2) keys in keymap. Fully Vial compatible.

Tests: 14/14 passed, 0 clippy warnings
Memory overhead: ~17 bytes (4-layer keyboard)
Backward compatible: default behavior unchanged

Addresses HaoboGu#703
Add comprehensive documentation and examples for the new per-layer
pointing modes feature (Cursor/Scroll/Sniper).

Changes:
- Add PMW33xx/PMW3610 docs with per-layer mode usage examples
- Update nrf52840_ble_split_dongle example with layer mode configuration
- Add 10 new unit tests for integration scenarios
- Document mode details, sensitivity tuning, and keymap integration

Documentation includes:
- Complete API usage examples with all three modes
- Sensitivity tuning guidelines (divisor ranges)
- Keymap integration patterns (MO/TG layer switches)
- Mode behavior details and use cases

Tests added:
- Mode selection and storage
- Scroll/Sniper divisor behavior
- Zero-motion prevention (scroll mode optimization)
- Asymmetric divisor handling
- Negative motion accumulation
- Default config values
- Saturation protection
- Array bounds verification

Total test coverage: 23 tests (14 existing + 9 from main commit + 10 new)
@github-actions
Copy link
Copy Markdown

github-actions bot commented Feb 11, 2026

Binary Size Report

use_config/nrf52832_ble

   text	   data	    bss	    dec	    hex	filename
 315412	   5072	  33020	 353504	  564e0	rmk-nrf52832
Diff
    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0%    +177  [ = ]       0    .debug_str
  +0.0%      +8  [ = ]       0    .debug_aranges
  -0.0%      -8  -0.0%      -8    .rodata
  -0.2%     -60  [ = ]       0    .debug_frame
  -0.1%     -96  [ = ]       0    .symtab
  -0.1%    -112  [ = ]       0    .debug_ranges
  -0.0%    -144  [ = ]       0    .debug_line
  -0.1%    -159  [ = ]       0    .strtab
  -0.1%    -184  -0.1%    -184    .text
  -0.1%    -753  [ = ]       0    .debug_loc
  -0.0%    -841  [ = ]       0    .debug_info
  -0.0% -2.12Ki  -0.1%    -192    TOTAL

use_config/nrf52840_ble

   text	   data	    bss	    dec	    hex	filename
 353576	   5072	  48636	 407284	  636f4	rmk-nrf52840
Diff
    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0% +1.03Ki  [ = ]       0    .debug_str
  [ = ]       0  +0.1%     +40    .bss
  +0.1%     +24  [ = ]       0    .debug_aranges
  +0.0%      +8  +0.0%      +8    .rodata
  +5.5%      +3  [ = ]       0    [Unmapped]
  -0.1%     -28  [ = ]       0    .debug_frame
  -0.0%     -32  [ = ]       0    .symtab
  -0.0%     -64  [ = ]       0    .debug_ranges
  -0.0%     -67  [ = ]       0    .debug_line
  -0.0%     -72  -0.0%     -72    .text
  -0.0%    -110  [ = ]       0    .strtab
  -0.0%    -272  [ = ]       0    .debug_info
  -0.1%    -519  [ = ]       0    .debug_loc
  -0.0%     -76  -0.0%     -24    TOTAL

use_config/nrf52840_ble_split

   text	   data	    bss	    dec	    hex	filename
 430232	   6376	  44940	 481548	  7590c	central

   text	   data	    bss	    dec	    hex	filename
 276152	   5740	  26236	 308128	  4b3a0	peripheral
Diff

Central Diff

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0%    +972  [ = ]       0    .debug_str
  +0.0%    +314  [ = ]       0    .debug_info
  [ = ]       0  +0.1%     +40    .bss
  +0.0%      +8  +0.0%      +8    .rodata
 -20.6%     -13  [ = ]       0    [Unmapped]
  -0.1%     -24  [ = ]       0    .debug_aranges
  -0.0%     -88  [ = ]       0    .debug_ranges
  -0.2%     -96  [ = ]       0    .debug_frame
  -0.0%    -145  [ = ]       0    .strtab
  -0.2%    -224  [ = ]       0    .symtab
  -0.2%    -658  [ = ]       0    .debug_line
  -0.4% -1.49Ki  -0.4% -1.49Ki    .text
  -0.7% -4.79Ki  [ = ]       0    .debug_loc
  -0.1% -6.24Ki  -0.3% -1.45Ki    TOTAL

Peripheral Diff

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.1% +2.65Ki  [ = ]       0    .debug_str
  +0.1% +1.81Ki  [ = ]       0    .debug_info
  +0.4% +1.62Ki  [ = ]       0    .debug_loc
  +0.4%    +512  [ = ]       0    .debug_ranges
  +0.2%    +424  +0.2%    +424    .text
  +0.2%    +355  [ = ]       0    .debug_line
  [ = ]       0  +0.2%     +40    .bss
  +0.0%     +16  +0.0%     +16    .rodata
 -33.3%     -23  [ = ]       0    [Unmapped]
  -0.2%     -44  [ = ]       0    .debug_frame
  -0.0%     -53  [ = ]       0    .strtab
  -0.2%    -160  [ = ]       0    .symtab
  +0.1% +7.08Ki  +0.2%    +480    TOTAL

use_rust/nrf52840_ble_split

   text	   data	    bss	    dec	    hex	filename
 436444	   6376	  51420	 494240	  78aa0	central

   text	   data	    bss	    dec	    hex	filename
 273756	   5196	  24980	 303932	  4a33c	peripheral
Diff

Central Diff

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.1% +3.02Ki  [ = ]       0    .debug_str
  +0.1% +1.38Ki  [ = ]       0    .debug_info
  +0.3%    +632  [ = ]       0    .debug_ranges
  +0.1%    +196  [ = ]       0    .debug_line
  +0.0%     +72  +0.0%     +72    .text
  +0.0%     +64  [ = ]       0    .symtab
  [ = ]       0  +0.1%     +40    .bss
  +0.1%     +32  [ = ]       0    .debug_aranges
  +0.0%      +8  +0.0%      +8    .rodata
  -0.0%     -12  [ = ]       0    .debug_frame
 -27.4%     -17  [ = ]       0    [Unmapped]
  -0.0%     -61  [ = ]       0    .strtab
  -0.0%    -245  [ = ]       0    .debug_loc
  +0.1% +5.05Ki  +0.0%    +120    TOTAL

Peripheral Diff

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.1% +2.77Ki  [ = ]       0    .debug_str
  +0.1% +1.93Ki  [ = ]       0    .debug_info
  +0.4% +1.62Ki  [ = ]       0    .debug_loc
  +0.3%    +456  [ = ]       0    .debug_ranges
  +0.2%    +360  +0.2%    +360    .text
  +0.1%    +179  [ = ]       0    .debug_line
  [ = ]       0  +0.2%     +40    .bss
  +0.0%     +16  +0.0%     +16    .rodata
  +7.8%      +5  [ = ]       0    [Unmapped]
  -0.0%      -8  [ = ]       0    .debug_aranges
  -0.3%     -76  [ = ]       0    .debug_frame
  -0.1%    -109  [ = ]       0    .strtab
  -0.3%    -256  [ = ]       0    .symtab
  +0.1% +6.88Ki  +0.1%    +416    TOTAL

use_config/pi_pico_w_ble

   text	   data	    bss	    dec	    hex	filename
 587528	      0	  54428	 641956	  9cba4	rmk-pi-pico-w
Diff
    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0%    +174  [ = ]       0    .debug_str
  +0.0%      +8  [ = ]       0    .debug_aranges
  -0.0%      -8  -0.0%      -8    .rodata
 -22.4%     -13  [ = ]       0    [Unmapped]
  -0.2%     -64  [ = ]       0    .debug_frame
  -0.1%     -96  [ = ]       0    .symtab
  -0.0%    -124  [ = ]       0    .debug_line
  -0.1%    -165  [ = ]       0    .strtab
  -0.1%    -184  [ = ]       0    .debug_ranges
  -0.1%    -200  -0.1%    -200    .text
  -0.1%    -614  [ = ]       0    .debug_loc
  -0.0%    -786  [ = ]       0    .debug_info
  -0.0% -2.02Ki  -0.0%    -208    TOTAL

use_config/pi_pico_w_ble_split

   text	   data	    bss	    dec	    hex	filename
 619536	      0	  60952	 680488	  a6228	central

   text	   data	    bss	    dec	    hex	filename
 486060	      0	  41852	 527912	  80e28	peripheral
Diff

Central Diff

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0% +1.16Ki  [ = ]       0    .debug_str
  +0.0%    +565  [ = ]       0    .debug_info
  +0.2%    +440  [ = ]       0    .debug_ranges
  [ = ]       0  +0.1%     +32    .bss
  +0.0%      +8  [ = ]       0    .debug_aranges
   +10%      +6  [ = ]       0    [Unmapped]
  -0.0%      -8  -0.0%      -8    .rodata
  -0.2%     -64  [ = ]       0    .debug_frame
  -0.1%     -64  [ = ]       0    .symtab
  -0.0%    -106  [ = ]       0    .debug_line
  -0.0%    -126  [ = ]       0    .strtab
  -0.1%    -188  -0.1%    -188    .text
  -0.2% -1.88Ki  [ = ]       0    .debug_loc
  -0.0%    -276  -0.0%    -164    TOTAL

Peripheral Diff

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.1% +2.32Ki  [ = ]       0    .debug_str
  +0.1% +1.64Ki  [ = ]       0    .debug_info
  +0.4%    +672  [ = ]       0    .debug_ranges
  +0.1%    +293  [ = ]       0    .debug_line
  +0.1%    +288  +0.1%    +288    .text
  +0.0%     +37  [ = ]       0    .strtab
  [ = ]       0  +0.1%     +32    .bss
  +0.1%     +32  [ = ]       0    .symtab
  +0.0%     +31  [ = ]       0    .debug_loc
  +0.1%     +24  [ = ]       0    .debug_aranges
  +0.1% +5.30Ki  +0.1%    +320    TOTAL

use_rust/pi_pico_w_ble_split

   text	   data	    bss	    dec	    hex	filename
 619760	      0	  61272	 681032	  a6448	central

   text	   data	    bss	    dec	    hex	filename
 486568	      0	  41852	 528420	  81024	peripheral
Diff

Central Diff

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0% +1.16Ki  [ = ]       0    .debug_str
  +0.0%    +565  [ = ]       0    .debug_info
  +0.2%    +440  [ = ]       0    .debug_ranges
  [ = ]       0  +0.1%     +32    .bss
  +0.0%      +8  [ = ]       0    .debug_aranges
   +12%      +7  [ = ]       0    [Unmapped]
  -0.0%      -8  -0.0%      -8    .rodata
  -0.2%     -64  [ = ]       0    .debug_frame
  -0.1%     -64  [ = ]       0    .symtab
  -0.0%    -104  [ = ]       0    .debug_line
  -0.0%    -126  [ = ]       0    .strtab
  -0.1%    -188  -0.1%    -188    .text
  -0.2% -1.87Ki  [ = ]       0    .debug_loc
  -0.0%    -260  -0.0%    -164    TOTAL

Peripheral Diff

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.1% +2.32Ki  [ = ]       0    .debug_str
  +0.1% +1.56Ki  [ = ]       0    .debug_info
  +0.4%    +672  [ = ]       0    .debug_ranges
  +0.1%    +298  [ = ]       0    .debug_line
  +0.1%    +276  +0.1%    +276    .text
  +0.0%    +161  [ = ]       0    .debug_loc
  +0.0%     +37  [ = ]       0    .strtab
  [ = ]       0  +0.1%     +32    .bss
  +0.1%     +32  [ = ]       0    .symtab
  +0.1%     +24  [ = ]       0    .debug_aranges
 -28.8%     -17  [ = ]       0    [Unmapped]
  +0.1% +5.32Ki  +0.1%    +308    TOTAL

use_config/rp2040

   text	   data	    bss	    dec	    hex	filename
 132344	      0	  15596	 147940	  241e4	rmk-rp2040
Diff
    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0%    +182  [ = ]       0    .debug_str
   +23%     +10  [ = ]       0    [Unmapped]
  +0.1%      +8  [ = ]       0    .debug_aranges
  -0.0%      -4  -0.0%      -4    .rodata
  -0.4%     -64  [ = ]       0    .debug_frame
  -0.3%     -96  [ = ]       0    .symtab
  -0.1%    -123  [ = ]       0    .debug_line
  -0.2%    -163  [ = ]       0    .strtab
  -0.2%    -184  [ = ]       0    .debug_ranges
  -0.2%    -200  -0.2%    -200    .text
  -0.2%    -614  [ = ]       0    .debug_loc
  -0.1%    -788  [ = ]       0    .debug_info
  -0.1% -1.99Ki  -0.1%    -204    TOTAL

use_config/rp2040_split

   text	   data	    bss	    dec	    hex	filename
 143836	      0	  16644	 160480	  272e0	central

   text	   data	    bss	    dec	    hex	filename
  23924	      0	   2508	  26432	   6740	peripheral
Diff

Central Diff

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.5% +1.74Ki  [ = ]       0    .debug_loc
  +0.1% +1.19Ki  [ = ]       0    .debug_str
  +0.0%    +279  [ = ]       0    .debug_info
  [ = ]       0  +0.3%     +52    .bss
  +0.1%     +48  [ = ]       0    .debug_ranges
  -0.0%      -4  -0.0%      -4    .rodata
  -0.1%      -8  [ = ]       0    .debug_abbrev
 -18.5%     -12  [ = ]       0    [Unmapped]
  -0.0%     -16  -0.0%     -16    .text
  -0.0%     -23  [ = ]       0    .debug_line
  -0.4%     -64  [ = ]       0    .debug_frame
  -0.3%     -96  [ = ]       0    .symtab
  -0.1%    -139  [ = ]       0    .strtab
  +0.1% +2.89Ki  +0.0%     +32    TOTAL

Peripheral Diff

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.4% +2.35Ki  [ = ]       0    .debug_str
  +0.6% +1.90Ki  [ = ]       0    .debug_info
  +1.8%   +1018  [ = ]       0    .debug_line
  +4.2%    +624  [ = ]       0    .debug_ranges
  +1.0%    +564  [ = ]       0    .debug_loc
  +1.8%    +336  +1.8%    +336    .text
  +3.6%    +214  [ = ]       0    .debug_abbrev
  +0.7%    +130  [ = ]       0    .strtab
  +0.9%     +80  [ = ]       0    .symtab
  +0.4%     +56  [ = ]       0    .debug_aranges
  +1.0%     +52  [ = ]       0    .debug_frame
  [ = ]       0  +2.2%     +32    .bss
   +31%     +14  [ = ]       0    [Unmapped]
  +0.6% +7.27Ki  +1.4%    +368    TOTAL

use_rust/rp2040_split

   text	   data	    bss	    dec	    hex	filename
 143340	      0	  16252	 159592	  26f68	central

   text	   data	    bss	    dec	    hex	filename
  24620	      0	   2772	  27392	   6b00	peripheral
Diff

Central Diff

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.5% +1.53Ki  [ = ]       0    .debug_loc
  +0.1% +1.19Ki  [ = ]       0    .debug_str
  +0.2%    +176  [ = ]       0    .debug_ranges
  +0.0%    +139  [ = ]       0    .debug_info
  +0.0%     +60  [ = ]       0    .debug_line
  [ = ]       0  +0.3%     +40    .bss
  +0.0%      +8  [ = ]       0    .debug_aranges
  -0.0%      -4  -0.0%      -4    .rodata
  -0.1%      -8  [ = ]       0    .debug_abbrev
 -20.8%     -10  [ = ]       0    [Unmapped]
  -0.0%     -48  -0.0%     -48    .text
  -0.4%     -64  [ = ]       0    .debug_frame
  -0.4%    -128  [ = ]       0    .symtab
  -0.1%    -145  [ = ]       0    .strtab
  +0.1% +2.70Ki  -0.0%     -12    TOTAL

Peripheral Diff

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.3% +2.07Ki  [ = ]       0    .debug_str
  +0.5% +1.71Ki  [ = ]       0    .debug_info
  +1.6%    +935  [ = ]       0    .debug_line
  +4.4%    +664  [ = ]       0    .debug_ranges
  +2.1%    +408  +2.2%    +408    .text
  +3.6%    +214  [ = ]       0    .debug_abbrev
  +0.6%    +124  [ = ]       0    .strtab
  +0.2%    +108  [ = ]       0    .debug_loc
  +0.4%     +56  [ = ]       0    .debug_aranges
  +1.0%     +52  [ = ]       0    .debug_frame
  +0.6%     +48  [ = ]       0    .symtab
  [ = ]       0  +1.9%     +32    .bss
 -36.1%     -22  [ = ]       0    [Unmapped]
  +0.5% +6.31Ki  +1.6%    +440    TOTAL

use_config/stm32f1

   text	   data	    bss	    dec	    hex	filename
  54260	     24	   7904	  62188	   f2ec	rmk-stm32f1
Diff
    FILE SIZE        VM SIZE    
 --------------  -------------- 
  -0.0%      -4  [ = ]       0    .debug_frame
  -0.0%      -6  [ = ]       0    .strtab
 -25.5%     -12  [ = ]       0    [Unmapped]
  -0.2%     -32  [ = ]       0    .symtab
  -0.3%    -104  [ = ]       0    .debug_ranges
  -0.2%    -116  -0.2%    -116    .text
  -0.2%    -144  [ = ]       0    .debug_line
  -0.0%    -233  [ = ]       0    .debug_str
  -0.4%    -433  [ = ]       0    .debug_loc
  -0.2% -1.10Ki  [ = ]       0    .debug_info
  -0.1% -2.16Ki  -0.2%    -116    TOTAL

use_config/stm32f4

   text	   data	    bss	    dec	    hex	filename
 134984	    320	  16316	 151620	  25044	rmk-stm32f4
Diff
    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0%    +148  [ = ]       0    .debug_str
  +0.0%      +8  [ = ]       0    .debug_aranges
  +5.4%      +2  [ = ]       0    [Unmapped]
  -0.0%      -4  -0.0%      -4    .rodata
  -0.3%     -60  [ = ]       0    .debug_frame
  -0.2%     -96  [ = ]       0    .symtab
  -0.1%    -112  [ = ]       0    .debug_ranges
  -0.1%    -158  [ = ]       0    .debug_line
  -0.1%    -164  [ = ]       0    .strtab
  -0.2%    -192  -0.2%    -192    .text
  -0.3%    -753  [ = ]       0    .debug_loc
  -0.1%    -835  [ = ]       0    .debug_info
  -0.1% -2.16Ki  -0.1%    -196    TOTAL

use_config/stm32h7

   text	   data	    bss	    dec	    hex	filename
  96432	    264	  10516	 107212	  1a2cc	rmk-stm32h7
Diff
    FILE SIZE        VM SIZE    
 --------------  -------------- 
   +37%     +16  [ = ]       0    [Unmapped]
  -0.0%      -4  -0.0%      -4    .rodata
  -0.0%      -8  [ = ]       0    .debug_aranges
  -0.3%     -44  [ = ]       0    .debug_frame
  -0.2%     -64  [ = ]       0    .symtab
  -0.3%    -146  [ = ]       0    .strtab
  -0.1%    -165  [ = ]       0    .debug_line
  -0.2%    -168  -0.2%    -168    .text
  -0.4%    -224  [ = ]       0    .debug_ranges
  -0.0%    -339  [ = ]       0    .debug_str
  -0.4%    -643  [ = ]       0    .debug_loc
  -0.1%   -1015  [ = ]       0    .debug_info
  -0.1% -2.74Ki  -0.2%    -172    TOTAL

Copy link
Copy Markdown
Contributor

@Schievel1 Schievel1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for doing this. I was about to do a similar thing, but I use scrolling mode on my trackball rather seldom. So the pressure of not having it wasn't motivating me enough. :)

I find sticking this to layers a bit too restrictive, also PointingProcessor is doing a bit too much I think. A more granular approach with different structs, where one, a controller, handles what should be done in a given situation (e.g. a layer change, a special key code etc.) and PointingProcessor just taking commands and executing them (the mode change) is more user friendly and flexible imo.

Also I do not know what this Gazell stuff is about. Is it there accidentally and belongs to a different PR actually?

KEYBOARD_REPORT_CHANNEL.send(Report::MouseReport(mouse_report)).await;
}

async fn on_layer_change_event(&mut self, event: LayerChangeEvent) {
Copy link
Copy Markdown
Contributor

@Schievel1 Schievel1 Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of keeping track of layers here, I would prefer having three new events similar to the PointingSetCpiEvent but for PointingProcessors instead of PointingDevice. Those events would be PointingSetCursor, -Scrolling, -Sniping and the PointingProcessor listens to them to set the mode.
This way we stay more flexible and users can implement controllers and activate the modes from there however they like. They are not bound to layers, they could activate the modes with a special key combination, user keys or whatever comes to their mind.

When implementing this please keep in mind that there could be more than one PointingProcessor on a keyboard, so you need a way to identify them and to send events to them separately. (Similar to what is done in the PointingDevices)

#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ScrollConfig {
/// Divisor for X axis (pan). Higher = slower scrolling. 0 treated as 1.
pub divisor_x: u8,
Copy link
Copy Markdown
Contributor

@Schievel1 Schievel1 Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe make those i8 so users can invert the scrolling, panning independently
(Do the calculations in accumulator still work then?)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion! I kept explicit invert_x/invert_y bools for the following reasons:
1.Readability — invert_y: true is clearer in intent than divisor_y: -8
2.divisor = 0 already has special meaning (disables the axis in ScrollConfig). Adding negative values as another special case increases cognitive load.
The accumulator math stays simple this way — it only deals with unsigned divisors, and inversion is applied as a straightforward sign flip afterward.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to have multipliers in addition to divisors. That allows more flexible configurations.
And set multiplier = 0 to disable the axis, that's more intuitive than divisor = 0.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to have multipliers in addition to divisors. That allows more flexible configurations. And set multiplier = 0 to disable the axis, that's more intuitive than divisor = 0.

Good point — using multiplier = 0 to disable an axis is indeed more intuitive than divisor = 0.
However, I prefer to stick with divisors only for the following reasons:

  1. Avoid overflow on embedded systems: If we used a multiplier, an input value multiplied by the multiplier could overflow 8-bit or 16-bit integers, forcing us to promote to 32-bit on every motion event. Using only divisors keeps the per-event arithmetic to a single integer division.
  2. Consistency with existing firmware: Using divisor = 0 as a disable flag is already a common pattern in other firmware (e.g., QMK), so it's familiar to developers and reduces surprise.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overflow on embedded systems

I'm not sure if it actually produces such large values that it overflows, but it's worth noting that ZMK input processor supports both multipliers and divisors and handles them in uint16.
https://github.com/zmkfirmware/zmk/blob/main/app/src/pointing/input_processor_scaler.c#L25-L40

Using divisor = 0 as a disable flag is already a common pattern in other firmware (e.g., QMK), so it's familiar to developers and reduces surprise.

I'm against it. Even if I had experience using divisor = 0 in QMK or other firmware, I wouldn't know if RMK is the same until I read the exception explained in the docs, and would brace myself for a potential division-by-zero error. That could be stress for users.


// Configure different modes for each layer
pointing_processor
.set_layer_mode(0, PointingMode::Cursor) // Layer 0: Normal cursor
Copy link
Copy Markdown
Contributor

@Schievel1 Schievel1 Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I understand it everything should be possible to configure via keyboard.toml as well. Mostly because once you go configuring in Rust you can't use keyboard.toml anymore. So if users want to use those layer modes with their pointing processor they would be forced to convert their whole configuration to rust.

But see my comment in pointing.rs suggesting to only listen for events in PointingProcessor and let the users program controllers to emit them. This way we can avoid this whole configuration section.
Controllers must be written in Rust, but they can be combined with keyboard.toml configuration.

When divisor is 0, that axis outputs 0 and does not accumulate remainder.
This allows configurations like scroll-only-vertical (divisor_x=0) to
prevent accidental horizontal panning on ergonomic trackballs.
Breaking change: PointingEvent is now a named struct carrying device_id,
replacing the previous tuple struct. Both halves of a split keyboard must
be flashed with the same firmware version.

Multi-device routing:
- PointingEvent gains device_id: u8 (producer-side: PointingDevice, NrfAdc)
- PointingProcessorConfig gains device_id: u8 (default 255 = ALL_POINTING_DEVICES)
- JoystickProcessor gains device_id: u8 filter
- NrfAdc gains event_device_ids: [u8; EVENT_NUM] per event slot
- JoystickConfig gains id: Option<u8> for TOML configuration
- Codegen (adc.rs, pmw3610.rs, pmw33xx.rs) updated to wire device_id end-to-end

Per-mode axis inversion:
- ScrollConfig gains invert_x / invert_y (reverse pan / wheel direction)
- SniperConfig gains invert_x / invert_y (reverse per-axis movement)
- Processing order: global invert -> global swap_xy -> mode-specific invert
- New unit tests: test_scroll_config_invert_y, test_sniper_config_invert_axes
- Add multi-device ID assignment guide for split keyboards
- Add breaking change warning (both halves must be flashed together)
- Document device_id field in PointingProcessorConfig example
- Document invert_x/invert_y in ScrollConfig and SniperConfig
- Clarify divisor=0 disables that axis entirely
- Fix variable name typo: pmw3360 -> pmw3610 in Rust config example
@Raymond8196
Copy link
Copy Markdown
Contributor Author

Thanks for doing this. I was about to do a similar thing, but I use scrolling mode on my trackball rather seldom. So the pressure of not having it wasn't motivating me enough. :)

I find sticking this to layers a bit too restrictive, also PointingProcessor is doing a bit too much I think. A more granular approach with different structs, where one, a controller, handles what should be done in a given situation (e.g. a layer change, a special key code etc.) and PointingProcessor just taking commands and executing them (the mode change) is more user friendly and flexible imo.

Also I do not know what this Gazell stuff is about. Is it there accidentally and belongs to a different PR actually?

Thanks for pointing that out! The Gazell code was accidentally included — it belongs to another experimental branch and isn't relevant to this PR. I have removed it and make sure only the intended changes are included.

Raymond8196 and others added 3 commits February 24, 2026 19:50
Unlike ScrollConfig where disabling one axis independently is useful,
SniperConfig has a single divisor for both axes. Setting divisor=0
would freeze cursor output entirely, which has no practical use case.
- Add invert_x/invert_y fields to ScrollConfig/SniperConfig struct
  literals in examples and doc code blocks
- Add event_device_ids parameter to all NrfAdc::new call sites
- Add device_id to JoystickProcessor::new in joystick docs
- Sync pmw33xx.md with pmw3610.md: add device_id to
  PointingProcessorConfig example, add per-mode inversion details
  to mode descriptions
- Fix input_device.md doc example to use inline literals instead
  of undefined variables
Signed-off-by: Haobo Gu <haobogu@outlook.com>
@Schievel1
Copy link
Copy Markdown
Contributor

I still think this should be bound to events switching the modes on instead of layers. Users can always implement their own processor that changes modes with layers, if that is really needed but the built-in stuff should be as generic as it gets.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants