Repository: pwr-Solaar/Solaar

Issue:
Problems when making CAPS LOCK work as CONTROL_L
**Information**
<!-- Make sure that your issue is not one of the known issues in the Solaar documentation at https://pwr-solaar.github.io/Solaar/ -->
<!-- Do not bother opening an issue for a version older than 1.1.0.  Upgrade to the latest version and see if your issue persists. -->
<!-- If you not running the current version of Solaar, strongly consider upgrading to the newest version. -->
- Solaar version (`solaar --version` or `git describe --tags` if cloned from this repository): Solaar version 1.1.4
- Distribution: Manjaro
- Kernel version (ex. `uname -srmo`): Linux 5.15.65-1-MANJARO x86_64 GNU/Linux
- Output of `solaar show`:

<details>

```
Solaar version 1.1.4

Bolt Receiver
  Device path  : /dev/hidraw6
  USB id       : 046d:C548
  Serial       : 45374441454436304233314230413538
  Has 1 paired device(s) out of a maximum of 6.
  Notifications: wireless, software present (0x000900)
  Device activity counters: 1=186

  1: MX Mechanical Mini
     Device path  : None
     WPID         : B367
     Codename     : MX MCHNCL M
     Kind         : keyboard
     Protocol     : HID++ 4.5
     Serial number: 915FAE83
     Model ID:      B36700000000
     Unit ID:       915FAE83
        Bootloader: BL1 50.01.B0011
          Firmware: RBK 72.01.B0011
             Other: 
     The power switch is located on the (unknown).
     Supports 32 HID++ 2.0 features:
         0: ROOT                   {0000}   
         1: FEATURE SET            {0001}   
         2: DEVICE FW VERSION      {0003}   
            Firmware: Bootloader BL1 50.01.B0011 B3679386D010
            Firmware: Firmware RBK 72.01.B0011 B3679386D010
            Firmware: Other   
            Unit ID: 915FAE83  Model ID: B36700000000  Transport IDs: {'btleid': 'B367'}
         3: DEVICE NAME            {0005}   
            Name: MX Mechanical Mini
            Kind: keyboard
         4: WIRELESS DEVICE STATUS {1D4B}   
         5: RESET                  {0020}   
         6: DEVICE FRIENDLY NAME   {0007}   
            Friendly Name: MX MCHNCL M
         7: UNIFIED BATTERY        {1004}   
         8: REPROG CONTROLS V4     {1B04}   
            Key/Button Diversion (saved): {MultiPlatform Search:Regular, Backlight Down:Regular, Backlight Up:Regular, Mute Fn:Regular, Volume Down Fn:Regular, Volume Up Fn:Regular, Voice Dictation:Regular, Open Emoji Panel:Regular, Snipping Tool:Regular, Grave Accent:Regular, Tab Key:Regular, Caps Lock:Diverted, Left Shift:Regular, Left Control:Regular, Left Option/Start:Regular, Left Command/Alt:Regular, Right Command/Alt:Regular, Right Shift:Regular, Delete:Regular, Home:Regular, End:Regular, Page Up:Regular, Page Down:Regular, Mute Microphone:Regular, Backslash:Regular, unknown:013C:Regular, unknown:0141:Regular}
            Key/Button Diversion        : {MultiPlatform Search:Regular, Backlight Down:Regular, Backlight Up:Regular, Mute Fn:Regular, Volume Down Fn:Regular, Volume Up Fn:Regular, Voice Dictation:Regular, Open Emoji Panel:Regular, Snipping Tool:Regular, Grave Accent:Regular, Tab Key:Regular, Caps Lock:Regular, Left Shift:Regular, Left Control:Regular, Left Option/Start:Regular, Left Command/Alt:Regular, Right Command/Alt:Regular, Right Shift:Regular, Delete:Regular, Home:Regular, End:Regular, Page Up:Regular, Page Down:Regular, Mute Microphone:Regular, Backslash:Regular, unknown:013C:Regular, unknown:0141:Regular}
         9: CHANGE HOST            {1814}   
            Change Host        : 1:HP-t620plus
        10: HOSTS INFO             {1815}   
            Host 0 (paired): HP-t620plus
            Host 1 (unpaired): 
            Host 2 (unpaired): 
        11: BACKLIGHT2             {1982}   
            Backlight (saved): True
            Backlight        : True
        12: K375S FN INVERSION     {40A3}   
            Swap Fx function (saved): False
            Swap Fx function        : False
        13: LOCK KEY STATE         {4220}   
        14: KEYBOARD DISABLE KEYS  {4521}   
            Disable keys (saved): {Caps Lock:True, Scroll Lock:False, Insert:False, Win:False}
            Disable keys        : {Caps Lock:False, Scroll Lock:False, Insert:False, Win:False}
        15: MULTIPLATFORM          {4531}   
            Set OS (saved): Windows
            Set OS        : Windows
        16: KEYBOARD LAYOUT 2      {4540}   
        17: DFUCONTROL             {00C3}   
        18: DEVICE RESET           {1802}   internal, hidden, unknown:000010
        19: unknown:1803           {1803}   internal, hidden, unknown:000010
        20: CONFIG DEVICE PROPS    {1806}   internal, hidden, unknown:000010
        21: unknown:1816           {1816}   internal, hidden, unknown:000010
        22: OOBSTATE               {1805}   internal, hidden
        23: unknown:1830           {1830}   internal, hidden, unknown:000010
        24: unknown:1891           {1891}   internal, hidden, unknown:000008
        25: unknown:18A1           {18A1}   internal, hidden, unknown:000010
        26: unknown:1E00           {1E00}   hidden
        27: unknown:1E02           {1E02}   internal, hidden
        28: unknown:1602           {1602}   
        29: unknown:1EB0           {1EB0}   internal, hidden, unknown:000010
        30: unknown:1861           {1861}   internal, hidden, unknown:000010
        31: unknown:1A20           {1A20}   internal, hidden, unknown:000010
     Has 32 reprogrammable keys:
         0: Host Switch Channel 1     , default: HostSwitch Channel 1        => HostSwitch Channel 1      
             is FN, FN sensitive, analytics key events, pos:1, group:0, group mask:empty
             reporting: default
         1: Host Switch Channel 2     , default: HostSwitch Channel 2        => HostSwitch Channel 2      
             is FN, FN sensitive, analytics key events, pos:2, group:0, group mask:empty
             reporting: default
         2: Host Switch Channel 3     , default: HostSwitch Channel 3        => HostSwitch Channel 3      
             is FN, FN sensitive, analytics key events, pos:3, group:0, group mask:empty
             reporting: default
         3: Backlight Down            , default: Backlight Down              => Backlight Down            
             is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:4, group:0, group mask:empty
             reporting: default
         4: Backlight Up              , default: Backlight Up                => Backlight Up              
             is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:5, group:0, group mask:empty
             reporting: default
         5: Voice Dictation           , default: Voice Dictation             => Voice Dictation           
             is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:6, group:0, group mask:empty
             reporting: default
         6: Open Emoji Panel          , default: Open Emoji Panel            => Open Emoji Panel          
             is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:7, group:0, group mask:empty
             reporting: default
         7: Snipping Tool             , default: Snipping Tool               => Snipping Tool             
             is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:8, group:0, group mask:empty
             reporting: default
         8: Mute Microphone           , default: Mute Microphone             => Mute Microphone           
             is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:9, group:0, group mask:empty
             reporting: default
         9: MultiPlatform Search      , default: Multiplatform Search        => Multiplatform Search      
             is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:10, group:0, group mask:empty
             reporting: default
        10: unknown:0141              , default: unknown:00FD                => unknown:00FD              
             is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:11, group:0, group mask:empty
             reporting: default
        11: Mute Fn                   , default: Mute                        => Mute                      
             is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:12, group:0, group mask:empty
             reporting: default
        12: Volume Down Fn            , default: Volume Down                 => Volume Down               
             nonstandard, reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty
             reporting: default
        13: Volume Up Fn              , default: Volume Up                   => Volume Up                 
             nonstandard, reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty
             reporting: default
        14: Grave Accent              , default: Grave Accent                => Grave Accent              
             reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty
             reporting: default
        15: Tab Key                   , default: Standard Tab Key            => Standard Tab Key          
             reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty
             reporting: default
        16: Caps Lock                 , default: Caps Lock                   => Caps Lock                 
             reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty
             reporting: default
        17: Left Shift                , default: Left Shift                  => Left Shift                
             reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty
             reporting: default
        18: Left Control              , default: Left Control                => Left Control              
             reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty
             reporting: default
        19: Left Option/Start         , default: Left Option/Start           => Left Option/Start         
             reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty
             reporting: default
        20: Left Command/Alt          , default: Left Command/Alt            => Left Command/Alt          
             reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty
             reporting: default
        21: Right Command/Alt         , default: Right Command/Alt           => Right Command/Alt         
             reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty
             reporting: default
        22: unknown:013C              , default: Right Option/Start/2        => Right Option/Start/2      
             reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty
             reporting: default
        23: Right Shift               , default: Right Shift                 => Right Shift               
             reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty
             reporting: default
        24: Backslash                 , default: Backslash                   => Backslash                 
             reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty
             reporting: default
        25: Delete                    , default: Delete                      => Delete                    
             reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty
             reporting: default
        26: Home                      , default: Home                        => Home                      
             reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty
             reporting: default
        27: End                       , default: End                         => End                       
             reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty
             reporting: default
        28: Page Up                   , default: Page Up                     => Page Up                   
             reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty
             reporting: default
        29: Page Down                 , default: Page Down                   => Page Down                 
             reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty
             reporting: default
        30: F Lock                    , default: Do Nothing One              => Do Nothing One            
             is FN, analytics key events, pos:0, group:0, group mask:empty
             reporting: default
        31: FN Key                    , default: Do Nothing One              => Do Nothing One            
             nonstandard, analytics key events, pos:0, group:0, group mask:empty
             reporting: default
     Battery: 100%, discharging.

Unifying Receiver
  Device path  : /dev/hidraw7
  USB id       : 046d:C52B
  Serial       : 444DA8DF
    Firmware   : 12.10.B0032
    Bootloader : 02.15
    Other      : AA.AA
  Has 1 paired device(s) out of a maximum of 6.
  Notifications: wireless, software present (0x000900)
  Device activity counters: 3=227

  3: MX Anywhere 3
     Device path  : /dev/hidraw8
     WPID         : 4090
     Codename     : MX Anywhere 3
     Kind         : mouse
     Protocol     : HID++ 4.5
     Polling rate : 8 ms (125Hz)
     Serial number: 96A9C5DF
     Model ID:      B02540900000
     Unit ID:       96A9C5DF
        Bootloader: BL1 13.01.B0015
          Firmware: MPM 24.01.B0015
             Other: 
     The power switch is located on the base.
     Supports 32 HID++ 2.0 features:
         0: ROOT                   {0000}   
         1: FEATURE SET            {0001}   
         2: DEVICE FW VERSION      {0003}   
            Firmware: Bootloader BL1 13.01.B0015 0000AACD8D78
            Firmware: Firmware MPM 24.01.B0015 4090AACD8D78
            Firmware: Other   
            Unit ID: 96A9C5DF  Model ID: B02540900000  Transport IDs: {'btleid': 'B025', 'wpid': '4090'}
         3: DEVICE NAME            {0005}   
            Name: MX Anywhere 3
            Kind: mouse
         4: WIRELESS DEVICE STATUS {1D4B}   
         5: RESET                  {0020}   
         6: CRYPTO ID              {0021}   
         7: DEVICE FRIENDLY NAME   {0007}   
            Friendly Name: MX Anywhere 3
         8: UNIFIED BATTERY        {1004}   
         9: REPROG CONTROLS V4     {1B04}   
            DPI Sliding Adjustment (saved): Off
            DPI Sliding Adjustment        : Off
            Mouse Gestures (saved): Off
            Mouse Gestures        : Off
            Key/Button Actions (saved): {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Smart Shift:Smart Shift}
            Key/Button Actions        : {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Smart Shift:Smart Shift}
            Key/Button Diversion (saved): {Middle Button:Regular, Back Button:Regular, Forward Button:Regular, Smart Shift:Regular}
            Key/Button Diversion        : {Middle Button:Regular, Back Button:Regular, Forward Button:Regular, Smart Shift:Regular}
        10: CHANGE HOST            {1814}   
            Change Host        : 2:HP-t620plus
        11: XY STATS               {2250}   
        12: ADJUSTABLE DPI         {2201}   
            Sensitivity (DPI) (saved): 1000
            Sensitivity (DPI)        : 1000
        13: SMART SHIFT ENHANCED   {2111}   
            Scroll Wheel Rachet (saved): 10
            Scroll Wheel Rachet        : 10
        14: HIRES WHEEL            {2121}   
            Multiplier: 15
            Has invert: Normal wheel motion
            Has ratchet switch: Normal wheel mode
            Low resolution mode
            HID notification
            Scroll Wheel Direction (saved): False
            Scroll Wheel Direction        : False
            Scroll Wheel Resolution (saved): False
            Scroll Wheel Resolution        : False
            Scroll Wheel Diversion (saved): False
            Scroll Wheel Diversion        : False
        15: WHEEL STATS            {2251}   
        16: DFUCONTROL SIGNED      {00C2}   
        17: DEVICE RESET           {1802}   internal, hidden
        18: unknown:1803           {1803}   internal, hidden
        19: CONFIG DEVICE PROPS    {1806}   internal, hidden
        20: unknown:1812           {1812}   internal, hidden
        21: OOBSTATE               {1805}   internal, hidden
        22: unknown:1830           {1830}   internal, hidden
        23: unknown:1890           {1890}   internal, hidden
        24: unknown:1891           {1891}   internal, hidden
        25: unknown:18A1           {18A1}   internal, hidden
        26: unknown:1E00           {1E00}   hidden
        27: unknown:1EB0           {1EB0}   internal, hidden
        28: unknown:1861           {1861}   internal, hidden
        29: unknown:9001           {9001}   internal, hidden
        30: unknown:1E22           {1E22}   internal, hidden
        31: unknown:9205           {9205}   internal, hidden
     Has 7 reprogrammable keys:
         0: Left Button               , default: Left Click                  => Left Click                
             mse, analytics key events, pos:0, group:1, group mask:g1
             reporting: default
         1: Right Button              , default: Right Click                 => Right Click               
             mse, analytics key events, pos:0, group:1, group mask:g1
             reporting: default
         2: Middle Button             , default: Mouse Middle Button         => Mouse Middle Button       
             mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2
             reporting: default
         3: Back Button               , default: Mouse Back Button           => Mouse Back Button         
             mse, reprogrammable, divertable, raw XY, analytics key events, unknown:000800, pos:0, group:2, group mask:g1,g2
             reporting: default
         4: Forward Button            , default: Mouse Forward Button        => Mouse Forward Button      
             mse, reprogrammable, divertable, raw XY, analytics key events, unknown:000800, pos:0, group:2, group mask:g1,g2
             reporting: default
         5: Smart Shift               , default: Smart Shift                 => Smart Shift               
             mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2
             reporting: default
         6: Virtual Gesture Button    , default: Virtual Gesture Button      => Virtual Gesture Button    
             divertable, virtual, raw XY, force raw XY, pos:0, group:3, group mask:empty
             reporting: default
     Battery: 95%, discharging.


```
</details>

