Skip to content
GitLab
Projects Groups Snippets
  • /
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Sign in / Register
  • S Solaar
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
  • Issues 10
    • Issues 10
    • List
    • Boards
    • Service Desk
    • Milestones
  • Merge requests 2
    • Merge requests 2
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
  • Deployments
    • Deployments
    • Environments
    • Releases
  • Packages and registries
    • Packages and registries
    • Package Registry
    • Infrastructure Registry
  • Monitor
    • Monitor
    • Incidents
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Repository
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • pwr-Solaar
  • Solaar
  • Issues
  • #815
Closed
Open
Issue created Jun 17, 2020 by Administrator@rootContributor

Reprogrammable keys do not work on K400+

Created by: viniciusbm

Information

  • Solaar version: 1.0.2-18-g59265962
  • Distribution: Linux Mint
  • Kernel version: 4.15.0-106
  • Output of solaar show:
Unifying Receiver
  Device path  : /dev/hidraw3
  USB id       : 046d:c52b
  Serial       : BE57977A
    Firmware   : 24.11.B0036
    Bootloader : 02.09
    Other      : AA.AC
  Has 1 paired device(s) out of a maximum of 6.
  Notifications: wireless, software present (0x000900)
  Device activity counters: 1=193

  1: Wireless Touch Keyboard K400 Plus
     Codename     : K400 Plus
     Kind         : keyboard
     Wireless PID : 404D
     Protocol     : HID++ 4.1
     Polling rate : 8 ms (125Hz)
     Serial number: 6DC5C81C
        Bootloader: BOT 22.02.B0002
          Firmware: RQK 63.02.B0016
             Other:
     The power switch is located on the top edge.
     Supports 24 HID++ 2.0 features:
         0: ROOT                   {0000}
         1: FEATURE SET            {0001}
         2: DEVICE FW VERSION      {0003}
         3: DEVICE NAME            {0005}
         4: WIRELESS DEVICE STATUS {1D4B}
         5: RESET                  {0020}
         6: BATTERY STATUS         {1000}
         7: REPROG CONTROLS V4     {1B04}
            Actions: {'84': NamedInt(84, 'Mouse Back Button'), '34': NamedInt(34, 'HomePage'), '8': NamedInt(8, 'Application Switcher'), '79': NamedInt(79, 'RightClick'), '62': NamedInt(62, 'Search Files'), '110': NamedInt(110, 'Show Desktop'), '145': NamedInt(145, 'Maximize Window'), '149': NamedInt(149, 'Switch Screen'), '40': NamedInt(40, 'Music'), '64': NamedInt(64, 'Sleep')}
         8: SWAP BUTTON CANCEL     {2005}
         9: NEW FN INVERSION       {40A2}
            Swap Fx function: True
        10: ENCRYPTION             {4100}
        11: KEYBOARD DISABLE       {4521}
        12: TOUCHPAD RAW XY        {6100}
        13: GESTURE 2              {6501}
        14: DFUCONTROL UNSIGNED    {00C1}
        15: unknown:1811           {1811}   internal, hidden
        16: unknown:1830           {1830}   internal, hidden
        17: unknown:1890           {1890}   internal, hidden
        18: unknown:1DF3           {1DF3}   internal, hidden
        19: unknown:1E00           {1E00}   hidden
        20: unknown:1EB0           {1EB0}   internal, hidden
        21: unknown:1861           {1861}   internal, hidden
        22: unknown:18B0           {18B0}   internal, hidden
        23: unknown:1F11           {1F11}   internal, hidden
     Has 20 reprogrammable keys:
         0: unknown:00B8              , default: LeftClick                   => unknown:00B8
             mse, divertable, pos:0, group:0, gmask:0
         1: Back                      , default: Mouse Back Button           => Back
             is FN, FN sensitive, reprogrammable, divertable, pos:1, group:0, gmask:0
         2: MY HOME                   , default: HomePage                    => MY HOME
             is FN, FN sensitive, reprogrammable, divertable, pos:2, group:0, gmask:0
         3: Application Switcher      , default: Application Switcher        => Application Switcher
             is FN, FN sensitive, reprogrammable, divertable, pos:3, group:0, gmask:0
         4: CONTEXTUAL MENU           , default: RightClick                  => CONTEXTUAL MENU
             is FN, FN sensitive, reprogrammable, divertable, pos:4, group:0, gmask:0
         5: Search                    , default: Search Files                => Search
             is FN, FN sensitive, reprogrammable, divertable, pos:5, group:0, gmask:0
         6: Show Desktop              , default: Show Desktop                => Show Desktop
             is FN, FN sensitive, reprogrammable, divertable, pos:6, group:0, gmask:0
         7: Maximize Window           , default: Maximize Window             => Maximize Window
             is FN, FN sensitive, reprogrammable, divertable, pos:7, group:0, gmask:0
         8: Switch Screen             , default: Switch Screen               => Switch Screen
             is FN, FN sensitive, reprogrammable, divertable, pos:8, group:0, gmask:0
         9: MEDIA PLAYER              , default: Music                       => MEDIA PLAYER
             is FN, FN sensitive, reprogrammable, divertable, pos:9, group:0, gmask:0
        10: Previous                  , default: Previous                    => Previous
             is FN, FN sensitive, divertable, pos:10, group:0, gmask:0
        11: Play/Pause                , default: Play/Pause                  => Play/Pause
             is FN, FN sensitive, divertable, pos:11, group:0, gmask:0
        12: Next                      , default: Next                        => Next
             is FN, FN sensitive, divertable, pos:12, group:0, gmask:0
        13: unknown:00B7              , default: ShowUI                      => unknown:00B7
             divertable, pos:0, group:0, gmask:0
        14: LEFT CLICK                , default: LeftClick                   => LEFT CLICK
             mse, pos:0, group:0, gmask:0
        15: RIGHT CLICK               , default: RightClick                  => RIGHT CLICK
             mse, pos:0, group:0, gmask:0
        16: Mute                      , default: Mute                        => Mute
             nonstandard, divertable, pos:0, group:0, gmask:0
        17: Volume Up                 , default: Volume Up                   => Volume Up
             nonstandard, divertable, pos:0, group:0, gmask:0
        18: Volume Down               , default: Volume Down                 => Volume Down
             nonstandard, divertable, pos:0, group:0, gmask:0
        19: SLEEP                     , default: Sleep                       => SLEEP
             reprogrammable, divertable, pos:0, group:0, gmask:0
     Battery: 90%, discharging, next level 50%.

Describe the bug This is a follow-up to #810. The K400+ keyboard supports REPROG CONTROLS V4, but the current implementation doesn't work. The reprogrammable keys and their options are displayed, but only the default action is executed regardless of the chosen option.

To Reproduce Choose a reprogrammable key and change its action. Press the key. Instead of the chosen action, the default one is performed.

Additional context

From #810:

Please remove any "reprogrammable-keys" entries from ~/.config/solaar/config.json Then run bin/solaar -dd, try a key change, and report any errors and tracebacks encountered.

K400plus_reprogramming_test.txt

I pressed the F1/back key once on a browser, tried to change its action and pressed it again on the browser. Only the default action was executed.

Then report the contents of ~/.config/solaar/config.json

{
  "404D:6DC5C81C": {
    "_name": "Wireless Touch Keyboard K400 Plus",
    "fn-swap": true,
    "reprogrammable-keys": {
      "110": 110,
      "145": 145,
      "149": 149,
      "34": 34,
      "40": 40,
      "62": 62,
      "64": 64,
      "79": 79,
      "8": 8,
      "84": 8
    }
  },
  "_version": "1.0.2"
}

By the way, I've noticed that several keystrokes (multimedia keys and other special buttons) are incorrectly interpreted by Solaar as notifications for DEVICE NAME, for example:

WARNING [ReceiverListener:hidraw3] logitech_receiver.notifications: <PairedDevice(1,404D,K400 Plus,6DC5C81C)>: unrecognized Notification(1,03,00,0000000000003D00000000) for feature DEVICE NAME (index 03)

In the following, in both read and written bytes, I'll omit the trailing zeroes in the data, the prefix (10/11), the device number (e.g. 01) and the feature index (e.g. 07; the one that refers to 1B 04 = REPROG CONTROLS V4 on the device), and I'll use s to represent the software ID (4-bit, apparently chosen arbitrarily).

The way Solaar tries to reprogram a key XX XX to an action YY YY (3s XX XX 00 YY YY) is ignored with no errors, and this is probably the reason for the unexpected group 0 and gmask 0: it expects another pattern.

By using Wireshark and observing what happens when Logitech Options is running on a Windows VM, I figured this out:

  • When we reprogram a key, LO just sends 3s XX XX 03 regardless of the chosen action. After that, the key no longer sends its normal value after being pressed; instead, it just sends 00 XX XX. At this point, if the Logitech processes are killed (or if the keyboard is transferred back to my Linux host), the key stops working, possibly because the data isn't recognised by the regular drivers and the keystrokes are actually handled by Logitech software. This does not persist after the device is powered off, and I found no other way of reverting this [EDIT: it can be reverted by sending the same bytes replacing 03 with 02].
  • When the key is released, it sends 00 00 00.
  • If reprogrammed keys are simultaneously pressed, they're added to the end: 00 XX XX ZZ ZZ WW WW... (real example: 00 00 54 00 08 00 22 00 3E), which could be used to create combinations of those keys. This isn't used by Logitech Options.
  • I've tested that all of this works with all divertable keys, even the non-programmable ones that LO doesn't allow to remap (including the yellow button). Therefore, it is possible to read that and execute any action such as sending arbitrary key combinations or running a program.

Just as a proof of concept, I temporarily added the following code to _process_feature_notification():

    if feature == _F.REPROG_CONTROLS_V4:
        if n.address == 0x00 and len(n.data) == 16:
            if _log.isEnabledFor(_INFO):
                _log.info("%s: reprogrammable key: %s", device, n)
                for i in range(8):
                    k = (n.data[2*i] << 8) | n.data[2*i + 1]  # two bytes
                    if k != 0:
                        _log.debug('JUST A TEST: pressed the key 0x%04X' % k)
        else:
            _log.warn("%s: unknown REPROGRAMMABLE KEYS V4 %s", device, n)
        return True

Now, after I manually send 0s XX XX 03 once for some of the divertable keys, they no longer execute the default actions, and their codes are logged whenever they're pressed.

To implement this feature, Solaar would have to re-implement all of the actions, just like Logitech Option does (it sends 0s XX XX 03 to all divertable keys on startup). To do this, Solaar would have to simulate keystrokes and mouse clicks. I'm not sure about the best way to do it from Python without depending on the Linux distribution or the destkop environment. Adding an external library as a dependency might help, but I'm not sure if it's worth it.

For now, regardless of whether this will be implemented, I think those "0" settings should be hidden, so that the user doesn't see options that produce no effect.

Assignee
Assign to
Time tracking