- Contents of `~/.config/solaar/config.yaml` (or `~/.config/solaar/config.json` if `~/.config/solaar/config.yaml` not present):

<details>

```
- 1.1.4
- {_NAME: Anywhere Mouse MX, _serial: 8EAE743F, _wpid: '1017', side-scroll: true, smooth-scroll: true}
- {_NAME: Wireless Mouse M325, _serial: 444DA8DF, _wpid: 400A}
- _NAME: MX Mechanical Mini
  _absent: [hi-res-scroll, lowres-scroll-mode, hires-smooth-invert, hires-smooth-resolution, hires-scroll-mode, smart-shift, thumb-scroll-invert, thumb-scroll-mode,
    onboard_profiles, report_rate, pointer_speed, dpi, dpi-sliding, speed-change, mouse-gestures, backlight-timed, reprogrammable-keys, persistent-remappable-keys,
    crown-smooth, divert-crown, divert-gkeys, m-key-leds, mr-key-led, gesture2-gestures, gesture2-divert, gesture2-params, sidetone, equalizer]
  _modelId: B36700000000
  _sensitive: {disable-keyboard-keys: true, divert-keys: true, fn-swap: true, multiplatform: false}
  _serial: 915FAE83
  _unitId: 915FAE83
  _wpid: B367
  backlight: true
  change-host: null
  disable-keyboard-keys: {1: true, 4: false, 8: false, 16: false}
  divert-keys: {212: 0, 226: 0, 227: 0, 231: 0, 232: 0, 233: 0, 259: 0, 264: 0, 266: 0, 267: 0, 268: 0, 269: 1, 270: 0, 271: 0, 272: 0, 273: 0, 274: 0,
    277: 0, 279: 0, 280: 0, 281: 0, 282: 0, 283: 0, 284: 0, 286: 0, 316: 0, 321: 0}
  fn-swap: false
  multiplatform: 0
- _NAME: MX Anywhere 3
  _absent: [hi-res-scroll, lowres-scroll-mode, thumb-scroll-invert, thumb-scroll-mode, onboard_profiles, report_rate, pointer_speed, speed-change, backlight,
    backlight-timed, fn-swap, persistent-remappable-keys, disable-keyboard-keys, crown-smooth, divert-crown, divert-gkeys, m-key-leds, mr-key-led, multiplatform,
    gesture2-gestures, gesture2-divert, gesture2-params, sidetone, equalizer]
  _modelId: B02540900000
  _serial: 96A9C5DF
  _unitId: 96A9C5DF
  _wpid: '4090'
  change-host: null
  divert-keys: {82: 0, 83: 0, 86: 0, 196: 0}
  dpi: 1000
  dpi-sliding: 0
  hires-scroll-mode: false
  hires-smooth-invert: false
  hires-smooth-resolution: false
  mouse-gestures: 0
  reprogrammable-keys: {80: 80, 81: 81, 82: 82, 83: 83, 86: 86, 196: 196}
  smart-shift: 10

```
</details>


- Errors or warrnings from Solaar:
<!-- Under normal operation Solaar keeps a log of warning and error messages in ~/.tmp
while it is running as a file starting with 'Solaar'.
If this file is not available or does not have useful information you can
run Solaar as `solaar -dd`, after killing any running Solaar processes to
have Solaar log informational, warning, and error messages to stdout. -->


**Describe the bug**
I've been trying to make the CAPS LOCK on my Logitech MX Mechanical Mini work as L-CONTROL. 

**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error

**Screenshots**
If applicable, add screenshots to help explain your problem. Setting the rule to "Click" gets what I would expect. It outputs a Control_L "tap". So, I've been trying to use the "Depress" and "Release settings and having the Rules watch for the Caps Lock release before releasing the Control-L key. At first blush this seems to work because it allows me to send Ctrl-Key sequences like "Ctrl-T" to the browser to open new browser tabs. However, after I use CAPS LOCK as CONTROL once, the system never seems to see or receive the CONTROL_L release signal. The system behaves as if I am continuing to hold down the CONTROL_L key. Is there something I am doing wrong with the rules or does it not yet work with the modifier keys. I assume I am doing something wrong but I just can't suss it out.

**Additional context**

FIRST TRY AT RULES YAML:
%YAML 1.3
---
- Rule:
  - Key: [Caps Lock, pressed]
  - KeyPress:
    - Control_L
    - depress
  - Key: [Caps Lock, released]
  - KeyPress:
    - Control_L
    - release
...

SECOND TRY AT RULES YAML:
%YAML 1.3
---
- Setting: [915FAE83, divert-keys, 269, 1]
- Rule:
  - Key: [Caps Lock, pressed]
  - KeyPress:
    - Control_L
    - depress
  - Key: [Caps Lock, released]
  - KeyPress:
    - Control_L
    - release
...



Discussion:
The Setting condition isn't going to help as the only way for rules to be triggered by pressing or releasing the Control_L key is when the key is diverted.  There should be nothing special about modifier keys so it appears that somehow the key release is not happening.


@jameskeim The problem is that Solaar was checking to see if a modifier key was already depressed when executing a KeyPress action and didn't then simulate the key.  This worked great when KeyPress actions were only clicks as it wouldn't release modifier keys that were actually down on the keyboard.  However, as you found out, this doesn't work right for explicit depress and release KeyPress actions.  I modified the code so that depress and (especially) release KeyPress actions always simulate the depress or release, even for modifier keys that are already down.
@jameskeim PR #1765 should fix your problem.   Please try it out and report whether it solves your problem.


To clone and use Solar from its GitHub repository
```
git clone https://github.com/pwr-Solaar/Solaar.git
cd Solaar
```
Run Solaar as bin/solaar from this directory.


To run PR #1765, first clone Solaar if you have not already done so and cd to the clone directory.  The first time you download the pull request, fetch it into a new branch and checkout that branch, as in:
```
git fetch origin pull/1765/head:pull_1765
git checkout pull_1765
```
To download a new version of the pull request, fetch it and then set your pull branch to the new fetch, as in:
```
git checkout pull_1765
git fetch origin pull/1765/head
git reset --hard FETCH_HEAD
```


Resolving patch:
diff --git a/lib/logitech_receiver/diversion.py b/lib/logitech_receiver/diversion.py
--- a/lib/logitech_receiver/diversion.py
+++ b/lib/logitech_receiver/diversion.py
@@ -925,14 +925,14 @@ def keyDown(self, keysyms, modifiers):
             (keycode, level) = self.keysym_to_keycode(k, modifiers)
             if keycode is None:
                 _log.warn('rule KeyPress key symbol not currently available %s', self)
-            elif self.needed(keycode, modifiers):
+            elif self.action != self.CLICK or self.needed(keycode, modifiers):  # only check needed when clicking
                 self.mods(level, modifiers, _KEY_PRESS)
                 simulate_key(keycode, _KEY_PRESS)
 
     def keyUp(self, keysyms, modifiers):
         for k in keysyms:
             (keycode, level) = self.keysym_to_keycode(k, modifiers)
-            if keycode and self.needed(keycode, modifiers):
+            if keycode and (self.action != self.CLICK or self.needed(keycode, modifiers)):  # only check needed when clicking
                 simulate_key(keycode, _KEY_RELEASE)
                 self.mods(level, modifiers, _KEY_RELEASE)
 


Test patch: